Programming/Win32 API2008. 12. 3. 10:05

DLL Injection - 참고

Posted 2007/06/19 14:16, Filed under: Study/Computer Science
================================================================================
Title : DLL Injection

Author : proXima
Contact : proxima ]at[ postech.ac.kr
Date : 2006/11/15 - 2006/11/15
================================================================================

2부에서 DLL Injection에 대해 간단하게 언급했었다. 이번 글에서는 DLL Injection이 무엇인지, 어떤 방법으로 이루어질 수 있는지를 알아보도록 하자.

일단, DLL Injection이 무엇인지부터 간단하게 알아보는 것이 좋을 것 같다.
단어 그대로 해석해 보자면, DLL Injection이라는 것은 'DLL 주입'이라는 뜻을 가지는데, 그냥 이 말만 가지고는 뜻이 명확하지가 않다. DLL을 주입한다고? 그게 무슨 뜻이지?

1. DLL을 주입하다
다들 알고 있겠지만(몰랐다면 앞으로 알면 좋다), 프로세스는 실행파일 하나만 메모리에 달랑 올린다고 실행이 되는 것이 아니라, 실행에 필요한 동적 라이브러리들을 같이 메모리에 올려야만 실행이 가능하다. DLL이라는 것은 Windows에서 제공하는 동적 라이브러리 형태로서, 리눅스에서의 .so 파일과 같은 역할을 한다고 보면 된다.
방금 설명했듯이, 동적 라이브러리도 프로세스의 메모리 공간에 올라가게 되는데, 디버거를 통해 확인해 보면 아마 보통 ntdll.dll, kernel32.dll, user32.dll 등의 dll 파일들이 프로세스의 실행파일 이미지와 같이 메모리에 올라가 있는 것을 확인할 수 있을 것이다.
DLL은 실행파일과 비슷하다. 그 자체로 실행파일이 되지 않는다는 것을 제외한다면, 파일의 내부는 실행파일과 다른 점이 없다. (.exe 파일도 함수를 export할 수 있다.)
그러면 DLL을 다른 프로세스에 주입한다는 게 무슨 뜻인가 하면... 다른 프로세스의 메모리 공간에 내가 만든 DLL을 매핑한다는 뜻이다. 하지만 단순이 메모리에 DLL을 매핑하는 것이 다는 아니다. DLL이 로드될 때 자동으로 실행되는 DllMain 에서 내가 원하는 코드를 '다른 프로세스 내에서' 실행시킬 수도 있다. 바로 이게 핵심이다.
DLL Injection을 통해 내가 원하는 코드를 '다른 프로세스 내에서' 실행시킬 수 있다.


2. 이게 어째서 가능한가
앞에서 이것저것 설명했었지만, 이게 가능한 이유는 Windows가 다음와 같은 환경을 제공해 주기 때문이다.
1) 런타임에 동적으로 DLL 파일을 로드할 수 있다.
2) 다른 프로세스의 메모리 공간에 데이터를 쓸 수 있다.
3) 다른 프로세스에서 함수를 실행시킬 수 있다
4) DLL이 로드될 때 로드된 DLL 파일 내의 DllMain 함수가 실행된다!


3. DLL Injection을 발생시키는 과정
Windows에서는 프로세스가 런타임에 동적으로 DLL 파일을 로드하도록 해 주는 LoadLibrary라는 함수가 있다. 그리고 다른 프로세스에서 그 LoadLibrary를 실행할 수가 있다. 그렇다면 내가 DLL을 만든 뒤에 그 DLL을 다른 프로세스에서 LoadLibrary를 통해 로드하도록 하면, 내가 만든 DLL을 주입할 수 있는 것이다.
그렇다면 어떻게 다른 프로세스에서 함수를 실행시킬 수 있을까? 바로 CreateRemoteThread라는 함수를 이용하는 것이다. CreateRemoteThread는 다른 프로세스 내에 새로운 쓰레드를 생성시키는데, 그 생성된 쓰레드로 하여금 어떤 함수를 실행하게 할 것인지를 인자로 넘길 수 있다.
그렇기 때문에 CreateRemoteThread를 이용하여 LoadLibrary를 실행하게 한다면, 내가 만든 DLL의 DllMain이 실행된다.
하지만 넘어가야 하는 난관이 조금 있는데, 일단 CreateRemoteThread와 LoadLibrary의 프로토타입을 보자.


