Π’ поисках LD_PRELOAD

Π­Ρ‚Π° Π·Π°ΠΌΠ΅Ρ‚ΠΊΠ° Π±Ρ‹Π»Π° написана Π² 2014-ΠΌ Π³ΠΎΠ΄Ρƒ, Π½ΠΎ я ΠΊΠ°ΠΊ Ρ€Π°Π· ΠΏΠΎΠΏΠ°Π» ΠΏΠΎΠ΄ рСпрСссии Π½Π° Ρ…Π°Π±Ρ€Π΅ ΠΈ ΠΎΠ½Π° Π½Π΅ ΡƒΠ²ΠΈΠ΄Π΅Π»Π° свСт. Π—Π° врСмя Π±Π°Π½Π° я ΠΏΡ€ΠΎ Π½Π΅Ρ‘ Π·Π°Π±Ρ‹Π», Π° сСйчас Π½Π°ΡˆΡ‘Π» Π² Ρ‡Π΅Ρ€Π½ΠΎΠ²ΠΈΠΊΠ°Ρ…. Π”ΡƒΠΌΠ°Π» Π±Ρ‹Π»ΠΎ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ, Π½ΠΎ авось ΠΊΠΎΠΌΡƒ пригодится.

Π’ поисках LD_PRELOAD

Π’ ΠΎΠ±Ρ‰Π΅ΠΌ, нСбольшоС пятничноС админскоС Ρ‡Ρ‚ΠΈΠ²ΠΎ Π½Π° Ρ‚Π΅ΠΌΡƒ поиска Β«Π²ΠΊΠ»ΡŽΡ‡Π΅Π½Π½ΠΎΠ³ΠΎΒ» LD_PRELOAD.

1. НСбольшоС отступлСниС для Ρ‚Π΅Ρ…, ΠΊΡ‚ΠΎ Π½Π΅ Π·Π½Π°ΠΊΠΎΠΌ с Π·Π°ΠΌΠ΅Ρ‰Π΅Π½ΠΈΠ΅ΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ

ΠžΡΡ‚Π°Π»ΡŒΠ½Ρ‹ΠΌ ΠΌΠΎΠΆΠ½ΠΎ сразу ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒ ΠΊ ΠΏ.2.

Начнём с классичСского ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°:

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

ΠšΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΡƒΠ΅ΠΌ Π±Π΅Π· ΠΊΠ°ΠΊΠΈΡ…-Π»ΠΈΠ±ΠΎ Ρ„Π»Π°Π³ΠΎΠ²:

$ gcc ./ld_rand.c -o ld_rand

И, ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌΠΎ, ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ 5 случайных чисСл мСньшС 100:

$ ./ld_rand
53
93
48
57
20

Но прСдставим, Ρ‡Ρ‚ΠΎ Ρƒ нас Π½Π΅Ρ‚ исходного ΠΊΠΎΠ΄Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹, Π° ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π½ΡƒΠΆΠ½ΠΎ.

Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ свою Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ с собствСнным ΠΏΡ€ΠΎΡ‚ΠΎΡ‚ΠΈΠΏΠΎΠΌ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€:

int rand(){
  return 42;
}

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

И Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ наш случайный Π²Ρ‹Π±ΠΎΡ€ Π²ΠΏΠΎΠ»Π½Π΅ прСдсказуСм:

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

Π­Ρ‚ΠΎΡ‚ Ρ‚Ρ€ΡŽΠΊ выглядит Π΅Ρ‰Ρ‘ Π±ΠΎΠ»Π΅Π΅ Π²ΠΏΠ΅Ρ‡Π°Ρ‚Π»ΡΡŽΡ‰ΠΈΠΌ, Ссли ΠΌΡ‹ сначала экспортируСм Π½Π°ΡˆΡƒ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ Ρ‡Π΅Ρ€Π΅Π·

$ export LD_PRELOAD=$PWD/ld_rand.so

ΠΈΠ»ΠΈ ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΠΌ

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

Π° Π·Π°Ρ‚Π΅ΠΌ запустим ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ Π² ΠΎΠ±Ρ‹Ρ‡Π½ΠΎΠΌ Ρ€Π΅ΠΆΠΈΠΌΠ΅. ΠœΡ‹ Π½Π΅ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠ»ΠΈ Π½ΠΈ строчки Π² ΠΊΠΎΠ΄Π΅ самой ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹, Π½ΠΎ Π΅Ρ‘ ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ зависит ΠΎΡ‚ ΠΊΡ€ΠΎΡˆΠ΅Ρ‡Π½ΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² нашСй Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ΅. Π‘ΠΎΠ»Π΅Π΅ Ρ‚ΠΎΠ³ΠΎ, Π½Π° ΠΌΠΎΠΌΠ΅Π½Ρ‚ написания ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ Ρ„Π°Π»ΡŒΡˆΠΈΠ²Ρ‹ΠΉ rand Π΄Π°ΠΆΠ΅ Π½Π΅ сущСствовал.

Π§Ρ‚ΠΎ ΠΆΠ΅ заставило Π½Π°ΡˆΡƒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠΎΠ΄Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ rand? Π Π°Π·Π±Π΅Ρ€Π΅ΠΌ ΠΏΠΎ шагам.
Когда ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ запускаСтся, Π·Π°Π³Ρ€ΡƒΠΆΠ°ΡŽΡ‚ΡΡ ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½Ρ‹Π΅ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ содСрТат Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹Π΅ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ΅. ΠœΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ ΠΈΡ… ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ 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)

Π­Ρ‚ΠΎΡ‚ список ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Ρ€Π°Π·Π»ΠΈΡ‡Π΅Π½ Π² зависимости ΠΎΡ‚ вСрсии OS, Π½ΠΎ Ρ‚Π°ΠΌ ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Ρ„Π°ΠΉΠ» libc.so. ИмСнно эта Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° обСспСчиваСт систСмныС Π²Ρ‹Π·ΠΎΠ²Ρ‹ ΠΈ основныС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ open, malloc, printf ΠΈ Ρ‚. Π΄. Наш rand Ρ‚Π°ΠΊΠΆΠ΅ Π²Ρ…ΠΎΠ΄ΠΈΡ‚ Π² ΠΈΡ… число. УбСдимся Π² этом:

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

ΠŸΠΎΡΠΌΠΎΡ‚Ρ€ΠΈΠΌ, Π½Π΅ измСнится Π»ΠΈ Π½Π°Π±ΠΎΡ€ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ ΠΏΡ€ΠΈ использовании 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)

ΠžΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ΡΡ установлСнная пСрСмСнная LD_PRELOAD заставляСт Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒΡΡ Π½Π°ΡˆΡƒ ld_rand.so Π΄Π°ΠΆΠ΅, Π½Π΅ смотря Π½Π° Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° сама Π΅Ρ‘ Π½Π΅ Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚. И, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ наша функция Β«randΒ» загруТаСтся Ρ€Π°Π½ΡŒΡˆΠ΅ Ρ‡Π΅ΠΌ rand ΠΎΡ‚ libc.so, Ρ‚ΠΎ ΠΎΠ½Π° ΠΈ ΠΏΡ€Π°Π²ΠΈΡ‚ Π±Π°Π»ΠΎΠΌ.

Ok, Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Ρ€ΠΎΠ΄Π½ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ Π½Π°ΠΌ ΡƒΠ΄Π°Π»ΠΎΡΡŒ, Π½ΠΎ ΠΊΠ°ΠΊ Π±Ρ‹ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‚Π°ΠΊ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΈ Π΅Ρ‘ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π» сохранился ΠΈ Π½Π΅ΠΊΠΈΠ΅ дСйствия добавились. ΠœΠΎΠ΄ΠΈΡ„ΠΈΡ†ΠΈΡ€ΡƒΠ΅ΠΌ наш Ρ€Π°Π½Π΄ΠΎΠΌ:

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

Π—Π΄Π΅ΡΡŒ Π² качСствС нашСй Β«Π΄ΠΎΠ±Π°Π²ΠΊΠΈΒ» ΠΌΡ‹ лишь ΠΏΠ΅Ρ‡Π°Ρ‚Π°Π΅ΠΌ ΠΎΠ΄Π½Ρƒ строку тСкста, послС Ρ‡Π΅Π³ΠΎ создаём ΡƒΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ Π½Π° ΠΈΡΡ…ΠΎΠ΄Π½ΡƒΡŽ функция rand. Для получСния адрСса этой Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π½Π°ΠΌ потрСбуСтся dlsym β€” это функция ΠΈΠ· Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ libdl, которая Π½Π°ΠΉΠ΄Π΅Ρ‚ наш rand Π² стСкС динамичСских Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ. ПослС Ρ‡Π΅Π³ΠΎ ΠΌΡ‹ Π²Ρ‹Π·ΠΎΠ²Π΅ΠΌ эту Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΈ Π²ΠΎΠ·Π²Ρ€Π°Ρ‚ΠΈΠΌ Π΅Ρ‘ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅. БоотвСтствСнно, Π½Π°ΠΌ понадобится Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ «-ldl» ΠΏΡ€ΠΈ сборкС:

