Op syk nei LD_PRELOAD

Dizze notysje is skreaun yn 2014, mar ik kaam krekt ûnder ûnderdrukking op Habré en it seach net it ljocht. Yn it ferbod bin ik it fergetten, mar no fûn ik it yn de ûntwerpen. Ik tocht oer it wiskjen, mar miskien sil it nuttich wêze foar ien.

Op syk nei LD_PRELOAD

Yn 't algemien, in bytsje freed admin-lêzing oer it ûnderwerp fan sykjen nei "ynbegrepen" LD_PRELOAD.

1. In koarte digression foar dyjingen dy't net bekend binne mei funksje ferfanging

De rest kin direkt nei p.2.

Litte wy begjinne mei in klassike foarbyld:

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

Wy kompilearje sûnder flaggen:

$ gcc ./ld_rand.c -o ld_rand

En, lykas ferwachte, krije wy 5 willekeurige getallen minder dan 100:

$ ./ld_rand
53
93
48
57
20

Mar lit ús yntinke dat wy de boarnekoade fan it programma net hawwe, mar wy moatte it gedrach feroarje.

Litte wy ús eigen bibleteek meitsje mei ús eigen funksjeprototype, bygelyks:

int rand(){
  return 42;
}

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

En no is ús willekeurige kar frij foarsisber:

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

Dizze trúk sjocht noch yndrukwekkender as wy ús bibleteek earst eksportearje fia

$ export LD_PRELOAD=$PWD/ld_rand.so

of wy dogge it earst

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

en dan rinne it programma lykas gewoanlik. Wy hawwe gjin inkele rigel koade yn it programma sels feroare, mar it gedrach is no ôfhinklik fan in lytse funksje yn ús bibleteek. Boppedat, op it stuit fan it skriuwen fan it programma, de rûn bestie net iens.

Wat makke ús programma gebrûk fan in nep rûn? Litte wy it stap foar stap nimme.
As in applikaasje begjint, wurde bepaalde biblioteken laden dy't de funksjes befetsje dy't nedich binne troch it programma. Wy kinne se besjen mei help 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)

Dizze list kin ferskille ôfhinklik fan 'e OS-ferzje, mar d'r moat in bestân wêze libc.so. It is dizze bibleteek dy't systeemoproppen en basisfunksjes leveret lykas iepen, malloc, printf etc. Us rûn is ek ûnder harren. Litte wy der wis fan wêze:

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

Lit ús sjen oft de set fan bibleteken feroaret as brûkt 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)

Blykt de fariabele is ynsteld LD_PRELOAD twingt ús te laden ld_rand.so sels nettsjinsteande it feit dat it programma sels net fereasket. En, sûnt ús funksje "rand" loads earder as rûn от libc.so, dan hearsket se de stoarm.

Ok, it is ús slagge om de native funksje te ferfangen, mar hoe kinne wy ​​derfoar soargje dat syn funksjonaliteit bewarre wurdt en guon aksjes wurde tafoege. Litte wy ús willekeurich feroarje:

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

Hjir, as ús "tafoeging", wy printsje mar ien rigel tekst, wêrnei't wy meitsje in oanwizer nei de oarspronklike funksje rûn. Om it adres fan dizze funksje te krijen hawwe wy nedich dlsym is in funksje út de bibleteek libdldy't sil fine ús rûn yn in steapel dynamyske biblioteken. Dêrnei sille wy dizze funksje neame en har wearde weromjaan. Dêrtroch sille wy moatte tafoegje "-ldl" by montage:

$ 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

En ús programma brûkt "native" rûn, dy't earder wat obsene aksjes útfierd hawwe.

2. De pine fan sykjen

Wittende oer in potinsjele bedriging, wolle wy dat ûntdekke foarlêze It waard útfierd. It is dúdlik dat de bêste manier om te ûntdekken is om it yn 'e kernel te triuwen, mar ik wie ynteressearre yn de deteksjeopsjes yn brûkersromte.

Folgjende, oplossings foar detectie en harren wjerlizzing sil komme yn pearen.

2.1. Litte wy ienfâldich begjinne

Lykas earder neamd, kinne jo de bibleteek opjaan om te laden mei de fariabele LD_PRELOAD of troch it yn in bestân te skriuwen /etc/ld.so.preload. Litte wy twa ienfâldige detektors meitsje.

De earste is om de ynstelde omjouwingsfariabele te kontrolearjen:

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

De twadde is om te kontrolearjen oft it bestân iepene is:

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

Litte wy de bibleteken laden:

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

Hjir en hjirûnder jout [+] suksesfolle deteksje oan.
Dêrtroch betsjut [-] deteksje omgean.

Hoe effektyf is sa'n detektor? Litte wy earst nei de omjouwingsfariabele sjen:

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

Op deselde manier krije wy de kontrôle kwyt iepen:

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

Ja, oare manieren om tagong te krijen ta it bestân kinne hjir brûkt wurde, lykas, iepen 64, stat ensfh., mar yn feite binne deselde 5-10 rigels koade nedich om se te ferrifeljen.

