A' coimhead airson LD_PRELOAD

Chaidh an nota seo a sgrìobhadh ann an 2014, ach thàinig mi dìreach fo mhùchadh air Habré agus chan fhaca e solas an latha. Rè an casg dhìochuimhnich mi mu dheidhinn, ach a-nis lorg mi e anns na dreachan. Bha mi a 'smaoineachadh mu dheidhinn a sguabadh às, ach is dòcha gum bi e feumail do chuideigin.

A' coimhead airson LD_PRELOAD

San fharsaingeachd, beagan leughadh rianachd Dihaoine air a ’chuspair a bhith a’ lorg “air a ghabhail a-steach” LD_PRELOAD.

1. Cuairt ghoirid dhaibhsan nach eil eòlach air ionadachadh gnìomh

Faodaidh an còrr a dhol dìreach gu p.2.

Feuch an tòisich sinn le eisimpleir clasaigeach:

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

Bidh sinn a’ cruinneachadh às aonais brataichean:

$ gcc ./ld_rand.c -o ld_rand

Agus, mar a bhiodh dùil, gheibh sinn 5 àireamhan air thuaiream nas lugha na 100:

$ ./ld_rand
53
93
48
57
20

Ach smaoinichidh sinn nach eil còd stòr a’ phrògraim againn, ach feumaidh sinn an giùlan atharrachadh.

Cruthaichidh sinn an leabharlann againn fhèin leis a’ prototype gnìomh againn fhèin, mar eisimpleir:

int rand(){
  return 42;
}

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

Agus a-nis tha an roghainn air thuaiream againn gu math sùbailte:

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

Bidh an cleas seo a’ coimhead eadhon nas drùidhtiche ma nì sinn às-mhalairt don leabharlann againn an toiseach

$ export LD_PRELOAD=$PWD/ld_rand.so

no nì sinn e an toiseach

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

agus an uairsin ruith am prògram mar as àbhaist. Chan eil sinn air aon loidhne de chòd atharrachadh sa phrògram fhèin, ach tha a ghiùlan a-nis an urra ri gnìomh beag bìodach san leabharlann againn. A bharrachd air an sin, aig àm sgrìobhaidh a’ phrògram, tha an Iar cha robh eadhon ann.

Dè thug air ar prògram a chleachdadh meallta Iar? Gabhamaid e ceum air cheum.
Nuair a thòisicheas tagradh, bidh cuid de leabharlannan air an luchdachadh anns a bheil na gnìomhan a dh’ fheumas am prògram. Faodaidh sinn coimhead orra a’ cleachdadh 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)

Faodaidh an liosta seo atharrachadh a rèir dreach an OS, ach feumaidh faidhle a bhith ann libc.so.. Is e an leabharlann seo a bheir seachad fiosan siostam agus gnìomhan bunaiteach leithid fosgailte, malloc, printf etc Our Iar tha cuideachd nam measg. Dèanamaid cinnteach à seo:

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

Feuch sinn a bheil an seata de leabharlannan ag atharrachadh nuair a thèid a chleachdadh 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)

Tionndaidh a-mach gu bheil an caochladair air a shuidheachadh LD_PRELOAD a’ toirt oirnn an luchdachadh ld_rand.so eadhon a dh’ aindeoin nach eil feum aig a’ phrògram fhèin air. Agus, bhon obair againn "randa" luchdan nas tràithe na Iar от libc.so., an uairsin bidh i a 'riaghladh a' chlais.

Ceart gu leòr, chaidh againn air a’ ghnìomh dhùthchasach a chuir an àite, ach ciamar as urrainn dhuinn dèanamh cinnteach gu bheil a ghnìomhachd air a ghleidheadh ​​​​agus cuid de ghnìomhan air an cur ris. Atharraichidh sinn ar thuaiream:

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

An seo, mar ar “cur-ris”, cha bhith sinn a’ clò-bhualadh ach aon loidhne de theacsa, agus às deidh sin bidh sinn a’ cruthachadh puing don ghnìomh thùsail Iar. Gus seòladh na gnìomh seo fhaighinn feumaidh sinn dlsym 's e gnìomh bhon leabharlann libdla gheibh ar Iar ann an stac de leabharlannan fiùghantach. Às deidh sin canaidh sinn an gnìomh seo agus tillidh sinn a luach. A rèir sin, feumaidh sinn cur ris "-ldl" rè co-chruinneachadh:

$ 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

Agus tha am prògram againn a’ cleachdadh “dùthchasach” Iar, an dèidh cuid de ghnìomhan drabasta a dhèanamh roimhe.

2. Am pian sireadh

Le fios mu chunnart a dh’ fhaodadh a bhith ann, tha sinn airson sin a lorg gun ro-luchdaich Chaidh a choileanadh. Tha e soilleir gur e an dòigh as fheàrr air lorg a bhith ga phutadh a-steach don kernel, ach bha ùidh agam anns na roghainnean lorgaidh ann an àite luchd-cleachdaidh.

An ath rud, thig fuasglaidhean airson lorg agus an ath-aithris ann an càraidean.

2.1. Feuch an tòisich sinn sìmplidh

Mar a chaidh a ràdh na bu tràithe, faodaidh tu an leabharlann a shònrachadh airson a luchdachadh a ’cleachdadh an caochladair LD_PRELOAD no le bhith ga sgrìobhadh ann am faidhle /etc/ld.so.preload. Cruthaichidh sinn dà lorgaire sìmplidh.

Is e a’ chiad fhear sgrùdadh a dhèanamh air caochladair àrainneachd stèidhichte:

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

Is e an dàrna fear dèanamh cinnteach a bheil am faidhle air fhosgladh:

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

Luchdaich sinn na leabharlannan:

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

An seo agus gu h-ìosal, tha [+] a’ comharrachadh lorg soirbheachail.
A rèir sin, tha [-] a’ ciallachadh lorg seach-rathad.

Dè cho èifeachdach sa tha an leithid de lorgaire? Bheir sinn sùil an-toiseach air caochladair na h-àrainneachd:

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

San aon dòigh, gheibh sinn cuidhteas an t-seic fosgailte:

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

Faodaidh, faodar dòighean eile air am faidhle fhaighinn an seo, leithid, fosgailte 64, stàite msaa, ach, gu dearbh, tha feum air na h-aon loidhnichean 5-10 de chòd gus am mealladh.

2.2. Rachamaid air adhart

Gu h-àrd chleachd sinn faigh () gus an luach fhaighinn LD_PRELOAD, ach tha dòigh nas “ìre ìosal” ann cuideachd airson faighinn thuige ENV-caochlaideach. Cha chleachd sinn gnìomhan eadar-mheadhanach, ach bheir sinn iomradh air an raon ** àrainneachd, a tha a’ stòradh leth-bhreac den àrainneachd:

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

Leis gu bheil sinn an seo a’ leughadh dàta gu dìreach bhon chuimhne, chan urrainnear a leithid de ghairm a ghlacadh, agus tha ar undetect_getenv chan eil e tuilleadh a’ cur bacadh air a bhith ag aithneachadh an t-sàrachadh.

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

Bhiodh e coltach gu bheil an duilgheadas seo air a fuasgladh? Tha e dìreach air tòiseachadh fhathast.

Às deidh don phrògram tòiseachadh, luach an caochladair LD_PRELOAD chan eil feum aig hackers air mar chuimhneachan tuilleadh, is e sin, is urrainn dhaibh a leughadh agus a dhubhadh às mus cuir iad an gnìomh stiùireadh sam bith. Gu dearbh, is e droch stoidhle prògramadh a th’ ann a bhith a’ deasachadh sreath mar chuimhneachan, aig a’ char as lugha, ach ciamar a chuireas seo stad air cuideigin nach eil a’ guidhe math dhuinn co-dhiù?

Gus seo a dhèanamh feumaidh sinn ar gnìomh meallta fhèin a chruthachadh init (), anns am bi sinn a 'toirt a-steach an stàladh LD_PRELOAD agus thoir e d'ar fear-ceann-

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

Bidh sinn a 'dèanamh agus a' sgrùdadh:

$ 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/fèin/

Ach, chan e cuimhne an t-àite mu dheireadh far an lorg thu neach-ionaid LD_PRELOAD, tha cuideachd /proc/. Feuch an tòisich sinn leis an rud follaiseach /proc/{PID}/àrainneachd.

Gu dearbh, tha fuasgladh uile-choitcheann ann airson neo-aithnichte ** àrainneachd и /proc/fèin/àrainneachd. Is e an duilgheadas an giùlan “ceàrr”. neo-shuidhichte (env).

roghainn cheart

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
$

