Se caută LD_PRELOAD

Această notă a fost scrisă în 2014, dar tocmai am intrat în represiune asupra lui Habré și nu a văzut lumina zilei. În timpul interdicției l-am uitat, dar acum l-am găsit în schițe. M-am gândit să-l șterg, dar poate va fi de folos cuiva.

Se caută LD_PRELOAD

În general, un mic admin de vineri citind despre subiectul căutării „inclus” LD_PRELOAD.

1. O scurtă digresiune pentru cei care nu sunt familiarizați cu înlocuirea funcției

Restul poate merge direct la p.2.

Să începem cu un exemplu clasic:

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

Compilăm fără steaguri:

$ gcc ./ld_rand.c -o ld_rand

Și, așa cum era de așteptat, obținem 5 numere aleatorii mai mici de 100:

$ ./ld_rand
53
93
48
57
20

Dar să ne imaginăm că nu avem codul sursă al programului, dar trebuie să schimbăm comportamentul.

Să ne creăm propria bibliotecă cu propriul prototip de funcție, de exemplu:

int rand(){
  return 42;
}

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

Și acum alegerea noastră aleatorie este destul de previzibilă:

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

Acest truc pare și mai impresionant dacă ne exportăm mai întâi biblioteca prin

$ export LD_PRELOAD=$PWD/ld_rand.so

sau o vom face mai întâi

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

și apoi rulați programul ca de obicei. Nu am schimbat o singură linie de cod în programul în sine, dar comportamentul acestuia depinde acum de o funcție mică din biblioteca noastră. Mai mult, la momentul scrierii programului, rand nici nu a existat.

Ce a făcut ca programul nostru să folosească un fals rand? Să o luăm pas cu pas.
Când o aplicație pornește, sunt încărcate anumite biblioteci care conțin funcțiile necesare programului. Le putem vizualiza folosind 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)

Această listă poate varia în funcție de versiunea sistemului de operare, dar trebuie să existe un fișier acolo libc.so. Această bibliotecă oferă apeluri de sistem și funcții de bază, cum ar fi deschide, malloc, printf etc Noastră rand se numără și printre ei. Să ne asigurăm de asta:

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

Să vedem dacă setul de biblioteci se modifică atunci când este folosit 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)

Se pare că variabila este setată LD_PRELOAD ne obligă să încărcăm ld_rand.so chiar și în ciuda faptului că programul în sine nu o cere. Și din moment ce funcția noastră "rand" incarca mai devreme de rand din libc.so, apoi ea stăpânește adăpostul.

Ok, am reușit să înlocuim funcția nativă, dar cum ne putem asigura că funcționalitatea acesteia este păstrată și sunt adăugate unele acțiuni. Să ne modificăm aleatoriu:

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

Aici, ca „adăugare”, imprimăm doar o linie de text, după care creăm un pointer către funcția originală rand. Pentru a obține adresa acestei funcții avem nevoie dlsym este o funcție din bibliotecă libdlcare ne va găsi rand într-un teanc de biblioteci dinamice. După care vom apela această funcție și vom returna valoarea ei. În consecință, va trebui să adăugăm "-ldl" in timpul asamblarii:

$ 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

Și programul nostru folosește „nativ” rand, având în prealabil săvârșirea unor acțiuni obscene.

2. Durerea căutării

Știind despre o potențială amenințare, vrem să detectăm asta preîncărcare S-a realizat. Este clar că cel mai bun mod de a detecta este să îl împingeți în kernel, dar m-au interesat opțiunile de detectare din spațiul utilizatorului.

În continuare, soluțiile de detectare și respingerea lor vor veni în perechi.

2.1. Să începem simplu

După cum am menționat mai devreme, puteți specifica biblioteca de încărcat folosind variabila LD_PRELOAD sau scriind-o într-un fișier /etc/ld.so.preload. Să creăm doi detectori simpli.

Prima este să verificați variabila de mediu setată:

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

Al doilea este să verificați dacă fișierul este deschis:

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

Să încărcăm bibliotecile:

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

Aici și mai jos, [+] indică detectarea reușită.
În consecință, [-] înseamnă ocolirea detectării.

Cât de eficient este un astfel de detector? Să aruncăm o privire mai întâi la variabila de mediu:

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

În mod similar, scăpăm de cec deschide:

#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, alte moduri de a accesa fișierul pot fi folosite aici, cum ar fi, open64, Stat etc., dar, de fapt, sunt necesare aceleași 5-10 linii de cod pentru a-i înșela.

2.2. Sa trecem peste

