Traži se LD_PRELOAD

Ova bilješka je napisana 2014. godine, ali sam upravo bio pod represijom na Habréu i nije ugledala svjetlo dana. Za vrijeme zabrane zaboravio sam na to, ali sada sam ga našao u nacrtima. Razmišljao sam da ga izbrišem, ali možda nekome bude od koristi.

Traži se LD_PRELOAD

Općenito, malo admin čitanja petkom na temu traženja "uključeno" LD_PRELOAD.

1. Kratka digresija za one koji nisu upoznati sa zamjenom funkcija

Ostalo može ići direktno p.2.

Počnimo s klasičnim primjerom:

#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);
  }
}

Sastavljamo bez ikakvih zastavica:

$ gcc ./ld_rand.c -o ld_rand

I, kao što se očekivalo, dobijamo 5 slučajnih brojeva manjih od 100:

$ ./ld_rand
53
93
48
57
20

Ali zamislimo da nemamo izvorni kod programa, ali moramo promijeniti ponašanje.

Kreirajmo vlastitu biblioteku s vlastitim prototipom funkcije, na primjer:

int rand(){
  return 42;
}

$ gcc -shared -fPIC ./o_rand.c -o ld_rand.so

A sada je naš slučajni izbor prilično predvidljiv:

# LD_PRELOAD=$PWD/ld_rand.so ./ld_rand
42
42
42
42
42

Ovaj trik izgleda još impresivnije ako prvo izvezemo našu biblioteku putem

$ export LD_PRELOAD=$PWD/ld_rand.so

ili ćemo to prvi uraditi

# echo "$PWD/ld_rand.so" > /etc/ld.so.preload

a zatim pokrenite program kao i obično. Nismo promijenili niti jednu liniju koda u samom programu, ali njegovo ponašanje sada ovisi o maloj funkciji u našoj biblioteci. Štaviše, u vrijeme pisanja programa, Rand nije ni postojao.

Zbog čega je naš program koristio lažni Rand? Idemo korak po korak.
Kada se aplikacija pokrene, učitavaju se određene biblioteke koje sadrže funkcije potrebne programu. Možemo ih vidjeti koristeći 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)

Ova lista može varirati ovisno o verziji OS-a, ali mora postojati datoteka libc.so. Upravo ova biblioteka pruža sistemske pozive i osnovne funkcije kao što su otvoreno, malloc, printf itd. Naše Rand je takođe među njima. Uvjerimo se u ovo:

# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep " rand$"
000000000003aef0 T rand

Hajde da vidimo da li se skup biblioteka menja kada se koristi 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)

Ispostavilo se da je varijabla postavljena LD_PRELOAD prisiljava nas na učitavanje ld_rand.so čak i uprkos činjenici da sam program to ne zahtijeva. I od naše funkcije "rand" učitava ranije od Rand iz libc.so, onda ona vlada utočištem.

Ok, uspjeli smo zamijeniti nativnu funkciju, ali kako možemo osigurati da je njena funkcionalnost očuvana i da se dodaju neke akcije. Izmijenimo naš slučajni odabir:

#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();
}

Ovdje, kao naš "dodatak", ispisujemo samo jedan red teksta, nakon čega kreiramo pokazivač na originalnu funkciju Rand. Da bismo dobili adresu ove funkcije nam je potrebna dlsym je funkcija iz biblioteke libdlkoji će pronaći naše Rand u hrpi dinamičkih biblioteka. Nakon toga ćemo pozvati ovu funkciju i vratiti njenu vrijednost. Shodno tome, moraćemo da dodamo "-ldl" tokom montaže:

$ 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

A naš program koristi "native" Rand, nakon što je prethodno izveo neke nepristojne radnje.

2. Bol traženja

Znajući za potencijalnu prijetnju, želimo je otkriti preload Izvedeno je. Jasno je da je najbolji način za otkrivanje da ga gurnete u kernel, ali mene su zanimale opcije detekcije u korisničkom prostoru.

Zatim će rješenja za otkrivanje i njihovo pobijanje dolaziti u parovima.

2.1. Počnimo jednostavno

Kao što je ranije spomenuto, možete odrediti biblioteku za učitavanje pomoću varijable LD_PRELOAD ili upisivanjem u datoteku /etc/ld.so.preload. Napravimo dva jednostavna detektora.

Prvi je da provjerite postavljenu varijablu okruženja:

#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");
}

Drugi je da provjerite da li je fajl otvoren:

#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");
}

Učitajmo biblioteke:

$ 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) [+]

Ovdje i ispod, [+] označava uspješnu detekciju.
Prema tome, [-] znači zaobići detekciju.

Koliko je efikasan takav detektor? Pogledajmo prvo varijablu okruženja:

#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) [-]

Slično, riješimo se čeka otvoreno:

#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) [-]

Da, ovdje se mogu koristiti drugi načini pristupa datoteci, kao što su, open64, stanje itd., ali je, u stvari, potrebno istih 5-10 linija koda da bi ih prevarili.

2.2. Idemo dalje

Gore smo koristili getenv() da dobijete vrednost LD_PRELOAD, ali postoji i „niskorazinski“ način da se do njega dođe ENV-varijable. Nećemo koristiti međufunkcije, već ćemo se pozivati ​​na niz **okolina, koji pohranjuje kopiju okruženja:

#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;
}

Pošto ovdje čitamo podatke direktno iz memorije, takav poziv se ne može presresti, a naš undetect_getenv više ne ometa identifikaciju upada.

$ LD_PRELOAD=./ld_undetect_getenv.so ./detect_environ
LD_PRELOAD (**environ) [+]

Čini se da je ovaj problem riješen? Još uvijek tek počinje.

Nakon što se program pokrene, vrijednost varijable LD_PRELOAD hakerima više nije potreban u memoriji, odnosno mogu ga pročitati i obrisati prije izvršavanja bilo koje instrukcije. Naravno, uređivanje niza u memoriji je, u najmanju ruku, loš stil programiranja, ali kako ovo može zaustaviti nekoga ko nam ionako ne želi dobro?

Da bismo to učinili moramo kreirati vlastitu lažnu funkciju u tome(), u kojem presrećemo instalirane LD_PRELOAD i proslijedite ga našem linkeru:

#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;
}

Izvodimo i provjeravamo:

$ 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/

Međutim, pamćenje nije posljednje mjesto gdje možete pronaći zamjenu LD_PRELOAD, takođe postoji /proc/. Počnimo sa očiglednim /proc/{PID}/environ.

Zapravo, postoji univerzalno rješenje za neotkriti **okolina и /proc/self/environ. Problem je u “pogrešnom” ponašanju unsetenv(env).

ispravna opcija

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
$

Ali zamislimo da ga nismo pronašli i /proc/self/environ sadrži "problematične" podatke.

Prvo hajde da probamo sa našim prethodnim "prerušavanjem":

$ (LD_PRELOAD=./ld_undetect_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
LD_PRELOAD=./ld_undetect_environ.so

mačka koristi isto za otvaranje datoteke otvori(), tako da je rješenje slično onome što je već urađeno u odjeljku 2.1, ali sada kreiramo privremenu datoteku u koju kopiramo vrijednosti prave memorije bez linija koje sadrže 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);
}

I ova faza je prošla:

$ (LD_PRELOAD=./ld_undetect_proc_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
$

Sledeće očigledno mesto je /proc/self/maps. Nema smisla zadržavati se na tome. Rješenje je potpuno identično prethodnom: kopirajte podatke iz datoteke minus redove između libc.so и ld.so.

2.4. Opcija iz Chokepoint-a

Posebno mi se svidjelo ovo rješenje zbog svoje jednostavnosti. Uspoređujemo adrese funkcija učitanih direktno iz libc, i “NEXT” adrese.

#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;
}

Učitavanje biblioteke presretnom "otvori()" i provjeri:

$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (syscall - open) [+]
Libc address: 0x7fa86893b160
Next address: 0x7fa868a26135

Pobijanje se pokazalo još jednostavnijim:

#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

Čini se da je to sve, ali mi ćemo se i dalje koprcati. Ako sistemski poziv uputimo direktno na kernel, to će zaobići cijeli proces presretanja. Rješenje u nastavku je, naravno, ovisno o arhitekturi (x86_64). Pokušajmo ga implementirati kako bismo otkrili otvor 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) [+]

I ovaj problem ima rješenje. Izvod iz čovjek'A:

ptrace je alat koji omogućava roditeljskom procesu da posmatra i kontroliše napredak drugog procesa, pregleda i menja njegove podatke i registre. Obično se ova funkcija koristi za kreiranje tačaka prekida u programu za otklanjanje grešaka i nadgledanje sistemskih poziva.

Roditeljski proces može započeti praćenje tako što će prvo pozvati fork(2), a zatim rezultirajući podređeni proces može izvršiti PTRACE_TRACEME, nakon čega (obično) slijedi izvršavanje exec(3). S druge strane, roditeljski proces može započeti otklanjanje grešaka u postojećem procesu koristeći PTRACE_ATTACH.

Prilikom praćenja, podređeni proces se zaustavlja svaki put kada primi signal, čak i ako se signal zanemari. (Izuzetak je SIGKILL, koji radi normalno.) Roditeljski proces će biti obaviješten o tome pozivanjem wait(2), nakon čega može vidjeti i modificirati sadržaj podređenog procesa prije nego što počne. Roditeljski proces tada dozvoljava djetetu da nastavi s radom, u nekim slučajevima zanemarujući signal koji mu je poslan ili umjesto toga šalje drugi signal).

Dakle, rješenje je pratiti proces, zaustavljajući ga prije svakog sistemskog poziva i, ako je potrebno, preusmjeriti nit na funkciju zakače.

#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, &regs);
                        // 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, &regs);
                    }
                }
                ptrace(PTRACE_SYSCALL, program, 0, 0);
                waitpid(program, &status, 0);
            }
        }
        exit(0);
    }
    else {
        sleep(0);
    }
}

Provjeravamo:

$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
$ LD_PRELOAD=./ld_undetect_syscall.so ./detect_syscall
LD_PRELOAD (open syscall) [-]

+0-0=5

Hvala ti puno

Charles Hubain
choke point
ValdikSS
Philippe Teuwen
derhass

, čiji su članci, izvori i komentari učinili mnogo više od mene da se ova bilješka pojavi ovdje.

izvor: www.habr.com

Dodajte komentar