Mencari LD_PRELOAD

Catatan ini ditulis pada tahun 2014, tetapi saya baru saja mendapat penindasan di Habré dan tidak terungkap. Saat pelarangan saya lupa, tapi sekarang saya menemukannya di draft. Saya berpikir untuk menghapusnya, tapi mungkin itu akan berguna bagi seseorang.

Mencari LD_PRELOAD

Secara umum, sedikit admin hari Jumat membaca tentang topik pencarian "termasuk" LD_PRELOAD.

1. Penyimpangan singkat bagi yang belum familiar dengan substitusi fungsi

Selebihnya bisa langsung ke .2.

Mari kita mulai dengan contoh klasik:

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

Kami mengkompilasi tanpa tanda apa pun:

$ gcc ./ld_rand.c -o ld_rand

Dan, seperti yang diharapkan, kita mendapatkan 5 angka acak kurang dari 100:

$ ./ld_rand
53
93
48
57
20

Namun bayangkan kita tidak memiliki kode sumber programnya, namun kita perlu mengubah perilakunya.

Mari kita buat perpustakaan kita sendiri dengan prototipe fungsi kita sendiri, misalnya:

int rand(){
  return 42;
}

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

Dan sekarang pilihan acak kami cukup dapat diprediksi:

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

Trik ini terlihat lebih mengesankan jika kita mengekspor perpustakaan kita terlebih dahulu melalui

$ export LD_PRELOAD=$PWD/ld_rand.so

atau kita akan melakukannya terlebih dahulu

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

lalu jalankan programnya seperti biasa. Kami belum mengubah satu baris kode pun dalam program itu sendiri, namun perilakunya sekarang bergantung pada fungsi kecil di perpustakaan kami. Terlebih lagi, pada saat penulisan program ini, baris bahkan tidak ada.

Apa yang membuat program kami menggunakan yang palsu baris? Mari kita lakukan selangkah demi selangkah.
Saat aplikasi dimulai, perpustakaan tertentu dimuat yang berisi fungsi-fungsi yang dibutuhkan oleh program. Kita dapat melihatnya menggunakan 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)

Daftar ini mungkin berbeda-beda tergantung versi OS, tetapi harus ada file di sana libc.so. Perpustakaan inilah yang menyediakan panggilan sistem dan fungsi dasar seperti Buka, malloc, Printf dll. Kami baris juga ada di antara mereka. Mari kita pastikan ini:

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

Mari kita lihat apakah kumpulan perpustakaan berubah saat digunakan 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)

Ternyata variabelnya sudah diatur LD_PRELOAD memaksa kita untuk memuat ld_rand.so meskipun program itu sendiri tidak memerlukannya. Dan sejak fungsi kami "rand" memuat lebih awal dari baris dari libc.so, lalu dia berkuasa.

Oke, kami berhasil mengganti fungsi asli, tetapi bagaimana kami dapat memastikan bahwa fungsinya tetap dipertahankan dan beberapa tindakan ditambahkan. Mari kita ubah acak kita:

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

Di sini, sebagai “tambahan” kami, kami hanya mencetak satu baris teks, setelah itu kami membuat pointer ke fungsi aslinya baris. Untuk mendapatkan alamat fungsi ini kita perlu dlsym adalah fungsi dari perpustakaan libdlyang akan menemukan milik kita baris di tumpukan perpustakaan dinamis. Setelah itu kita akan memanggil fungsi ini dan mengembalikan nilainya. Oleh karena itu, kita perlu menambahkan "-ldl" selama perakitan:

$ 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

Dan program kami menggunakan "asli" baris, setelah sebelumnya melakukan beberapa tindakan cabul.

2. Rasa sakit saat mencari

Mengetahui potensi ancaman, kami ingin mendeteksinya preload Hal itu dilakukan. Jelas bahwa cara terbaik untuk mendeteksinya adalah dengan memasukkannya ke dalam kernel, tetapi saya tertarik dengan opsi deteksi di ruang pengguna.

Selanjutnya, keputusan untuk mendeteksi dan menyangkalnya akan dilakukan secara berpasangan.

2.1. Mari kita mulai dengan sederhana

Seperti disebutkan sebelumnya, Anda dapat menentukan perpustakaan yang akan dimuat menggunakan variabel LD_PRELOAD atau dengan menuliskannya dalam file /etc/ld.so.preload. Mari kita buat dua detektor sederhana.

Yang pertama adalah memeriksa variabel lingkungan yang disetel:

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

Yang kedua adalah memeriksa apakah file dibuka:

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

Mari memuat perpustakaan:

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

Di sini dan di bawah, [+] menunjukkan deteksi berhasil.
Oleh karena itu, [-] berarti melewati deteksi.

Seberapa efektifkah detektor semacam itu? Mari kita lihat variabel lingkungan terlebih dahulu:

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

Demikian pula, kami membuang cek tersebut Buka:

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

Ya, cara lain untuk mengakses file dapat digunakan di sini, seperti, buka64, Stat dll., tetapi sebenarnya 5-10 baris kode yang sama diperlukan untuk menipu mereka.

