Auf der Suche nach LD_PRELOAD

Diese Notiz wurde im Jahr 2014 verfasst, aber ich bin gerade an der Drehscheibe unter Druck geraten, und sie hat das Licht nicht gesehen. Während des Verbots habe ich es vergessen, aber jetzt habe ich es in Entwurfskopien gefunden. Ich dachte, es wäre zu löschen, aber vielleicht ist jemand hilfreich.



Im Allgemeinen eine kleine Freitag Admin Lesung zum Thema der Suche nach dem "enthaltenen" LD_PRELOAD .

1. Ein kleiner Exkurs für diejenigen, die mit Funktionssubstitution nicht vertraut sind


Der Rest kann direkt mit Schritt 2 fortfahren .

Beginnen wir mit dem klassischen Beispiel:

#include <stdio.h> #include <stdlib.h> #include <time.h> int main() { srand (time(NULL)); for(int i=0; i<5; i++){ printf ("%d\n", rand()%100); } } 

Kompiliere ohne irgendwelche Flags:

 $ gcc ./ld_rand.c -o ld_rand 

Und wie erwartet erhalten wir 5 Zufallszahlen kleiner als 100:

 $ ./ld_rand 53 93 48 57 20 

Angenommen, wir haben nicht den Quellcode des Programms und müssen das Verhalten ändern.

Erstellen wir eine eigene Bibliothek mit unserem eigenen Funktionsprototyp, zum Beispiel:

 int rand(){ return 42; } 

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

Und jetzt ist unsere zufällige Wahl ziemlich vorhersehbar:

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

Dieser Trick sieht noch beeindruckender aus, wenn wir unsere Bibliothek zuerst durch exportieren

 $ export LD_PRELOAD=$PWD/ld_rand.so 

oder vorab ausführen

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

Führen Sie dann das Programm in dem normalen Modus aus. Wir haben keine einzige Zeile im Code des Programms selbst geändert, aber sein Verhalten hängt jetzt von einer winzigen Funktion in unserer Bibliothek ab. Außerdem existierte der falsche Rand zum Zeitpunkt des Schreibens nicht einmal.

Was hat unser Programm dazu gebracht, Fake Rand zu verwenden ? Lass uns die Schritte durchgehen.
Beim Start der Anwendung werden bestimmte Bibliotheken geladen, die die für das Programm erforderlichen Funktionen enthalten. Wir können sie mit ldd sehen :

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

Diese Liste kann je nach Betriebssystemversion variieren, es muss jedoch eine libc.so- Datei vorhanden sein . Es ist diese Bibliothek, die Systemaufrufe und Grundfunktionen wie open , malloc , printf usw. bereitstellt. Unser rand ist auch einer von ihnen. Stellen Sie sicher, dass:

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

Mal sehen, ob sich die Bibliotheken ändern, wenn LD_PRELOAD verwendet wird

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

Es stellt sich heraus, dass die gesetzte Variable LD_PRELOAD unser ld_rand.so zum Laden zwingt, obwohl das Programm es selbst nicht benötigt. Und da unsere rand- Funktion früher als rand aus libc.so geladen wird , regiert sie den Ball.

Ok, wir haben es geschafft, die native Funktion zu ersetzen, aber wie wir sicherstellen, dass ihre Funktionalität erhalten bleibt und einige Aktionen hinzugefügt werden. Wir modifizieren unseren Zufall:

 #define _GNU_SOURCE #include <dlfcn.h> #include <stdio.h> typedef int (*orig_rand_f_type)(void); int rand() { /*    */ printf("Evil injected code\n"); orig_rand_f_type orig_rand; orig_rand = (orig_rand_f_type)dlsym(RTLD_NEXT,"rand"); return orig_rand(); } 

