DLL Injection REVERSING CreateRemoteThread() 를중점으로 DLL Injection 에대하여설명 [ Rnfwoa] 김경민
목차 목차 1 개요 1 2 DLL ( Dynamic Link Library ) 2 3 AppInit_DLLs 3 4 원격스레드생성 5 4.1 핸들확보 6 4.2 공간할당 7 4.3 DLL Name 기록 8 4.4 LoadLibrary 함수주소구하기 9 4.5 원격스레드생성 11 5 결론 14 그림 / 표목차 그림 1.REVERSE_L01.EXE 에서사용될 DLL 목록... 2 그림 2. APPINIT_DLLS... 3 그림 3.USER32.DLL 을로드하는프로세스에게만인젝션... 3 그림 4. APPINIT_DLLS_MYHACK2.DLL... 4 그림 5. MSDN_CREATEREMOTETHREAD... 5 그림 6. DLL INJECTION 과정... 5 그림 7. MSDN_OPENPROCESS API... 6 그림 8. MSDN_VIRTUALALLOCEX API... 7 그림 9. MSDN_WRITEPROCESSMEMORY API... 8 그림 10. MSDN_GETMODULEHANDLE API... 9 그림 11. MSDN_GETPROCADDRESS API... 10 그림 12. MSDN_CREATEREMOTETHREAD API... 11 그림 13. DLL INJECTION - KAKAOTALK 1... 12 그림 14. DLL INJCETION - KAKAOTALK 2... 12 그림 15. DLL INJECTOR CODE - PYTHON... 13
페이지 01 1 개요 최근에컴퓨터와사람과의관계는결코분리할수없는관계가되었다. 이러한관계를악용하고자예로부터악성코드들이존재하였으며이들은대부분의사람들에게피해를입힌다. 하지만컴퓨터시스템이진화함에따라이러한악성코드역시진화해오고있는데악성코드제작자들은악성코드를은닉하기위해일반윈도우환경에악성코드를숨기는기법들을개발했다. 가장일반적인위장실행기법이바로 DLL Injection 기법이다. 이는주로 LoadLibrary 와 CreateRemoteThread API 를이용하여다른프로세스에해당 DLL 을주입하는방식이다. 이에대하여많은문서들이있지만사용되는함수인자에대한설명이구체적이지않거나정리가너무난잡하게되어있어직접만들면서공부도다시하고자만들었다. 인젝션에는다양한방법들이있지만이문서에서는 CreateRemoteThread 를이용한원격 스레드생성을통한 DLL Injection 에초점을맞추고있다. 다른방법들의경우는다른 문서들을참고하는것이더욱좋을것같다. 또한 DLL 인젝션에사용될 DLL 의악성코드는주로 DllMain() 의위치에있으며이는프로세스에해당 Dll 이로드되면자동적으로실행이되는부분이다. 그렇기에 Dll 에서다른 Export 함수를정의하는것이아니라인젝션되는악성코드들은 DllMain() 의부분에기록되어있다. 이제이러한 DLL Injection 에대하여우리는살펴볼것이다.
페이지 02 2 DLL ( Dynamic Link Library ) DLL 은흔히 동적연결라이브러리 라고한다. 예전 16 비트 DOS 시절에는 DLL 의개념이없고그냥 Library 만존재하였다. 그로인하여함수를사용할때컴파일러는해당라이브러리에서해당함수의 Binary 코드를그대로가져와서프로그램에포함시켜야했다. 리눅스와유닉스의경우아직도정적링크가일반적으로사용된다. 하지만 Windows OS 에서는멀티태스킹을지원하기에이러한방식이비효율적이되었고, 이렇기에효율적인방법이요구되었다. 그래서 Windows OS 설계자들은아래와같은 DLL 개념을생각했다. 프로그램에라이브러리를포함시키지않고별도의파일로구성 구성된별도의라이브러리들을필요할때마다불러사용 로딩된 DLL 의코드, 리소스는메모리맵핑기술로여러 Process 에서공유해서사용 이러한방식을사용할경우프로그램이해당함수코드를직접포함하지않기에메모리에 적재되는용량을줄일수가있다. 또한해당라이브러리가변경될때마다프로그램을통째로 교체하는것이아니라해당라이브러리만교체를하면되기에편의성을제공한다. 그림 1.Reverse_L01.exe 에서사용될 DLL 목록 위의그림의경우프로그램이메모리에로딩될때, PE Loader 에의하여해당 DLL 들이 메모리에올라가게되며프로세스가해당라이브러리의함수를호출할수있게된다.
페이지 03 3 AppInit_DLLs 악성코드제작자는 AppInit_DLLs 라고불리는특별한 Registry 경로를통하여자신들의 DLL 을 Injection 시키기도하며지속적으로유지되게할수있다. 이레지스트리는 User32.dll 을로드하는모든프로세스에서해당 DLL 을로딩하게한다. HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs 위의경로에 Injection 시키고자하는 DLL 의경로를넣어주고재부팅하면 Windows 는 재부팅하면서실행되는 User32.dll 을로드하는프로세스들에게해당 DLL 을인젝션시킨다. 그림 2. AppInit_DLLs Windows XP 이후로 LoadAppInit_DLLs 라는부분이생겼는데 Windows XP 까지는 APPInit_DLLs 의부분에경로만입력해주면되었지만, XP 이후로는 LoadAppInit_DLLs 부분에 1 의값을넣어주어야해당 DLL 이인젝션된다. 그림 3.User32.dll 을로드하는프로세스에게만인젝션
페이지 04 이방법을사용할경우많은프로세스에인젝션되기때문에자칫큰 CPU 소비를가지고올수도있으므로감염자가인지할확률이높다. 그렇기에이를통한악성코드의경우특정프로세스에게서만동작하도록설계되는것이보통이다. 이러한선별적작업으로인하여부하를줄일수가있으며인지될확률을줄일수가있다. 아래는 myhack2.dll 이많은프로세스에로딩된것을확인할수가있다. 이예제 DLL 의경우 아무작업도하지않아아무런부작용이없지만, 아무기능이나포함된 DLL 을로딩시킬경우 BSOD 가나타날수도있으며프로세스마다오류창이나타날수도있다. 그림 4. AppInit_DLLs_myhack2.dll. 이러한방법을쓰는악성코드의경우 Registry 를비교하면바로탐지할수가있다. 해당 영역의 Registry 는기본적으로비어있으며, 만약어떠한악성코드로인하여추가되었다고 하더라해당 DLL 의경로가나타나기에제거또한쉽다고할수있다.
페이지 05 4 원격스레드생성 이제 DLL Injection 을구현하는방법중가장유명한방법인 CreateRemoteThread() API 를이용하는방식에대하여알아볼것이다. 우선 CreateRemoteThread 에대해알아보자. 아래는 CreateRemoteThread 에대한 MSDN 의문서이다. 함수명과같이다른프로세스에서실행될스레드를생성하는것이다. 그림 5. MSDN_CreateRemoteThread DLL Injection 은 LoadLibrary 를호출하는원격프로세스에코드를삽입해해당프로세스에 DLL 을강제적으로로드하게만드는것이다. 감염된프로세스가해당 DLL 을로드하면운영체제는자동으로 DllMain() 함수를호출하게된다. 이러한인젝션을하기위해서는다음과같은몇단계의차례가필요하다. 1. DLL 을인젝션시킬해당프로세스의핸들을구해야함. 2. 인젝션시킬 DLL 의이름을넣어주어야하므로이름을넣어줄공간을할당. 3. 할당된공간에해당 DLL 의 Name 을기록해야함. 4. Kernel32.dll 의 LoadLibrary 의주소를구해야함.. 5. 위에서구한것들을가지고 CreateRemoteThread 를통하여원격스레드를생성그림 6. DLL Injection 과정
페이지 06 4.1 핸들확보 대상프로세스내부에 DLL 을인젝션하기위해서는우선목표프로세스의핸들을확보해야한다. 대상핸들이있어야만그프로세스를다룰수있기에우리는해당핸들의확보를위하여해당프로세스의 PID 를이용할것이며이를통해 OpenProcess API 를호출할것이다. 그림 7. MSDN_OpenProcess API dwdesireaccess 를통하여권한을설정하고 binherithandle 을통하여상속여부를결정하며 마지막의 dwprocessid 에대상프로세스의 PID 를넣어주므로대상프로세스의핸들을구할 수있다. 핸들에대한권한으로 PROCESS_ALL_ACCESS(0x1F0FFF) 를넣어주므로핸들에대한모든접근권한을갖게된다. 상속여부에 True 를입력할경우이프로세스에의해생성된프로세스는핸들을상속하게될것이며 False 를입력할경우상속하지않는다. 여기서우리는 False 를입력할것이다. 그리고마지막인자로는해당 PID 를주어야한다. Hproc = OpenProcess(0x1F0FFF, False, PID)
페이지 07 4.2 공간할당 해당 DLL 을로드시키게하기위해서는 LoadLibrary 함수와그 DLL 의이름이필요하다. 이러한인젝션시킬 DLL 의이름을프로세스에넣기위하여우리는공간을할당해야한다. 이를위하여우리는 VirtualAllocEx API 를사용할것이다. 그림 8. MSDN_VirtualAllocEx API VritualAllocEx 함수는원격프로세스에대한핸들이전달된다면원격프로세스에서공간을할당한다. hprocess 에는우리가위에서구했던대상프로세스의핸들을넘겨주어야하며 lpaddress 에는우리가할당을원하는주소를입력받는다. dwsize 에는얼마큼의크기를할당받을지를정하는것이며 flallocationtype 은할당유형을결정짓고마지막 flprotect 는보호 ( 권한 ) 에대하여설정을한다. hprocess 에는위에서구한핸들 ( hproc 라하자.) 을넘겨주며 lpaddress 에 NULL 값이입력될경우함수가알아서할당할위치를정해줄것이다. dwsize 에는우리가입력할 DLL 의길이만큼공간을할당받을것이기에 len(dll_file) 만큼이며할당유형으로 MEM_COMMIT 과 MEM_RESERVE 을넣어주기위하여우리는 0x3000 으로설정할것이다. 마지막보호 ( 권한 ) 는 PAGE_READWRITE 을위하여 0x04 의값을넣어준다. Virtual_addr = VirtualAllocEx(hproc, None, len(dll_file), 0x3000, 0x04)
페이지 08 4.3 DLL Name 기록 위에서할당한대상프로세스의공간에우리가인젝션하고자하는 DLL 의이름을기록할 것이다. 이를위하여우리는 WriteProcessMemory API 를사용할것이다. 그림 9. MSDN_WriteProcessMemory API hprocess 에는대상프로세스의핸들을넣어주고 lpbaseaddress 에는어느주소에기록을할것인지를넣어주어야한다. lpbuffer 에는쓰고자하는내용, nsize 는쓰고자하는내용의크기, 마지막 lpnumberofbyteswritten 는기록된프로세스변수의포인터를설정해주는것이다. 우리는 hprocess 에는위에서구했던 hproc 를줄것이며 BaseAddress 에는바로위에서 VirtualAllocEx 를통하여할당된공간 (virtual_addr 이라하자.) 을인자로넣어주어야한다. 기록할내용인 Buffer 에는해당 DLL 파일의이름을넣어주며그 DLL 길이만큼 nsize 를설정하며마지막인자에는 NULL 을넘겨줄것이다. WriteProcessMemory(hproc, virtual_addr, Dll_file, len(dll_file), None)
페이지 09 4.4 LoadLibrary 함수주소구하기 강제로로딩시키고자하는 DLL 의이름이대상프로세스에기록되었다. 이제 LoadLibrary 를 * LoadLibrary API Loads the specified module into the address space of the calling process -MSDN 통하여해당 DLL 을대상프로세스가로딩하게끔만들어야한다. 하지만그러기위해선해당함수인 Kernel32.dll 에있는 LoadLibrary 의주소를먼저구해야한다. Kernel32.dll 모듈핸들을먼저구한후해당모듈에서함수의주소를구할것이다. 이를위하여우리는 GetModuleHandle 과 GetProcAddress API 를사용할것이다. 그림 10. MSDN_GetModuleHandle API GetModuleHandle 함수는해당함수에대한모듈핸들을갖게해준다. 여기서우리는 LoadLibrary 함수를위하여 Kernel32 모듈이필요하므로이를인자로넣어주면된다. Hmod = GetModuleHandleA( kernel32 )
페이지 10 그림 11. MSDN_GetProcAddress API GetProcAddress 는해당모듈에서함수의주소를찾게해준다. 첫번쨰인자인 hmodule 에는모듈의핸들을넣어주어야하며여기서우리는위의 GetModuleHandle 에서구한핸들 (hmod 라고하자.) 을넣을것이며, lpprocname 은 DLL 을로딩시키기위한 LoadLibraryA 를넣어줄것이다. Load_addr = GetProcAddress(hmod, LoadLibraryA ) 여기서하나의문점이생길수도있다. LoadLibrary 함수는 DLL 을로딩하는것인데우리는지금인젝터에서해당함수의주소를구하고있다. 보통 DLL 이로딩될때 ImageBase 가 100000 으로 A.dll 이로드되고 B.dll 이로드될떄해당위치에이미로드된 DLL 이있을경우 Relocation 을통하여다른주소를찾아서로드된다. 그러므로인젝터프로세스에서해당함수주소를구해도대상프로세스의 DLL 의위치에따라실행이안될것이다. 하지만 Microsoft 는 OS 핵심 DLL 파일들의 ImasgeBase 를미리정리해놓았으며이는절대 겹치지않도록되어있다. 다시말해 DLL Relocation 이발생하지않는데이러한특성으로 인해인젝터에서해당함수의주소를구해도지장이없는것이다.
페이지 11 4.5 원격스레드생성 DLL Injeciotn 을위한마지막순서로 CreateRemoteThread API 를이용해원격의스레드를생성해야한다. 이스레드로대상프로세스에서 LoadLibrary 를이용해해당 DLL 을로드하게만드는것이다. 그림 12. MSDN_CreateRemoteThread API -hprocess 에는대상프로세스의핸들을넣어야한다. OpenProcess 를통해구한 hproc 를넣어준다. -lpthreadattribute 는 SECURITY_ATTRIBUTES 로써 NULL 일경우스레드의보안설정이 Default 가되며핸들이상속되지않는다. 따라서우리는 NULL 을넣을것이다. -dwstacksize 에는스택의초기크기를설정하는것이며 0 으로설정할것이다. -lpstartaddress 는스레드에의해실행될함수나명령어의주소를넣어야한다. 여기선 LoadLibrary 의주소를가리키는 load_addr 을넣는다. -lpparameter 는스레드의내용이전달될위치를넣어야한다. Virtual_aadr 을넣을것이다. -dwcreationflags 는스레드의속성을설정한다. 별도로설정하지않을것이기에 0 -lpthreadid 는스레드의 ID 를설정하는인자이지만우리는 NULL 로한다. CreateRemoteThread(hproc,None,0,load_addr,virtual_addr,0,None)
페이지 12 아래는직접인젝터를만들어서실습을해본사진이다. 아래의사진을보면카카오톡이라는 프로세스에 msgbox.dll 이라는메시지를출력하는박스를띄우는단순한 dll 을인젝션할 것이다. CMD > python Dll_injector.py <PID> Dll_patch 과같이입력한다. 그림 13. DLL Injection - KaKaoTalk 1 인젝션에성공할경우메시지박스가나오면서이게오른쪽에서와같이카카오톡과같은 소속임을알수가있다. 그리고아래에서와같이 ProcExp 를통해확인하면카카오톡이해당 DLL 을로드한것을확인할수가있다. 그림 14. DLL Injcetion - KaKaoTalk 2 이렇게인젝션된 DLL 은해당프로세스와같은권한을갖는다. 이러한방법을통해 악성코드들은 DLL 인젝션을시도하여다른프로세스에서악성코드를실행하도록한다. 그리고그자신의모습은삭제되므로흔적을최소화하고자할것이다.
페이지 13 # Dll_Injector.py # Kali-KM Blog: kali-km.tistory.com from ctypes import * import sys,ctypes def Injection(pid,dll_file): hproc = kernel32.openprocess(0x1f0fff,false,pid) virtual_addr = kernel32.virtualallocex(hproc,none,len(dll_file),0x3000,0x04) kernel32.writeprocessmemory(hproc,virtual_addr,dll_file,len(dll_file),none) hmod=kernel32.getmodulehandlea('kernel32') load_addr=kernel32.getprocaddress(hmod,'loadlibrarya') if not kernel32.createremotethread(hproc,none,0,load_addr,virtual_addr,0,none): print "[#] Failed Injection.." else: print "[#] Success Injection!!" print"[#] Kali-KM's Dll Injector v.01 Since 15.03.25" if not sys.argv[1:]: print"[#] Usage : Injector <pid> <dll_path>" sys.exit(0) kernel32 = windll.kernel32 pid=int(sys.argv[1]) dll_file=str(sys.argv[2]) Injection(pid,dll_file) 그림 15. DLL Injector Code - Python
페이지 14 5 결론 이렇게 DLL 인젝션을살펴보았다. 사실 Code Injection 또한매우유사한방식으로진행이되는데단지 LoadLibrary 함수가필요하지않기에더욱코드의이식이쉽지만프로세스의안정을위하여최적화된코드또한필요하다. 악성코드를분석할때위와같은 API 들이나올경우 DLL Injection 을의심할수가있으며어떠한 DLL 이인젝션되는지확인후해당 DLL 을분석할수있어야한다. 이제 CreateRemoteThread 를이용하여 DLL 을인젝션하는방법에대하여이해를했을 것이다. 위에 Python 을통한 DLL Injector 의코드를같이넣긴하지만본인이직접 MSDN 등을찾아보며해보는것이더깊숙히이해가가능할것이다. 참고 http://kali-km.tistory.com/ 리버싱핵심원리 ( 악성코드분석가의리버싱이야기 ) 이승원저인사이트 2012.09.30. 실전악성코드와멀웨어분석 (Practical Malware Analysis) 마이클시코스키, 앤드류호닉저여성구, 구형준외 1 명역에이콘출판 2013.10.29. https://snipt.net/raw/9ea72eda2b96eff2d0d60956bd216aaa/?nice https://msdn.microsoft.com/ http://hisjournal.net/doc/how_does_the_dll_injection.pdf http://www.reversecore.com/ http://gmng.tistory.com/40 http://john09.tistory.com/53 http://hackersstudy.tistory.com/75 http://kuaaan.tistory.com/112