Inatafuta LD_PRELOAD

Ujumbe huu uliandikwa mnamo 2014, lakini nilikandamizwa na Habré na sikuona mwangaza wa siku. Wakati wa marufuku niliisahau, lakini sasa nimeipata kwenye rasimu. Nilifikiri juu ya kuifuta, lakini labda itakuwa na manufaa kwa mtu.

Inatafuta LD_PRELOAD

Kwa ujumla, msimamizi mdogo wa Ijumaa akisoma juu ya mada ya kutafuta "iliyojumuishwa" LD_PRELOAD.

1. Kicheko kifupi kwa wale ambao hawajui uingizwaji wa chaguo la kukokotoa

Wengine wanaweza kwenda moja kwa moja p.2.

Wacha tuanze na mfano wa classic:

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

Tunakusanya bila bendera yoyote:

$ gcc ./ld_rand.c -o ld_rand

Na, kama inavyotarajiwa, tunapata nambari 5 za nasibu chini ya 100:

$ ./ld_rand
53
93
48
57
20

Lakini hebu fikiria kwamba hatuna msimbo wa chanzo wa programu, lakini tunahitaji kubadilisha tabia.

Wacha tuunde maktaba yetu na mfano wetu wa kazi, kwa mfano:

int rand(){
  return 42;
}

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

Na sasa chaguo letu la bahati nasibu linatabirika kabisa:

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

Ujanja huu unaonekana kuvutia zaidi ikiwa tutahamisha maktaba yetu kwanza kupitia

$ export LD_PRELOAD=$PWD/ld_rand.so

au tutafanya kwanza

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

na kisha endesha programu kama kawaida. Hatujabadilisha safu moja ya msimbo katika programu yenyewe, lakini tabia yake sasa inategemea kazi ndogo katika maktaba yetu. Zaidi ya hayo, wakati wa kuandika programu, rand hata haikuwepo.

Ni nini kilifanya programu yetu kutumia bandia rand? Hebu tuchukue hatua kwa hatua.
Wakati programu inapoanza, maktaba fulani hupakiwa ambazo zina vipengele vinavyohitajika na programu. Tunaweza kuzitazama kwa kutumia 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)

Orodha hii inaweza kutofautiana kulingana na toleo la OS, lakini lazima kuwe na faili hapo libc.so. Ni maktaba hii ambayo hutoa simu za mfumo na kazi za kimsingi kama vile kufungua, malloc, printf nk Yetu rand pia ni miongoni mwao. Hebu tuhakikishe hili:

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

Wacha tuone ikiwa seti ya maktaba inabadilika inapotumiwa 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)

Inageuka variable imewekwa LD_PRELOAD inatulazimisha kubeba ld_rand.hivyo hata licha ya ukweli kwamba mpango yenyewe hauhitaji. Na tangu kazi yetu "randi" mizigo mapema kuliko rand kutoka libc.so, kisha anatawala roost.

Sawa, tumeweza kuchukua nafasi ya chaguo la kukokotoa asili, lakini tunawezaje kuhakikisha kwamba utendakazi wake umehifadhiwa na baadhi ya vitendo vinaongezwa. Wacha turekebishe nasibu yetu:

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

Hapa, kama "nyongeza" yetu, tunachapisha mstari mmoja tu wa maandishi, baada ya hapo tunaunda pointer kwa kazi ya asili. rand. Ili kupata anwani ya kazi hii tunahitaji dlsym ni kipengele kutoka kwa maktaba libdlambayo itapata yetu rand katika rundo la maktaba zinazobadilika. Baada ya hapo tutaita kazi hii na kurudisha thamani yake. Ipasavyo, tutahitaji kuongeza "-ldl" wakati wa kusanyiko:

$ 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

Na programu yetu hutumia "asili" rand, baada ya kufanya vitendo vichafu hapo awali.

2. Maumivu ya kutafuta

Kwa kujua kuhusu tishio linaloweza kutokea, tunataka kugundua hilo pakia mapema Ilifanyika. Ni wazi kuwa njia bora ya kugundua ni kuisukuma kwenye kernel, lakini nilipendezwa na chaguzi za kugundua katika nafasi ya watumiaji.

Ifuatayo, suluhisho za kugundua na kukanusha kwao zitakuja kwa jozi.

2.1. Hebu tuanze rahisi

Kama ilivyoelezwa hapo awali, unaweza kutaja maktaba ya kupakia kwa kutumia kutofautisha LD_PRELOAD au kwa kuiandika kwenye faili /etc/ld.so.preload. Wacha tuunde vigunduzi viwili rahisi.

Ya kwanza ni kuangalia utofauti wa mazingira uliowekwa:

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

Ya pili ni kuangalia ikiwa faili imefunguliwa:

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

Wacha tupakie maktaba:

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

Hapa na chini, [+] inaonyesha utambuzi uliofaulu.
Ipasavyo, [-] inamaanisha kupitisha utambuzi.

Kigunduzi kama hicho kina ufanisi gani? Wacha tuangalie utofauti wa mazingira kwanza:

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

Vile vile, tunaondoa hundi kufungua:

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

Ndio, njia zingine za kufikia faili zinaweza kutumika hapa, kama vile, wazi64, stat nk, lakini, kwa kweli, mistari sawa ya 5-10 ya kanuni inahitajika ili kuwadanganya.

2.2. Hebu tuendelee

