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 하도록 하면 되겠네요. 다음과 같은 순서로 하면 됩니다.
- gethostname 함수를 구현한다.
- 해당 파일을 컴파일하여 so 파일을 만든다.
- LD_PRELOAD 환경변수를 설정한다. export LD_PRELOAD=./gethostname.so
- 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 와 같이 해도 됩니다.