Dieser Artikel wurde im Jahr 2014 verfasst, aber ich war gerade von Repressalien auf Habré betroffen, und er wurde nicht veröffentlicht. Während meines Bannes habe ich ihn vergessen, aber jetzt habe ich ihn in den Entwürfen wiedergefunden. Ich dachte daran, ihn zu löschen, aber vielleicht ist er doch nützlich für jemanden.

Im Allgemeinen handelt es sich um eine kleine Lektüre für Administratoren am Freitag zum Thema der Suche nach "aktivierten" Funktionen LD_PRELOAD.
1. Eine kleine Anmerkung für diejenigen, die mit der Funktionsersetzung nicht vertraut sind
Die anderen können direkt zu Punkt 2.
Lassen Sie uns mit einem klassischen Beispiel beginnen:
#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);
}
}
Kompilieren wir ohne irgendwelche Flags:
$ gcc ./ld_rand.c -o ld_rand
Und erwartungsgemäß erhalten wir 5 zufällige Zahlen unter 100:
$ ./ld_rand
53
93
48
57
20
Aber stellen wir uns vor, dass wir keinen Quellcode des Programms haben und das Verhalten geändert werden muss.
Erstellen wir unsere 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 Auswahl völlig vorhersehbar:
# LD_PRELOAD=$PWD/ld_rand.so ./ld_rand
42
42
42
42
42
Dieser Trick wirkt noch beeindruckender, wenn wir zunächst unsere Bibliothek über
$ export LD_PRELOAD=$PWD/ld_rand.so
oder sie zuvor ausführen
# echo "$PWD/ld_rand.so" > /etc/ld.so.preload
und dann werden wir das Programm im normalen Modus starten. Wir haben keinen einzigen Code-Zeile der Programmierung geändert, aber sein Verhalten hängt jetzt von einer kleinen Funktion in unserer Bibliothek ab. Darüber hinaus existierte zum Zeitpunkt des Schreibens des Programms die gefälschte rand nicht einmal.
Was hat unser Programm also dazu gebracht, die gefälschte rand? Разберем по шагам.
Wenn die Anwendung gestartet wird, werden bestimmte Bibliotheken geladen, die Funktionen enthalten, die für das Programm erforderlich sind. Wir können sie mit 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)
einsehen. Diese Liste kann je nach Version des Betriebssystems variieren, aber sie muss unbedingt die Datei libc.soenthalten. Genau diese Bibliothek gewährleistet Systemaufrufe und grundlegende Funktionen wie öffnen, malloc, printf usw. Unser rand gehört ebenfalls dazu. Lassen Sie uns das überprüfen:
# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep " rand$"
000000000003aef0 T rand
Sehen wir, ob sich die Bibliothekenliste bei der Verwendung von 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)
tatsächlich ändert, wenn die Umgebungsvariable LD_PRELOAD unsere ld_rand.so lädt, obwohl das Programm sie nicht benötigt. Und da unsere Funktion "rand" früher geladen wird als rand ab libc.so, hat sie auch das Sagen.
Wir haben die ursprüngliche Funktion erfolgreich ersetzt, aber wie können wir sicherstellen, dass ihre Funktionalität erhalten bleibt und zusätzliche Aktionen hinzugefügt werden? Lassen Sie uns unsere Zufallszahl modifizieren:
#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();
}
Hier drucken wir lediglich eine Zeile Text aus, bevor wir einen Verweis auf die ursprüngliche Funktion erstellen. randUm die Adresse dieser Funktion zu erhalten, benötigen wir dlsym — das ist eine Funktion aus der Bibliothek libdl, die unseren rand im Stack der dynamischen Bibliotheken finden wird. Danach rufen wir diese Funktion auf und geben ihren Wert zurück. Folglich müssen wir „-ldl“ beim Kompilieren 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 die „native“ rand, nachdem es zuvor einige unerfreuliche Aktionen ausgeführt hat.
2. Die Qual der Wahl
In Anbetracht der potenziellen Bedrohung möchten wir feststellen, dass preload ausgeführt wurde. Offensichtlich ist der beste Weg zur Detektion, es in den Kernel zu integrieren, aber ich interessierte mich gerade für Methoden zur Bestimmung im Userspace.
Im Folgenden folgen Paare von Lösungen zur Erkennung und deren Widerlegung.
2.1. Beginnen wir mit etwas Einfachem
Wie bereits erwähnt, kann die zu ladende Bibliothek über die Variable angegeben werden, LD_PRELOAD oder indem sie in der Datei angegeben wird, /etc/ld.so.preload. Lassen Sie uns zwei einfache Detektoren erstellen.
Der erste — zur Überprüfung der gesetzten Umgebungsvariable:
#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");
}
Der zweite — zur Überprüfung des Dateiöffnens:
#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");
}
Laden Sie die Bibliotheken:
$ 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) [+]
Hier und weiterhin bedeutet [+] eine erfolgreiche Erkennung.
Entsprechend bedeutet [-] das Umgehen der Erkennung.
Wie effektiv ist ein solcher Detektor? Zuerst beschäftigen wir uns mit der Umgebungsvariable:
#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) [-]
Ähnlich entledigen wir uns der Überprüfung: öffnen:
#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 auch andere Zugriffsmethoden für Dateien verwendet werden, wie zum Beispiel, open64, stat usw., aber im Grunde benötigt man die gleichen 5-10 Zeilen Code, um sie zu täuschen.
2.2. Gehen wir weiter
Vorher haben wir getenv() verwendet, um den Wert zu erhalten, LD_PRELOADaber es gibt auch eine ‚niedrigere‘ Methode, um Zugang zu erhalten. ENV-Variablen. Wir werden keine Intermediärfunktionen verwenden, sondern auf das Array zugreifen **environ, das eine Kopie der Umgebung speichert:
#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 die Daten direkt aus dem Speicher lesen, kann ein solcher Aufruf nicht abgefangen werden, und unsere undetect_getenv stößt nicht mehr auf Hindernisse bei der Feststellung eines Eingriffs.
$ LD_PRELOAD=./ld_undetect_getenv.so ./detect_environ
LD_PRELOAD (**environ) [+]
Es scheint, als wäre damit das Problem gelöst? Es fängt erst richtig an.
Nachdem das Programm gestartet wurde, ist der Wert der Variable LD_PRELOAD im Speicher für Hacker nicht mehr von Interesse, das heißt, man kann ihn löschen, bevor irgendwelche Anweisungen ausgeführt werden. Natürlich ist es, das Array im Speicher zu ändern, mindestens schlechter Programmierstil, aber kann das wirklich jemanden aufhalten, der uns ohnehin nicht wohlgesonnen ist?
Dazu müssen wir unsere eigene gefälschte Funktion erstellen init(), in der wir das installierte Abfangen LD_PRELOAD und es unserem 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;
}
Ausführen und ü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/
Allerdings ist der Speicher nicht der letzte Ort, an dem eine Manipulation entdeckt werden kann LD_PRELOAD, es gibt noch /proc/. Lassen Sie uns mit dem Offensichtlichen beginnen /proc/{PID}/environ.
Tatsächlich gibt es eine universelle Lösung für undetect’а **environ und /proc/self/environ. Das Problem liegt im "falschen" Verhalten unsetenv(env).
die richtige Variante
void evil_init()
{
// Zuerst speichern wir den aktuellen Wert von 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) {
// Entfernen des aktuellen LD_PRELOAD
//unsetenv(env);
// Statt unset setzen wir einfach unsere Variable zurück
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
$
Aber stellen wir uns vor, dass wir es nicht gefunden haben und /proc/self/environ enthält "problematische" Daten.
Zuerst versuchen wir es mit unserer vorherigen "Maskierung":
$ (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 verwendet zum Öffnen der Datei immer noch die gleiche open(),, daher ist die Lösung ähnlich wie die, die bereits in Punkt 2.1 gemacht wurde, aber jetzt erstellen wir eine temporäre Datei, in die wir die Werte des echten Speichers ohne die enthaltenen Zeilen kopieren 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);
}
Und dieser Schritt ist abgeschlossen:
$ (LD_PRELOAD=./ld_undetect_proc_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
$
Der nächste offensichtliche Ort ist — /proc/self/maps. Es ist sinnlos, dort zu verweilen. Die Lösung ist absolut identisch zur vorherigen: Wir kopieren die Daten aus der Datei unter Ausschluss der Zeilen zwischen libc.so und ld.so.
2.4. Variante von Chokepoint
Diese Lösung hat mir besonders wegen ihrer Einfachheit gefallen. Wir vergleichen die Adressen der Funktionen, die direkt aus libc, und den „NEXT“-Adressen geladen werden.
#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;
}
Laden Sie die Bibliothek mit der Abfangfunktion „open()“ und überprüfen Sie:
$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (Syscall - open) [+]
Libc-Adresse: 0x7fa86893b160
Nächste Adresse: 0x7fa868a26135
Das Widerlegen war noch einfacher:
#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, als wäre damit alles gesagt, aber wir werden es noch etwas ausprobieren. Wenn wir den Systemaufruf direkt an den Kernel richten, ermöglicht dies, den gesamten Abfangprozess zu umgehen. Die folgende Lösung ist natürlich architekturabhängig (x86_64). Lassen Sie uns versuchen, dies für die Erkennung von Öffnungen zu implementieren 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) [+]
Und diese Aufgabe hat eine Lösung. Auszug aus man‘a:
ptrace ist ein Tool, das es einem Elternprozess ermöglicht, einen anderen Prozess zu überwachen und zu steuern, seine Daten und Register einzusehen und zu ändern. Diese Funktion wird in der Regel verwendet, um Breakpoints im Debugging-Programm zu setzen und Systemaufrufe zu verfolgen.
Der Elternprozess kann das Tracing starten, indem er zunächst die Funktion fork(2) aufruft, und der daraus resultierende Kindprozess kann dann PTRACE_TRACEME ausführen, gefolgt von der Ausführung von exec(3). Auf der anderen Seite kann der Elternprozess einen bestehenden Prozess mit PTRACE_ATTACH debuggen.
Während des Tracings wird der Kindprozess jedes Mal angehalten, wenn er ein Signal erhält, auch wenn dieses Signal ignoriert wird. (Eine Ausnahme bildet SIGKILL, das wie gewohnt funktioniert.) Der Elternprozess wird darüber bei einem Aufruf von wait(2) informiert, wonach er den Inhalt des Kindprozesses ansehen und ändern kann, bevor dieser fortgesetzt wird. Danach erlaubt der Elternprozess dem Kind, weiterzuarbeiten, indem er in einigen Fällen das gesendete Signal ignoriert oder stattdessen ein anderes Signal sendet.
Das Problem besteht darin, den Prozess zu überwachen, ihn vor jedem Systemaufruf anzuhalten und bei Bedarf den Fluss in 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, ®s);
// 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, ®s);
}
}
ptrace(PTRACE_SYSCALL, program, 0, 0);
waitpid(program, &status, 0);
}
}
exit(0);
}
else {
sleep(0);
}
}
Überprüfen wir:
$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
$ LD_PRELOAD=./ld_undetect_syscall.so ./detect_syscall
LD_PRELOAD (open syscall) [-]
+0-0=5
Vielen Dank
, dessen Artikel, Quellcodes und Kommentare viel mehr gemacht haben als ich, damit dieser Hinweis hier erscheint.
Quelle: habr.com
