S'està buscant LD_PRELOAD

Aquesta nota es va escriure l'any 2014, però acabo de patir repressió a Habré i no va veure la llum. Durant la prohibició me'n vaig oblidar, però ara ho vaig trobar als esborranys. Vaig pensar a esborrar-lo, però potser li serà útil a algú.

S'està buscant LD_PRELOAD

En general, una petita lectura de l'administrador de divendres sobre el tema de la cerca de "inclòs" LD_PRELOAD.

1. Una breu digressió per a aquells que no estan familiaritzats amb la substitució de funcions

La resta pot anar directament clàusula 2.

Comencem amb un exemple clàssic:

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

Compilem sense cap bandera:

$ gcc ./ld_rand.c -o ld_rand

I, com era d'esperar, obtenim 5 nombres aleatoris inferiors a 100:

$ ./ld_rand
53
93
48
57
20

Però imaginem que no tenim el codi font del programa, però hem de canviar el comportament.

Creem la nostra pròpia biblioteca amb el nostre propi prototip de funció, per exemple:

int rand(){
  return 42;
}

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

I ara la nostra elecció aleatòria és bastant previsible:

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

Aquest truc sembla encara més impressionant si primer exportem la nostra biblioteca mitjançant

$ export LD_PRELOAD=$PWD/ld_rand.so

o ho farem primer

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

i després executeu el programa com de costum. No hem canviat ni una línia de codi al programa en si, però el seu comportament ara depèn d'una petita funció de la nostra biblioteca. A més, en el moment d'escriure el programa, el fila ni tan sols existia.

Què va fer que el nostre programa utilitzés una falsificació fila? Anem-ho pas a pas.
Quan s'inicia una aplicació, es carreguen determinades biblioteques que contenen les funcions necessàries pel programa. Les podem veure utilitzant 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)

Aquesta llista pot variar segons la versió del sistema operatiu, però hi ha d'haver un fitxer libc.so. És aquesta biblioteca la que proporciona trucades al sistema i funcions bàsiques com ara obrir, malloc, imprimirf etc. El nostre fila també està entre ells. Assegurem-nos d'això:

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

Vegem si el conjunt de biblioteques canvia quan s'utilitza 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)

Resulta que la variable està establerta LD_PRELOAD ens obliga a carregar ld_rand.so fins i tot malgrat que el programa en si no ho requereix. I des de la nostra funció "rand" càrregues abans fila d' libc.so, llavors ella governa el galliner.

D'acord, hem aconseguit substituir la funció nativa, però com podem assegurar-nos que la seva funcionalitat es conserva i s'afegeixen algunes accions. Modifiquem el nostre aleatori:

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

Aquí, com a "addició", només imprimim una línia de text, després de la qual creem un punter a la funció original fila. Per obtenir l'adreça d'aquesta funció necessitem dlsym és una funció de la biblioteca libdlque trobarà el nostre fila en una pila de biblioteques dinàmiques. Després de la qual cosa cridarem aquesta funció i retornarem el seu valor. En conseqüència, haurem d'afegir "-ldl" durant el muntatge:

$ 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 el nostre programa utilitza "natiu" fila, havent realitzat prèviament algunes accions obscenes.

2. El dolor de buscar

Coneixent una amenaça potencial, volem detectar-la precàrrega Es va realitzar. Està clar que la millor manera de detectar és introduir-lo al nucli, però m'interessen les opcions de detecció a l'espai d'usuari.

A continuació, les solucions per a la detecció i la seva refutació vindran per parelles.

2.1. Comencem senzill

Com s'ha esmentat anteriorment, podeu especificar la biblioteca per carregar mitjançant la variable LD_PRELOAD o escrivint-lo en un fitxer /etc/ld.so.preload. Creem dos detectors senzills.

El primer és comprovar la variable d'entorn establerta:

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

El segon és comprovar si el fitxer està obert:

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

Carreguem les biblioteques:

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

Aquí i sota, [+] indica la detecció correcta.
En conseqüència, [-] significa ometre la detecció.

Quina eficàcia té un detector així? Vegem primer la variable d'entorn:

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

De la mateixa manera, ens desfer del xec obrir:

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

Sí, aquí es poden utilitzar altres maneres d'accedir al fitxer, com ara, obert64, stat etc., però, de fet, calen les mateixes 5-10 línies de codi per enganyar-los.

