LD_PRELOAD เชถเซ‹เชงเซ€ เชฐเชนเซเชฏเชพเช‚ เช›เซ€เช

เช† เชจเซ‹เช‚เชง 2014 เชฎเชพเช‚ เชฒเช–เชตเชพเชฎเชพเช‚ เช†เชตเซ€ เชนเชคเซ€, เชชเชฐเช‚เชคเซ เชนเซเช‚ เชฎเชพเชคเซเชฐ Habrรฉ เชชเชฐ เชฆเชฎเชจ เชนเซ‡เช เชณ เช†เชตเซเชฏเซ‹ เชนเชคเซ‹ เช…เชจเซ‡ เชคเซ‡ เชฆเชฟเชตเชธเชจเซ‹ เชชเซเชฐเช•เชพเชถ เชœเซ‹เชคเซ‹ เชจ เชนเชคเซ‹. เชชเซเชฐเชคเชฟเชฌเช‚เชง เชฆเชฐเชฎเชฟเชฏเชพเชจ เชนเซเช‚ เชคเซ‡เชจเชพ เชตเชฟเชถเซ‡ เชญเซ‚เชฒเซ€ เช—เชฏเซ‹ เชนเชคเซ‹, เชชเชฐเช‚เชคเซ เชนเชตเซ‡ เชฎเชจเซ‡ เชคเซ‡ เชกเซเชฐเชพเชซเซเชŸเซเชธเชฎเชพเช‚ เชฎเชณเซเชฏเซ‹. เชฎเซ‡เช‚ เชคเซ‡เชจเซ‡ เช•เชพเชขเซ€ เชจเชพเช–เชตเชพ เชตเชฟเชถเซ‡ เชตเชฟเชšเชพเชฐเซเชฏเซเช‚, เชชเชฐเช‚เชคเซ เช•เชฆเชพเชš เชคเซ‡ เช•เซ‹เชˆเชจเซ‡ เช‰เชชเชฏเซ‹เช—เซ€ เชฅเชถเซ‡.

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

เช…เชจเซ‡ เชชเช›เซ€ เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎเชจเซ‡ เชนเช‚เชฎเซ‡เชถเชจเซ€ เชœเซ‡เชฎ เชšเชฒเชพเชตเซ‹. เช…เชฎเซ‡ เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎเชฎเชพเช‚ เชœ เช•เซ‹เชกเชจเซ€ เชเช• เชชเชฃ เชฒเชพเช‡เชจ เชฌเชฆเชฒเซ€ เชจเชฅเซ€, เชชเชฐเช‚เชคเซ เชคเซ‡เชจเซ€ เชตเชฐเซเชคเชฃเซ‚เช• เชนเชตเซ‡ เช…เชฎเชพเชฐเซ€ เชฒเชพเช‡เชฌเซเชฐเซ‡เชฐเซ€เชจเชพ เชจเชพเชจเชพ เช•เชพเชฐเซเชฏ เชชเชฐ เช†เชงเชพเชฐเชฟเชค เช›เซ‡. เชคเชฆเซเชชเชฐเชพเช‚เชค, เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎ เชฒเช–เชคเซ€ เชตเช–เชคเซ‡, เช† เชฐเซ‡เชจเซเชก เช…เชธเซเชคเชฟเชคเซเชตเชฎเชพเช‚ เชชเชฃ เชจเชนเซ‹เชคเซเช‚.

เช…เชฎเชพเชฐเชพ เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎเชจเซ‹ เชจเช•เชฒเซ€ เช‰เชชเชฏเซ‹เช— เช•เซ‡เชฎ เช•เชฐเซเชฏเซ‹ เชฐเซ‡เชจเซเชก? เชšเชพเชฒเซ‹ เชคเซ‡เชจเซ‡ เชธเซเชŸเซ‡เชช เชฌเชพเชฏ เชธเซเชŸเซ‡เชช เชฒเชˆเช.
เชœเซเชฏเชพเชฐเซ‡ เชเชชเซเชฒเชฟเช•เซ‡เชถเชจ เชถเชฐเซ‚ เชฅเชพเชฏ เช›เซ‡, เชคเซเชฏเชพเชฐเซ‡ เช…เชฎเซเช• เชชเซเชธเซเชคเช•เชพเชฒเชฏเซ‹ เชฒเซ‹เชก เชฅเชพเชฏ เช›เซ‡ เชœเซ‡เชฎเชพเช‚ เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎ เชฆเซเชตเชพเชฐเชพ เชœเชฐเซ‚เชฐเซ€ เช•เชพเชฐเซเชฏเซ‹ เชนเซ‹เชฏ เช›เซ‡. เช…เชฎเซ‡ เชคเซ‡เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เชœเซ‹เชˆ เชถเช•เซ€เช เช›เซ€เช เชเชฒ.เชกเซ€.เชกเซ€.:

