DLL Injection - 다른 프로세스에 침투하기DLL Injection 구현 방법
그 중에서 가장 유명한 방법이 CreateRemoteThread() API 를 이용하는 방법입니다. 이 방법은 윈도우즈 프로그래밍 서적의 바이블인 Jeffrey Richter 의 Programming Applications for Microsoft Windows 에 소개된 내용입니다. 일단 소스 코드를 보겠습니다. (엔지니어에게는 백 마디 설명 보다는 역시 소스 코드를 한번 보는게 낫죠.) 먼저 Injection 시킬 myhack.dll 소스 코드입니다. // myhack.cpp #include "stdio.h" #pragma comment(lib, "urlmon.lib") #define DEF_NAVER_ADDR ("http://www.naver.com/index.html") DWORD WINAPI ThreadProc(LPVOID lParam) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID
lpvReserved) switch( fdwReason )
DllMain() 을 보시면 DLL 이 로딩(DLL_PROCESS_ATTACH)될 때 스레드(ThreadProc)를 실행합니다. ThreadProc() 의 내용은 urlmon.dll 의 URLDownloadToFile() 함수를 실행시켜서 네이버 초기화면(index.html)을 다운받습니다. 프로세스에 DLL Injection 이 발생하면 해당 DLL 의 DllMain() 함수가 호출된다고 이전 포스트에서 설명드렸습니다. 따라서 notepad.exe 프로세스에 myhack.dll 이 Injection 되면 결국 URLDownloadToFile() 함수가 실행될 것입니다. * DLLMain() 에서 직접 URLDownloadToFile() 을 호출하면 간혹 hang 이 걸리는 경우가 있어서, 별도의 스레드를 생성하여 호출하도록 프로그래밍 하였습니다. 이제 myhack.dll 을 notepad.exe 프로세스에 Injection 시켜줄 프로그램(InjectDll.exe)의 소스코드를 보시겠습니다. // InjectDll.exe #include "stdio.h" #include "windows.h" #include "tlhelp32.h" #define DEF_PROC_NAME ("notepad.exe") DWORD FindProcessID(LPCTSTR
szProcessName); int main(int argc, char* argv[]) // inject dll return 0; DWORD FindProcessID(LPCTSTR szProcessName) // Get the snapshot of the
system // find process CloseHandle(hSnapShot); return dwPID; BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
// #2. 대상 프로세스(notepad.exe) 메모리에 szDllName 크기만큼 메모리를 할당 // #3. 할당 받은 메모리에 myhack.dll 경로("c:\\myhack.dll")를 씀 // #4. LoadLibraryA() API 주소를 구함 hMod =
GetModuleHandle("kernel32.dll"); pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA"); CloseHandle(hThread); return TRUE;
main() 함수에서는 2개의 서브 함수를 호출하고 있습니다. FindProcessID(DEF_PROC_NAME) 함수는 프로세스 이름으로 PID(Process ID) 를 구해주는 함수입니다. (설명은 생략합니다.) 그리고 InjectDll(dwPID, DEF_DLL_PATH) 함수가 바로 DLL Injection 을 해주는 핵심 함수입니다.
InjectDll() 함수는 대상 프로세스(notepad.exe)로 하여금 스스로 LoadLibrary("myhack.dll") API 를 호출하도록 명령하는 기능을 가지고 있습니다. #1. 대상 프로세스 핸들 구하기 OpenProcess() API 를
이용해서 notepad.exe 의 프로세스 핸들을 구합니다. (이때 미리 구해놓은 PID 를 사용함) #2-3. 대상 프로세스 메모리에 Injection 시킬 DLL 경로를 써주기 대상 프로세스(notepad.exe)에게 로딩할 DLL 파일의 경로(문자열)를 알려줘야 합니다. WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL); 할당 받은 버퍼 주소(pRemoteBuf)에 WriteProcessMemory() API 를 이용하여 DLL 경로 문자열("C:\\myhack.dll")을 써줍니다. 이로써 대상 프로세스(notepad.exe) 메모리 공간에 Injection 시킬 DLL 파일의 경로가 생겼습니다. * 참고 hMod = GetModuleHandle("kernel32.dll"); LoadLibrary() API 를 호출시키기 위해 그 주소가 필요합니다. 위 코드의 의미를 잘 생각해봐야 합니다. notepad.exe 에 로딩된 kernel32.dll 과 InjectDll.exe 에 로딩된 kernel32.dll 의 메모리 시작 위치(ImageBase)가 동일 하다면 위 코드는 문제가 없습니다. 일반적인 DLL 파일의 ImageBase 는 0x10000000 으로 설정되기 때문에 a.dll 과 b.dll 을 차례대로 로딩하면 a.dll 은 정상적으로 0x1000000 주소에 로딩이 되겠지만 b.dll 은 자신이 원하는 0x10000000 주소에 로딩되지 못하고 다른 비어 있는 주소 공간에 로딩됩니다. 즉, DLL Relocation 이 발생하는 것입니다. (a.dll 이 같은 주소에 이미 로딩되어 있기 때문입니다.) 만약 kernel32.dll 이 프로세스마다 다른 주소에 로딩된다면 위 코드는 잘못된 것입니다. 어째서 그런걸까요? PE View 를 통해서 Windows 운영체제의 핵심 DLL
파일들의 ImageBase 값을 조사해 봤습니다. DLL file ImageBase SizeOfImage
Dll Injection 기법은 위와같이 OS 핵심 DLL 들은 자신만의 고유한 주소에 로딩된는 것을 보장해주는 Windows 특성을 이용한 것입니다. (이 특성이 Windows 보안 취약점으로 이용되기도 합니다.) * 참고! #5. 대상 프로세스에 스레드를 실행 시킴 모든 준비는 끝났고 마지막으로 notepad.exe 로 하여금 LoadLibraryA() API 를 호출하도록 명령만 내리면 됩니다. 하지만 Windows 에서는 그런 API 를 제공하지 않습니다. 그래서 편법(?)으로 CreateRemoteThread() API 를 사용합니다. CreateRemoteThread() API 는 다른 프로세스에게 스레드를 실행시켜주는 함수입니다. HANDLE WINAPI CreateRemoteThread(
hProcess 파라미터가 바로 스레드를 실행시킬 프로세스의 핸들입니다. 좀 어리둥절 하시죠? 스레드 함수 ThreadProc() 과 LoadLibrary() API 를 보시면 힌트를 얻을 수 있습니다. DWORD WINAPI ThreadProc( HMODULE WINAPI LoadLibrary(
CreateRemoteThread() 를 호출해서 4 번째 파라미터 lpStartAddress 에 "LoadLibrary() 주소"를 주고, 5 번째 파라미터 lpParameter 에 원하는 "DLL 의 경로" 문자열을 주면 됩니다. (반드시 대상 프로세스의 가상 메모리 공간에의 주소이여야 합니다.) 우린 이미 위에서 다 준비해놨지요. 편안하게 호출해주면 됩니다. hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
pThreadProc = notepad.exe 의 LoadLibraryA() 주소 |