Hapo juu tulitumia getenv() kupata thamani LD_PRELOAD, lakini pia kuna njia ya "kiwango cha chini" zaidi ya kufika ENV-vigeu. Hatutatumia vitendaji vya kati, lakini tutarejelea safu **mazingira, ambayo huhifadhi nakala ya mazingira:

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

Kwa kuwa hapa tunasoma data moja kwa moja kutoka kwa kumbukumbu, simu kama hiyo haiwezi kuingiliwa, na yetu undetect_getv haiingiliani tena na kutambua uingilizi.

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

Inaweza kuonekana kuwa shida hii imetatuliwa? Bado ni mwanzo tu.

Baada ya programu kuanza, thamani ya kutofautiana LD_PRELOAD wadukuzi hawahitaji tena kwenye kumbukumbu, yaani, wanaweza kuisoma na kuifuta kabla ya kutekeleza maagizo yoyote. Bila shaka, kuhariri safu katika kumbukumbu ni, kwa kiwango cha chini, mtindo mbaya wa programu, lakini hii inaweza kumzuia mtu ambaye hata hivyo hatutaki mema?

Ili kufanya hivyo tunahitaji kuunda kazi yetu ya bandia ndani yake(), ambayo tunaingilia iliyosanikishwa LD_PRELOAD na uipitishe kwa kiunganishi chetu:

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

Tunafanya na kuangalia:

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

Walakini, kumbukumbu sio mahali pa mwisho ambapo unaweza kupata mbadala LD_PRELOAD, pia kuna /proc/. Wacha tuanze na dhahiri /proc/{PID}/environ.

Kwa kweli, kuna suluhisho zima kwa ondoa kugundua **mazingira и /proc/self/environ. Tatizo ni tabia "mbaya". unsetenv(env).

chaguo sahihi

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
$

Lakini hebu fikiria kwamba hatukuipata na /proc/self/environ ina data "matatizo".

Kwanza hebu tujaribu na "kujificha" yetu ya awali:

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

paka hutumia sawa kufungua faili fungua (), kwa hivyo suluhisho ni sawa na ile iliyofanywa tayari katika sehemu ya 2.1, lakini sasa tunaunda faili ya muda ambapo tunakili maadili ya kumbukumbu ya kweli bila mistari iliyo na 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);
}

Na hatua hii imepitishwa:

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

Mahali panapofuata ni dhahiri /proc/self/maps. Hakuna maana ya kukaa juu yake. Suluhisho ni sawa kabisa na lile lililotangulia: nakili data kutoka kwa faili ukiondoa mistari kati libc.so и ld.hivyo.

2.4. Chaguo kutoka Chokepoint

Nilipenda sana suluhisho hili kwa unyenyekevu wake. Tunalinganisha anwani za kazi zilizopakiwa moja kwa moja kutoka libc, na anwani "INAYOFUATA".

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

Inapakia maktaba kwa kukatiza "fungua ()" na angalia:

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

Kukanusha iligeuka kuwa rahisi zaidi:

#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

Inaweza kuonekana kuwa hii ndio yote, lakini bado tutatetemeka. Ikiwa tutaelekeza simu ya mfumo moja kwa moja kwenye kernel, hii itakwepa mchakato mzima wa kukatiza. Suluhisho hapa chini ni, kwa kweli, tegemezi la usanifu (x86_64) Wacha tujaribu kuitekeleza ili kugundua ufunguzi ld.so.pakia mapema.

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

Na shida hii ina suluhisho. Dondoo kutoka mtu'A:

ptrace ni zana inayoruhusu mchakato wa mzazi kuchunguza na kudhibiti maendeleo ya mchakato mwingine, kutazama na kubadilisha data na rejista zake. Kwa kawaida chaguo hili la kukokotoa hutumiwa kuunda vizuizi katika programu ya utatuzi na kufuatilia simu za mfumo.

Mchakato wa mzazi unaweza kuanza kufuatilia kwa kupiga simu kwanza fork(2), kisha mchakato unaotokana na mtoto unaweza kutekeleza PTRACE_TRACEME, ikifuatiwa (kawaida) kwa kutekeleza exec(3). Kwa upande mwingine, mchakato wa mzazi unaweza kuanza kutatua mchakato uliopo kwa kutumia PTRACE_ATTACH.

Wakati wa kufuatilia, mchakato wa mtoto huacha kila wakati inapokea ishara, hata ikiwa ishara imepuuzwa. (Kighairi ni SIGKILL, ambayo hufanya kazi kama kawaida.) Mchakato wa mzazi utaarifiwa kuhusu hili kwa kupiga simu wait(2), kisha inaweza kuangalia na kurekebisha maudhui ya mchakato wa mtoto kabla ya kuanza. Mchakato wa mzazi basi huruhusu mtoto kuendelea kufanya kazi, katika hali zingine akipuuza mawimbi yaliyotumwa kwake au kutuma ishara nyingine badala yake).

Kwa hivyo, suluhisho ni kufuatilia mchakato, kuacha kabla ya kila simu ya mfumo na, ikiwa ni lazima, kuelekeza thread kwenye kazi ya ndoano.

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

Tunaangalia:

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

+0-0=5

Asante sana

Charles Hubain
choko uhakika
ValdikSS
Philippe Teuwen
ngozi

, ambaye makala, vyanzo na maoni yake yalifanya mengi zaidi kuliko nilivyofanya kufanya dokezo hili kuonekana hapa.

Chanzo: mapenzi.com

Kuongeza maoni