Bu eslatma 2014 yilda yozilgan, lekin men Habrega nisbatan repressiyaga uchradim va u kun yorug'ligini ko'rmadi. Taqiq paytida men buni unutib qo'ydim, lekin hozir uni qoralamalarda topdim. Men uni o'chirish haqida o'yladim, lekin kimdir uchun foydali bo'lishi mumkin.

Umuman olganda, juma kuni administratorning "qo'shilgan" ni qidirish mavzusini o'qishi LD_PRELOAD.
1. Funktsiyalarni almashtirish bilan tanish bo'lmaganlar uchun qisqacha ma'lumot
Qolganlari to'g'ridan-to'g'ri borishlari mumkin 2-bet.
Keling, klassik misol bilan boshlaylik:
#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);
}
}
Biz hech qanday bayroqlarsiz tuzamiz:
$ gcc ./ld_rand.c -o ld_rand
Va kutilganidek, biz 5 dan kichik 100 ta tasodifiy raqamni olamiz:
$ ./ld_rand
53
93
48
57
20
Ammo bizda dasturning manba kodi yo'qligini tasavvur qilaylik, lekin biz xatti-harakatni o'zgartirishimiz kerak.
Keling, o'z funktsional prototipimiz bilan o'z kutubxonamizni yarataylik, masalan:
int rand(){
return 42;
}
$ gcc -shared -fPIC ./o_rand.c -o ld_rand.so
Va endi bizning tasodifiy tanlovimiz oldindan taxmin qilinadi:
# LD_PRELOAD=$PWD/ld_rand.so ./ld_rand
42
42
42
42
42
Agar biz kutubxonamizni avval ushbu orqali eksport qilsak, bu hiyla yanada ta'sirli ko'rinadi
$ export LD_PRELOAD=$PWD/ld_rand.so
yoki biz buni birinchi bo'lib qilamiz
# echo "$PWD/ld_rand.so" > /etc/ld.so.preload
va keyin dasturni odatdagidek ishga tushiring. Biz dasturning o'zida bitta kod qatorini o'zgartirmadik, lekin uning harakati endi kutubxonamizdagi kichik funksiyaga bog'liq. Bundan tashqari, dasturni yozish vaqtida rand hatto mavjud emas edi.
Bizning dasturimizni soxta ishlatishga nima majbur qildi rand? Keling, buni bosqichma-bosqich ko'rib chiqaylik.
Ilova ishga tushganda, dastur uchun zarur bo'lgan funktsiyalarni o'z ichiga olgan ma'lum kutubxonalar yuklanadi. Biz ularni foydalanib ko'rishimiz mumkin 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)
Ushbu ro'yxat OS versiyasiga qarab farq qilishi mumkin, lekin u erda fayl bo'lishi kerak libc.so. Aynan shu kutubxona tizim qo'ng'iroqlari va kabi asosiy funktsiyalarni ta'minlaydi ochiq, malloc, printf va boshqalar bizning rand ham ular orasida. Keling, bunga ishonch hosil qilaylik:
# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep " rand$"
000000000003aef0 T rand
Keling, kutubxonalar to'plami ishlatilganda o'zgarishini ko'rib chiqaylik 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)
O'zgaruvchi o'rnatilganligi ma'lum bo'ladi LD_PRELOAD yuklashga majbur qiladi ld_rand.so dasturning o'zi buni talab qilmasa ham. Va bizning vazifamizdan beri "rand" dan oldin yuklaydi rand от libc.so, keyin u xo'jayinni boshqaradi.
OK, biz mahalliy funktsiyani almashtirishga muvaffaq bo'ldik, lekin uning funksionalligi saqlanib qolganiga va ba'zi harakatlar qo'shilganligiga qanday ishonch hosil qilishimiz mumkin. Keling, tasodifiyimizni o'zgartiraylik:
#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();
}
Bu erda, bizning "qo'shimchamiz" sifatida biz faqat bitta matn qatorini chop qilamiz, shundan so'ng biz asl funktsiyaga ko'rsatgich yaratamiz. rand. Ushbu funktsiyaning manzilini olish uchun bizga kerak dlsym kutubxonadan olingan funksiyadir libdlqaysi bizni topadi rand dinamik kutubxonalar to'plamida. Shundan so'ng biz ushbu funktsiyani chaqiramiz va uning qiymatini qaytaramiz. Shunga ko'ra, biz qo'shishimiz kerak bo'ladi "-ldl" yig'ish paytida:
$ 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
Va bizning dasturimiz "native" dan foydalanadi rand, ilgari ba'zi odobsiz harakatlar qilgan.
2. Qidiruv azobi
Potentsial tahdid haqida bilib, biz buni aniqlamoqchimiz preload Bu ijro etildi. Aniqlashning eng yaxshi usuli uni yadroga surish ekanligi aniq, lekin men foydalanuvchilar maydonidagi aniqlash imkoniyatlari bilan qiziqdim.
Keyinchalik, aniqlash va ularni rad etish uchun echimlar juft bo'ladi.
2.1. Oddiydan boshlaylik
Yuqorida aytib o'tilganidek, o'zgaruvchidan foydalanib yuklanadigan kutubxonani belgilashingiz mumkin LD_PRELOAD yoki faylga yozish orqali /etc/ld.so.preload. Keling, ikkita oddiy detektorni yarataylik.
Birinchisi, o'rnatilgan muhit o'zgaruvchisini tekshirish:
#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");
}
Ikkinchisi, fayl ochilganligini tekshirish:
#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");
}
Keling, kutubxonalarni yuklaymiz:
$ 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) [+]
Bu yerda va pastda [+] muvaffaqiyatli aniqlashni bildiradi.
Shunga ko'ra, [-] aniqlashni chetlab o'tishni bildiradi.
Bunday detektor qanchalik samarali? Avval atrof-muhit o'zgaruvchisini ko'rib chiqaylik:
#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) [-]
Xuddi shunday, biz chekdan qutulamiz ochiq:
#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) [-]
Ha, bu yerda faylga kirishning boshqa usullaridan foydalanish mumkin, masalan: ochiq 64, Davlat va hokazo, lekin, aslida, ularni aldash uchun bir xil 5-10 qator kod kerak.
2.2. Keling, davom etaylik
Yuqorida biz foydalandik getenv() qiymatini olish uchun LD_PRELOAD, lekin borishning ko'proq "past darajadagi" yo'li ham bor ENV- o'zgaruvchilar. Biz oraliq funktsiyalardan foydalanmaymiz, lekin massivga murojaat qilamiz ** atrof-muhit, bu muhitning nusxasini saqlaydi:
#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;
}
Bu erda biz to'g'ridan-to'g'ri xotiradan ma'lumotlarni o'qiyotganimiz sababli, bunday qo'ng'iroqni ushlab bo'lmaydi va bizning undetect_getenv u endi bosqinni aniqlashga xalaqit bermaydi.
$ LD_PRELOAD=./ld_undetect_getenv.so ./detect_environ
LD_PRELOAD (**environ) [+]
Bu muammo hal qilinganga o'xshaydi? Hali endigina boshlanmoqda.
Dastur ishga tushirilgandan so'ng, o'zgaruvchining qiymati LD_PRELOAD xakerlar endi xotirada kerak emas, ya'ni har qanday ko'rsatmalarni bajarishdan oldin uni o'qib, o'chirib tashlashi mumkin. Albatta, xotiradagi massivni tahrirlash, hech bo'lmaganda, yomon dasturlash uslubidir, ammo bu baribir bizga yaxshilik tilamagan odamni to'xtata oladimi?
Buning uchun biz o'z soxta funktsiyamizni yaratishimiz kerak init(), unda biz o'rnatilganni ushlab turamiz LD_PRELOAD va uni bog'lovchimizga o'tkazing:
#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;
}
Biz bajaramiz va tekshiramiz:
$ 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/
Biroq, xotira almashtirishni topishingiz mumkin bo'lgan oxirgi joy emas LD_PRELOAD, ham bor /proc/. Keling, aniq narsadan boshlaylik /proc/{PID}/environ.
Aslida, buning universal echimi mavjud aniqlash ** atrof-muhit и /proc/self/environ. Muammo "noto'g'ri" xatti-harakatlardir unsetenv(env).
to'g'ri variant
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
$
Ammo tasavvur qilaylik, biz uni topa olmadik va /proc/self/environ "muammoli" ma'lumotlarni o'z ichiga oladi.
Avval oldingi "niqobimizni" sinab ko'raylik:
$ (LD_PRELOAD=./ld_undetect_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
LD_PRELOAD=./ld_undetect_environ.so
mushuk faylni ochish uchun xuddi shunday foydalanadi ochiq(), shuning uchun yechim 2.1-bo'limda bajarilgan narsaga o'xshaydi, ammo endi biz vaqtinchalik fayl yaratamiz, unda biz haqiqiy xotira qiymatlarini qatorlarsiz nusxalaymiz. 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);
}
Va bu bosqich o'tdi:
$ (LD_PRELOAD=./ld_undetect_proc_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
$
Keyingi aniq joy /proc/self/xaritalar. Bu haqda to'xtalib o'tishning ma'nosi yo'q. Yechim avvalgisi bilan mutlaqo bir xil: fayldan ma'lumotlarni nusxa ko'chiring, ular orasidagi qatorlarni chiqarib tashlang libc.so и ld.so.
2.4. Chokepoint-dan variant
Menga bu yechim, ayniqsa, soddaligi uchun yoqdi. Biz to'g'ridan-to'g'ri yuklangan funktsiyalar manzillarini taqqoslaymiz libc, va “KEYINGI” manzillari.
#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;
}
Kutubxonani ushlash bilan yuklash "ochiq()" va tekshiring:
$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (syscall - open) [+]
Libc address: 0x7fa86893b160
Next address: 0x7fa868a26135
Rad etish yanada sodda bo'lib chiqdi:
#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. Tizimlar
Hammasi shu bo'lib tuyulishi mumkin, lekin biz hali ham chayqalib ketamiz. Agar biz tizim chaqiruvini to'g'ridan-to'g'ri yadroga yo'naltirsak, bu butun ushlash jarayonini chetlab o'tadi. Quyidagi yechim, albatta, arxitekturaga bog'liq (x86_64). Keling, ochilishni aniqlash uchun uni amalga oshirishga harakat qilaylik 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) [+]
Va bu muammoning yechimi bor. dan parcha kishi'A:
ptrace - bu asosiy jarayonga boshqa jarayonning borishini kuzatish va nazorat qilish, uning ma'lumotlari va registrlarini ko'rish va o'zgartirish imkonini beruvchi vositadir. Odatda bu funksiya disk raskadrovka dasturida uzilish nuqtalarini yaratish va tizim chaqiruvlarini kuzatish uchun ishlatiladi.
Asosiy jarayon avval fork(2) ni chaqirish orqali kuzatuvni boshlashi mumkin, so'ngra hosil bo'lgan bola jarayoni PTRACE_TRACEME ni, keyin esa (odatda) exec(3) ni bajarishi mumkin. Boshqa tomondan, asosiy jarayon PTRACE_ATTACH yordamida mavjud jarayonni tuzatishni boshlashi mumkin.
Tracing, signal e'tiborga olinmasa ham, har safar signal qabul qilganda bola jarayonini to'xtatadi. (Istisno SIGKILL, normal ishlaydi.) Ota-ona jarayoni bu haqda wait(2) ga qo'ng'iroq qilish orqali xabardor qilinadi, shundan so'ng u boshlang'ich jarayonning mazmunini ko'rishi va o'zgartirishi mumkin. Keyin ota-ona jarayoni bolaning ishlashni davom ettirishiga imkon beradi, ba'zi hollarda unga yuborilgan signalni e'tiborsiz qoldiradi yoki o'rniga boshqa signal yuboradi).
Shunday qilib, yechim jarayonni kuzatish, har bir tizim chaqiruvidan oldin uni to'xtatish va kerak bo'lganda ipni kanca funktsiyasiga yo'naltirishdir.
#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);
}
}
Biz tekshiramiz:
$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
$ LD_PRELOAD=./ld_undetect_syscall.so ./detect_syscall
LD_PRELOAD (open syscall) [-]
+0-0=5
Katta rahmat
, kimning maqolalari, manbalari va sharhlari bu eslatmani bu erda ko'rsatish uchun men qilganimdan ko'ra ko'proq narsani qildi.
Manba: www.habr.com
