Er wordt gezocht naar LD_PRELOAD

Deze nota is in 2014 geschreven, maar ik kwam net onder repressie tegen Habré terecht en het zag het daglicht niet. Tijdens het verbod vergat ik het, maar nu vond ik het in de concepten. Ik dacht erover om het te verwijderen, maar misschien is het nuttig voor iemand.

Er wordt gezocht naar LD_PRELOAD

Over het algemeen een kleine vrijdag-admin-lezing over het zoeken naar “inbegrepen” LD_PRELOAD.

1. Een korte uitweiding voor degenen die niet bekend zijn met functievervanging

De rest kan direct naar toe p.2.

Laten we beginnen met een klassiek voorbeeld:

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

Wij compileren zonder vlaggen:

$ gcc ./ld_rand.c -o ld_rand

En zoals verwacht krijgen we 5 willekeurige getallen kleiner dan 100:

$ ./ld_rand
53
93
48
57
20

Maar laten we ons voorstellen dat we niet over de broncode van het programma beschikken, maar dat we het gedrag moeten veranderen.

Laten we onze eigen bibliotheek maken met ons eigen functieprototype, bijvoorbeeld:

int rand(){
  return 42;
}

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

En nu is onze willekeurige keuze behoorlijk voorspelbaar:

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

Deze truc ziet er nog indrukwekkender uit als we eerst onze bibliotheek exporteren via

$ export LD_PRELOAD=$PWD/ld_rand.so

of we doen het eerst

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

en voer het programma vervolgens uit zoals gewoonlijk. We hebben geen enkele regel code in het programma zelf gewijzigd, maar het gedrag ervan hangt nu af van een kleine functie in onze bibliotheek. Bovendien was op het moment dat het programma werd geschreven de rand bestond niet eens.

Wat zorgde ervoor dat ons programma een nepprogramma gebruikte rand? Laten we het stap voor stap bekijken.
Wanneer een applicatie start, worden bepaalde bibliotheken geladen die de functies bevatten die het programma nodig heeft. We kunnen ze bekijken met behulp van 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)

Deze lijst kan variëren afhankelijk van de versie van het besturingssysteem, maar er moet een bestand aanwezig zijn libc.zo. Het is deze bibliotheek die systeemoproepen en basisfuncties biedt, zoals open, malloc, printf enz. Onze rand behoort daar ook toe. Laten we hier zeker van zijn:

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

Laten we eens kijken of de set bibliotheken verandert wanneer deze wordt gebruikt 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)

Blijkt dat de variabele is ingesteld LD_PRELOAD dwingt ons om te laden ld_rand.so zelfs ondanks het feit dat het programma zelf dit niet nodig heeft. En sinds onze functie "rand" laadt eerder dan rand van libc.zo, dan regeert zij de baas.

Oké, het is ons gelukt de oorspronkelijke functie te vervangen, maar hoe kunnen we ervoor zorgen dat de functionaliteit behouden blijft en dat er enkele acties worden toegevoegd. Laten we onze willekeurige wijzigen:

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

Hier drukken we als “toevoeging” slechts één regel tekst af, waarna we een verwijzing naar de originele functie creëren rand. Om het adres van deze functie te verkrijgen hebben we nodig dlsym is een functie uit de bibliotheek libdldie de onze zal vinden rand in een stapel dynamische bibliotheken. Daarna zullen we deze functie aanroepen en de waarde ervan retourneren. Dienovereenkomstig zullen we moeten toevoegen "-ldl" tijdens 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

En ons programma gebruikt "native" rand, nadat hij eerder enkele obscene acties had uitgevoerd.

2. De pijn van het zoeken

Omdat we een potentiële dreiging kennen, willen we die detecteren preload Het werd uitgevoerd. Het is duidelijk dat de beste manier om te detecteren is door het in de kernel te duwen, maar ik was geïnteresseerd in de detectieopties in de gebruikersruimte.

Vervolgens zullen oplossingen voor detectie en hun weerlegging in paren komen.

2.1. Laten we eenvoudig beginnen

Zoals eerder vermeld, kunt u met behulp van de variabele opgeven welke bibliotheek moet worden geladen LD_PRELOAD of door het in een bestand te schrijven /etc/ld.so.preload. Laten we twee eenvoudige detectoren maken.

De eerste is het controleren van de ingestelde omgevingsvariabele:

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

De tweede is om te controleren of het bestand is geopend:

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

Laten we de bibliotheken laden:

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

Hier en hieronder geeft [+] een succesvolle detectie aan.
Dienovereenkomstig betekent [-] het omzeilen van detectie.

Hoe effectief is zo’n detector? Laten we eerst eens kijken naar de omgevingsvariabele:

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

Op dezelfde manier verwijderen we de cheque open:

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

Ja, andere manieren om toegang te krijgen tot het bestand kunnen hier worden gebruikt, zoals: open64, staat etc., maar in feite zijn dezelfde 5-10 regels code nodig om ze te misleiden.

2.2. Laten we verder gaan