Mai sus am folosit getenv() pentru a obține valoarea LD_PRELOAD, dar există și o modalitate mai „la nivel scăzut” de a ajunge la ENV-variabile. Nu vom folosi funcții intermediare, ci ne vom referi la matrice **înconjurător, care stochează o copie a mediului:

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

Deoarece aici citim date direct din memorie, un astfel de apel nu poate fi interceptat, iar noi undetect_getenv nu mai interferează cu identificarea intruziunii.

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

S-ar părea că această problemă este rezolvată? Încă abia începe.

După ce programul este pornit, valoarea variabilei LD_PRELOAD hackerii nu mai au nevoie de el în memorie, adică îl pot citi și șterge înainte de a executa orice instrucțiuni. Desigur, editarea unei matrice în memorie este, cel puțin, un stil de programare prost, dar cum poate acest lucru să oprească pe cineva care oricum nu ne dorește bine?

Pentru a face acest lucru, trebuie să ne creăm propria noastră funcție falsă init (), în care interceptăm instalatul LD_PRELOAD și transmiteți-l linkerului nostru:

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

Executam si verificam:

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

Cu toate acestea, memoria nu este ultimul loc în care puteți găsi o înlocuire LD_PRELOAD, de asemenea este si /proc/. Să începem cu ceea ce este evident /proc/{PID}/environ.

De fapt, există o soluție universală pentru nedetectat **înconjurător и /proc/self/environment. Problema este comportamentul „greșit”. unsetenv(env).

varianta corecta

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
$

Dar să ne imaginăm că nu l-am găsit și /proc/self/environment conţine date „problematice”.

Mai întâi să încercăm cu „deghizarea” noastră anterioară:

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

pisică folosește același lucru pentru a deschide fișierul deschis(), deci soluția este similară cu ceea ce sa făcut deja în secțiunea 2.1, dar acum creăm un fișier temporar în care copiem valorile memoriei adevărate fără linii care să conțină 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 această etapă a fost trecută:

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

Următorul loc evident este /proc/self/maps. Nu are rost să ne oprim asupra ei. Soluția este absolut identică cu cea anterioară: copiați datele din fișier minus liniile dintre acestea libc.so и ld.deci.

2.4. Opțiune de la Chokepoint

Mi-a plăcut în special această soluție pentru simplitatea ei. Comparăm adresele funcțiilor încărcate direct din libc, și adresele „URMĂTOR”.

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

Încărcarea bibliotecii cu interceptare "deschis()" si verifica:

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

Infirmarea s-a dovedit a fi și mai simplă:

#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. Apeluri de sistem

S-ar părea că asta este tot, dar încă ne vom zbuciuma. Dacă direcționăm apelul de sistem direct către nucleu, acest lucru va ocoli întregul proces de interceptare. Soluția de mai jos este, desigur, dependentă de arhitectură (x86_64). Să încercăm să o implementăm pentru a detecta o deschidere ld.so.preîncărcare.

#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 această problemă are o soluție. Extras din om'A:

ptrace este un instrument care permite unui proces părinte să observe și să controleze progresul altui proces, să vizualizeze și să modifice datele și registrele acestuia. De obicei, această funcție este folosită pentru a crea puncte de întrerupere într-un program de depanare și pentru a monitoriza apelurile de sistem.

Procesul părinte poate începe urmărirea apelând mai întâi fork(2), iar apoi procesul copil rezultat poate executa PTRACE_TRACEME, urmat (de obicei) de executarea exec(3). Pe de altă parte, un proces părinte poate începe depanarea unui proces existent folosind PTRACE_ATTACH.

La urmărire, procesul copil se oprește de fiecare dată când primește un semnal, chiar dacă semnalul este ignorat. (Excepția este SIGKILL, care funcționează normal.) Procesul părinte va fi notificat despre acest lucru prin apelarea wait(2), după care poate vizualiza și modifica conținutul procesului copil înainte de a începe. Procesul părinte îi permite apoi copilului să continue să alerge, în unele cazuri ignorând semnalul trimis acestuia sau trimițând în schimb un alt semnal).

Astfel, soluția este monitorizarea procesului, oprirea acestuia înainte de fiecare apel de sistem și, dacă este necesar, redirecționarea firului de execuție către o funcție de cârlig.

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

Verificăm:

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

+0-0=5

Va multumesc mult

Charles Hubain
Punct de sufocare
ValdikSS
Philippe Teuwen
derhass

, ale cărui articole, surse și comentarii au făcut mult mai mult decât mine pentru ca această notă să apară aici.

Sursa: www.habr.com

Adauga un comentariu