I jagten på LD_PRELOAD

Denne note blev skrevet i 2014, men jeg kom lige ind under repressionerne på Habra, og den så aldrig dagens lys. I løbet af mit forbud glemte jeg den, men nu fandt jeg den i mine kladder. Jeg overvejede at slette den, men hvem ved, måske vil den være nyttig for nogen.

I jagten på LD_PRELOAD

Generelt, en lille fredag teknik artikel om at finde "aktiveret". LD_PRELOAD.

1. En lille afvigelse til dem, der ikke er bekendt med funktionssubstitution.

Andre kan gå direkte til punkt 2..

Lad os starte med et klassisk eksempel:

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

Kompiler uden nogen flags:

$ gcc ./ld_rand.c -o ld_rand

Og, som forventet, får vi 5 tilfældige tal mindre end 100:

$ ./ld_rand
53
93
48
57
20

Men lad os forestille os, at vi ikke har kildekoden til programmet, men vi har brug for at ændre adfærden.

Lad os skabe vores egen bibliotek med vores egen prototype af funktionen, for eksempel:

int rand(){
  return 42;
}

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

Og nu er vores tilfældige valg helt forudsigeligt:

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

Dette trick ser endnu mere imponerende ud, hvis vi først eksporterer vores bibliotek gennem

$ export LD_PRELOAD=$PWD/ld_rand.so

eller udfører det på forhånd

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

og derefter kører programmet som normalt. Vi har ikke ændret en linje i selve programmets kode, men dens adfærd afhænger nu af en lille funktion i vores bibliotek. Desuden eksisterede den falske rand overhovedet ikke, da programmet blev skrevet.

Hvad fik så vores program til at bruge den falske rand? Разберем по шагам.
Når applikationen startes, indlæses bestemte biblioteker, der indeholder de funktioner, programmet har brug for. Vi kan se dem ved at bruge 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)

Denne liste kan variere afhængigt af OS-versionen, men der skal bestemt være en fil. libc.so. Det er netop dette bibliotek, der håndterer systemopkald og grundlæggende funktioner såsom open, malloc, printf og så videre. Vores rand er også en del af dem. Lad os sikre os dette:

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

Lad os se, om sætningen af biblioteker ændres ved at bruge 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 viser sig, at den installerede variable LD_PRELOAD får vores ld_rand.so til at blive indlæst, selvom programmet selv ikke kræver den. Og da vores funktion "rand" bliver indlæst før rand fra libc.so, så er det den, der har kontrol.

Okay, vi har været i stand til at erstatte den oprindelige funktion, men hvordan kan vi bevare dens funktionalitet og tilføje nogle handlinger. Lad os modificere vores random:

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

Her som vores "tilføjelse" printer vi blot en sætning tekst, hvorefter vi opretter en pointer til den oprindelige funktion. randFor at få adressen på denne funktion, vil vi have brug for dlsym — det er en funktion fra biblioteket libdl, som vil finde vores rand i stakken af dynamiske biblioteker. Derefter vil vi kalde denne funktion og returnere dens værdi. Derfor skal vi tilføje «-ldl» ved kompilering:

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

$ LD_PRELOAD=$PWD /ld_rand_evil.so ./ld_rand
Ond kode injiceret
66
Ond kode injiceret
28
Ond kode injiceret
93
Ond kode injiceret
93
Ond kode injiceret
95

Og vores program bruger «native» rand, efter at have udført nogle uhøflige handlinger.

2. Søgesmerter

Ud fra en potentiel trussel ønsker vi at opdage, hvad preload er blevet udført. Det er klart, at den bedste metode til detektion ville være at putte det i kernen, men jeg var interesseret i alternativer til at identificere det i user space.

Derefter vil parret være løsninger til opdage og deres tilbagevisning.

2.1. Lad os starte med det simple

Som nævnt tidligere, kan den indlæste bibliotek angives ved hjælp af variablen LD_PRELOAD eller ved at skrive den i filen /etc/ld.so.preload. Lad os oprette to enkle detektorer.

Den første — til at kontrollere den etablerede miljøvariabel:

#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 anden — til at kontrollere åbningen af filen:

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

Lad os indlæse bibliotekerne:

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

Her og fremad angiver [+] en vellykket detektion.
Derfor betyder [-] at detektion er blevet omgået.

Hvor effektiv er sådan en detektor? Lad os først beskæftige os med miljøvariablen:

#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å lignende måde slipper vi for 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, der kan bruges andre metoder til adgang til filen, såsom, open64, stat og så videre, men grundlæggende kræves de samme 5-10 linjer kode for at snyde dem.

2.2. Lad os gå videre