Hierboven gebruikten we getenv() om de waarde te krijgen LD_PRELOAD, maar er is ook een meer “low-level” manier om er te komen ENV-variabelen. We zullen geen tussenliggende functies gebruiken, maar verwijzen naar de array **omgeving, waarin een kopie van de omgeving wordt opgeslagen:

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

Omdat we hier gegevens rechtstreeks uit het geheugen lezen, kan een dergelijke oproep niet worden onderschept, en onze undetect_getenv het interfereert niet langer met het identificeren van de inbraak.

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

Het lijkt erop dat dit probleem is opgelost? Het begint nog maar net.

Nadat het programma is gestart, wordt de waarde van de variabele weergegeven LD_PRELOAD hackers hebben het niet langer nodig in het geheugen, dat wil zeggen dat ze het kunnen lezen en verwijderen voordat ze instructies uitvoeren. Natuurlijk is het bewerken van een array in het geheugen op zijn minst een slechte programmeerstijl, maar hoe kan dit iemand tegenhouden die ons toch niet echt het beste wenst?

Om dit te doen moeten we onze eigen nepfunctie creëren in het(), waarin we het geïnstalleerde onderscheppen LD_PRELOAD en geef het door aan onze linker:

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

Wij voeren uit en controleren:

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

Het geheugen is echter niet de laatste plaats waar je een vervanging kunt vinden LD_PRELOAD, er is ook / proces /. Laten we beginnen met het voor de hand liggende /proc/{PID}/environ.

Er bestaat zelfs een universele oplossing voor onopgemerkt **omgeving и /proc/self/environ. Het probleem is het ‘verkeerde’ gedrag unsetenv(env).

juiste optie

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
$

Maar laten we ons voorstellen dat we het niet hebben gevonden en /proc/self/environ bevat "problematische" gegevens.

Laten we eerst eens proberen met onze vorige "vermomming":

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

hoe gebruikt hetzelfde om het bestand te openen Open(), dus de oplossing is vergelijkbaar met wat al is gedaan in sectie 2.1, maar nu maken we een tijdelijk bestand waarin we de waarden van het echte geheugen kopiëren zonder regels met daarin 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);
}

En deze fase is voorbij:

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

De volgende voor de hand liggende plaats is /proc/self/kaarten. Het heeft geen zin om daar bij stil te staan. De oplossing is absoluut identiek aan de vorige: kopieer de gegevens uit het bestand minus de regels ertussen libc.zo и zo.

2.4. Optie van Chokepoint

Ik vond deze oplossing vooral leuk vanwege de eenvoud ervan. We vergelijken de adressen van functies die rechtstreeks worden geladen libcen “NEXT”-adressen.

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

De bibliotheek laden met onderschepping "open()" en check:

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

De weerlegging bleek nog eenvoudiger:

#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. Systeemoproepen

Het lijkt erop dat dit alles is, maar we zullen nog steeds botsen. Als we de systeemaanroep rechtstreeks naar de kernel sturen, wordt het hele onderscheppingsproces omzeild. Onderstaande oplossing is uiteraard architectuurafhankelijk (x86_64). Laten we proberen het te implementeren om een ​​opening te detecteren 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) [+]

En dit probleem heeft een oplossing. Uittreksel uit man'A:

ptrace is een tool waarmee een ouderproces de voortgang van een ander proces kan observeren en controleren, en de gegevens en registers ervan kan bekijken en wijzigen. Normaal gesproken wordt deze functie gebruikt om breekpunten in een foutopsporingsprogramma te maken en systeemaanroepen te monitoren.

Het ouderproces kan beginnen met traceren door eerst fork(2) aan te roepen, en vervolgens kan het resulterende onderliggende proces PTRACE_TRACEME uitvoeren, gevolgd (meestal) door het uitvoeren van exec(3). Aan de andere kant kan een ouderproces beginnen met het debuggen van een bestaand proces met behulp van PTRACE_ATTACH.

Bij het traceren stopt het onderliggende proces elke keer dat het een signaal ontvangt, zelfs als het signaal wordt genegeerd. (De uitzondering is SIGKILL, die normaal werkt.) Het ouderproces wordt hiervan op de hoogte gebracht door wait(2) aan te roepen, waarna het de inhoud van het onderliggende proces kan bekijken en wijzigen voordat het start. Het ouderproces zorgt er vervolgens voor dat het kind kan blijven rennen, waarbij in sommige gevallen het signaal dat naar het kind wordt gestuurd wordt genegeerd of in plaats daarvan een ander signaal wordt verzonden.

De oplossing is dus om het proces te monitoren, het vóór elke systeemaanroep te stoppen en, indien nodig, de thread om te leiden naar een hook-functie.

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

controleren:

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

+0-0=5

Hartelijk bedankt

Charles Hubain
Knelpunt
ValdikSS
Philippe Teuwen
derhass

, wiens artikelen, bronnen en commentaren veel meer hebben gedaan dan ik om deze notitie hier te laten verschijnen.

Bron: www.habr.com

Voeg een reactie