2.2. Mari kita lanjutkan

Di atas kami menggunakan dapatkanv() untuk mendapatkan nilainya LD_PRELOAD, namun ada juga cara yang lebih “tingkat rendah” untuk mencapainya ENV-variabel. Kami tidak akan menggunakan fungsi perantara, tetapi akan merujuk ke array **mengepung, yang menyimpan salinan lingkungan:

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

Karena di sini kita membaca data langsung dari memori, panggilan seperti itu tidak dapat disadap, dan panggilan kita tidak terdeteksi_getenv itu tidak lagi mengganggu identifikasi intrusi.

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

Tampaknya masalah ini sudah terpecahkan? Ini masih baru saja dimulai.

Setelah program dimulai, nilai variabelnya LD_PRELOAD peretas tidak lagi membutuhkannya di memori, artinya mereka dapat membacanya dan menghapusnya sebelum menjalankan instruksi apa pun. Tentu saja, mengedit array di memori, paling tidak, merupakan gaya pemrograman yang buruk, tetapi bagaimana hal ini dapat menghentikan seseorang yang sebenarnya tidak menginginkan kita baik-baik saja?

Untuk melakukan ini kita perlu membuat fungsi palsu kita sendiri init (), di mana kami mencegat yang diinstal LD_PRELOAD dan menyebarkannya ke linker kami:

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

Kami menjalankan dan memeriksa:

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

Namun, ingatan bukanlah tempat terakhir di mana Anda dapat menemukan penggantinya LD_PRELOAD, ada juga /proc/. Mari kita mulai dengan yang sudah jelas /proc/{PID}/environ.

Faktanya, ada solusi universal untuk mengatasi hal tersebut tidak terdeteksi **mengepung и /proc/self/environ. Masalahnya adalah perilaku yang “salah”. tidak disetelv(env).

pilihan yang benar

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
$

Tapi mari kita bayangkan kita tidak menemukannya dan /proc/self/environ berisi data "bermasalah".

Pertama mari kita coba dengan "penyamaran" kita sebelumnya:

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

kucing menggunakan hal yang sama untuk membuka file Buka(), jadi solusinya mirip dengan yang sudah dilakukan di bagian 2.1, tapi sekarang kita membuat file sementara tempat kita menyalin nilai memori sebenarnya tanpa baris yang berisi 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);
}

Dan tahap ini telah dilewati:

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

Tempat jelas berikutnya adalah /proc/self/maps. Tidak ada gunanya memikirkannya. Solusinya benar-benar identik dengan yang sebelumnya: salin data dari file tanpa garis di antaranya libc.so и ld.jadi.

2.4. Opsi dari Chokepoint

Saya terutama menyukai solusi ini karena kesederhanaannya. Kami membandingkan alamat fungsi yang dimuat langsung darinya libc, dan alamat “BERIKUTNYA”.

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

Memuat perpustakaan dengan intersepsi "membuka()" dan cek:

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

Sanggahannya ternyata lebih sederhana:

#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. panggilan telepon

Tampaknya itu saja, tetapi kita masih akan gagal. Jika kita mengarahkan panggilan sistem langsung ke kernel, ini akan melewati seluruh proses intersepsi. Solusi di bawah ini, tentu saja, bergantung pada arsitektur (x86_64). Mari kita coba menerapkannya untuk mendeteksi celah 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) [+]

Dan masalah ini ada solusinya. Kutipan dari pria'A:

ptrace adalah alat yang memungkinkan proses induk mengamati dan mengontrol kemajuan proses lain, melihat dan mengubah data dan registernya. Biasanya fungsi ini digunakan untuk membuat breakpoint dalam program debugging dan memonitor panggilan sistem.

Proses induk dapat memulai penelusuran dengan terlebih dahulu memanggil fork(2), dan kemudian proses anak yang dihasilkan dapat mengeksekusi PTRACE_TRACEME, diikuti (biasanya) dengan mengeksekusi exec(3). Di sisi lain, proses induk dapat mulai men-debug proses yang ada menggunakan PTRACE_ATTACH.

Saat menelusuri, proses anak berhenti setiap kali menerima sinyal, meskipun sinyal diabaikan. (Pengecualian adalah SIGKILL, yang bekerja secara normal.) Proses induk akan diberitahu tentang hal ini dengan memanggil wait(2), setelah itu proses tersebut dapat melihat dan mengubah konten proses anak sebelum dimulai. Proses induk kemudian mengizinkan anak untuk terus berjalan, dalam beberapa kasus mengabaikan sinyal yang dikirimkan kepadanya atau malah mengirimkan sinyal lain).

Jadi, solusinya adalah memantau proses, menghentikannya sebelum setiap panggilan sistem dan, jika perlu, mengarahkan thread ke fungsi 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);
    }
}

Kami memeriksa:

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

+0-0=5

Terima kasih banyak

Charles Hubain
titik tersedak
ValdikSS
Philippe Teuwen
derhass

, yang artikel, sumber, dan komentarnya melakukan lebih dari yang saya lakukan untuk membuat catatan ini muncul di sini.

Sumber: www.habr.com

Tambah komentar