2.2. Posem-nos en marxa

A dalt hem utilitzat getenv() per obtenir el valor LD_PRELOAD, però també hi ha una manera més "de baix nivell" per arribar-hi ENV-les variables. No utilitzarem funcions intermèdies, sinó que farem referència a la matriu **entorn, que emmagatzema una còpia de l'entorn:

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

Com que aquí estem llegint dades directament de la memòria, aquesta trucada no es pot interceptar, i la nostra undetect_getenv ja no interfereix en la identificació de la intrusió.

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

Sembla que aquest problema està solucionat? Encara està començant.

Després d'iniciar el programa, el valor de la variable LD_PRELOAD els pirates informàtics ja no el necessiten a la memòria, és a dir, poden llegir-lo i esborrar-lo abans d'executar cap instruccions. Per descomptat, editar una matriu a la memòria és, com a mínim, un mal estil de programació, però això pot aturar algú que realment no ens desitja el millor de totes maneres?

Per fer-ho, hem de crear la nostra pròpia funció falsa init (), en el qual interceptem l'instal·lat LD_PRELOAD i passa-ho al nostre enllaç:

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

Executem i comprovem:

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

Tanmateix, la memòria no és l'últim lloc on es pot trobar una substitució LD_PRELOAD, també hi ha /proc/. Comencem per l'obvi /proc/{PID}/environ.

De fet, hi ha una solució universal sense detectar **entorn и /proc/self/environ. El problema és el comportament "equivocat". unsetenv(env).

opció correcta

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
$

Però imaginem que no l'hem trobat i /proc/self/environ conté dades "problemàtiques".

Primer, provem amb la nostra "disfressa" anterior:

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

gat utilitza el mateix per obrir el fitxer obert (), així que la solució és semblant a la que ja es va fer a la secció 2.1, però ara creem un fitxer temporal on copiem els valors de la memòria real sense línies que continguin 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 aquesta etapa ha estat superada:

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

El següent lloc evident és /proc/self/maps. No té sentit insistir-hi. La solució és absolutament idèntica a l'anterior: copieu les dades del fitxer menys les línies entre elles libc.so и ld.so.

2.4. Opció de Chokepoint

Aquesta solució m'ha agradat especialment per la seva senzillesa. Comparem les adreces de les funcions carregades directament des libc, i adreces "SEGUENT".

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

Carregant la biblioteca amb intercepció "obrir()" i comproveu:

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

La refutació va resultar ser encara més senzilla:

#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

Sembla que això és tot, però encara ens estarem despistant. Si dirigim la trucada del sistema directament al nucli, això evitarà tot el procés d'intercepció. La solució a continuació depèn, per descomptat, de l'arquitectura (x86_64). Intentem implementar-lo per detectar una obertura ld.so.precarrega.

#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 aquest problema té solució. Extracte de home'A:

ptrace és una eina que permet a un procés pare observar i controlar el progrés d'un altre procés, veure i canviar les seves dades i registres. Normalment, aquesta funció s'utilitza per crear punts d'interrupció en un programa de depuració i supervisar les trucades del sistema.

El procés pare pot començar el seguiment cridant primer a fork(2), i després el procés fill resultant pot executar PTRACE_TRACEME, seguit (normalment) d'executar exec(3). D'altra banda, un procés pare pot començar a depurar un procés existent mitjançant PTRACE_ATTACH.

El seguiment atura el procés secundari cada vegada que rep un senyal, fins i tot si el senyal s'ignora. (L'excepció és SIGKILL, que funciona normalment.) El procés pare rebrà una notificació cridant wait(2), després de la qual cosa pot veure i modificar el contingut del procés fill abans que comenci. Aleshores, el procés principal permet que el nen continuï corrent, en alguns casos ignorant el senyal que se li envia o enviant un altre senyal).

Així, la solució és supervisar el procés, aturar-lo abans de cada trucada al sistema i, si cal, redirigir el fil a una funció de ganxo.

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

Comprovem:

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

+0-0=5

Moltes gràcies

Charles Hubain
Punt d'asfixia
ValdikSS
Philippe Teuwen
derhass

, els articles, fonts i comentaris del qual van fer molt més que jo perquè aquesta nota aparegués aquí.

Font: www.habr.com

Afegeix comentari