2.2. Lit ús fierder gean

Boppe wy brûkten getenv() om de wearde te krijen LD_PRELOAD, mar der is ek in mear "leech-nivo" manier om te kommen ta ENV- fariabelen. Wy sille gjin tuskenfunksjes brûke, mar ferwize nei de array **omjouwing, dy't in kopy fan 'e omjouwing opslaat:

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

Sûnt hjir binne wy ​​lêze gegevens direkt út it ûnthâld, sa'n oprop kin net ûnderskept wurde, en ús undetect_getenv it hinderet net mear by it identifisearjen fan de ynbraak.

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

It soe lykje dat dit probleem is oplost? It begjint noch mar.

Nei it programma is begûn, de wearde fan de fariabele LD_PRELOAD hackers hawwe it net mear nedich yn it ûnthâld, dat is, se kinne it lêze en wiskje foardat se ynstruksjes útfiere. Fansels is it bewurkjen fan in array yn it ûnthâld op syn minst in minne programmearstyl, mar hoe kin dit ien stopje dy't ús dochs net echt goed winsket?

Om dit te dwaan moatte wy ús eigen falske funksje oanmeitsje yn it(), dêr't wy ûnderskeppe de ynstallearre LD_PRELOAD en jou it troch oan ús linker:

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

Wy útfiere en kontrolearje:

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

Lykwols, ûnthâld is net it lêste plak dêr't kinst fine in ferfanging LD_PRELOAD, dêr is ek /proc/. Litte wy begjinne mei it dúdlike /proc/{PID}/omjouwing.

Yn feite is der in universele oplossing foar undetect **omjouwing и /proc/self/environ. It probleem is it "ferkearde" gedrach unsetenv (env).

juste opsje

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
$

Mar lit ús yntinke dat wy net fûn it en /proc/self/environ befettet "problematyske" gegevens.

Litte wy earst besykje mei ús foarige "fermomming":

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

kat brûkt itselde om it bestân te iepenjen iepen(), dus de oplossing is fergelykber mei wat al dien wie yn seksje 2.1, mar no meitsje wy in tydlik bestân wêr't wy de wearden fan it wiere ûnthâld kopiearje sûnder rigels dy't befetsje 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);
}

En dit stadium is trochjûn:

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

It folgjende foar de hân lizzende plak is /proc/self/maps. D'r hat gjin sin om der by te wenjen. De oplossing is absolút identyk oan de foarige: kopiearje de gegevens fan it bestân minus de rigels tusken libc.so и ld.so.

2.4. Opsje fan Chokepoint

Ik vond dizze oplossing foaral foar har ienfâld. Wy ferlykje de adressen fan funksjes laden direkt út libc, en "NEXT" adressen.

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

It laden fan de bibleteek mei ûnderskepping "iepen()" en kontrolearje:

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

De wjerlizzing die bliken noch ienfâldiger te wêzen:

#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

It soe lykje dat dit alles is, mar wy sille noch flodderje. As wy de systeemoprop direkt nei de kernel rjochtsje, sil dit it heule ûnderskeppingsproses omgean. De oplossing hjirûnder is fansels arsjitektuer-ôfhinklik (x86_64). Litte wy besykje it te ymplementearjen om in iepening te detektearjen 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) [+]

En dit probleem hat in oplossing. Úttreksel út ien'IN:

ptrace is in ark wêrmei in âlder proses te observearjen en kontrolearje de fuortgong fan in oar proses, besjen en feroarje syn gegevens en registers. Typysk wurdt dizze funksje brûkt om brekpunten te meitsjen yn in debuggenprogramma en systeemoproppen te kontrolearjen.

It âlderproses kin tracing begjinne troch earst fork (2) te roppen, en dan kin it resultearjende bernproses PTRACE_TRACEME útfiere, folge (meastentiids) troch exec (3) út te fieren. Oan 'e oare kant kin in âlderproses begjinne mei it debuggen fan in besteande proses mei PTRACE_ATTACH.

By tracing stopet it bernproses elke kear as it in sinjaal ûntfangt, sels as it sinjaal wurdt negearre. (De útsûndering is SIGKILL, dat normaal wurket.) It âlderproses sil hjirfan op 'e hichte brocht wurde troch wait(2) op te roppen, wêrnei't it de ynhâld fan it bernproses besjen en oanpasse kin foardat it begjint. It âlderproses lit it bern dan trochgean mei rinnen, yn guon gefallen negearje it sinjaal dat nei it stjoerd is of ynstee in oar sinjaal ferstjoere).

Sa is de oplossing om it proses te kontrolearjen, it te stopjen foar elke systeemoprop en, as it nedich is, de tried omliede nei in heakfunksje.

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

Wy kontrolearje:

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

+0-0=5

Tige dank

Charles Hubain
choke punt
ValdikSS
Philippe Teuwen
derhass

, waans artikels, boarnen en opmerkings folle mear diene as ik dien om dizze notysje hjir te ferskinen.

Boarne: www.habr.com

Add a comment