HANDLE CreateRemoteThread(  HANDLE hProcess,
 LPSECURITY_ATTRIBUTES lpThreadAttributes,
 SIZE_T dwStackSize,
 LPTHREAD_START_ROUTINE lpStartAddress,
 LPVOID lpParameter,
 DWORD dwCreationFlags, 
 LPDWORD lpThreadId
);

HMODULE LoadLibrary(
  LPCTSTR lpFileName
);

 
프로토타입을 보면서 조금 더 설명을 하자면, DLL Injection이 가능한 이유는, LoadLibrary가 인자를 '단 하나'만 받기 때문이다. CreateRemoteThread는 쓰레드를 새로 만들면서 쓰레드로 실행되는 함수에게 인자를 하나 넘길 수 있는데, LoadLibrary는 아주 다행스럽게 인자를 '단 하나'만 받는다.

하지만, LoadLibrary를 실행하기 위해서는 '로드할 DLL 파일명'을 LoadLibrary의 인자로 넘겨주어야 한다. 그렇다면 다른 프로세스의 메모리 공간에 내가 원하는 '로드할 DLL 파일명'을 써 넣어야 하는데, 그것은 이 전에 쓴 글인 'Controlling Memory Space of Other Process'에서 언급했듯이, Windows 에서 제공해 주는 API들을 사용해서 가능하게 할 수 있다.

그렇다고 하더라도 가장 큰 문제가 있는데, 그게 무엇인가 하면 바로 LoadLibrary의 주소는 '타겟 프로세스'의 가상메모리 상에서의 주소여야 한다는 점이다. 알고 있다시피, DLL들은 프로그램이 실행될 때마다 다른 메모리 주소에 매핑될 수 있는데, 그것 때문에 타겟 프로세스의 LoadLibrary가 어떤 주소에 위치하는지를 모른다면 이것은 말짱 꽝이 될 수도 있다.
그래도 이걸 해결하는 방법이 있다. 다른 프로세스의 메모리를 읽어 kernel32.dll 이 그 프로세스의 어느 메모리 주소에 위치하는지 알아내고, 그 kernel32.dll 이 export하는 함수들을 읽으면서 LoadLibrary의 주소가 어디 있는지 알아낼 수 있다. 뭐 그 밖에도 다른 방법도 있을 수 있지만, 지금은 이게 문제가 되지 않는다. 왜냐하면...

서로 다른 프로세스라도 kernel32.dll 은 가상메모리 상에서 거의 항상 같은 메모리 주소에 매핑되기 때문에, 그냥 내 프로세스의 LoadLibrary 주소를 넘기면 된다.


4. 코드를 짜 보자


HANDLE hProcess;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

LPVOID pFileName;
pFileName = HeapAlloc(NULL, strlen(fileName)+1, MEM_COMMIT, \
                             PAGE_READWRITE);

WriteProcessMemory(hProcess, pFileName, fileName, strlen(fileName)+1, \
                   &nWritten);

HANDLE hRemoteThread;
hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, \
                                   (LPTHREAD_START_ROUTINE)LoadLibrary,
                                   pFileName, 0, NULL);

CloseHandle(hRemoteThread);
CloseHandle(hProcess);
 

이제 DLL Injection 을 할 수가 있게 됐다. 내가 DLL을 만들어서 다른 프로세스에 넣어보자. 실제로 스피드핵의 경우 timeGetTime 함수를 뺏는 역할을 하는 DLL을 프로세스에 주입함으로써 빠르게 돌아가게 하는 방법도 사용하는 프로그램도 있다.

다른 프로세스의 내부에 내가 원하는 코드를 집어넣는다는 것은 굉장히 강력할 수 있다. 때문에 악용될 경우 큰 피해가 날 수도 있는데, 아무쪼록 좋은 곳에 사용하길 바란다.
Posted by skensita