ΠΡΠ° Π·Π°ΠΌΠ΅ΡΠΊΠ° Π±ΡΠ»Π° Π½Π°ΠΏΠΈΡΠ°Π½Π° Π² 2014-ΠΌ Π³ΠΎΠ΄Ρ, Π½ΠΎ Ρ ΠΊΠ°ΠΊ ΡΠ°Π· ΠΏΠΎΠΏΠ°Π» ΠΏΠΎΠ΄ ΡΠ΅ΠΏΡΠ΅ΡΡΠΈΠΈ Π½Π° Ρ Π°Π±ΡΠ΅ ΠΈ ΠΎΠ½Π° Π½Π΅ ΡΠ²ΠΈΠ΄Π΅Π»Π° ΡΠ²Π΅Ρ. ΠΠ° Π²ΡΠ΅ΠΌΡ Π±Π°Π½Π° Ρ ΠΏΡΠΎ Π½Π΅Ρ Π·Π°Π±ΡΠ», Π° ΡΠ΅ΠΉΡΠ°Ρ Π½Π°ΡΡΠ» Π² ΡΠ΅ΡΠ½ΠΎΠ²ΠΈΠΊΠ°Ρ . ΠΡΠΌΠ°Π» Π±ΡΠ»ΠΎ ΡΠ΄Π°Π»ΠΈΡΡ, Π½ΠΎ Π°Π²ΠΎΡΡ ΠΊΠΎΠΌΡ ΠΏΡΠΈΠ³ΠΎΠ΄ΠΈΡΡΡ.
Π ΠΎΠ±ΡΠ΅ΠΌ, Π½Π΅Π±ΠΎΠ»ΡΡΠΎΠ΅ ΠΏΡΡΠ½ΠΈΡΠ½ΠΎΠ΅ Π°Π΄ΠΌΠΈΠ½ΡΠΊΠΎΠ΅ ΡΡΠΈΠ²ΠΎ Π½Π° ΡΠ΅ΠΌΡ ΠΏΠΎΠΈΡΠΊΠ° Β«Π²ΠΊΠ»ΡΡΠ΅Π½Π½ΠΎΠ³ΠΎΒ» LD_PRELOAD.
1. ΠΠ΅Π±ΠΎΠ»ΡΡΠΎΠ΅ ΠΎΡΡΡΡΠΏΠ»Π΅Π½ΠΈΠ΅ Π΄Π»Ρ ΡΠ΅Ρ , ΠΊΡΠΎ Π½Π΅ Π·Π½Π°ΠΊΠΎΠΌ Ρ Π·Π°ΠΌΠ΅ΡΠ΅Π½ΠΈΠ΅ΠΌ ΡΡΠ½ΠΊΡΠΈΠΉ
ΠΡΡΠ°Π»ΡΠ½ΡΠΌ ΠΌΠΎΠΆΠ½ΠΎ ΡΡΠ°Π·Ρ ΠΏΠ΅ΡΠ΅Ρ ΠΎΠ΄ΠΈΡΡ ΠΊ ΠΏ.2.
ΠΠ°ΡΠ½ΡΠΌ Ρ ΠΊΠ»Π°ΡΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΠΏΡΠΈΠΌΠ΅ΡΠ°:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand (time(NULL));
for(int i=0; i<5; i++){
printf ("%dn", rand()%100);
}
}
ΠΠΎΠΌΠΏΠΈΠ»ΠΈΡΡΠ΅ΠΌ Π±Π΅Π· ΠΊΠ°ΠΊΠΈΡ -Π»ΠΈΠ±ΠΎ ΡΠ»Π°Π³ΠΎΠ²:
$ gcc ./ld_rand.c -o ld_rand
Π, ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌΠΎ, ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ 5 ΡΠ»ΡΡΠ°ΠΉΠ½ΡΡ ΡΠΈΡΠ΅Π» ΠΌΠ΅Π½ΡΡΠ΅ 100:
$ ./ld_rand
53
93
48
57
20
ΠΠΎ ΠΏΡΠ΅Π΄ΡΡΠ°Π²ΠΈΠΌ, ΡΡΠΎ Ρ Π½Π°Ρ Π½Π΅Ρ ΠΈΡΡ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ, Π° ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡΡ Π½ΡΠΆΠ½ΠΎ.
Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΡΠ²ΠΎΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ Ρ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΠΌ ΠΏΡΠΎΡΠΎΡΠΈΠΏΠΎΠΌ ΡΡΠ½ΠΊΡΠΈΠΈ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ:
int rand(){
return 42;
}
$ gcc -shared -fPIC ./o_rand.c -o ld_rand.so
Π ΡΠ΅ΠΏΠ΅ΡΡ Π½Π°Ρ ΡΠ»ΡΡΠ°ΠΉΠ½ΡΠΉ Π²ΡΠ±ΠΎΡ Π²ΠΏΠΎΠ»Π½Π΅ ΠΏΡΠ΅Π΄ΡΠΊΠ°Π·ΡΠ΅ΠΌ:
# LD_PRELOAD=$PWD/ld_rand.so ./ld_rand
42
42
42
42
42
ΠΡΠΎΡ ΡΡΡΠΊ Π²ΡΠ³Π»ΡΠ΄ΠΈΡ Π΅ΡΡ Π±ΠΎΠ»Π΅Π΅ Π²ΠΏΠ΅ΡΠ°ΡΠ»ΡΡΡΠΈΠΌ, Π΅ΡΠ»ΠΈ ΠΌΡ ΡΠ½Π°ΡΠ°Π»Π° ΡΠΊΡΠΏΠΎΡΡΠΈΡΡΠ΅ΠΌ Π½Π°ΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ ΡΠ΅ΡΠ΅Π·
$ export LD_PRELOAD=$PWD/ld_rand.so
ΠΈΠ»ΠΈ ΠΏΡΠ΅Π΄Π²Π°ΡΠΈΡΠ΅Π»ΡΠ½ΠΎ Π²ΡΠΏΠΎΠ»Π½ΠΈΠΌ
# echo "$PWD/ld_rand.so" > /etc/ld.so.preload
Π° Π·Π°ΡΠ΅ΠΌ Π·Π°ΠΏΡΡΡΠΈΠΌ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ Π² ΠΎΠ±ΡΡΠ½ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅. ΠΡ Π½Π΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠ»ΠΈ Π½ΠΈ ΡΡΡΠΎΡΠΊΠΈ Π² ΠΊΠΎΠ΄Π΅ ΡΠ°ΠΌΠΎΠΉ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ, Π½ΠΎ Π΅Ρ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ ΡΠ΅ΠΏΠ΅ΡΡ Π·Π°Π²ΠΈΡΠΈΡ ΠΎΡ ΠΊΡΠΎΡΠ΅ΡΠ½ΠΎΠΉ ΡΡΠ½ΠΊΡΠΈΠΈ Π² Π½Π°ΡΠ΅ΠΉ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅. ΠΠΎΠ»Π΅Π΅ ΡΠΎΠ³ΠΎ, Π½Π° ΠΌΠΎΠΌΠ΅Π½Ρ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ ΡΠ°Π»ΡΡΠΈΠ²ΡΠΉ rand Π΄Π°ΠΆΠ΅ Π½Π΅ ΡΡΡΠ΅ΡΡΠ²ΠΎΠ²Π°Π».
Π§ΡΠΎ ΠΆΠ΅ Π·Π°ΡΡΠ°Π²ΠΈΠ»ΠΎ Π½Π°ΡΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΏΠΎΠ΄Π΄Π΅Π»ΡΠ½ΡΠΉ rand? Π Π°Π·Π±Π΅ΡΠ΅ΠΌ ΠΏΠΎ ΡΠ°Π³Π°ΠΌ.
ΠΠΎΠ³Π΄Π° ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π·Π°ΠΏΡΡΠΊΠ°Π΅ΡΡΡ, Π·Π°Π³ΡΡΠΆΠ°ΡΡΡΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½Π½ΡΠ΅ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠ°Ρ ΡΡΠ½ΠΊΡΠΈΠΈ Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΡΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ΅. ΠΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ ΠΈΡ
ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ ldd:
# ldd ./ld_rand
linux-vdso.so.1 (0x00007ffc8b1f3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe3da8af000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe3daa7e000)
ΠΡΠΎΡ ΡΠΏΠΈΡΠΎΠΊ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΡΠ°Π·Π»ΠΈΡΠ΅Π½ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ Π²Π΅ΡΡΠΈΠΈ OS, Π½ΠΎ ΡΠ°ΠΌ ΠΎΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±ΡΡΡ ΡΠ°ΠΉΠ» libc.so. ΠΠΌΠ΅Π½Π½ΠΎ ΡΡΠ° Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΎΠ±Π΅ΡΠΏΠ΅ΡΠΈΠ²Π°Π΅Ρ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠ΅ Π²ΡΠ·ΠΎΠ²Ρ ΠΈ ΠΎΡΠ½ΠΎΠ²Π½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ, ΡΠ°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ open, malloc, printf ΠΈ Ρ. Π΄. ΠΠ°Ρ rand ΡΠ°ΠΊΠΆΠ΅ Π²Ρ ΠΎΠ΄ΠΈΡ Π² ΠΈΡ ΡΠΈΡΠ»ΠΎ. Π£Π±Π΅Π΄ΠΈΠΌΡΡ Π² ΡΡΠΎΠΌ:
# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep " rand$"
000000000003aef0 T rand
ΠΠΎΡΠΌΠΎΡΡΠΈΠΌ, Π½Π΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡΡΡ Π»ΠΈ Π½Π°Π±ΠΎΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊ ΠΏΡΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ LD_PRELOAD
# LD_PRELOAD=$PWD/ld_rand.so ldd ./ld_rand
linux-vdso.so.1 (0x00007ffea52ae000)
/scripts/c/ldpreload/ld_rand.so (0x00007f690d3f9000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f690d230000)
/lib64/ld-linux-x86-64.so.2 (0x00007f690d405000)
ΠΠΊΠ°Π·ΡΠ²Π°Π΅ΡΡΡ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½Π½Π°Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½Π°Ρ LD_PRELOAD Π·Π°ΡΡΠ°Π²Π»ΡΠ΅Ρ Π·Π°Π³ΡΡΠ·ΠΈΡΡΡΡ Π½Π°ΡΡ ld_rand.so Π΄Π°ΠΆΠ΅, Π½Π΅ ΡΠΌΠΎΡΡΡ Π½Π° ΡΠΎ, ΡΡΠΎ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ° ΡΠ°ΠΌΠ° Π΅Ρ Π½Π΅ ΡΡΠ΅Π±ΡΠ΅Ρ. Π, ΡΠ°ΠΊ ΠΊΠ°ΠΊ Π½Π°ΡΠ° ΡΡΠ½ΠΊΡΠΈΡ Β«randΒ» Π·Π°Π³ΡΡΠΆΠ°Π΅ΡΡΡ ΡΠ°Π½ΡΡΠ΅ ΡΠ΅ΠΌ rand ΠΎΡ libc.so, ΡΠΎ ΠΎΠ½Π° ΠΈ ΠΏΡΠ°Π²ΠΈΡ Π±Π°Π»ΠΎΠΌ.
Ok, Π·Π°ΠΌΠ΅Π½ΠΈΡΡ ΡΠΎΠ΄Π½ΡΡ ΡΡΠ½ΠΊΡΠΈΡ Π½Π°ΠΌ ΡΠ΄Π°Π»ΠΎΡΡ, Π½ΠΎ ΠΊΠ°ΠΊ Π±Ρ ΡΠ΄Π΅Π»Π°ΡΡ ΡΠ°ΠΊ, ΡΡΠΎΠ±Ρ ΠΈ Π΅Ρ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π» ΡΠΎΡ ΡΠ°Π½ΠΈΠ»ΡΡ ΠΈ Π½Π΅ΠΊΠΈΠ΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΡ Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈΡΡ. ΠΠΎΠ΄ΠΈΡΠΈΡΠΈΡΡΠ΅ΠΌ Π½Π°Ρ ΡΠ°Π½Π΄ΠΎΠΌ:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
typedef int (*orig_rand_f_type)(void);
int rand()
{
/* ΠΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌ Π½Π΅ΠΊΠΈΠΉ ΠΊΠΎΠ΄ */
printf("Evil injected coden");
orig_rand_f_type orig_rand;
orig_rand = (orig_rand_f_type)dlsym(RTLD_NEXT,"rand");
return orig_rand();
}
ΠΠ΄Π΅ΡΡ Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ Π½Π°ΡΠ΅ΠΉ Β«Π΄ΠΎΠ±Π°Π²ΠΊΠΈΒ» ΠΌΡ Π»ΠΈΡΡ ΠΏΠ΅ΡΠ°ΡΠ°Π΅ΠΌ ΠΎΠ΄Π½Ρ ΡΡΡΠΎΠΊΡ ΡΠ΅ΠΊΡΡΠ°, ΠΏΠΎΡΠ»Π΅ ΡΠ΅Π³ΠΎ ΡΠΎΠ·Π΄Π°ΡΠΌ ΡΠΊΠ°Π·Π°ΡΠ΅Π»Ρ Π½Π° ΠΈΡΡ ΠΎΠ΄Π½ΡΡ ΡΡΠ½ΠΊΡΠΈΡ rand. ΠΠ»Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ Π°Π΄ΡΠ΅ΡΠ° ΡΡΠΎΠΉ ΡΡΠ½ΠΊΡΠΈΠΈ Π½Π°ΠΌ ΠΏΠΎΡΡΠ΅Π±ΡΠ΅ΡΡΡ dlsym β ΡΡΠΎ ΡΡΠ½ΠΊΡΠΈΡ ΠΈΠ· Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ libdl, ΠΊΠΎΡΠΎΡΠ°Ρ Π½Π°ΠΉΠ΄Π΅Ρ Π½Π°Ρ rand Π² ΡΡΠ΅ΠΊΠ΅ Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΈΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊ. ΠΠΎΡΠ»Π΅ ΡΠ΅Π³ΠΎ ΠΌΡ Π²ΡΠ·ΠΎΠ²Π΅ΠΌ ΡΡΡ ΡΡΠ½ΠΊΡΠΈΡ ΠΈ Π²ΠΎΠ·Π²ΡΠ°ΡΠΈΠΌ Π΅Ρ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅. Π‘ΠΎΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎ, Π½Π°ΠΌ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ «-ldl» ΠΏΡΠΈ ΡΠ±ΠΎΡΠΊΠ΅:
$ gcc -ldl -shared -fPIC ./o_rand_evil.c -o ld_rand_evil.so
$ LD_PRELOAD=$PWD/ld_rand_evil.so ./ld_rand
Evil injected code
66
Evil injected code
28
Evil injected code
93
Evil injected code
93
Evil injected code
95
Π Π½Π°ΡΠ° ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ° ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Β«ΡΠΎΠ΄Π½ΠΎΠΉΒ» rand, ΠΏΡΠ΅Π΄Π²Π°ΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»Π½ΠΈΠ² Π½Π΅ΠΊΠΈΠ΅ Π½Π΅ΠΏΠΎΡΡΠ΅Π±Π½ΡΠ΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΡ.
2. ΠΡΠΊΠΈ ΠΏΠΎΠΈΡΠΊΠ°
ΠΠ½Π°Ρ ΠΎ ΠΏΠΎΡΠ΅Π½ΡΠΈΠ°Π»ΡΠ½ΠΎΠΉ ΡΠ³ΡΠΎΠ·Π΅, ΠΌΡ Ρ ΠΎΡΠΈΠΌ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΡΡ, ΡΡΠΎ preload Π±ΡΠ» Π²ΡΠΏΠΎΠ»Π½Π΅Π½. ΠΠΎΠ½ΡΡΠ½ΠΎ, ΡΡΠΎ Π»ΡΡΡΠΈΠΉ ΡΠΏΠΎΡΠΎΠ± Π΄Π΅ΡΠ΅ΠΊΡΠΈΠ½Π³Π° β Π·Π°ΠΏΠΈΡ Π½ΡΡΡ Π΅Π³ΠΎ Π² ΡΠ΄ΡΠΎ, Π½ΠΎ ΠΌΠ΅Π½Ρ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠΎΠ²Π°Π»ΠΈ ΠΈΠΌΠ΅Π½Π½ΠΎ Π²Π°ΡΠΈΠ°Π½ΡΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΡ Π² ΡΠ·Π΅ΡΡΠΏΡΠΉΡΠ΅.
ΠΠ°Π»Π΅Π΅ ΠΏΠ°ΡΠ°ΠΌΠΈ ΠΏΠΎΠΉΠ΄ΡΡ ΡΠ΅ΡΠ΅Π½ΠΈΡ Π΄Π»Ρ ΠΎΠ±Π½Π°ΡΡΠΆΠ΅Π½ΠΈΡ ΠΈ ΠΈΡ ΠΎΠΏΡΠΎΠ²Π΅ΡΠΆΠ΅Π½ΠΈΠ΅.
2.1. ΠΠ°ΡΠ½ΡΠΌ Ρ ΠΏΡΠΎΡΡΠΎΠ³ΠΎ
ΠΠ°ΠΊ Π³ΠΎΠ²ΠΎΡΠΈΠ»ΠΎΡΡ ΡΠ°Π½Π΅Π΅, ΡΠΊΠ°Π·Π°ΡΡ Π·Π°Π³ΡΡΠΆΠ°Π΅ΠΌΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ ΠΌΠΎΠΆΠ½ΠΎ Ρ ΠΏΠΎΠΌΠΎΡΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΉ LD_PRELOAD ΠΈΠ»ΠΈ ΠΏΡΠΎΠΏΠΈΡΠ°Π² Π΅Ρ Π² ΡΠ°ΠΉΠ»Π΅ /etc/ld.so.preload. Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ Π΄Π²Π° ΠΏΡΠΎΡΡΠ΅ΠΉΡΠΈΡ Π΄Π΅ΡΠ΅ΠΊΡΠΎΡΠ°.
ΠΠ΅ΡΠ²ΡΠΉ β Π΄Π»Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½Π½ΠΎΠΉ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΉ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
char* pGetenv = getenv("LD_PRELOAD");
pGetenv != NULL ?
printf("LD_PRELOAD (getenv) [+]n"):
printf("LD_PRELOAD (getenv) [-]n");
}
ΠΡΠΎΡΠΎΠΉ β Π΄Π»Ρ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ ΠΎΡΠΊΡΡΡΠΈΡ ΡΠ°ΠΉΠ»Π°:
#include <stdio.h>
#include <fcntl.h>
int main()
{
open("/etc/ld.so.preload", O_RDONLY) != -1 ?
printf("LD_PRELOAD (open) [+]n"):
printf("LD_PRELOAD (open) [-]n");
}
ΠΠΎΠ΄Π³ΡΡΠ·ΠΈΠΌ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ:
$ export LD_PRELOAD=$PWD/ld_rand.so
$ echo "$PWD/ld_rand.so" > /etc/ld.so.preload
$ ./detect_base_getenv
LD_PRELOAD (getenv) [+]
$ ./detect_base_open
LD_PRELOAD (open) [+]
ΠΠ΄Π΅ΡΡ ΠΈ Π΄Π°Π»Π΅Π΅ [+] ΡΠΊΠ°Π·ΡΠ²Π°Π΅Ρ Π½Π° ΡΡΠΏΠ΅ΡΠ½ΠΎΠ΅ ΠΎΠ±Π½Π°ΡΡΠΆΠ΅Π½ΠΈΠ΅.
Π‘ΠΎΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎ, [-] ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ ΠΎΠ±Ρ
ΠΎΠ΄ Π΄Π΅ΡΠ΅ΠΊΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ.
ΠΠ°ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΆΠ΅ Π΄Π΅ΠΉΡΡΠ²Π΅Π½Π΅Π½ ΡΠ°ΠΊΠΎΠΉ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΡΠ΅Π»Ρ? Π‘Π½Π°ΡΠ°Π»Π° Π·Π°ΠΉΠΌΡΠΌΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΉ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
char* (*orig_getenv)(const char *) = NULL;
char* getenv(const char *name)
{
if(!orig_getenv) orig_getenv = dlsym(RTLD_NEXT, "getenv");
if(strcmp(name, "LD_PRELOAD") == 0) return NULL;
return orig_getenv(name);
}
$ gcc -shared -fpic -ldl ./ld_undetect_getenv.c -o ./ld_undetect_getenv.so
$ LD_PRELOAD=./ld_undetect_getenv.so ./detect_base_getenv
LD_PRELOAD (getenv) [-]
ΠΠ½Π°Π»ΠΎΠ³ΠΈΡΠ½ΠΎ ΠΈΠ·Π±Π°Π²Π»ΡΠ΅ΠΌΡΡ ΠΈ ΠΎΡ ΠΏΡΠΎΠ²Π΅ΡΠΊΠΈ open:
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <errno.h>
int (*orig_open)(const char*, int oflag) = NULL;
int open(const char *path, int oflag, ...)
{
char real_path[256];
if(!orig_open) orig_open = dlsym(RTLD_NEXT, "open");
realpath(path, real_path);
if(strcmp(real_path, "/etc/ld.so.preload") == 0){
errno = ENOENT;
return -1;
}
return orig_open(path, oflag);
}
$ gcc -shared -fpic -ldl ./ld_undetect_open.c -o ./ld_undetect_open.so
$ LD_PRELOAD=./ld_undetect_open.so ./detect_base_open
LD_PRELOAD (open) [-]
ΠΠ°, Π·Π΄Π΅ΡΡ ΠΌΠΎΠ³ΡΡ Π±ΡΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½Ρ Π΄ΡΡΠ³ΠΈΠ΅ ΡΠΏΠΎΡΠΎΠ±Ρ Π΄ΠΎΡΡΡΠΏΠ° ΠΊ ΡΠ°ΠΉΠ»Ρ, ΡΠ°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ, open64, stat ΠΈ Ρ.Π΄., Π½ΠΎ, ΠΏΠΎ ΡΡΡΠΈ, Π΄Π»Ρ ΠΈΡ ΠΎΠ±ΠΌΠ°Π½Π° Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΡ ΡΠ΅ ΠΆΠ΅ 5-10 ΡΡΡΠΎΠΊ ΠΊΠΎΠ΄Π°.
2.2. ΠΠ²ΠΈΠ³Π°Π΅ΠΌΡΡ Π΄Π°Π»ΡΡΠ΅
ΠΡΡΠ΅ ΠΌΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π»ΠΈ getenv() Π΄Π»Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ Π·Π½Π°ΡΠ΅Π½ΠΈΡ LD_PRELOAD, Π½ΠΎ Π΅ΡΡΡ ΠΆΠ΅ ΠΈ Π±ΠΎΠ»Π΅Π΅ Β«Π½ΠΈΠ·ΠΊΠΎΡΡΠΎΠ²Π½Π΅Π²ΡΠΉΒ» ΡΠΏΠΎΡΠΎΠ± Π΄ΠΎΠ±ΡΠ°ΡΡΡΡ Π΄ΠΎ ENV-ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ . ΠΠ΅ Π±ΡΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ, Π° ΠΎΠ±ΡΠ°ΡΠΈΠΌΡΡ ΠΊ ΠΌΠ°ΡΡΠΈΠ²Ρ **environ, Π² ΠΊΠΎΡΠΎΡΠΎΠΌ Ρ ΡΠ°Π½ΠΈΡΡΡ ΠΊΠΎΠΏΠΈΡ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΡ:
#include <stdio.h>
#include <string.h>
extern char **environ;
int main(int argc, char **argv) {
int i;
char env[] = "LD_PRELOAD";
if (environ != NULL)
for (i = 0; environ[i] != NULL; i++)
{
char * pch;
pch = strstr(environ[i],env);
if(pch != NULL)
{
printf("LD_PRELOAD (**environ) [+]n");
return 0;
}
}
printf("LD_PRELOAD (**environ) [-]n");
return 0;
}
Π’Π°ΠΊ ΠΊΠ°ΠΊ Π·Π΄Π΅ΡΡ ΠΌΡ ΡΠΈΡΠ°Π΅ΠΌ Π΄Π°Π½Π½ΡΠ΅ Π½Π°ΠΏΡΡΠΌΡΡ ΠΈΠ· ΠΏΠ°ΠΌΡΡΠΈ, ΡΠΎ ΡΠ°ΠΊΠΎΠΉ Π²ΡΠ·ΠΎΠ² Π½Π΅Π»ΡΠ·Ρ ΠΏΠ΅ΡΠ΅Ρ Π²Π°ΡΠΈΡΡ, ΠΈ Π½Π°Ρ undetect_getenv ΡΠΆΠ΅ Π½Π΅ ΠΌΠ΅ΡΠ°Π΅Ρ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ Π²ΡΠΎΡΠΆΠ΅Π½ΠΈΠ΅.
$ LD_PRELOAD=./ld_undetect_getenv.so ./detect_environ
LD_PRELOAD (**environ) [+]
ΠΠ°Π·Π°Π»ΠΎΡΡ Π±Ρ Π½Π° ΡΡΠΎΠΌ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ° ΡΠ΅ΡΠ΅Π½Π°? ΠΡΡ Π΅ΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π½Π°ΡΠΈΠ½Π°Π΅ΡΡΡ.
ΠΠΎΡΠ»Π΅ ΡΠΎΠ³ΠΎ ΠΊΠ°ΠΊ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ° Π·Π°ΠΏΡΡΠ΅Π½Π°, Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΉ LD_PRELOAD Π² ΠΏΠ°ΠΌΡΡΠΈ ΡΠΆΠ΅ Π½Π΅ Π½ΡΠΆΠ½ΠΎ Π²Π·Π»ΠΎΠΌΡΠΈΠΊΠ°ΠΌ, ΡΠΎ Π΅ΡΡΡ ΠΌΠΎΠΆΠ½ΠΎ ΡΡΠΈΡΠ°ΡΡ Π΅Ρ ΠΈ ΡΠ΄Π°Π»ΠΈΡΡ Π΄ΠΎ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ ΠΊΠ°ΠΊΠΈΡ -Π»ΠΈΠ±ΠΎ ΠΈΠ½ΡΡΡΡΠΊΡΠΈΠΉ. ΠΠΎΠ½Π΅ΡΠ½ΠΎ, ΠΏΡΠ°Π²ΠΈΡΡ ΠΌΠ°ΡΡΠΈΠ² Π² ΠΏΠ°ΠΌΡΡΠΈ, ΠΊΠ°ΠΊ ΠΌΠΈΠ½ΠΈΠΌΡΠΌ, ΠΏΠ»ΠΎΡ ΠΎΠΉ ΡΡΠΈΠ»Ρ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠΈΡΠΎΠ²Π°Π½ΠΈΡ, Π½ΠΎ ΡΠ°Π·Π²Π΅ ΡΡΠΎ ΠΌΠΎΠΆΠ΅Ρ ΠΎΡΡΠ°Π½ΠΎΠ²ΠΈΡΡ ΡΠΎΠ³ΠΎ, ΠΊΡΠΎ ΠΈ ΡΠ°ΠΊ Π½Π΅ ΠΎΡΠΎΠ±ΠΎ ΠΆΠ΅Π»Π°Π΅Ρ Π½Π°ΠΌ Π΄ΠΎΠ±ΡΠ°?
ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ Π½Π°ΠΌ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡ ΡΠΎΠ·Π΄Π°ΡΡ ΡΠ²ΠΎΡ ΡΠ΅ΠΉΠΊΠΎΠ²ΡΡ ΡΡΠ½ΠΊΡΠΈΡ init(), Π² ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΏΠ΅ΡΠ΅Ρ Π²Π°ΡΠΈΠΌ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½Π½ΡΠΉ LD_PRELOAD ΠΈ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΡ Π΅Ρ Π½Π°ΡΠ΅ΠΌΡ ΠΊΠΎΠΌΠΏΠΎΠ½ΠΎΠ²ΡΠΈΠΊΡ:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdlib.h>
extern char **environ;
char *evil_env;
int (*orig_execve)(const char *path, char *const argv[], char *const envp[]) = NULL;
// Π‘ΠΎΠ·Π΄Π°ΡΠΌ ΡΠ΅ΠΉΠΊΠΎΠ²ΡΡ Π²Π΅ΡΡΠΈΡ init
// ΠΊΠΎΡΠΎΡΠ°Ρ Π±ΡΠ΄Π΅Ρ Π²ΡΠ·Π²Π°Π½Π° ΠΏΡΠΈ Π·Π°Π³ΡΡΠ·ΠΊΠ΅ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ
// Π΄ΠΎ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ ΠΊΠ°ΠΊΠΈΡ
-Π»ΠΈΠ±ΠΎ ΠΈΠ½ΡΡΡΡΠΊΡΠΈΠΉ
void evil_init()
{
// Π‘Π½Π°ΡΠ°Π»Π° ΡΠΎΡ
ΡΠ°Π½ΠΈΠΌ ΡΠ΅ΠΊΡΡΠ΅Π΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ LD_PRELOAD
static const char *ldpreload = "LD_PRELOAD";
int len = strlen(getenv(ldpreload));
evil_env = (char*) malloc(len+1);
strcpy(evil_env, getenv(ldpreload));
int i;
char env[] = "LD_PRELOAD";
if (environ != NULL)
for (i = 0; environ[i] != NULL; i++) {
char * pch;
pch = strstr(environ[i],env);
if(pch != NULL) {
// ΠΠ·Π±Π°Π²Π»ΡΠ΅ΠΌΡΡ ΠΎΡ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ LD_PRELOAD
unsetenv(env);
break;
}
}
}
int execve(const char *path, char *const argv[], char *const envp[])
{
int i = 0, j = 0, k = -1, ret = 0;
char** new_env;
if(!orig_execve) orig_execve = dlsym(RTLD_NEXT,"execve");
// ΠΡΠΎΠ²Π΅ΡΡΠΌ Π½Π΅ ΡΡΡΠ΅ΡΡΠ²ΡΠ΅Ρ Π»ΠΈ Π΄ΡΡΠ³ΠΈΡ
ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½Π½ΡΡ
LD_PRELOAD
for(i = 0; envp[i]; i++){
if(strstr(envp[i], "LD_PRELOAD")) k = i;
}
// ΠΡΠ»ΠΈ LD_PRELOAD Π½Π΅ Π±ΡΠ»ΠΎ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ΠΎ Π΄ΠΎ Π½Π°Ρ, ΡΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π΅Π³ΠΎ
if(k == -1){
k = i;
i++;
}
// Π‘ΠΎΠ·Π΄Π°ΡΠΌ Π½ΠΎΠ²ΠΎΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΠ΅
new_env = (char**) malloc((i+1)*sizeof(char*));
// ΠΠΎΠΏΠΈΡΡΠ΅ΠΌ ΡΡΠ°ΡΠΎΠ΅ ΠΎΠΊΡΡΠΆΠ΅Π½ΠΈΠ΅, Π·Π° ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ΠΌ LD_PRELOAD
for(j = 0; j < i; j++) {
// ΠΏΠ΅ΡΠ΅Π·Π°ΠΏΠΈΡΡΠ²Π°Π΅ΠΌ ΠΈΠ»ΠΈ ΡΠΎΠ·Π΄Π°ΡΠΌ LD_PRELOAD
if(j == k) {
new_env[j] = (char*) malloc(256);
strcpy(new_env[j], "LD_PRELOAD=");
strcat(new_env[j], evil_env);
}
else new_env[j] = (char*) envp[j];
}
new_env[i] = NULL;
ret = orig_execve(path, argv, new_env);
free(new_env[k]);
free(new_env);
return ret;
}
ΠΡΠΏΠΎΠ»Π½ΡΠ΅ΠΌ, ΠΏΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ:
$ gcc -shared -fpic -ldl -Wl,-init,evil_init ./ld_undetect_environ.c -o ./ld_undetect_environ.so
$ LD_PRELOAD=./ld_undetect_environ.so ./detect_environ
LD_PRELOAD (**environ) [-]
2.3. /proc/self/
ΠΠ΄Π½Π°ΠΊΠΎ, ΠΏΠ°ΠΌΡΡΡ β ΡΡΠΎ Π½Π΅ ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΠΌΠ΅ΡΡΠΎ, Π³Π΄Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΡΡ ΠΏΠΎΠ΄ΠΌΠ΅Π½Ρ LD_PRELOAD, Π΅ΡΡΡ ΠΆΠ΅ Π΅ΡΡ ΠΈ /proc/. ΠΠ°ΡΠ½ΡΠΌ Ρ ΠΎΡΠ΅Π²ΠΈΠ΄Π½ΠΎΠ³ΠΎ /proc/{PID}/environ.
ΠΠ° ΡΠ°ΠΌΠΎΠΌ Π΄Π΅Π»Π΅ Π΅ΡΡΡ ΡΠ½ΠΈΠ²Π΅ΡΡΠ°Π»ΡΠ½ΠΎΠ΅ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π΄Π»Ρ undetect’Π° **environ ΠΈ /proc/self/environ. ΠΡΠΎΠ±Π»Π΅ΠΌΠ° Π·Π°ΠΊΠ»ΡΡΠ°Π΅ΡΡΡ Π² Β«Π½Π΅ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎΠΌΒ» ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠΈ unsetenv(env).
ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΡΠΉ Π²Π°ΡΠΈΠ°Π½Ρ
void evil_init()
{
// Π‘Π½Π°ΡΠ°Π»Π° ΡΠΎΡ
ΡΠ°Π½ΠΈΠΌ ΡΠ΅ΠΊΡΡΠ΅Π΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ LD_PRELOAD
static const char *ldpreload = "LD_PRELOAD";
int len = strlen(getenv(ldpreload));
evil_env = (char*) malloc(len+1);
strcpy(evil_env, getenv(ldpreload));
int i;
char env[] = "LD_PRELOAD";
if (environ != NULL)
for (i = 0; environ[i] != NULL; i++) {
char * pch;
pch = strstr(environ[i],env);
if(pch != NULL) {
// ΠΠ·Π±Π°Π²Π»ΡΠ΅ΠΌΡΡ ΠΎΡ ΡΠ΅ΠΊΡΡΠ΅Π³ΠΎ LD_PRELOAD
//unsetenv(env);
// ΠΠΌΠ΅ΡΡΠΎ unset ΠΏΡΠΎΡΡΠΎ ΠΎΠ±Π½ΡΠ»ΠΈΠΌ Π½Π°ΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ
for(int j = 0; environ[i][j] != ' '; j++) environ[i][j] = ' ';
break;
}
}
}
$ gcc -shared -fpic -ldl -Wl,-init,evil_init ./ld_undetect_environ_2.c -o ./ld_undetect_environ_2.so
$ (LD_PRELOAD=./ld_undetect_environ_2.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
$
ΠΠΎ ΠΏΡΠ΅Π΄ΡΡΠ°Π²ΠΈΠΌ, ΡΡΠΎ ΠΌΡ Π΅Π³ΠΎ Π½Π΅ Π½Π°ΡΠ»ΠΈ ΠΈ /proc/self/environ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ Β«ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ½ΡΠ΅Β» Π΄Π°Π½Π½ΡΠ΅.
Π‘Π½Π°ΡΠ°Π»Π° ΠΏΠΎΠΏΡΠΎΠ±ΡΠ΅ΠΌ Ρ Π½Π°ΡΠ΅ΠΉ ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠ΅ΠΉ Β«ΠΌΠ°ΡΠΊΠΈΡΠΎΠ²ΠΊΠΎΠΉΒ»:
$ (LD_PRELOAD=./ld_undetect_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
LD_PRELOAD=./ld_undetect_environ.so
cat ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Π΄Π»Ρ ΠΎΡΠΊΡΡΡΠΈΡ ΡΠ°ΠΉΠ»Π° Π²ΡΡ ΡΠΎΡ ΠΆΠ΅ open(), ΠΏΠΎΡΡΠΎΠΌΡ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ ΡΡ ΠΎΠ΄Π½ΠΎ Ρ ΡΠ΅ΠΌ, ΡΡΠΎ ΡΠΆΠ΅ Π΄Π΅Π»Π°Π»ΠΎΡΡ Π² ΠΏ.2.1, Π½ΠΎ ΡΠ΅ΠΏΠ΅ΡΡ ΠΌΡ ΡΠΎΠ·Π΄Π°Π΅ΠΌ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΉ ΡΠ°ΠΉΠ», ΠΊΡΠ΄Π° ΠΊΠΎΠΏΠΈΡΡΠ΅ΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΡ ΠΈΡΡΠΈΠ½Π½ΠΎΠΉ ΠΏΠ°ΠΌΡΡΠΈ Π±Π΅Π· ΡΡΡΠΎΠΊ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠΈΡ LD_PRELOAD.
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#define BUFFER_SIZE 256
int (*orig_open)(const char*, int oflag) = NULL;
char *soname = "fakememory_preload.so";
char *sstrstr(char *str, const char *sub)
{
int i, found;
char *ptr;
found = 0;
for(ptr = str; *ptr != ' '; ptr++) {
found = 1;
for(i = 0; found == 1 && sub[i] != ' '; i++){
if(sub[i] != ptr[i]) found = 0;
}
if(found == 1)
break;
}
if(found == 0)
return NULL;
return ptr + i;
}
void fakeMaps(char *original_path, char *fake_path, char *pattern)
{
int fd;
char buffer[BUFFER_SIZE];
int bytes = -1;
int wbytes = -1;
int k = 0;
pid_t pid = getpid();
int fh;
if ((fh=orig_open(fake_path,O_CREAT|O_WRONLY))==-1) {
printf("LD: Cannot open write-file [%s] (%d) (%s)n", fake_path, errno, strerror(errno));
exit (42);
}
if((fd=orig_open(original_path, O_RDONLY))==-1) {
printf("LD: Cannot open read-file.n");
exit(42);
}
do
{
char t = 0;
bytes = read(fd, &t, 1);
buffer[k++] = t;
//printf("%c", t);
if(t == ' ') {
//printf("n");
if(!sstrstr(buffer, "LD_PRELOAD")) {
if((wbytes = write(fh,buffer,k))==-1) {
//printf("write errorn");
}
else {
//printf("writed %dn", wbytes);
}
}
k = 0;
}
}
while(bytes != 0);
close(fd);
close(fh);
}
int open(const char *path, int oflag, ...)
{
char real_path[PATH_MAX], proc_path[PATH_MAX], proc_path_0[PATH_MAX];
pid_t pid = getpid();
if(!orig_open)
orig_open = dlsym(RTLD_NEXT, "open");
realpath(path, real_path);
snprintf(proc_path, PATH_MAX, "/proc/%d/environ", pid);
if(strcmp(real_path, proc_path) == 0) {
snprintf(proc_path, PATH_MAX, "/tmp/%d.fakemaps", pid);
realpath(proc_path_0, proc_path);
fakeMaps(real_path, proc_path, soname);
return orig_open(proc_path, oflag);
}
return orig_open(path, oflag);
}
Π ΡΡΠΎΡ ΡΡΠ°ΠΏ ΠΏΡΠΎΠΉΠ΄Π΅Π½:
$ (LD_PRELOAD=./ld_undetect_proc_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
$
Π‘Π»Π΅Π΄ΡΡΡΠ΅Π΅ ΠΎΡΠ΅Π²ΠΈΠ΄Π½ΠΎΠ΅ ΠΌΠ΅ΡΡΠΎ β /proc/self/maps. ΠΠ°Π΄Π΅ΡΠΆΠΈΠ²Π°ΡΡΡΡ Π½Π° Π½Π΅ΠΌ Π½Π΅Ρ ΡΠΌΡΡΠ»Π°. Π Π΅ΡΠ΅Π½ΠΈΠ΅ Π°Π±ΡΠΎΠ»ΡΡΠ½ΠΎ ΠΈΠ΄Π΅Π½ΡΠΈΡΠ½ΠΎΠ΅ ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠ΅ΠΌΡ: ΠΊΠΎΠΏΠΈΡΡΠ΅ΠΌ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· ΡΠ°ΠΉΠ»Π° Π·Π° Π²ΡΡΠ΅ΡΠΎΠΌ ΡΡΡΠΎΠΊ ΠΌΠ΅ΠΆΠ΄Ρ libc.so ΠΈ ld.so.
2.4. ΠΠ°ΡΠΈΠ°Π½Ρ ΠΎΡ Chokepoint
ΠΡΠΎ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ ΠΌΠ½Π΅ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎ ΠΏΠΎΠ½ΡΠ°Π²ΠΈΠ»ΠΎΡΡ ΡΠ²ΠΎΠ΅ΠΉ ΠΏΡΠΎΡΡΠΎΡΠΎΠΉ. Π‘ΡΠ°Π²Π½ΠΈΠ²Π°Π΅ΠΌ Π°Π΄ΡΠ΅ΡΠ° ΡΡΠ½ΠΊΡΠΈΠΉ, Π·Π°Π³ΡΡΠΆΠ°Π΅ΠΌΡΡ Π½Π΅ΠΏΠΎΡΡΠ΅Π΄ΡΡΠ²Π΅Π½Π½ΠΎ ΠΈΠ· libc, ΠΈ Β«NEXTΒ»-Π°Π΄ΡΠ΅ΡΠ°.
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#define LIBC "/lib/x86_64-linux-gnu/libc.so.6"
int main(int argc, char *argv[]) {
void *libc = dlopen(LIBC, RTLD_LAZY); // Open up libc directly
char *syscall_open = "open";
int i;
void *(*libc_func)();
void *(*next_func)();
libc_func = dlsym(libc, syscall_open);
next_func = dlsym(RTLD_NEXT, syscall_open);
if (libc_func != next_func) {
printf("LD_PRELOAD (syscall - %s) [+]n", syscall_open);
printf("Libc address: %pn", libc_func);
printf("Next address: %pn", next_func);
}
else {
printf("LD_PRELOAD (syscall - %s) [-]n", syscall_open);
}
return 0;
}
ΠΠ°Π³ΡΡΠΆΠ°Π΅ΠΌ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ Ρ ΠΏΠ΅ΡΠ΅Ρ Π²Π°ΡΠΎΠΌ Β«open()Β» ΠΈ ΠΏΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ:
$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (syscall - open) [+]
Libc address: 0x7fa86893b160
Next address: 0x7fa868a26135
ΠΠΏΡΠΎΠ²Π΅ΡΠΆΠ΅Π½ΠΈΠ΅ ΠΎΠΊΠ°Π·Π°Π»ΠΎΡΡ Π΅ΡΡ ΠΏΡΠΎΡΠ΅:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
extern void * _dl_sym (void *, const char *, void *);
void * dlsym (void * handle, const char * symbol)
{
return _dl_sym (handle, symbol, dlsym);
}
# LD_PRELOAD=./ld_undetect_chokepoint.so ./detect_chokepoint
LD_PRELOAD (syscall - open) [-]
2.5. Syscalls
ΠΠ°Π·Π°Π»ΠΎΡΡ Π±Ρ Π½Π° ΡΡΠΎΠΌ Π²ΡΡ, Π½ΠΎ Π΅ΡΡ ΠΏΠΎΠ±Π°ΡΠ°Ρ ΡΠ°Π΅ΠΌΡΡ. ΠΡΠ»ΠΈ ΠΌΡ Π½Π°ΠΏΡΠ°Π²ΠΈΠΌ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠΉ Π²ΡΠ·ΠΎΠ² Π½Π°ΠΏΡΡΠΌΡΡ ΠΊ ΡΠ΄ΡΡ, ΡΠΎ ΡΡΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ ΠΎΠ±ΠΎΠΉΡΠΈ Π²Π΅ΡΡ ΠΏΡΠΎΡΠ΅ΡΡ ΠΏΠ΅ΡΠ΅Ρ Π²Π°ΡΠ°. Π Π΅ΡΠ΅Π½ΠΈΠ΅ Π½ΠΈΠΆΠ΅, ΡΠ°Π·ΡΠΌΠ΅Π΅ΡΡΡ, Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠΎΠ·Π°Π²ΠΈΡΠΈΠΌΠΎΠ΅ (x86_64). ΠΠΎΠΏΡΠΎΠ±ΡΠ΅ΠΌ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°ΡΡ Π΄Π»Ρ ΠΎΠ±Π½Π°ΡΡΠΆΠ΅Π½ΠΈΡ ΠΎΡΠΊΡΡΡΠΈΡ ld.so.preload.
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFER_SIZE 256
int syscall_open(char *path, long oflag)
{
int fd = -1;
__asm__ (
"mov $2, %%rax;" // Open syscall number
"mov %1, %%rdi;" // Address of our string
"mov %2, %%rsi;" // Open mode
"mov $0, %%rdx;" // No create mode
"syscall;" // Straight to ring0
"mov %%eax, %0;" // Returned file descriptor
:"=r" (fd)
:"m" (path), "m" (oflag)
:"rax", "rdi", "rsi", "rdx"
);
return fd;
}
int main()
{
syscall_open("/etc/ld.so.preload", O_RDONLY) > 0 ?
printf("LD_PRELOAD (open syscall) [+]n"):
printf("LD_PRELOAD (open syscall) [-]n");
}
$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
Π Π΄Π°Π½Π½Π°Ρ Π·Π°Π΄Π°ΡΠΊΠ° ΠΈΠΌΠ΅Π΅Ρ ΡΠ΅ΡΠ΅Π½ΠΈΠ΅. ΠΡΠ΄Π΅ΡΠΆΠΊΠ° ΠΈΠ· man‘Π°:
ptrace β ΡΡΠΎ ΡΡΠ΅Π΄ΡΡΠ²ΠΎ, ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡΡΠ΅Π΅ ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΎΠΌΡ ΠΏΡΠΎΡΠ΅ΡΡΡ Π½Π°Π±Π»ΡΠ΄Π°ΡΡ ΠΈ ΠΊΠΎΠ½ΡΡΠΎΠ»ΠΈΡΠΎΠ²Π°ΡΡ ΠΏΡΠΎΡΠ΅ΠΊΠ°Π½ΠΈΠ΅ Π΄ΡΡΠ³ΠΎΠ³ΠΎ ΠΏΡΠΎΡΠ΅ΡΡΠ°, ΠΏΡΠΎΡΠΌΠ°ΡΡΠΈΠ²Π°ΡΡ ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΡΡΡ Π΅Π³ΠΎ Π΄Π°Π½Π½ΡΠ΅ ΠΈ ΡΠ΅Π³ΠΈΡΡΡΡ. ΠΠ±ΡΡΠ½ΠΎ ΡΡΠ° ΡΡΠ½ΠΊΡΠΈΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠΎΡΠ΅ΠΊ ΠΏΡΠ΅ΡΡΠ²Π°Π½ΠΈΡ Π² ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ΅ ΠΎΡΠ»Π°Π΄ΠΊΠΈ ΠΈ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΡ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΡ Π²ΡΠ·ΠΎΠ²ΠΎΠ².
Π ΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ ΠΌΠΎΠΆΠ΅Ρ Π½Π°ΡΠ°ΡΡ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΡ, ΡΠ½Π°ΡΠ°Π»Π° Π²ΡΠ·Π²Π°Π² ΡΡΠ½ΠΊΡΠΈΡ fork(2), Π° Π·Π°ΡΠ΅ΠΌ ΠΏΠΎΠ»ΡΡΠΈΠ²ΡΠΈΠΉΡΡ Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ ΠΌΠΎΠΆΠ΅Ρ Π²ΡΠΏΠΎΠ»Π½ΠΈΡΡ PTRACE_TRACEME, Π·Π° ΠΊΠΎΡΠΎΡΡΠΌ (ΠΎΠ±ΡΡΠ½ΠΎ) ΡΠ»Π΅Π΄ΡΠ΅Ρ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ exec(3). Π‘ Π΄ΡΡΠ³ΠΎΠΉ ΡΡΠΎΡΠΎΠ½Ρ, ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ ΠΌΠΎΠΆΠ΅Ρ Π½Π°ΡΠ°ΡΡ ΠΎΡΠ»Π°Π΄ΠΊΡ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠ΅Π³ΠΎ ΠΏΡΠΎΡΠ΅ΡΡΠ° ΠΏΡΠΈ ΠΏΠΎΠΌΠΎΡΠΈ PTRACE_ATTACH.
ΠΡΠΈ ΡΡΠ°ΡΡΠΈΡΠΎΠ²ΠΊΠ΅ Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ ΠΎΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΡΡΡ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠ°Π· ΠΏΡΠΈ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΠΈ ΡΠΈΠ³Π½Π°Π»Π°, Π΄Π°ΠΆΠ΅ Π΅ΡΠ»ΠΈ ΡΡΠΎΡ ΡΠΈΠ³Π½Π°Π» ΠΈΠ³Π½ΠΎΡΠΈΡΡΠ΅ΡΡΡ. (ΠΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ΠΌ ΡΠ²Π»ΡΠ΅ΡΡΡ SIGKILL, ΡΠ°Π±ΠΎΡΠ°ΡΡΠΈΠΉ ΠΎΠ±ΡΡΠ½ΡΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ.) Π ΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ Π±ΡΠ΄Π΅Ρ ΡΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ ΠΎΠ± ΡΡΠΎΠΌ ΠΏΡΠΈ Π²ΡΠ·ΠΎΠ²Π΅ wait(2), ΠΏΠΎΡΠ»Π΅ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ ΠΎΠ½ ΠΌΠΎΠΆΠ΅Ρ ΠΏΡΠΎΡΠΌΠ°ΡΡΠΈΠ²Π°ΡΡ ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΡΡΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ ΠΏΡΠΎΡΠ΅ΡΡΠ° Π΄ΠΎ Π΅Π³ΠΎ Π·Π°ΠΏΡΡΠΊΠ°. ΠΠΎΡΠ»Π΅ ΡΡΠΎΠ³ΠΎ ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ ΡΠ°Π·ΡΠ΅ΡΠ°Π΅Ρ Π΄ΠΎΡΠ΅ΡΠ½Π΅ΠΌΡ ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°ΡΡ ΡΠ°Π±ΠΎΡΡ, Π² Π½Π΅ΠΊΠΎΡΠΎΡΡΡ ΡΠ»ΡΡΠ°ΡΡ ΠΈΠ³Π½ΠΎΡΠΈΡΡΡ ΠΏΠΎΡΡΠ»Π°Π΅ΠΌΡΠΉ Π΅ΠΌΡ ΡΠΈΠ³Π½Π°Π» ΠΈΠ»ΠΈ ΠΎΡΠΏΡΠ°Π²Π»ΡΡ Π²ΠΌΠ΅ΡΡΠΎ ΡΡΠΎΠ³ΠΎ Π΄ΡΡΠ³ΠΎΠΉ ΡΠΈΠ³Π½Π°Π»).
Π’Π΅ΠΌ ΡΠ°ΠΌΡΠΌ, ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ Π·Π°ΠΊΠ»ΡΡΠ°Π΅ΡΡΡ Π² ΡΠΎΠΌ, ΡΡΠΎΠ±Ρ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°ΡΡ ΠΏΡΠΎΡΠ΅ΡΡ, ΠΎΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Ρ Π΅Π³ΠΎ ΠΏΠ΅ΡΠ΅Π΄ ΠΊΠ°ΠΆΠ΄ΡΠΌ ΡΠΈΡΡΠ΅ΠΌΠ½ΡΠΌ Π²ΡΠ·ΠΎΠ²ΠΎΠΌ ΠΈ, ΠΏΡΠΈ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎΡΡΠΈ, ΠΏΠ΅ΡΠ΅Π½Π°ΠΏΡΠ°Π²Π»ΡΡΡ ΠΏΠΎΡΠΎΠΊ Π² ΡΡΠ½ΠΊΡΠΈΡ Π»ΠΎΠ²ΡΡΠΊΠΈ.
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#include <asm/unistd.h>
#if defined(__x86_64__)
#define REG_SYSCALL ORIG_RAX
#define REG_SP rsp
#define REG_IP rip
#endif
long NOHOOK = 0;
long evil_open(const char *path, long oflag, long cflag)
{
char real_path[PATH_MAX], maps_path[PATH_MAX];
long ret;
pid_t pid;
pid = getpid();
realpath(path, real_path);
if(strcmp(real_path, "/etc/ld.so.preload") == 0)
{
errno = ENOENT;
ret = -1;
}
else
{
NOHOOK = 1; // Entering NOHOOK section
ret = open(path, oflag, cflag);
}
// Exiting NOHOOK section
NOHOOK = 0;
return ret;
}
void init()
{
pid_t program;
// Π€ΠΎΡΠΊΠ°Π΅ΠΌ Π΄ΠΎΡΠ΅ΡΠ½ΠΈΠΉ ΠΏΡΠΎΡΠ΅ΡΡ
program = fork();
if(program != 0) {
int status;
long syscall_nr;
struct user_regs_struct regs;
// ΠΠΎΠ΄ΠΊΠ»ΡΡΠ°Π΅ΠΌΡΡ ΠΊ Π΄ΠΎΡΠ΅ΡΠ½Π΅ΠΌΡ ΠΏΡΠΎΡΠ΅ΡΡΡ
if(ptrace(PTRACE_ATTACH, program) != 0) {
printf("Failed to attach to the program.n");
exit(1);
}
waitpid(program, &status, 0);
// ΠΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅ΠΌ ΡΠΎΠ»ΡΠΊΠΎ SYSCALLs
ptrace(PTRACE_SETOPTIONS, program, 0, PTRACE_O_TRACESYSGOOD);
while(1) {
ptrace(PTRACE_SYSCALL, program, 0, 0);
waitpid(program, &status, 0);
if(WIFEXITED(status) || WIFSIGNALED(status)) break;
else if(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP|0x80) {
// ΠΠΎΠ»ΡΡΠ°Π΅ΠΌ Π½ΠΎΠΌΠ΅Ρ ΡΠΈΡΡΠ΅ΠΌΠ½ΠΎΠ³ΠΎ Π²ΡΠ·ΠΎΠ²Π°
syscall_nr = ptrace(PTRACE_PEEKUSER, program, sizeof(long)*REG_SYSCALL);
if(syscall_nr == __NR_open) {
// Π§ΠΈΡΠ°Π΅ΠΌ ΡΠ»ΠΎΠ²ΠΎ ΠΈΠ· ΠΏΠ°ΠΌΡΡΠΈ Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ ΠΏΡΠΎΡΠ΅ΡΡΠ°
NOHOOK = ptrace(PTRACE_PEEKDATA, program, (void*)&NOHOOK);
// ΠΠ΅ΡΠ΅Ρ
Π²Π°ΡΡΠ²Π°Π΅ΠΌ Π²ΡΠ·ΠΎΠ²
if(!NOHOOK) {
// ΠΠΎΠΏΠΈΡΡΠ΅ΠΌ ΡΠ΅Π³ΠΈΡΡΡΡ Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ ΠΏΡΠΎΡΠ΅ΡΡΠ°
// Π² ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ regs ΡΠΎΠ΄ΠΈΡΠ΅Π»ΡΡΠΊΠΎΠ³ΠΎ
ptrace(PTRACE_GETREGS, program, 0, ®s);
// Push return address on the stack
regs.REG_SP -= sizeof(long);
// ΠΠΎΠΏΠΈΡΡΠ΅ΠΌ ΡΠ»ΠΎΠ²ΠΎ Π² ΠΏΠ°ΠΌΡΡΡ Π΄ΠΎΡΠ΅ΡΠ½Π΅Π³ΠΎ ΠΏΡΠΎΡΠ΅ΡΡΠ°
ptrace(PTRACE_POKEDATA, program, (void*)regs.REG_SP, regs.REG_IP);
// Π£ΡΡΠ°Π½Π°Π²Π»ΠΈΠ²Π°Π΅ΠΌ RIP ΠΏΠΎ Π°Π΄ΡΠ΅ΡΡ evil_open
regs.REG_IP = (unsigned long) evil_open;
// ΠΠ°ΠΏΠΈΡΡΠ²Π°Π΅ΠΌ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΡΠ΅Π³ΠΈΡΡΡΠΎΠ² ΠΏΡΠΎΡΠ΅ΡΡΠ°
ptrace(PTRACE_SETREGS, program, 0, ®s);
}
}
ptrace(PTRACE_SYSCALL, program, 0, 0);
waitpid(program, &status, 0);
}
}
exit(0);
}
else {
sleep(0);
}
}
ΠΡΠΎΠ²Π΅ΡΡΠ΅ΠΌ:
$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
$ LD_PRELOAD=./ld_undetect_syscall.so ./detect_syscall
LD_PRELOAD (open syscall) [-]
+0-0=5
ΠΠ³ΡΠΎΠΌΠ½ΠΎΠ΅ ΡΠΏΠ°ΡΠΈΠ±ΠΎ
, ΡΡΠΈ ΡΡΠ°ΡΡΠΈ, ΠΈΡΡ
ΠΎΠ΄Π½ΠΈΠΊΠΈ ΠΈ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ ΡΠ΄Π΅Π»Π°Π»ΠΈ Π½Π°ΠΌΠ½ΠΎΠ³ΠΎ Π±ΠΎΠ»ΡΡΠ΅, ΡΠ΅ΠΌ Ρ, Π΄Π»Ρ ΡΠΎΠ³ΠΎ ΡΡΠΎΠ±Ρ ΡΡΠ° Π·Π°ΠΌΠ΅ΡΠΊΠ° ΠΏΠΎΡΠ²ΠΈΠ»Π°ΡΡ Π·Π΄Π΅ΡΡ.
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com