$ 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

И наша ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ Β«Ρ€ΠΎΠ΄Π½ΠΎΠΉΒ» rand, ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ исполнив Π½Π΅ΠΊΠΈΠ΅ Π½Π΅ΠΏΠΎΡ‚Ρ€Π΅Π±Π½Ρ‹Π΅ дСйствия.

2. ΠœΡƒΠΊΠΈ поиска

Зная ΠΎ ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎΠΉ ΡƒΠ³Ρ€ΠΎΠ·Π΅, ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ preload Π±Ρ‹Π» Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½. ΠŸΠΎΠ½ΡΡ‚Π½ΠΎ, Ρ‡Ρ‚ΠΎ Π»ΡƒΡ‡ΡˆΠΈΠΉ способ Π΄Π΅Ρ‚Π΅ΠΊΡ‚ΠΈΠ½Π³Π° β€” Π·Π°ΠΏΠΈΡ…Π½ΡƒΡ‚ΡŒ Π΅Π³ΠΎ Π² ядро, Π½ΠΎ мСня интСрСсовали ΠΈΠΌΠ΅Π½Π½ΠΎ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹ опрСдСлСния Π² ΡŽΠ·Π΅Ρ€ΡΠΏΡΠΉΡΠ΅.

Π”Π°Π»Π΅Π΅ ΠΏΠ°Ρ€Π°ΠΌΠΈ ΠΏΠΎΠΉΠ΄ΡƒΡ‚ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ для обнаруТСния ΠΈ ΠΈΡ… ΠΎΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΆΠ΅Π½ΠΈΠ΅.

2.1. Начнём с простого

Как Π³ΠΎΠ²ΠΎΡ€ΠΈΠ»ΠΎΡΡŒ Ρ€Π°Π½Π΅Π΅, ΡƒΠΊΠ°Π·Π°Ρ‚ΡŒ Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌΡƒΡŽ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ ΠΌΠΎΠΆΠ½ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ LD_PRELOAD ΠΈΠ»ΠΈ прописав Π΅Ρ‘ Π² Ρ„Π°ΠΉΠ»Π΅ /etc/ld.so.preload. Π‘ΠΎΠ·Π΄Π°Π΄ΠΈΠΌ Π΄Π²Π° ΠΏΡ€ΠΎΡΡ‚Π΅ΠΉΡˆΠΈΡ… Π΄Π΅Ρ‚Π΅ΠΊΡ‚ΠΎΡ€Π°.

ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ β€” для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ установлСнной ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ окруТСния:

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

Π’Ρ‚ΠΎΡ€ΠΎΠΉ β€” для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ открытия Ρ„Π°ΠΉΠ»Π°:

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

ΠŸΠΎΠ΄Π³Ρ€ΡƒΠ·ΠΈΠΌ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ:

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

Π—Π΄Π΅ΡΡŒ ΠΈ Π΄Π°Π»Π΅Π΅ [+] ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Π½Π° ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠ΅ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅.
БоотвСтствСнно, [-] ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚ ΠΎΠ±Ρ…ΠΎΠ΄ дСтСктирования.

Насколько ΠΆΠ΅ дСйствСнСн Ρ‚Π°ΠΊΠΎΠΉ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚Π΅Π»ΡŒ? Π‘Π½Π°Ρ‡Π°Π»Π° займёмся ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ окруТСния:

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

Аналогично избавляСмся ΠΈ ΠΎΡ‚ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ 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) [-]

Π”Π°, здСсь ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½Ρ‹ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ способы доступа ΠΊ Ρ„Π°ΠΉΠ»Ρƒ, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ, open64, stat ΠΈ Ρ‚.Π΄., Π½ΠΎ, ΠΏΠΎ сути, для ΠΈΡ… ΠΎΠ±ΠΌΠ°Π½Π° Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹ Ρ‚Π΅ ΠΆΠ΅ 5-10 строк ΠΊΠΎΠ΄Π°.

