Në kërkim të LD_PRELOAD

Ky kjo shënim u shkrua në vitin 2014, por përfunda nën represionet në Habre dhe ajo nuk pa dritë. Gjatë kohës së ndalesës e harrova, dhe tani e gjeta në skicat. Mendova ta fshij, por ndoshta ndokujt do t'i vie në ndihmë.

Në kërkim të LD_PRELOAD

Në thelb, një lexim i vogël administrativ për të premten mbi kërkimin e "të aktivizuarit" LD_PRELOAD.

1. Një shkëputje e vogël për ata që nuk janë të njohur me zëvendësimin e funksioneve

Tjerët mund të kalojnë menjëherë në 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);
  }
}

Kompiluam pa asnjë flag:

$ gcc ./ld_rand.c -o ld_rand

Dhe, siç pritej, marim 5 numra të rastësishëm më pak se 100:

$ ./ld_rand
53
93
48
57
20

Por le të imaginarim se nuk e kemi kodin burimor të programit, ndërsa duhet të ndryshojmë sjelljen.

Të krijojmë bibliotekën tonë me prototipin e vet të funksionit, 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 trik duket akoma më impresionues, nëse më parë eksportojmë bibliotekën tonë përmes

$ export LD_PRELOAD=$PWD/ld_rand.so

ose e ekzekutojmë më parë

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

dhe më pas e hapim programin në mënyrën e zakonshme. Nuk kemi ndryshuar asnjë rresht në kodin e programit vetë, por sjellja e tij tani varet nga funksioni i vogël në bibliotekën tonë. Më tepër, në momentin e shkruarjes së programit, "rand" i falsifikuar rand as që ekzistonte.

Çfarë e detyroi programin tonë të përdorë të rremë rand? Разберем по шагам.
Kur aplikacioni fillon, ngarkohen disa biblioteka të caktuara, të cilat përmbajnë funksionet e nevojshme për programin. Ne mund ta shohim këtë 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ë ndryshojë në varësi të versionit të OS, por atje patjetër duhet të jetë ndonjë skedar libc.so. Pikërisht kjo bibliotekë siguron thirrjet sistemore dhe funksionet bazë, si open, malloc, printf etj. "rand" ynë rand po ashtu është në mesin e tyre. Le të sigurohemi për këtë:

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

Le të shohim nëse lista e bibliotekave do të ndryshojë duke përdorur 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 detyron të ngarkohet "ld_rand.so" edhe pse programi vetë nuk e kërkon atë. Dhe, pasi funksioni ynë "rand" ngarkohet para se , ai e merr kontrollin. rand nga libc.soOk, e zëvendësuam funksionin e vet, por si të bëjmë që edhe funksionaliteti i saj të ruhet dhe disa veprime të shtohen. Modifikojmë rastësinë tonë:

Këtu si "shtesë" ne vetëm printojmë një linjë teksti, pas së cilës krijojmë një tregues në funksionin burimor

#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ë" e jonë, 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 funksionaliteti, do të na nevojitet dlsym — kjo është një funksion nga biblioteka libdl, e cila do të gjejë tonë rand në stakun e librarive dinamike. Më pas, do ta thërrasim këtë funksion dhe do të kthejmë vlerën e tij. Prandaj, do të 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
Kod i injektuar e keq
66
Kod i injektuar e keq
28
Kod i injektuar e keq
93
Kod i injektuar e keq
93
Kod i injektuar e keq
95

Dhe programi ynë përdor «native» rand, pasi ka kryer disa veprime të padobishme.

2. Mundime kërkimi

Duke ditur për kërcënimin potencial, ne duam të zbulojmë se preload është kryer. Sigurisht, mënyra më e mirë për të identifikuar është ta fusim në bërthamë, por më interesojnë pikërisht opsionet për identifikim në hapësirën e përdoruesit.

Më poshtë do të japim disa zgjidhje për identifikimin dhe kundërshtimin e tyre.

2.1. Të fillojmë me të thjeshtën

Siç u tha më parë, mund të specifikoni bibliotekën e ngarkuar me anë të variableve LD_PRELOAD ose ta përcaktoni atë në skedarin /etc/ld.so.preload. Të krijojmë dy detektorë të thjeshtë.

I pari — për rregullimin e variablit të mjedisit:

#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ë verifikuar 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ë identifikim të suksesshëm.
Prandaj, [-] tregon anashkalimin e detektimit.

Sa efektiv është një identifikues i tillë? Fillimisht do të merremi me variablit të mjedisit:

#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, ne i shmangim edhe verifikimin 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 metoda të tjera për qasje në skedar, siç janë, open64, stat etj., por, në thelb, për t'i mashtruar ato nevojiten po ato 5-10 rreshta kodi.

2.2. Të ecim përpara

