Er að leita að LD_PRELOAD

Þessi minnismiði var skrifaður árið 2014, en ég lenti bara í kúgun á Habré og leit ekki dagsins ljós. Í banninu gleymdi ég því, en nú fann ég það í drögunum. Ég hugsaði um að eyða því, en kannski kemur það einhverjum að gagni.

Er að leita að LD_PRELOAD

Almennt séð, smá föstudagsstjórnunarlestur um efnið að leita að „innifalið“ LD_PRELOAD.

1. Stuttur frágangur fyrir þá sem ekki þekkja til virkniskipta

Restin getur farið beint á bls.2.

Við skulum byrja á klassísku dæmi:

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

Við setjum saman án fána:

$ gcc ./ld_rand.c -o ld_rand

Og eins og búist var við fáum við 5 handahófskenndar tölur sem eru minni en 100:

$ ./ld_rand
53
93
48
57
20

En við skulum ímynda okkur að við höfum ekki frumkóða forritsins, en við þurfum að breyta hegðuninni.

Við skulum búa til okkar eigið bókasafn með eigin frumgerð aðgerðir, til dæmis:

int rand(){
  return 42;
}

$ gcc -shared -fPIC ./o_rand.c -o ld_rand.so

Og nú er tilviljunarkennt val okkar nokkuð fyrirsjáanlegt:

# LD_PRELOAD=$PWD/ld_rand.so ./ld_rand
42
42
42
42
42

Þetta bragð lítur enn áhrifameira út ef við flytjum út bókasafnið okkar fyrst í gegnum

$ export LD_PRELOAD=$PWD/ld_rand.so

eða við gerum það fyrst

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

og keyra síðan forritið eins og venjulega. Við höfum ekki breytt einni línu af kóða í forritinu sjálfu, en hegðun þess er nú háð örlítilli aðgerð í bókasafninu okkar. Þar að auki, þegar forritið er skrifað, er rand var ekki einu sinni til.

Hvað varð til þess að forritið okkar notaði falsa rand? Tökum það skref fyrir skref.
Þegar forrit byrjar hlaðast ákveðin bókasöfn sem innihalda þær aðgerðir sem forritið þarfnast. Við getum skoðað þær með því að nota 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)

Þessi listi getur verið mismunandi eftir stýrikerfisútgáfu, en það verður að vera skrá þar libc.so. Það er þetta bókasafn sem veitir kerfissímtöl og grunnaðgerðir eins og opna, malloc, prentf o.fl. Okkar rand er líka meðal þeirra. Við skulum ganga úr skugga um þetta:

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

Við skulum sjá hvort safn bókasöfn breytist þegar þau eru notuð 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)

Í ljós kemur að breytan er stillt LD_PRELOAD neyðir okkur til að hlaða ld_rand.so jafnvel þrátt fyrir að forritið sjálft krefjist þess ekki. Og þar sem hlutverk okkar "rand" hleðst fyrr en rand frá libc.so, þá ræður hún ríkjum.

Allt í lagi, okkur tókst að skipta um innfædda aðgerð, en hvernig getum við tryggt að virkni hennar sé varðveitt og einhverjum aðgerðum er bætt við. Við skulum breyta handahófinu okkar:

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

Hér, sem „viðbót“ okkar, prentum við aðeins eina línu af texta, eftir það búum við til bendil á upprunalegu fallið rand. Til að fá heimilisfang þessarar aðgerðar þurfum við dlsym er fall úr bókasafninu libdlsem mun finna okkar rand í stafla af kraftmiklum bókasöfnum. Eftir það munum við kalla þessa aðgerð og skila gildi hennar. Í samræmi við það þurfum við að bæta við "-ldl" við samsetningu:

$ 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

Og forritið okkar notar "native" rand, eftir að hafa áður framkvæmt nokkrar ruddalegar aðgerðir.

2. Sársaukinn við að leita

Þegar við vitum um hugsanlega ógn viljum við uppgötva það Forhlaða Það var flutt. Það er ljóst að besta leiðin til að greina er að ýta því inn í kjarnann, en ég hafði áhuga á uppgötvunarmöguleikum í notendarými.

Næst munu lausnir fyrir uppgötvun og afsönnun þeirra koma í pörum.

2.1. Byrjum einfalt

Eins og fyrr segir geturðu tilgreint bókasafnið sem á að hlaða með því að nota breytuna LD_PRELOAD eða með því að skrifa það í skrá /etc/ld.so.preload. Við skulum búa til tvo einfalda skynjara.

Sú fyrsta er að athuga stilltu umhverfisbreytuna:

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

Annað er að athuga hvort skráin sé opnuð:

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

Við skulum hlaða bókasöfnin:

$ 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) [+]

Hér og hér að neðan gefur [+] til kynna að uppgötvun hafi tekist.
Samkvæmt því þýðir [-] að komast framhjá uppgötvun.

Hversu áhrifaríkur er slíkur skynjari? Við skulum líta á umhverfisbreytuna fyrst:

#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) [-]

Á sama hátt losnum við við ávísunina opna:

#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) [-]

Já, aðrar leiðir til að fá aðgang að skránni er hægt að nota hér, svo sem, open64, stat o.s.frv., en í rauninni þarf sömu 5-10 línurnar af kóða til að blekkja þær.

2.2. Höldum áfram