2.2. ДвигаСмся дальшС

Π’Ρ‹ΡˆΠ΅ ΠΌΡ‹ использовали getenv() для получСния значСния LD_PRELOAD, Π½ΠΎ Π΅ΡΡ‚ΡŒ ΠΆΠ΅ ΠΈ Π±ΠΎΠ»Π΅Π΅ Β«Π½ΠΈΠ·ΠΊΠΎΡƒΡ€ΠΎΠ²Π½Π΅Π²Ρ‹ΠΉΒ» способ Π΄ΠΎΠ±Ρ€Π°Ρ‚ΡŒΡΡ Π΄ΠΎ ENV-ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ…. НС Π±ΡƒΠ΄Π΅ΠΌ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½Ρ‹Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ, Π° обратимся ΠΊ массиву **environ, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ хранится копия окруТСния:

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

Π’Π°ΠΊ ΠΊΠ°ΠΊ здСсь ΠΌΡ‹ Ρ‡ΠΈΡ‚Π°Π΅ΠΌ Π΄Π°Π½Π½Ρ‹Π΅ Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ ΠΈΠ· памяти, Ρ‚ΠΎ Ρ‚Π°ΠΊΠΎΠΉ Π²Ρ‹Π·ΠΎΠ² нСльзя ΠΏΠ΅Ρ€Π΅Ρ…Π²Π°Ρ‚ΠΈΡ‚ΡŒ, ΠΈ наш undetect_getenv ΡƒΠΆΠ΅ Π½Π΅ ΠΌΠ΅ΡˆΠ°Π΅Ρ‚ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ Π²Ρ‚ΠΎΡ€ΠΆΠ΅Π½ΠΈΠ΅.

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

Казалось Π±Ρ‹ Π½Π° этом ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Ρ€Π΅ΡˆΠ΅Π½Π°? Всё Π΅Ρ‰Ρ‘ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ начинаСтся.

ПослС Ρ‚ΠΎΠ³ΠΎ ΠΊΠ°ΠΊ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ° Π·Π°ΠΏΡƒΡ‰Π΅Π½Π°, Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ LD_PRELOAD Π² памяти ΡƒΠΆΠ΅ Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ Π²Π·Π»ΠΎΠΌΡ‰ΠΈΠΊΠ°ΠΌ, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ ΠΌΠΎΠΆΠ½ΠΎ ΡΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π΅Ρ‘ ΠΈ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ Π΄ΠΎ выполнСния ΠΊΠ°ΠΊΠΈΡ…-Π»ΠΈΠ±ΠΎ инструкций. ΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ, ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ массив Π² памяти, ΠΊΠ°ΠΊ ΠΌΠΈΠ½ΠΈΠΌΡƒΠΌ, ΠΏΠ»ΠΎΡ…ΠΎΠΉ ΡΡ‚ΠΈΠ»ΡŒ программирования, Π½ΠΎ Ρ€Π°Π·Π²Π΅ это ΠΌΠΎΠΆΠ΅Ρ‚ ΠΎΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Ρ‚ΠΎΠ³ΠΎ, ΠΊΡ‚ΠΎ ΠΈ Ρ‚Π°ΠΊ Π½Π΅ особо ΠΆΠ΅Π»Π°Π΅Ρ‚ Π½Π°ΠΌ Π΄ΠΎΠ±Ρ€Π°?

Для этого Π½Π°ΠΌ понадобится ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ свою Ρ„Π΅ΠΉΠΊΠΎΠ²ΡƒΡŽ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ init(), Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ ΠΏΠ΅Ρ€Π΅Ρ…Π²Π°Ρ‚ΠΈΠΌ установлСнный LD_PRELOAD ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ Π΅Ρ‘ Π½Π°ΡˆΠ΅ΠΌΡƒ ΠΊΠΎΠΌΠΏΠΎΠ½ΠΎΠ²Ρ‰ΠΈΠΊΡƒ:

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

Однако, ΠΏΠ°ΠΌΡΡ‚ΡŒ β€” это Π½Π΅ послСднСС мСсто, Π³Π΄Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚ΡŒ ΠΏΠΎΠ΄ΠΌΠ΅Π½Ρƒ LD_PRELOAD, Π΅ΡΡ‚ΡŒ ΠΆΠ΅ Π΅Ρ‰Ρ‘ ΠΈ /proc/. Начнём с ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎΠ³ΠΎ /proc/{PID}/environ.