# 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. เชคเซ‡ เช† เชชเซเชธเซเชคเช•เชพเชฒเชฏ เช›เซ‡ เชœเซ‡ เชธเชฟเชธเซเชŸเชฎ เช•เซ‰เชฒเซเชธ เช…เชจเซ‡ เชฎเซ‚เชณเชญเซ‚เชค เช•เชพเชฐเซเชฏเซ‹ เชœเซ‡เชฎ เช•เซ‡ เชชเซเชฐเชฆเชพเชจ เช•เชฐเซ‡ เช›เซ‡ เช“เชชเชจ, malloc, printf เชตเช—เซ‡เชฐเซ‡. เช…เชฎเชพเชฐเชพ เชฐเซ‡เชจเซเชก เชคเซ‡เชฎเชจเซ€ เชตเชšเซเชšเซ‡ เชชเชฃ เช›เซ‡. เชšเชพเชฒเซ‹ เช†เชจเซ€ เช–เชพเชคเชฐเซ€ เช•เชฐเซ€เช:

# 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 เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎเชจเซ‡ เชคเซ‡เชจเซ€ เชœเชฐเซ‚เชฐ เชจเชฅเซ€ เชคเซ‡ เชนเช•เซ€เช•เชค เชนเซ‹เชตเชพ เช›เชคเชพเช‚ เชชเชฃ. เช…เชจเซ‡ เช…เชฎเชพเชฐเชพ เช•เชพเชฐเซเชฏ เชฅเซ€ "เชฐเซ‡เชจเซเชก" เช•เชฐเชคเชพเช‚ เชตเชนเซ‡เชฒเชพ เชฒเซ‹เชก เชฅเชพเชฏ เช›เซ‡ เชฐเซ‡เชจเซเชก เชฅเซ€ libc.so, เชชเช›เซ€ เชคเซ‡ เชฐเซเชธเซเชŸ เชชเชฐ เชถเชพเชธเชจ เช•เชฐเซ‡ เช›เซ‡.

เช เซ€เช• เช›เซ‡, เช…เชฎเซ‡ เชฎเซ‚เชณ เชซเช‚เช•เซเชถเชจเชจเซ‡ เชฌเชฆเชฒเชตเชพเชฎเชพเช‚ เชตเซเชฏเชตเชธเซเชฅเชพเชชเชฟเชค เช›เซ€เช, เชชเชฐเช‚เชคเซ เช…เชฎเซ‡ เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เช–เชพเชคเชฐเซ€ เช•เชฐเซ€ เชถเช•เซ€เช เช•เซ‡ เชคเซ‡เชจเซ€ เช•เชพเชฐเซเชฏเช•เซเชทเชฎเชคเชพ เชธเชพเชšเชตเซ‡เชฒ เช›เซ‡ เช…เชจเซ‡ เช•เซ‡เชŸเชฒเซ€เช• เช•เซเชฐเชฟเชฏเชพเช“ เช‰เชฎเซ‡เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ€ เช›เซ‡. เชšเชพเชฒเซ‹ เช†เชชเชฃเชพ เชฐเซ‡เชจเซเชกเชฎเชจเซ‡ เชธเช‚เชถเซ‹เชงเชฟเชค เช•เชฐเซ€เช:

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