Ach smaoinicheamaid nach do lorg sinn e agus /proc/fèin/àrainneachd tha dàta "duilgheadas" ann.

An-toiseach feuchaidh sinn leis an “disguise” a bh’ againn roimhe:

$ (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 cleachdadh an aon rud gus am faidhle fhosgladh fosgail (), mar sin tha am fuasgladh coltach ris na chaidh a dhèanamh mu thràth ann an earrann 2.1, ach a-nis bidh sinn a’ cruthachadh faidhle sealach far an dèan sinn lethbhreac de luachan na fìor chuimhne gun loidhnichean anns a bheil 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);
}

Agus chaidh an ìre seo seachad:

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

Tha an ath àite follaiseach /proc/fèin/mapaichean. Chan eil feum air fuireach air. Tha am fuasgladh gu tur co-ionann ris an fhear roimhe: dèan lethbhreac den dàta bhon fhaidhle às aonais na loidhnichean eadar libc.so. и ld.so..

2.4. Roghainn bho Chokepoint

Chòrd am fuasgladh seo rium gu sònraichte airson cho sìmplidh ‘s a tha e. Bidh sinn a’ dèanamh coimeas eadar seòlaidhean ghnìomhan air an luchdachadh gu dìreach bho libc, agus seòlaidhean “ATH”.

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

Luchdaich a-nuas an leabharlann le interception "fosgail()" agus thoir sùil air:

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

Thionndaidh an refutation a-mach gu bhith eadhon nas sìmplidh:

#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

Bhiodh e coltach gur e seo a h-uile càil, ach bidh sinn fhathast a 'sreapadh. Ma stiùireas sinn gairm an t-siostaim gu dìreach chun kernel, thèid seo seachad air a’ phròiseas eadar-ghuidhe gu lèir. Tha am fuasgladh gu h-ìosal, gu dearbh, an urra ri ailtireachd (x86_64). Feuchaidh sinn ri a chuir an gnìomh gus fosgladh a lorg 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) [+]

Agus tha fuasgladh aig an duilgheadas seo. Earrann bho fear'A:

Is e inneal a th’ ann am ptrace a leigeas le pròiseas pàrant sùil a chumail air adhartas pròiseas eile agus smachd a chumail air, an dàta agus na clàran aige fhaicinn agus atharrachadh. Mar as trice bidh an gnìomh seo air a chleachdadh gus puingean brisidh a chruthachadh ann am prògram deasbaid agus sùil a chumail air fiosan siostam.

Faodaidh am pròiseas pàrant tòiseachadh a ’lorg le bhith a’ gairm forc (2) an-toiseach, agus an uairsin faodaidh am pròiseas cloinne a thig às a leantainn PTRACE_TRACEME, air a leantainn (mar as trice) le bhith a’ cur an gnìomh exec(3). Air an làimh eile, faodaidh pròiseas pàrant tòiseachadh a’ dì-bhugachadh pròiseas a tha ann mar-thà a’ cleachdadh PTRACE_ATTACH.

Nuair a thèid lorg a dhèanamh, stadaidh pròiseas an leanaibh a h-uile uair a gheibh e comharra, eadhon ged a thèid an comharra a leigeil seachad. (Is e an eisgeachd SIGKILL, a bhios ag obair gu h-àbhaisteach.) Thèid fios a thoirt don phròiseas phàrant mu dheidhinn seo le bhith a’ gairm feitheamh(2), às deidh sin faodaidh e susbaint pròiseas an leanaibh fhaicinn agus atharrachadh mus tòisich e. Bidh am pròiseas pàrant an uairsin a’ leigeil leis a’ phàiste cumail a’ dol, ann an cuid de chùisean a’ seachnadh a’ chomharra a chaidh a chuir thuige no a’ cur comharra eile na àite).

Mar sin, is e am fuasgladh sùil a chumail air a’ phròiseas, ga stad ro gach gairm siostam agus, ma tha sin riatanach, ath-stiùireadh an t-snàthainn gu gnìomh dubhan.

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

Bidh sinn a ’sgrùdadh:

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

+0-0=5

Mòran taing gu dearbh

Teàrlach Hubain
puing tachdadh
ValdikSS
Philippe Teuwen
derhass

, a rinn na h-artaigilean, na stòran agus na beachdan aca tòrr a bharrachd na rinn mi gus an nota seo nochdadh an seo.

Source: www.habr.com

Cuir beachd ann