Hier drucken wir als „Additiv“ nur eine Textzeile und erstellen dann einen Zeiger auf die ursprüngliche Rand- Funktion. Um die Adresse dieser Funktion zu erhalten, benötigen wir dlsym - dies ist eine Funktion aus der libdl- Bibliothek, die unseren rand im Stapel dynamischer Bibliotheken findet. Danach rufen wir diese Funktion auf und geben ihren Wert zurück. Dementsprechend müssen wir beim Erstellen "-ldl" hinzufügen:

 $ 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 

Und unser Programm verwendet den "nativen" Rand , nachdem es einige unanständige Aktionen ausgeführt hat.

2. Mehlsuche


In Kenntnis der potenziellen Bedrohung möchten wir feststellen, dass die Vorspannung ausgeführt wurde. Es ist klar, dass der beste Weg, dies zu erkennen, darin besteht, es in den Kernel zu schieben, aber ich war an genau den Definitionen im Benutzerraum interessiert.

Als nächstes werden die Lösungen für die Erkennung und deren Widerlegung paarweise ausgeführt.

2.1. Beginnen wir mit einem einfachen


Wie bereits erwähnt, können Sie die zu ladende Bibliothek mithilfe der Variablen LD_PRELOAD oder durch Schreiben in die Datei /etc/ld.so.preload angeben . Lassen Sie uns zwei einfachste Detektoren erstellen.

Die erste besteht darin, die eingestellte Umgebungsvariable zu überprüfen:

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

Die zweite ist das Öffnen der Datei zu überprüfen:

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

Bibliotheken laden:

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

Nachfolgend zeigt [+] eine erfolgreiche Erkennung an.
Dementsprechend bedeutet [-] Bypass-Erkennung.

Wie effektiv ist ein solcher Detektor? Schauen wir uns zunächst die Umgebungsvariable an:

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

Ebenso werden wir den offenen Scheck los:

 #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, hier können andere Methoden für den Zugriff auf die Datei verwendet werden, z. B. open64 , stat usw. Tatsächlich werden jedoch dieselben 5-10 Codezeilen benötigt, um sie zu täuschen.

2.2. Weitermachen


Oben haben wir getenv () verwendet , um den Wert von LD_PRELOAD abzurufen , aber es gibt auch einen einfacheren Weg, um zu ENV- Variablen zu gelangen. Wir werden keine Zwischenfunktionen verwenden, sondern auf das Umgebungsarray ** verweisen, in dem eine Kopie der Umgebung gespeichert ist:

 #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 wir hier Daten direkt aus dem Speicher lesen, kann ein solcher Anruf nicht abgefangen werden, und unser undetect_getenv stört die Bestimmung des Eindringens nicht mehr.

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

Es scheint, dass das Problem gelöst wurde? Ich fange gerade erst an.

Nach dem Start des Programms ist der Wert der Variable LD_PRELOAD im Speicher für Cracker nicht mehr erforderlich. Sie können ihn also lesen und löschen, bevor Anweisungen ausgeführt werden. Das Bearbeiten eines Arrays im Speicher ist natürlich zumindest ein schlechter Programmierstil, aber kann dies wirklich jemanden davon abhalten, der uns nicht wirklich alles Gute wünscht?

Dazu müssen wir unsere eigene Fake-Funktion init () erstellen, in der wir das installierte LD_PRELOAD abfangen und an unseren Linker übergeben:

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

Wir führen durch, überprüfen:

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


Der Speicher ist jedoch nicht der letzte Ort, an dem Sie LD_PRELOAD- Spoofing erkennen können . Es gibt auch / proc / . Beginnen wir mit dem offensichtlichen / proc / {PID} / environ .

Tatsächlich gibt es eine universelle Lösung für Undetect ** Environ und / proc / self / environ . Das Problem ist das "falsche" Verhalten von unsetenv (env) .

richtige Option
 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] != '\0'; j++) environ[i][j] = '\0'; 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 "\000" "\n" | grep -F LD_PRELOAD $ 


Angenommen, wir haben es nicht gefunden und / proc / self / environ enthält „problematische“ Daten.

Versuchen Sie zuerst mit unserer vorherigen "Verkleidung":

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