Hér að ofan notuðum við getenv() til að fá verðmæti LD_PRELOAD, en það er líka „lágmarks“ leið til að komast að ENV-breytur. Við munum ekki nota milliaðgerðir heldur vísa til fylkisins **umhverfi, sem geymir afrit af umhverfinu:

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

Þar sem hér er verið að lesa gögn beint úr minni er ekki hægt að hlera slíkt símtal, og okkar undetect_getenv það truflar ekki lengur að bera kennsl á innbrotið.

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

Það virðist sem þetta vandamál sé leyst? Það er samt rétt að byrja.

Eftir að forritið er ræst, gildi breytunnar LD_PRELOAD tölvuþrjótar þurfa það ekki lengur í minni, það er að segja, þeir geta lesið það og eytt því áður en þeir framkvæma einhverjar leiðbeiningar. Auðvitað er það að minnsta kosti slæmur forritunarstíll að breyta fylki í minni, en hvernig getur þetta stöðvað einhvern sem vill okkur ekki vel samt?

Til að gera þetta þurfum við að búa til okkar eigin falsa aðgerð í því(), þar sem við hlerum uppsett LD_PRELOAD og sendu það til tengiliðsins okkar:

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

Við framkvæmum og athugum:

$ 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/

Hins vegar er minnið ekki síðasti staðurinn þar sem þú getur fundið staðgöngu LD_PRELOAD, það er einnig /proc/. Við skulum byrja á því augljósa /proc/{PID}/umhverfi.

Í raun er til allsherjarlausn fyrir vangreina **umhverfi и /proc/self/umhverfi. Vandamálið er „röng“ hegðun unsetenv (env).

réttur kostur

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
$

En við skulum ímynda okkur að við höfum ekki fundið það og /proc/self/umhverfi inniheldur „vandræða“ gögn.

Fyrst skulum við reyna með fyrri "dulargervi" okkar:

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

köttur notar það sama til að opna skrána opna(), þannig að lausnin er svipuð því sem þegar var gert í kafla 2.1, en nú búum við til bráðabirgðaskrá þar sem við afritum gildi hins sanna minni án þess að línur innihaldi 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);
}

Og þetta stig er liðið:

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

Næsti augljósi staður er /proc/self/maps. Það þýðir ekkert að staldra við það. Lausnin er alveg eins og sú fyrri: afritaðu gögnin úr skránni að frádregnum línum á milli libc.so и ld.svo.

2.4. Valkostur frá Chokepoint

Mér líkaði sérstaklega við þessa lausn vegna einfaldleika hennar. Við berum saman vistföng aðgerða sem hlaðið er beint frá libc, og „NEXT“ heimilisföng.

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

Hleður bókasafnið með hlerun "opna()" og athugaðu:

$ export LD_PRELOAD=$PWD/ld_undetect_open.so
$ ./detect_chokepoint
LD_PRELOAD (syscall - open) [+]
Libc address: 0x7fa86893b160
Next address: 0x7fa868a26135

Afsönnunin reyndist enn einfaldari:

#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

Það virðist sem þetta sé allt, en við munum samt flækjast. Ef við sendum kerfiskallið beint í kjarnann mun þetta fara framhjá öllu hlerunarferlinu. Lausnin hér að neðan er að sjálfsögðu háð arkitektúr (x86_64). Við skulum reyna að útfæra það til að greina opnun ld.so.forhlaða.

#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) [+]

Og þetta vandamál hefur lausn. Útdráttur úr maður'A:

ptrace er tæki sem gerir foreldraferli kleift að fylgjast með og stjórna framvindu annars ferlis, skoða og breyta gögnum þess og skrám. Venjulega er þessi aðgerð notuð til að búa til brotpunkta í villuleitarforriti og fylgjast með kerfissímtölum.

Foreldraferlið getur byrjað að rekja með því að kalla fyrst fork(2), og síðan getur barnferlið sem myndast framkvæmt PTRACE_TRACEME, fylgt eftir (venjulega) með því að keyra exec(3). Á hinn bóginn getur foreldraferli byrjað að kemba núverandi ferli með PTRACE_ATTACH.

Við rakningu stöðvast barnaferlið í hvert sinn sem það fær merki, jafnvel þótt merki sé hunsað. (Undantekningin er SIGKILL, sem virkar eðlilega.) Foreldrisferlinu verður tilkynnt um þetta með því að kalla wait(2), eftir það getur það skoðað og breytt innihaldi undirferlisins áður en það byrjar. Foreldraferlið gerir barninu síðan kleift að halda áfram að hlaupa, í sumum tilfellum hunsar það merki sem sent er til þess eða sendir annað merki í staðinn).

Þannig er lausnin að fylgjast með ferlinu, stöðva það fyrir hvert kerfiskall og, ef nauðsyn krefur, beina þræðinum í krókaaðgerð.

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

Við athugum:

$ ./detect_syscall
LD_PRELOAD (open syscall) [+]
$ LD_PRELOAD=./ld_undetect_syscall.so ./detect_syscall
LD_PRELOAD (open syscall) [-]

+0-0=5

Þakka þér fyrir

Charles Hubain
köfnunarpunktur
ValdikSS
Philippe Teuwen
drullusokkur

, þar sem greinar, heimildir og athugasemdir gerðu miklu meira en ég til að láta þessa athugasemd birtast hér.

Heimild: www.habr.com

Bæta við athugasemd