На самом Π΄Π΅Π»Π΅ Π΅ΡΡ‚ΡŒ ΡƒΠ½ΠΈΠ²Π΅Ρ€ΡΠ°Π»ΡŒΠ½ΠΎΠ΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ для undetect’Π° **environ ΠΈ /proc/self/environ. ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² Β«Π½Π΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎΠΌΒ» ΠΏΠΎΠ²Π΅Π΄Π΅Π½ΠΈΠΈ unsetenv(env).

ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚

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
$

Но прСдставим, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ Π΅Π³ΠΎ Π½Π΅ нашли ΠΈ /proc/self/environ содСрТит Β«ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ½Ρ‹Π΅Β» Π΄Π°Π½Π½Ρ‹Π΅.

Π‘Π½Π°Ρ‡Π°Π»Π° ΠΏΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ с нашСй ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΉ «маскировкой»:

$ (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 ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ для открытия Ρ„Π°ΠΉΠ»Π° всё Ρ‚ΠΎΡ‚ ΠΆΠ΅ open(), поэтому Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ сходно с Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΡƒΠΆΠ΅ дСлалось Π² ΠΏ.2.1, Π½ΠΎ Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ создаСм Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ Ρ„Π°ΠΉΠ», ΠΊΡƒΠ΄Π° ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ значСния истинной памяти Π±Π΅Π· строк содСрТащих 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
$

Π‘Π»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅ ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎΠ΅ мСсто β€” /proc/self/maps. Π—Π°Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Ρ‚ΡŒΡΡ Π½Π° Π½Π΅ΠΌ Π½Π΅Ρ‚ смысла. РСшСниС Π°Π±ΡΠΎΠ»ΡŽΡ‚Π½ΠΎ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ‡Π½ΠΎΠ΅ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌΡƒ: ΠΊΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· Ρ„Π°ΠΉΠ»Π° Π·Π° Π²Ρ‹Ρ‡Π΅Ρ‚ΠΎΠΌ строк ΠΌΠ΅ΠΆΠ΄Ρƒ libc.so ΠΈ ld.so.

2.4. Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ ΠΎΡ‚ Chokepoint

Π­Ρ‚ΠΎ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ ΠΌΠ½Π΅ особСнно ΠΏΠΎΠ½Ρ€Π°Π²ΠΈΠ»ΠΎΡΡŒ своСй простотой. Π‘Ρ€Π°Π²Π½ΠΈΠ²Π°Π΅ΠΌ адрСса Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ, Π·Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌΡ‹Ρ… нСпосрСдствСнно ΠΈΠ· libc, ΠΈ Β«NEXTΒ»-адрСса.

#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()Β» ΠΈ провСряСм:

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

ΠžΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΆΠ΅Π½ΠΈΠ΅ оказалось Π΅Ρ‰Ρ‘ ΠΏΡ€ΠΎΡ‰Π΅:

#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

Казалось Π±Ρ‹ Π½Π° этом всё, Π½ΠΎ Π΅Ρ‰Ρ‘ побарахтаСмся. Если ΠΌΡ‹ Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ систСмный Π²Ρ‹Π·ΠΎΠ² Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ ΠΊ ядру, Ρ‚ΠΎ это ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ ΠΎΠ±ΠΎΠΉΡ‚ΠΈ вСсь процСсс ΠΏΠ΅Ρ€Π΅Ρ…Π²Π°Ρ‚Π°. РСшСниС Π½ΠΈΠΆΠ΅, разумССтся, архитСктурозависимоС (x86_64). ΠŸΠΎΠΏΡ€ΠΎΠ±ΡƒΠ΅ΠΌ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ для обнаруТСния открытия 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) [+]

И данная Π·Π°Π΄Π°Ρ‡ΠΊΠ° ΠΈΠΌΠ΅Π΅Ρ‚ Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅. Π’Ρ‹Π΄Π΅Ρ€ΠΆΠΊΠ° ΠΈΠ· man‘Π°:

ptrace β€” это срСдство, ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‰Π΅Π΅ Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΎΠΌΡƒ процСссу Π½Π°Π±Π»ΡŽΠ΄Π°Ρ‚ΡŒ ΠΈ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΡ‚Π΅ΠΊΠ°Π½ΠΈΠ΅ Π΄Ρ€ΡƒΠ³ΠΎΠ³ΠΎ процСсса, ΠΏΡ€ΠΎΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°Ρ‚ΡŒ ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ Π΅Π³ΠΎ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈ рСгистры. ΠžΠ±Ρ‹Ρ‡Π½ΠΎ эта функция ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для создания Ρ‚ΠΎΡ‡Π΅ΠΊ прСрывания Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ΅ ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ ΠΈ отслСТивания систСмных Π²Ρ‹Π·ΠΎΠ²ΠΎΠ².

Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ процСсс ΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°Ρ‡Π°Ρ‚ΡŒ трассировку, сначала Π²Ρ‹Π·Π²Π°Π² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ fork(2), Π° Π·Π°Ρ‚Π΅ΠΌ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ²ΡˆΠΈΠΉΡΡ Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΠΉ процСсс ΠΌΠΎΠΆΠ΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ PTRACE_TRACEME, Π·Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ (ΠΎΠ±Ρ‹Ρ‡Π½ΠΎ) слСдуСт Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ exec(3). Π‘ Π΄Ρ€ΡƒΠ³ΠΎΠΉ стороны, Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ процСсс ΠΌΠΎΠΆΠ΅Ρ‚ Π½Π°Ρ‡Π°Ρ‚ΡŒ ΠΎΡ‚Π»Π°Π΄ΠΊΡƒ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ процСсса ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ PTRACE_ATTACH.

ΠŸΡ€ΠΈ трассировкС Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΠΉ процСсс останавливаСтся ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π· ΠΏΡ€ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ сигнала, Π΄Π°ΠΆΠ΅ Ссли этот сигнал игнорируСтся. (Π˜ΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ΠΌ являСтся SIGKILL, Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‰ΠΈΠΉ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ.) Π ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ процСсс Π±ΡƒΠ΄Π΅Ρ‚ ΡƒΠ²Π΅Π΄ΠΎΠΌΠ»Π΅Π½ ΠΎΠ± этом ΠΏΡ€ΠΈ Π²Ρ‹Π·ΠΎΠ²Π΅ wait(2), послС ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ ΠΎΠ½ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€ΠΎΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°Ρ‚ΡŒ ΠΈ ΠΈΠ·ΠΌΠ΅Π½ΡΡ‚ΡŒ содСрТимоС Π΄ΠΎΡ‡Π΅Ρ€Π½Π΅Π³ΠΎ процСсса Π΄ΠΎ Π΅Π³ΠΎ запуска. ПослС этого Ρ€ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ процСсс Ρ€Π°Π·Ρ€Π΅ΡˆΠ°Π΅Ρ‚ Π΄ΠΎΡ‡Π΅Ρ€Π½Π΅ΠΌΡƒ ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Ρ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρƒ, Π² Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… случаях игнорируя посылаСмый Π΅ΠΌΡƒ сигнал ΠΈΠ»ΠΈ отправляя вмСсто этого Π΄Ρ€ΡƒΠ³ΠΎΠΉ сигнал).

Π’Π΅ΠΌ самым, Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Ρ‚ΡŒ процСсс, останавливая Π΅Π³ΠΎ ΠΏΠ΅Ρ€Π΅Π΄ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΌ систСмным Π²Ρ‹Π·ΠΎΠ²ΠΎΠΌ ΠΈ, ΠΏΡ€ΠΈ нСобходимости, ΠΏΠ΅Ρ€Π΅Π½Π°ΠΏΡ€Π°Π²Π»ΡΡ‚ΡŒ ΠΏΠΎΡ‚ΠΎΠΊ Π² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ Π»ΠΎΠ²ΡƒΡˆΠΊΠΈ.

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

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ:

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

+0-0=5

ΠžΠ³Ρ€ΠΎΠΌΠ½ΠΎΠ΅ спасибо

Charles Hubain
Chokepoint
ValdikSS
Philippe Teuwen
derhass

, Ρ‡ΡŒΠΈ ΡΡ‚Π°Ρ‚ΡŒΠΈ, исходники ΠΈ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ сдСлали Π½Π°ΠΌΠ½ΠΎΠ³ΠΎ большС, Ρ‡Π΅ΠΌ я, для Ρ‚ΠΎΠ³ΠΎ Ρ‡Ρ‚ΠΎΠ±Ρ‹ эта Π·Π°ΠΌΠ΅Ρ‚ΠΊΠ° появилась здСсь.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com