LD_PRELOAD 환경변수를 이용한 공유라이브러리(Dynamic Library) 후킹(Hooking)

여러분은 후킹(Hooking)이라는 것이 무엇인지 알고 계실 것입니다.
아직 이것이 무엇인지 모르시는 분은 http://ko.wikipedia.org/wiki/%ED%9B%84%ED%82%B9 에서 확인하고 다시 돌아오시길 바랍니다.
한마디로 중간에서 무언가를 가로채서 원래의 기능을 자기가 다시 정의한 기능으로 교체하는 기술이라고 보면 됩니다.

이러한 기술은 아주 여러 분야에서 다양하게 사용될 수 있습니다.
그 유명한 제니퍼소프트(JenniferSoft)에서 만든 APM 툴들도 이러한 기술을 사용하여 중간에서 데이터를 분석해내고, Network 송수신 속도를 비약적으로 발전시킨 SFN(Solaflare)의 Network Interface Card 도 이러한 후킹 기술을 사용합니다.

만약 사용자가 Socket File Descriptor 를 이용하여 send 나 recv 함수를 호출하여 데이터를 송수신하는 프로그램을 작성하였을 때, 그러한 native send, recv 함수들은 User Layer 부터 Kernel Layer 를 거쳐 Network 카드를 통해 데이터를 전송하기까지 많은 단계의 함수호출과 데이터 복제과정이 발생합니다.
이러한 과정이 Network Latency 를 현격히 감소시키는 원인이 됩니다.
그래서, 해당함수들을 별도로 구현하여 Kernel 의 TCP 나 UDP Stack 을 거치지 않고, User Buffer 영역에서 곧 바로 전송이 이루어질 수 있도록 zero copy 기술을 구현할 때 사용된 기술 중의 하나가 이러한 후킹 기술입니다.

지금부터 이러한 후킹용도로 사용되는 LD_PRELOAD 환경변수에 대해 알아보도록 하겠습니다.
굉장히 쉽고 간단하니까 겁먹지는 마시구요. ^^

일단 후킹할 대상함수 하나를 골라야 할텐데요.
우리가 시스템에 접속하면 제일 먼저 실행해보는 명령 중의 하나인 hostname 명령어 내부에서 사용하는 함수를 잡아서 후킹을 해보겠습니다.

sh> hostname
smurf

sh> ltrace hostname
...
gethostname(???, 140735734941335)                                                                       = 0
...

sh> which hostname
/bin/hostname

sh> nm -D /bin/hostname | grep gethostname
                 U gethostname          # 다른 library 에 정의되어 있다는 뜻 (Undefined)

sh> ldd /bin/hostname
        linux-vdso.so.1 =>  (0x00007fff077ff000)
        libselinux.so.1 => /lib64/libselinux.so.1 (0x000000301d600000)
        libc.so.6 => /lib64/libc.so.6 (0x000000301ba00000)
        libdl.so.2 => /lib64/libdl.so.2 (0x000000301c200000)
        /lib64/ld-linux-x86-64.so.2 (0x000000301b600000)

sh> nm -D /lib64/libc.so.6 | grep gethostname
000000301bb01400 T __gethostname_chk
000000301bae0b40 W gethostname

위에서 hostname 명령어의 ltrace 결과를 통해 확인해보니 내부적으로 gethostname 이라는 함수를 사용하는 것이 보이는군요.
symbol table 과 link 관계를 확인해보니 결국 hostname 명령은 libc 에 있는 gethoaname 이라는 함수를 사용한다는 것이 밝혀졌습니다.

이제 상황파악은 됐으니 우리가 요 gethostname 이라는 함수를 별도로 만들어서 이 함수를 먼저 call 하도록 하면 되겠네요. 다음과 같은 순서로 하면 됩니다.

  1. gethostname 함수를 구현한다.
  2. 해당 파일을 컴파일하여 so 파일을 만든다.
  3. LD_PRELOAD 환경변수를 설정한다.  export LD_PRELOAD=./gethostname.so
  4. hostname 을 실행한다.
sh> cat -n gethostname.c
     1 #include <stdlib.h>
     2 #include <string.h>
     3 
     4 int gethostname(char *name, size_t len)
     5 {
     6    strcpy(name, "myhost");
     7    name[len-1] = '';
     8 
     9    return 0;
    10 }

sh> gcc -shared -fPIC -o gethostname.so gethostname.c
sh> LD_PRELOAD=./gethostname.so hostname
myhost

어때요? 참 쉽죠잉 ? ^^
위의 예에서는 LD_PRELOAD=./gethostname.so 와 같이 현재 directory 를 지정했는데, gethostname.so 파일을 LD_LIBRARY_PATH 에 지정된 directory 에 넣어두고 export LD_PRELOAD=gethostname.so 와 같이 해도 됩니다.

You may also like...

4 1 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x