Chwilio am LD_PRELOAD

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.

Chwilio am LD_PRELOAD

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, &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);
    }
}

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

Charles Hubain
tagu pwynt
ValdikSS
Philippe Teuwen
dehass

, y gwnaeth ei erthyglau, ffynonellau a sylwadau lawer mwy nag a wnes i i wneud i'r nodyn hwn ymddangos yma.

Ffynhonnell: hab.com

Ychwanegu sylw