เช…เชนเซ€เช‚, เช…เชฎเชพเชฐเชพ "เชเชกเชฟเชถเชจ" เชคเชฐเซ€เช•เซ‡, เช…เชฎเซ‡ เชซเช•เซเชค เชŸเซ‡เช•เซเชธเซเชŸเชจเซ€ เชเช• เชฒเชพเช‡เชจ เช›เชพเชชเซ€เช เช›เซ€เช, เชœเซ‡ เชชเช›เซ€ เช…เชฎเซ‡ เชฎเซ‚เชณ เช•เชพเชฐเซเชฏ เชฎเชพเชŸเซ‡ เชจเชฟเชฐเซเชฆเซ‡เชถเช• เชฌเชจเชพเชตเซ€เช เช›เซ€เช. เชฐเซ‡เชจเซเชก. เช† เช•เชพเชฐเซเชฏเชจเซเช‚ เชธเชฐเชจเชพเชฎเซเช‚ เชฎเซ‡เชณเชตเชตเชพ เชฎเชพเชŸเซ‡ เช†เชชเชฃเชจเซ‡ เชœเชฐเซ‚เชฐ เช›เซ‡ dlsym เชชเซเชธเซเชคเช•เชพเชฒเชฏเชจเซเช‚ เช•เชพเชฐเซเชฏ เช›เซ‡ libdlเชœเซ‡ เช†เชชเชฃเซ€ เชถเซ‹เชง เช•เชฐเชถเซ‡ เชฐเซ‡เชจเซเชก เช—เชคเชฟเชถเซ€เชฒ เชชเซเชธเซเชคเช•เชพเชฒเชฏเซ‹เชจเชพ เชธเซเชŸเซ‡เช•เชฎเชพเช‚. เชœเซ‡ เชชเช›เซ€ เช†เชชเชฃเซ‡ เช† เชซเช‚เช•เซเชถเชจเชจเซ‡ เช•เซ‰เชฒ เช•เชฐเซ€เชถเซเช‚ เช…เชจเซ‡ เชคเซ‡เชจเซ€ เช•เชฟเช‚เชฎเชค เชชเชฐเชค เช•เชฐเซ€เชถเซเช‚. เชคเชฆเชจเซเชธเชพเชฐ, เช†เชชเชฃเซ‡ เช‰เชฎเซ‡เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เชชเชกเชถเซ‡ "-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

เช…เชจเซ‡ เช…เชฎเชพเชฐเซ‹ เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎ "เชฎเซ‚เชณ" เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ‡ เช›เซ‡ เชฐเซ‡เชจเซเชก, เช…เช—เชพเช‰ เช•เซ‡เชŸเชฒเซ€เช• เช…เชถเซเชฒเซ€เชฒ เช•เซเชฐเชฟเชฏเชพเช“ เช•เชฐเซ€ เชนเชคเซ€.

2. เชถเซ‹เชงเชจเซ€ เชชเซ€เชกเชพ

เชธเช‚เชญเชตเชฟเชค เช–เชคเชฐเชพ เชตเชฟเชถเซ‡ เชœเชพเชฃเซ€เชจเซ‡, เช…เชฎเซ‡ เชคเซ‡เชจเซ‡ เชถเซ‹เชงเชตเชพ เชฎเชพเช‚เช—เซ€เช เช›เซ€เช เชชเซเชฐเซ€เชฒเซ‹เชก เชคเซ‡ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เชนเชคเซเช‚. เชคเซ‡ เชธเซเชชเชทเซเชŸ เช›เซ‡ เช•เซ‡ เชถเซ‹เชงเชตเชพเชจเซ€ เชถเซเชฐเซ‡เชทเซเช  เชฐเซ€เชค เชคเซ‡เชจเซ‡ เช•เชฐเซเชจเชฒเชฎเชพเช‚ เชงเช•เซ‡เชฒเชตเซ€ เช›เซ‡, เชชเชฐเช‚เชคเซ เชฎเชจเซ‡ เชฏเซเชเชฐเชธเซเชชเซ‡เชธเชฎเชพเช‚ เชถเซ‹เชง เชตเชฟเช•เชฒเซเชชเซ‹เชฎเชพเช‚ เชฐเชธ เชนเชคเซ‹.

เช†เช—เชณ, เชถเซ‹เชง เช…เชจเซ‡ เชคเซ‡เชฎเชจเชพ เช–เช‚เชกเชจ เชฎเชพเชŸเซ‡เชจเชพ เช‰เช•เซ‡เชฒเซ‹ เชœเซ‹เชกเซ€เชฎเชพเช‚ เช†เชตเชถเซ‡.

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

เช เชœ เชฐเซ€เชคเซ‡, เช…เชฎเซ‡ เชšเซ‡เช•เชฅเซ€ เช›เซเชŸเช•เชพเชฐเซ‹ เชฎเซ‡เชณเชตเซ€เช เช›เซ€เช เช“เชชเชจ:

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

เชนเชพ, เชซเชพเช‡เชฒเชจเซ‡ เชเช•เซเชธเซ‡เชธ เช•เชฐเชตเชพเชจเซ€ เช…เชจเซเชฏ เชฐเซ€เชคเซ‹ เช…เชนเซ€เช‚ เชตเชพเชชเชฐเซ€ เชถเช•เชพเชฏ เช›เซ‡, เชœเซ‡เชฎ เช•เซ‡, เช“เชชเชจ64, stat เชตเช—เซ‡เชฐเซ‡., เชชเชฐเช‚เชคเซ, เชนเช•เซ€เช•เชคเชฎเชพเช‚, เช•เซ‹เชกเชจเซ€ เชธเชฎเชพเชจ 5-10 เชฒเซ€เชŸเซ€เช“ เชคเซ‡เชฎเชจเซ‡ เช›เซ‡เชคเชฐเชตเชพ เชฎเชพเชŸเซ‡ เชœเชฐเซ‚เชฐเซ€ เช›เซ‡.

2.2. เชšเชฒเซ‹ เช†เช—เชณ เชตเชงเซ€เช

เช‰เชชเชฐ เช†เชชเชฃเซ‡ เช‰เชชเชฏเซ‹เช— เช•เชฐเซเชฏเซ‹ getenv() เชฎเซ‚เชฒเซเชฏ เชฎเซ‡เชณเชตเชตเชพ เชฎเชพเชŸเซ‡ LD_PRELOAD, เชชเชฐเช‚เชคเซ เชคเซเชฏเชพเช‚ เชœเชตเชพ เชฎเชพเชŸเซ‡ เชตเชงเซ "เชจเซ€เชšเชพ-เชธเซเชคเชฐ" เชฎเชพเชฐเซเช— เชชเชฃ เช›เซ‡ ENV-เชšเชฒ. เช…เชฎเซ‡ เชฎเชงเซเชฏเชตเชฐเซเชคเซ€ เช•เชพเชฐเซเชฏเซ‹เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชถเซเช‚ เชจเชนเซ€เช‚, เชชเชฐเช‚เชคเซ เชเชฐเซ‡เชจเซ‹ เชธเช‚เชฆเชฐเซเชญ เชฒเชˆเชถเซเช‚ ** เชชเชฐเซเชฏเชพเชตเชฐเชฃ, เชœเซ‡ เชชเชฐเซเชฏเชพเชตเชฐเชฃเชจเซ€ เชจเช•เชฒ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเซ‡ เช›เซ‡:

#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 เชนเซ‡เช•เชฐเซเชธเชจเซ‡ เชนเชตเซ‡ เชฎเซ‡เชฎเชฐเซ€เชฎเชพเช‚ เชคเซ‡เชจเซ€ เชœเชฐเซ‚เชฐ เชจเชฅเซ€, เชเชŸเชฒเซ‡ เช•เซ‡, เชคเซ‡เช“ เชคเซ‡เชจเซ‡ เชตเชพเช‚เชšเซ€ เชถเช•เซ‡ เช›เซ‡ เช…เชจเซ‡ เช•เซ‹เชˆเชชเชฃ เชธเซ‚เชšเชจเชพเช“ เช…เชฎเชฒเชฎเชพเช‚ เชฎเซ‚เช•เชคเชพ เชชเชนเซ‡เชฒเชพ เชคเซ‡เชจเซ‡ เช•เชพเชขเซ€ เชถเช•เซ‡ เช›เซ‡. เช…เชฒเชฌเชคเซเชค, เชฎเซ‡เชฎเชฐเซ€เชฎเชพเช‚ เชเชฐเซ‡เชจเซ‡ เชธเช‚เชชเชพเชฆเชฟเชค เช•เชฐเชตเซเช‚ เช เช“เช›เชพเชฎเชพเช‚ เช“เช›เซเช‚, เช–เชฐเชพเชฌ เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎเชฟเช‚เช— เชถเซˆเชฒเซ€ เช›เซ‡, เชชเชฐเช‚เชคเซ เช† เชคเซ‡ เชตเซเชฏเช•เซเชคเชฟเชจเซ‡ เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชฐเซ‹เช•เซ€ เชถเช•เซ‡ เช›เซ‡ เชœเซ‡ เช–เชฐเซ‡เช–เชฐ เช•เซ‹เชˆเชชเชฃ เชฐเซ€เชคเซ‡ เช†เชชเชฃเซเช‚ เชธเชพเชฐเซเช‚ เช‡เชšเซเช›เชคเซ‹ เชจเชฅเซ€?

เช† เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช†เชชเชฃเซ‡ เช†เชชเชฃเซเช‚ เชชเซ‹เชคเชพเชจเซเช‚ เชจเช•เชฒเซ€ เช•เชพเชฐเซเชฏ เชฌเชจเชพเชตเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ เชคเซ‡เชฎเชพเช‚(), เชœเซ‡เชฎเชพเช‚ เช…เชฎเซ‡ เช‡เชจเซเชธเซเชŸเซ‹เชฒ เช•เชฐเซ‡เชฒเชพเชจเซ‡ เช…เชŸเช•เชพเชตเซ€เช เช›เซ€เช 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}/เชชเชฐเซเชฏเชพเชตเชฐเชฃ.

เชนเช•เซ€เช•เชคเชฎเชพเช‚, เชฎเชพเชŸเซ‡ เชเช• เชธเชพเชฐเซเชตเชคเซเชฐเชฟเช• เช‰เช•เซ‡เชฒ เช›เซ‡ เชถเซ‹เชงเซ€ เช•เชพเชขเชตเซเช‚ ** เชชเชฐเซเชฏเชพเชตเชฐเชฃ ะธ /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

เชฌเชฟเชฒเชพเชกเซ€ เชซเชพเช‡เชฒ เช–เซ‹เชฒเชตเชพ เชฎเชพเชŸเซ‡ เชคเซ‡เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ‡ เช›เซ‡ เช–เซเชฒเซเชฒเชพ(), เชคเซ‡เชฅเซ€ เชธเซ‹เชฒเซเชฏเซเชถเชจ เชชเชนเซ‡เชฒเชพเชฅเซ€ เชœ เชตเชฟเชญเชพเช— 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. เชšเซ‹เช•เชชเซ‹เช‡เชจเซเชŸเชจเซ‹ เชตเชฟเช•เชฒเซเชช

เชฎเชจเซ‡ เช–เชพเชธ เช•เชฐเซ€เชจเซ‡ เชคเซ‡เชจเซ€ เชธเชฐเชณเชคเชพ เชฎเชพเชŸเซ‡ เช† เชธเซ‹เชฒเซเชฏเซเชถเชจ เช—เชฎเซเชฏเซเช‚. เช…เชฎเซ‡ เชธเซ€เชงเชพ เชฒเซ‹เชก เช•เชฐเซ‡เชฒเชพ เชซเช‚เช•เซเชถเชจเซเชธเชจเชพ เชธเชฐเชจเชพเชฎเชพเช‚เชจเซ€ เชคเซเชฒเชจเชพ เช•เชฐเซ€เช เช›เซ€เช libc, เช…เชจเซ‡ "เช†เช—เชฒเซเช‚" เชธเชฐเชจเชพเชฎเชพเช‚.

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

เช‡เชจเซเชŸเชฐเชธเซ‡เชชเซเชถเชจ เชธเชพเชฅเซ‡ เชฒเชพเช‡เชฌเซเชฐเซ‡เชฐเซ€ เชฒเซ‹เชก เช•เชฐเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡ "เช–เซเชฒเซเชฒเชพ()" เช…เชจเซ‡ เชคเชชเชพเชธเซ‹:

$ 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. เชธเชฟเชธเซเช•เชฒเซเชธ

เชเชตเซเช‚ เชฒเชพเช—เซ‡ เช›เซ‡ เช•เซ‡ เช† เชฌเชงเซเช‚ เช›เซ‡, เชชเชฐเช‚เชคเซ เช†เชชเชฃเซ‡ เชนเชœเซ€ เชชเชฃ เชซเชซเชกเซ€เชถเซเช‚. เชœเซ‹ เช†เชชเชฃเซ‡ เชธเชฟเชธเซเชŸเชฎ เช•เซ‹เชฒเชจเซ‡ เชธเซ€เชงเซ‹ เช•เชฐเซเชจเชฒ เชชเชฐ เชกเชพเชฏเชฐเซ‡เช•เซเชŸ เช•เชฐเซ€เช, เชคเซ‹ เช† เชธเชฎเช—เซเชฐ เช‡เชจเซเชŸเชฐเชธเซ‡เชชเซเชถเชจ เชชเซเชฐเช•เซเชฐเชฟเชฏเชพเชจเซ‡ เชฌเชพเชฏเชชเชพเชธ เช•เชฐเชถเซ‡. เชจเซ€เชšเซ‡เชจเซ‹ เช‰เช•เซ‡เชฒ, เช…เชฒเชฌเชคเซเชค, เช†เชฐเซเช•เชฟเชŸเซ‡เช•เซเชšเชฐ เช†เชงเชพเชฐเชฟเชค เช›เซ‡ (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) [+]

เช…เชจเซ‡ เช† เชธเชฎเชธเซเชฏเชพเชจเซ‹ เช‰เช•เซ‡เชฒ เช›เซ‡. เชฎเชพเช‚เชฅเซ€ เช…เชตเชคเชฐเชฃ เชฎเชพเชฃเชธ'เช:

ptrace เช เชเช• เชธเชพเชงเชจ เช›เซ‡ เชœเซ‡ เชชเชฟเชคเซƒ เชชเซเชฐเช•เซเชฐเชฟเชฏเชพเชจเซ‡ เช…เชจเซเชฏ เชชเซเชฐเช•เซเชฐเชฟเชฏเชพเชจเซ€ เชชเซเชฐเช—เชคเชฟเชจเซเช‚ เชจเชฟเชฐเซ€เช•เซเชทเชฃ เช…เชจเซ‡ เชจเชฟเชฏเช‚เชคเซเชฐเชฃ เช•เชฐเชตเชพ, เชคเซ‡เชจเซ‹ เชกเซ‡เชŸเชพ เช…เชจเซ‡ เชฐเชœเชฟเชธเซเชŸเชฐ เชœเซ‹เชตเชพ เช…เชจเซ‡ เชฌเชฆเชฒเชตเชพเชจเซ€ เชฎเช‚เชœเซ‚เชฐเซ€ เช†เชชเซ‡ เช›เซ‡. เชธเชพเชฎเชพเชจเซเชฏ เชฐเซ€เชคเซ‡ เช† เชซเช‚เช•เซเชถเชจเชจเซ‹ เช‰เชชเชฏเซ‹เช— เชกเชฟเชฌเช—เซ€เช‚เช— เชชเซเชฐเซ‹เช—เซเชฐเชพเชฎเชฎเชพเช‚ เชฌเซเชฐเซ‡เช•เชชเซ‹เช‡เชจเซเชŸ เชฌเชจเชพเชตเชตเชพ เช…เชจเซ‡ เชธเชฟเชธเซเชŸเชฎ เช•เซ‹เชฒเซเชธ เชฎเซ‹เชจเชฟเชŸเชฐ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชฅเชพเชฏ เช›เซ‡.

เชชเชฟเชคเซƒ เชชเซเชฐเช•เซเชฐเชฟเชฏเชพ เชชเซเชฐเชฅเชฎ เชซเซ‹เชฐเซเช•(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

เชคเชฎเชพเชฐเซ‹ เช–เซ‚เชฌ เช–เซ‚เชฌ เช†เชญเชพเชฐ

เชšเชพเชฐเซเชฒเซเชธ เชนเซเชฌเซˆเชจ
เชšเซ‹เช• เชฌเชฟเช‚เชฆเซ
ValdikSS
เชซเชฟเชฒเชฟเชช เชŸเซเชฏเซเชตเซ‡เชจ
เชกเซ‡เชฐเชนเชพเชธ

, เชœเซ‡เชฎเชจเชพ เชฒเซ‡เช–เซ‹, เชธเซเชคเซเชฐเซ‹เชคเซ‹ เช…เชจเซ‡ เชŸเชฟเชชเซเชชเชฃเซ€เช“เช เช† เชจเซ‹เช‚เชง เช…เชนเซ€เช‚ เชชเซเชฐเชฆเชฐเซเชถเชฟเชค เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชฎเซ‡เช‚ เช•เชฐเซเชฏเซเช‚ เชคเซ‡เชจเชพ เช•เชฐเชคเชพเช‚ เช˜เชฃเซเช‚ เชตเชงเชพเชฐเซ‡ เช•เชฐเซเชฏเซเช‚.

เชธเซ‹เชฐเซเชธ: www.habr.com

เชเช• เชŸเชฟเชชเซเชชเชฃเซ€ เช‰เชฎเซ‡เชฐเซ‹