Më sipër kemi përdorur getenv() për të marrë vlerën LD_PRELOAD, por ka edhe një mënyrë më «në nivel të ulët» për të arritur tek ENV-variablat. Nuk do të përdorim funksione ndërmjetëse, por do të drejtohemi në array-n **environ, në të cilin ruhet një kopje e mjedisit:

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

Duke qenë se këtu ne lexojmë të dhënat direkt nga memoria, një thirrje e tillë nuk mund të zbulohet, dhe tonë undetect_getenv nuk e pengon më të identifikosh ndërhyrjen.

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

Duket sikur këtu problemi është zgjidhur? Gjithçka sapo ka filluar.

Pasi që programi të jetë i aktivizuar, vlera e variablës LD_PRELOAD në kujtesë tashmë nuk ka nevojë për hackers, kështu që mund të mendohet se është e sigurt për t'u fshirë para se të kryhen ndonjë udhëzim. Sigurisht, modifikimi i një vargu në kujtesë është, të paktën, një stil i keq programimi, por a mund të ndalojë kjo atë që nuk dëshiron vërtet të na bëjë mirë?

Për këtë, na nevojitet të krijojmë një funksion të rremë init(), në të cilin do të kapim atë që është vendosur LD_PRELOAD dhe ta kalojmë te kompontuesi ynë:

#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 bëjmë, 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ë, kujtesa nuk është vendi i fundit ku mund të zbulojmë një zëvendësim LD_PRELOAD, ka gjithashtu /proc/. Le të fillojmë me të qartën /proc/{PID}/environ.

Në të vërtetë, ka një zgjidhje universale për undetect’imin **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) {
        // Eliminoni aktualin LD_PRELOAD 
        //unsetenv(env);
        // Në vend të unset thjesht bëjmë zero 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, le të provojmë me "maskimin" tonë paraprak:

$ (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ëjtën open(), prandaj zgjidhja është e ngjashme me atë që është bërë më parë në p.2.1, por tani krijojmë një skedar të përkohshëm, ku kopjojmë vlerat reale të kujtesës 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 kjo fazë është përfunduar:

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

Vendi i ardhshëm i qartë është /proc/self/maps. Nuk ka kuptim të qëndrosh aty. Zgjidhja është absolutisht identike me atë të mëparshmen: kopjojmë të dhënat nga skedari duke përjashtuar rreshtat ndërmjet libc.so dhe ld.so.

2.4. Varianti i Chokepoint

Më pëlqeu veçanërisht ky zgjidhje për thjeshtësinë e saj. Krahasojmë adresat e funksioneve që ngarkohen direkt 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) [+]
Adresa Libc: 0x7fa86893b160
Adresa tjetër: 0x7fa868a26135

Kundërshtimi ishte akoma 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 që këtu është gjithçka, por le të bëjmë pak më shumë kërkime. Nëse i drejtojmë thirrjen e sistemit drejtpërdrejt në bërthamë, kjo do të lejojë kalimin e të gjithë procesit të kapjes. Zgjidhja më poshtë, natyrisht, është e varur nga arkitektura (x86_64). Le të provojmë të implementojmë për zbulimin e hapjes 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 problemin ka zgjidhje. Një përmbledhje nga man‘a:

ptrace është një mjet që i lejon një procesi prind të vëzhgojë dhe të kontrollojë zhvillimin e një tjetër procesi, të shikojë dhe modifikojë të dhënat dhe regjistrat e tij. Zakonisht kjo funksion përdoret për të krijuar pika ndërprerjeje në një program për debug dhe për të ndjekur thirrjet e sistemit.

Procesi prind mund të fillojë ndjekjen duke thirrur së pari funksionin fork(2), dhe pastaj procesi i rritur mund të ekzekutojë PTRACE_TRACEME, ndjekur (zakonisht) nga ekzekutimi i exec(3). Nga ana tjetër, procesi prind mund të fillojë debug një proces ekzistues duke përdorur PTRACE_ATTACH.

Gjatë ndjekjes, procesi i rritur ndalet çdo herë kur merr një sinjal, edhe nëse ky sinjal injorohet. (Përjashtim është SIGKILL, që funksionon si zakonisht.) Procesi prind do të njoftohet për këtë me thirrjen wait(2), pas së cilës ai mund të shikojë dhe modifikojë përmbajtjen e processit të ri derisa e lejon atë të vazhdojë punën, duke injoruar ndonjëherë sinjalin që i dërgohet ose dërguar një sinjal tjetër në vend të tij).

Pra, zgjidhja qëndron në ndjekjen e procesit, duke e ndalur para çdo thirrjeje të sistemit dhe, nëse është e nevojshme, duke drejtuar 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, &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);
    }
}

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ë

Charles Hubain
Chokepoint
ValdikSS
Philippe Teuwen
derhass

, të cilat artikuj, kodet burimore dhe komentet e tyre bënë shumë më tepër nga sa kam bërë unë që kjo shënim të shfaqet këtu.

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster