Ysgrifennwyd y nodyn hwn yn 2014, ond des i dan ormes ar Habré ac ni welodd olau dydd. Yn ystod y gwaharddiad anghofiais amdano, ond nawr fe wnes i ddod o hyd iddo yn y drafftiau. Meddyliais am ei ddileu, ond efallai y bydd yn ddefnyddiol i rywun.
Yn gyffredinol, ychydig o ddarlleniad gweinyddol dydd Gwener ar y pwnc o chwilio am “wedi'i gynnwys” LD_PRELOAD.
1. Gwyriad byr i'r rhai nad ydynt yn gyfarwydd ag amnewid swyddogaeth
Gall y gweddill fynd yn syth i p.2.
Gadewch i ni ddechrau gydag enghraifft glasurol:
#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);
}
}
Rydym yn llunio heb unrhyw fflagiau:
$ gcc ./ld_rand.c -o ld_rand
Ac, yn ôl y disgwyl, rydym yn cael 5 rhif ar hap yn llai na 100:
$ ./ld_rand
53
93
48
57
20
Ond gadewch i ni ddychmygu nad oes gennym ni god ffynhonnell y rhaglen, ond mae angen i ni newid yr ymddygiad.
Gadewch i ni greu ein llyfrgell ein hunain gyda'n prototeip swyddogaeth ein hunain, er enghraifft:
int rand(){
return 42;
}
$ gcc -shared -fPIC ./o_rand.c -o ld_rand.so
Ac yn awr mae ein dewis ar hap yn eithaf rhagweladwy:
# LD_PRELOAD=$PWD/ld_rand.so ./ld_rand
42
42
42
42
42
Mae'r tric hwn yn edrych hyd yn oed yn fwy trawiadol os ydym yn allforio ein llyfrgell am y tro cyntaf
$ export LD_PRELOAD=$PWD/ld_rand.so
neu byddwn yn ei wneud yn gyntaf
# echo "$PWD/ld_rand.so" > /etc/ld.so.preload
ac yna rhedeg y rhaglen fel arfer. Nid ydym wedi newid un llinell o god yn y rhaglen ei hun, ond mae ei hymddygiad bellach yn dibynnu ar swyddogaeth fach yn ein llyfrgell. Ar ben hynny, ar adeg ysgrifennu'r rhaglen, mae'r rand ddim hyd yn oed yn bodoli.
Beth wnaeth i'n rhaglen ddefnyddio ffug rand? Gadewch i ni ei gymryd gam wrth gam.
Pan fydd cais yn cychwyn, mae rhai llyfrgelloedd yn cael eu llwytho sy'n cynnwys y swyddogaethau sydd eu hangen ar y rhaglen. Gallwn eu gweld yn defnyddio 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)
Gall y rhestr hon amrywio yn dibynnu ar y fersiwn OS, ond mae'n rhaid bod ffeil yno libc.so.. Y llyfrgell hon sy'n darparu galwadau system a swyddogaethau sylfaenol megis agor, malloc, printf etc Ein rand yn eu plith hefyd. Gadewch i ni wneud yn siŵr o hyn:
# nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep " rand$"
000000000003aef0 T rand
Gadewch i ni weld a yw'r set o lyfrgelloedd yn newid pan gânt eu defnyddio 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)
Troi allan y newidyn yn cael ei osod LD_PRELOAD gorfodi ein i lwytho ld_rand.so hyd yn oed er gwaethaf y ffaith nad yw'r rhaglen ei hun yn ei gwneud yn ofynnol. Ac ers ein swyddogaeth "rand" llwythi yn gynt na rand o libc.so., yna mae hi'n rheoli'r glwydfan.
Iawn, rydym wedi llwyddo i ddisodli'r swyddogaeth frodorol, ond sut y gallwn wneud yn siŵr bod ei ymarferoldeb yn cael ei gadw a rhai camau gweithredu yn cael eu hychwanegu. Gadewch i ni addasu ein hap:
#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();
}
Yma, fel ein “ychwanegiad”, dim ond un llinell o destun rydyn ni'n ei hargraffu, ac ar ôl hynny rydyn ni'n creu pwyntydd i'r swyddogaeth wreiddiol rand. Er mwyn cael cyfeiriad y swyddogaeth hon mae arnom ei angen dlsym yn swyddogaeth o'r llyfrgell libdla fydd yn dod o hyd i'n rand mewn pentwr o lyfrgelloedd deinamig. Ar ôl hynny byddwn yn galw'r swyddogaeth hon ac yn dychwelyd ei werth. Yn unol â hynny, bydd angen inni ychwanegu "-ldl" yn ystod y gwasanaeth:
$ 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
Ac mae ein rhaglen yn defnyddio "brodorol" rand, wedi cyflawni rhai gweithredoedd anweddus o'r blaen.
2. Y boen o chwilio
Gan wybod am fygythiad posibl, rydym am ganfod hynny rhaglwyth Fe'i perfformiwyd. Mae'n amlwg mai'r ffordd orau o ganfod yw ei wthio i'r cnewyllyn, ond roedd gennyf ddiddordeb yn yr opsiynau canfod yn y gofod defnyddwyr.
Nesaf, bydd atebion ar gyfer canfod a'u gwrthbrofi yn dod mewn parau.
2.1. Gadewch i ni ddechrau syml
Fel y soniwyd yn gynharach, gallwch chi nodi'r llyfrgell i'w llwytho gan ddefnyddio'r newidyn LD_PRELOAD neu drwy ei ysgrifennu mewn ffeil /etc/ld.so.preload. Gadewch i ni greu dau synhwyrydd syml.
Yr un cyntaf yw gwirio'r newidyn amgylchedd gosod:
#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");
}
Yr ail yw gwirio a yw'r ffeil yn cael ei hagor:
#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");
}
Gadewch i ni lwytho'r llyfrgelloedd:
$ 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) [+]
Yma ac isod, [+] yn dynodi canfod llwyddiannus.
Yn unol â hynny, mae [-] yn golygu canfod ffordd osgoi.
Pa mor effeithiol yw synhwyrydd o'r fath? Gadewch i ni edrych ar y newidyn amgylchedd yn gyntaf:
#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) [-]
Yn yr un modd, rydym yn cael gwared ar y siec agor:
#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) [-]
Oes, gellir defnyddio ffyrdd eraill o gael mynediad at y ffeil yma, megis, agored64, stat ac ati, ond, mewn gwirionedd, mae angen yr un 5-10 llinell o god i'w twyllo.
2.2. Gadewch i ni symud ymlaen
Uchod rydym yn defnyddio cael () i gael y gwerth LD_PRELOAD, ond mae yna hefyd ffordd fwy “lefel isel” o gyrraedd ENV-newidyn. Ni fyddwn yn defnyddio swyddogaethau canolraddol, ond byddwn yn cyfeirio at yr arae **amgylchedd, sy'n storio copi o'r amgylchedd:
#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;
}
Ers yma rydym yn darllen data yn uniongyrchol o'r cof, ni ellir rhyng-gipio galwad o'r fath, ac mae ein undetect_getenv nid yw bellach yn ymyrryd â nodi'r ymwthiad.
$ LD_PRELOAD=./ld_undetect_getenv.so ./detect_environ
LD_PRELOAD (**environ) [+]
Mae'n ymddangos bod y broblem hon wedi'i datrys? Megis dechrau ydyw o hyd.
Ar ôl i'r rhaglen ddechrau, gwerth y newidyn LD_PRELOAD nid oes ei angen ar hacwyr yn y cof mwyach, hynny yw, gallant ei ddarllen a'i ddileu cyn gweithredu unrhyw gyfarwyddiadau. Wrth gwrs, mae golygu arae yn y cof, o leiaf, yn arddull rhaglennu wael, ond a all hyn atal rhywun nad yw'n dymuno'n dda inni beth bynnag?
I wneud hyn mae angen i ni greu ein swyddogaeth ffug ein hunain ynddo(), yr ydym yn rhyng-gipio'r gosod LD_PRELOAD a'i drosglwyddo i'n cysylltydd:
#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;
}
Rydym yn gweithredu ac yn gwirio:
$ 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/hunan/
Fodd bynnag, nid cof yw'r lle olaf lle gallwch ddod o hyd i eilydd LD_PRELOAD, mae hefyd /proc/. Gadewch i ni ddechrau gyda'r amlwg /proc/{PID}/amgylchedd.
Mewn gwirionedd, mae ateb cyffredinol ar gyfer anganfod **amgylchedd и /proc/self/environ. Y broblem yw ymddygiad “anghywir”. anosodedig(env).
opsiwn cywir
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] != ' '; 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
$
Ond gadewch i ni ddychmygu na wnaethom ddod o hyd iddo a /proc/self/environ yn cynnwys data "problemus".
Yn gyntaf, gadewch i ni roi cynnig ar ein "cuddwisg" blaenorol:
$ (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 yn defnyddio'r un peth i agor y ffeil agored (), felly mae'r datrysiad yn debyg i'r hyn a wnaed eisoes yn adran 2.1, ond nawr rydym yn creu ffeil dros dro lle rydym yn copïo gwerthoedd y gwir gof heb linellau'n cynnwys 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);
}
Ac mae'r cam hwn wedi'i basio:
$ (LD_PRELOAD=./ld_undetect_proc_environ.so cat /proc/self/environ; echo) | tr " 00" "n" | grep -F LD_PRELOAD
$
Y lle amlwg nesaf yw /proc/self/mapiau. Nid oes diben trigo arno. Mae'r datrysiad yn hollol union yr un fath â'r un blaenorol: copïwch y data o'r ffeil heb y llinellau rhyngddynt libc.so. и ld.so..
2.4. Opsiwn o Chokepoint
Hoffais yr ateb hwn yn arbennig oherwydd ei symlrwydd. Rydym yn cymharu cyfeiriadau swyddogaethau a lwythwyd yn uniongyrchol o libc, a chyfeiriadau “NESAF”.
#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;
}
Llwytho'r llyfrgell gyda rhyng-gipio "agor()" a gwirio:
$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (syscall - open) [+]
Libc address: 0x7fa86893b160
Next address: 0x7fa868a26135
Trodd y gwrthbrofiad yn symlach fyth:
#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
Mae'n ymddangos mai dyma'r cyfan, ond byddwn yn dal i ymdrybaeddu. Os byddwn yn cyfeirio'r alwad system yn uniongyrchol i'r cnewyllyn, bydd hyn yn osgoi'r broses rhyng-gipio gyfan. Mae'r ateb isod, wrth gwrs, yn dibynnu ar bensaernïaeth (x86_64). Gadewch i ni geisio ei weithredu i ganfod agoriad 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) [+]
Ac mae gan y broblem hon ateb. Dyfyniad o dyn'A:
Offeryn yw ptrace sy'n caniatáu i riant broses arsylwi a rheoli cynnydd proses arall, gweld a newid ei data a'i chofrestrau. Yn nodweddiadol, defnyddir y swyddogaeth hon i greu torbwyntiau mewn rhaglen ddadfygio a monitro galwadau system.
Gall y broses rhiant ddechrau olrhain trwy fforch galw (2) yn gyntaf, ac yna gall y broses plentyn ddilynol weithredu PTRACE_TRACEME, ac yna (fel arfer) trwy weithredu exec(3). Ar y llaw arall, gall proses rhiant ddechrau dadfygio proses sy'n bodoli eisoes gan ddefnyddio PTRACE_ATTACH.
Wrth olrhain, mae'r broses plentyn yn stopio bob tro y bydd yn derbyn signal, hyd yn oed os anwybyddir y signal. (Yr eithriad yw SIGKILL, sy'n gweithio fel arfer.) Hysbysir y rhiant broses o hyn trwy ffonio wait(2), ac wedi hynny gall weld ac addasu cynnwys proses y plentyn cyn iddi ddechrau. Yna mae'r broses rhiant yn caniatáu i'r plentyn barhau i redeg, mewn rhai achosion anwybyddu'r signal a anfonwyd ato neu anfon signal arall yn lle hynny).
Felly, yr ateb yw monitro'r broses, ei atal cyn pob galwad system ac, os oes angen, ailgyfeirio'r edau i swyddogaeth bachyn.
#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);
}
}
Rydym yn gwirio:
$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
$ LD_PRELOAD=./ld_undetect_syscall.so ./detect_syscall
LD_PRELOAD (open syscall) [-]
+0-0=5
Diolch yn fawr
, y gwnaeth ei erthyglau, ffynonellau a sylwadau lawer mwy nag a wnes i i wneud i'r nodyn hwn ymddangos yma.
Ffynhonnell: hab.com