cat verwendet dasselbe open () , um die Datei zu öffnen, daher ähnelt die Lösung der bereits in Abschnitt 2.1 durchgeführten, aber jetzt erstellen wir eine temporäre Datei, in die wir die wahren Speicherwerte ohne Zeilen kopieren, die LD_PRELOAD enthalten.

 #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 != '\0'; ptr++) { found = 1; for(i = 0; found == 1 && sub[i] != '\0'; 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 == '\0') { //printf("\n"); if(!sstrstr(buffer, "LD_PRELOAD")) { if((wbytes = write(fh,buffer,k))==-1) { //printf("write error\n"); } else { //printf("writed %d\n", 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); } 

Und diese Phase ist abgeschlossen:

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

Der nächste naheliegende Ort ist / proc / self / maps . Es macht keinen Sinn, sich darauf einzulassen. Die Lösung ist absolut identisch mit der vorherigen: Kopieren Sie die Daten aus der Datei abzüglich der Zeilen zwischen libc.so und ld.so.

2.4. Option von Chokepoint


Mir hat diese Lösung wegen ihrer Einfachheit besonders gut gefallen. Vergleichen Sie die Adressen von Funktionen, die direkt aus libc geladen wurden, mit der Adresse "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: %p\n", libc_func); printf("Next address: %p\n", next_func); } else { printf("LD_PRELOAD (syscall - %s) [-]\n", syscall_open); } return 0; } 

Wir laden die Bibliothek mit Interception "open ()" und überprüfen:

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

Die Widerlegung stellte sich als noch einfacher heraus:

 #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


Es scheint, dass das alles ist, aber immer noch Flunder. Wenn wir einen Systemaufruf direkt an den Kernel richten, umgeht dies den gesamten Abfangprozess. Die folgende Lösung ist natürlich architekturabhängig ( x86_64 ). Versuchen wir, ld.so.preload zu implementieren, um die Öffnung zu erkennen.

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

Und dieses Problem hat eine Lösung. Auszug aus dem Menschen :
ptrace ist ein Tool, mit dem ein übergeordneter Prozess den Ablauf eines anderen Prozesses beobachten und steuern, seine Daten und Register anzeigen und ändern kann. In der Regel wird diese Funktion verwendet, um Haltepunkte in einem Debugging-Programm zu erstellen und Systemaufrufe zu verfolgen.

Der übergeordnete Prozess kann die Ablaufverfolgung starten, indem er zuerst die Funktion fork (2) aufruft. Anschließend kann der resultierende untergeordnete Prozess PTRACE_TRACEME ausführen, gefolgt von (normalerweise) der Ausführung von exec (3). Andererseits kann der übergeordnete Prozess das Debuggen des vorhandenen Prozesses mit PTRACE_ATTACH starten.

Bei der Ablaufverfolgung stoppt der untergeordnete Prozess jedes Mal, wenn ein Signal empfangen wird, auch wenn dieses Signal ignoriert wird. (Die Ausnahme ist SIGKILL, was auf die übliche Weise funktioniert.) Der übergeordnete Prozess wird benachrichtigt, wenn wait (2) aufgerufen wird. Anschließend kann er den Inhalt des untergeordneten Prozesses anzeigen und ändern, bevor er gestartet wird. Danach kann das Kind über den übergeordneten Prozess weiterarbeiten. In einigen Fällen wird das an ihn gesendete Signal ignoriert oder stattdessen ein anderes Signal gesendet.

Daher besteht die Lösung darin, den Prozess zu verfolgen, ihn vor jedem Systemaufruf anzuhalten und den Thread bei Bedarf an die Trap-Funktion umzuleiten.

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

Wir prüfen:

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

+ 0-0 = 5

Vielen Dank

Charles Hubain
Chokepoint
Valdikss
Philippe Teuwen
derhass

deren Artikel, Quellcodes und Kommentare viel mehr als ich getan haben, um diesen Artikel hier erscheinen zu lassen.

Source: https://habr.com/ru/post/de479858/


All Articles