Ovenfor brugte vi getenv() til at få værdien LD_PRELOAD, men der er også en mere «low-level» metode til at nå ENV-variabler. Vi undgår at bruge mellemfunktioner og adresserer direkte til arrayet **environ, som opbevarer en kopi af miljøet:

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

Da vi læser data direkte fra hukommelsen her, kan denne opkald ikke blive fanget, og vores undetect_getenv forstyrrer ikke længere detektion af intrusion.

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

Det kunne tilsyneladende se ud som om problemet var løst? Det starter faktisk kun.

Når programmet er startet, er værdien af variablen LD_PRELOAD i hukommelsen ikke længere nødvendigt for hackere, så den kan betragtes som sikker at slette før nogen instruktioner udføres. Selvfølgelig er det en dårlig programmeringspraksis at ændre et array i hukommelsen, men kan det virkelig stoppe nogen, der som minimum ikke ønsker os det godt?

Til dette skal vi skabe vores egen falske funktion init(), hvor vi opfanger den fastsatte LD_PRELOAD og sender den til vores linker:

#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 udfører, verificerer:

$ 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 hukommelse er ikke det sidste sted, hvor man kan opdage undertrykkelse LD_PRELOAD, der er også /proc/. Lad os starte med det indlysende /proc/{PID}/environ.

Der findes faktisk en universel løsning for undetection. **environ og /proc/self/environProblemet ligger i den "forkerte" adfærd unsetenv(env).

den rigtige løsning

void evil_init()
{
  // Først gemmer vi den nuværende værdi af 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) {
        // Fjern den nuværende LD_PRELOAD 
        //unsetenv(env);
        // I stedet for at unsette bare nulstiller vi vores 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 lad os forestille os, at vi ikke fandt det og /proc/self/environ indeholder "problematiske" data.

Først prøver vi med vores tidligere "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 bruger stadig den samme open(), så løsningen ligner det, der allerede er gjort i afsnit 2.1, men nu opretter vi en midlertidig fil, hvor vi kopierer værdierne fra den sande hukommelse uden linjer der indeholder 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);
}

Og dette trin er gennemført:

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

Det næste oplagte sted er /proc/self/maps. Der er ingen grund til at dvæle ved det. Løsningen er præcis den samme som den forrige: vi kopierer data fra filen undtagen linjer mellem libc.so og ld.so.

2.4. Chokepoint-versionen

Denne løsning kunne jeg især lide på grund af sin enkelhed. Vi sammenligner adresserne på funktioner, der indlæses direkte fra libc, og "NEXT"-adresserne.

#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 indlæser biblioteket med interception "open()" og tjekker:

$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (syscall - open) [+]
Libc adresse: 0x7fa86893b160
Næste adresse: 0x7fa868a26135

Afkræftelsen viste sig at være endnu lettere:

#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

Det ville umiddelbart være alt, men lad os dykke lidt dybere. Hvis vi sender systemopkaldet direkte til kernen, vil det gøre det muligt at omgå hele opsnapningsprocessen. Løsningen nedenfor er naturligvis arkitekturafhængig (JIT-kompilereren var den første i rækken af ændringer: i 2012). Lad os prøve at implementere det for at opdage åbning 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) [+]

Og denne opgave har en løsning. Uddrag fra man'a:

ptrace er et middel, der giver forælderprocessen mulighed for at overvåge og kontrollere udførelsen af en anden proces, se og ændre dens data og registre. Denne funktion bruges normalt til at oprette breakpoint i debuggingprogrammer og spore systemopkald.

Forælderprocessen kan starte sporing ved først at kalde funktionen fork(2), hvorefter den resulterende datterproces kan udføre PTRACE_TRACEME, efterfulgt (normalt) af eksekvering af exec(3). På den anden side kan forælderprocessen begynde at debugge en eksisterende proces ved hjælp af PTRACE_ATTACH.

Ved sporing stopper datterprocessen hver gang den modtager et signal, selvom dette signal ignoreres. (Undtagelsen er SIGKILL, som fungerer normalt.) Forælderprocessen vil blive informeret om dette ved at kalde wait(2), hvorefter den kan se og ændre indholdet af datterprocessen, før den genstartes. Herefter giver forælderprocessen datterprocessen lov til at fortsætte, ofte ved at ignorere det sendte signal eller sende et andet signal i stedet).

Dermed er løsningen at overvåge processen, stoppe den før hvert systemopkald og, om nødvendigt, omdirigere strømmen til en trapfunktion.

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

Vi tjekker:

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

+0-0=5

Mange tak

Charles Hubain
Chokepoint
ValdikSS
Philippe Teuwen
derhass

, hvis artikler, kildekoder og kommentarer har gjort langt mere end jeg for at få denne note til at se dagens lys.

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster