또한 프로세스에 인젝션(Injection)된 DLL 파일을 디버깅하는 법을 살펴보도록 하겠습니다.
<IAT Hooking 코드>
본 내용은 이전 포스트에서 이어지는 내용입니다.
API Hooking – 계산기, 한글을 배우다. (1)
API Hooking – 계산기, 한글을 배우다. (2)
API Hooking – 계산기, 한글을 배우다. (3)
인젝션(Injection)된 DLL 의 디버깅
계산기(calc.exe) 프로세스에 인젝션(Injection)된 hookiat.dll 을 디버깅 하겠습니다.
DLL Injection 에 대한 더 자세한 내용은 아래 글을 참고해 주세요.
DLL Injection – 다른 프로세스에 침투하기 (1)
인젝션(Injection)된 DLL 의 디버깅 방법에 대한 더 자세한 내용은 아래 글을 참고해 주세요.
키로거(KeyLogger) 분석 (2)
계산기 프로그램을 실행하신 후 Process Explorer 로 PID 값을 확인합니다.
<Fig. 1>
이제 OllyDbg를 calc.exe 프로세스에 attach 시킵니다.
<Fig. 2>
* 참고!
OllyDbg 2.0 beta 2 를 사용합니다. 기존 1.10 버전은 Injection DLL을 디버깅 할 때 약간의 버그가 있어서 debuggee 프로세스가 종료되는 경우가 있습니다.
Attach 가 잘 되었으면 아래 그림과 같이 OllyDbg 의 옵션을 수정하여 DLL 파일(hookiat.dll)이 Injection 될 때 제어가 디버거에게 넘어오도록 합니다.
<Fig. 3>
위 "Pause on new module (DLL)" 옵션을 체크하면 debuggee 프로세스에 DLL 이 로딩(injection 포함) 될 때마다 제어가 debugger 에게 넘어옵니다.
이제 준비를 마쳤으니 OllyDbg 를 Run [F9] 시켜서 계산기 프로세스가 정상적으로 실행되도록 합니다.
InjectDll.exe 에 파라미터를 적절히 주고 실행하면 계산기 프로세스에 hookiat.dll 이 인젝션 됩니다. (아래 그림 참고)
<Fig. 4>
calc.exe 프로세스에 “DLL Load” 이벤트가 발생하였으므로, OllyDbg 에 해당 이벤트가 통지되고 <Fig. 3> 의 옵션에 따라서 아래 그림처럼 hookiat.dll 의 EP(EntryPoint) 에서 실행이 멈추게 됩니다.
<Fig. 5>
* 로딩된 DLL 의 EP 에 자동으로 멈추는 것은 OllyDbg 2.0 에서 지원되는 기능입니다. 이전 버전에서는 EP 가 아닌 다른 코드 위치(ntdll.dll 영역)에서 실행이 멈춥니다.
이제 <Fig. 3> 의 “Pause on new module (DLL)” 옵션을 해제(uncheck)하고 DllMain() 코드를 찾아 보겠습니다.
디버거에서 hookiat.dll 의 DllMain() 함수로 쉽게 찾아가는 방법은 DllMain() 에서 사용되는 문자열 혹은 API 를 검색하는 것입니다. (물론 하나 하나 StepIn 명령으로 따라가셔도 좋습니다.)
아래에 DllMain() 코드를 표시하였습니다.
(자세한 코드 설명은 API Hooking – 계산기, 한글을 배우다. (3) 포스트를 참고하세요.)
{
switch( fdwReason )
{
case DLL_PROCESS_ATTACH :
// original API 주소 저장
g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"),
"SetWindowTextW");
// # hook
// user32!SetWindowTextW() 를 hookiat!MySetWindowText() 로 후킹
hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
break;
case DLL_PROCESS_DETACH :
// # unhook
// calc.exe 의 IAT 를 원래대로 복원
hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
break;
}
return TRUE;
}
<Code 1>
코드에서 사용되는 "user32.dll" 과 "SetWindowTextW" 문자열로 DllMain() 을 찾아보겠습니다.
<Fig. 6>
위 그림을 보시면 "user32.dll" 문자열은 2 군데가 있고, "SetWindowTextW" 문자열은 1 군데가 있습니다. "SetWindowTextW" 문자열이 참조되는 코드 주소 1000113E 로 가봅니다.
<Fig. 7>
위 그림에서 빨간 테두리의 디스어셈 코드는 <Code 1> 의 C 언어 코드와 정확히 일치합니다. 따라서 이 부분이 DllMain() 입니다. (참고로 DllMain() 의 시작 주소는 10001130 입니다.)
여기까지가 프로세스에 인젝션된 DLL 을 디버깅하는 방법에 대한 설명입니다. (이외에도 여러 가지 꼼수가 있습니다만 위에 소개한 방법이 정석이라고 할 수 있습니다.)
DllMain()
앞에서 구한 DllMain() 함수 위치부터 디버깅을 시작해 보겠습니다. (위의 <Code 1> 과 비교하면서 보시면 좀 더 쉽게 파악할 수 있습니다.)
DllMain() 에서 조금만 디버깅해 내려가면 아래와 같은 코드가 나타납니다.
<Fig. 8>
10001160 주소의 CALL 10001090 명령이 바로 hook_iat() 함수 호출 부분입니다. 함수 파라미터는 역순으로 스택에 저장되므로 각 파라미터의 의미는 주석으로 쓰여진 부분 그대로 입니다. (<Code 1> 과 <Fig. 8> 를 함께 비교해서 보세요.)
잘 보시면 hook_iat() 의 첫 번째 파라미터인 "user32.dll" 문자열이 생략된 걸 알 수 있습니다. 이것은 VC++ 2008 컴파일러의 코드 최적화 기능이 적용되어서 문자열 주소 (4 byte 상수) 는 함수 파라미터로 넘기지 않고 hook_iat() 함수 내에 하드코딩 해버린 것입니다.
앞으로 여러분이 직접 작성한 프로그램을 디버깅 하실 때 위와 같은 코드 최적화 기능을 자주 보게 되실 겁니다.
hook_iat()
실제로 IAT 를 후킹하는 핵심 함수인 hook_iat() 입니다.
# user32.dll 의 IMAGE_IMPORT_DESCRIPTOR 찾기
<Fig. 9>
위 그림에서 빨간색으로 표시된 부분의 코드는 PE Header 에서 어떻게 IMAGE_IMPORT_DESCRIPTOR Table (이하 IID Table) 을 찾아내는지 보여줍니다. (IID 를 PEView 에서는 Import Directory Table 로 표시합니다.)
위 코드(100010A1 ~ 100010AD)에서는 어셈블리 명령어 4 개만을 써서 IID Table 을 따라갑니다.
아직 PE Header 구조에 익숙하지 않으신 분들 또는 위와 같은 어셈블리 코드를 처음 접하신 분들께서는 코드의 내용이 잘 이해되지 않으실 겁니다.
이건 숙련도의 문제이기 때문에 계속해서 보시면 점차 나아집니다. 나중에는 [EDI+3C], [EDI+EAX+80] 등의 코드만 봐도 이게 대충 IID Table 을 따라가는 코드라는걸 알 수 있게 됩니다.
100010CA 주소의 CALL 100075CA 명령어는 stricmp() 함수 호출 코드입니다. IID Table 을 훑으면서 IID.Name 항목과 "user32.dll" 문자열을 비교하여 user32.dll 에 해당하는 IID 를 찾습니다.
# IAT 에서 SetWindowTextW API 위치 찾기
user32.dll 에 해당하는 IID 를 찾았다면 이제는 IAT(Import Address Table) 에서 SetWindowTextW API 를 찾는 코드가 이어집니다. (아래 그림 참고)
우리는 그 위치의 내용을 바꿔 쳐서 API 를 후킹할 겁니다.
<Fig. 10>
100010E0 주소의 CMP DWORD PTR DS:[ESI], EBP 명령어에서 ESI 값은 user32.dll 의 IAT(Import Address Table) 의 시작 주소(010010A4) 입니다. 그리고 EBP 의 값은 SetWindowTextW 의 주소(77D0960E) 입니다.
즉, <Fig. 10> 의 코드는 루프를 돌면서 IAT 를 따라 내려가다가 01001110 주소의 SetWindowTextW 주소값(77D0960E) 을 찾는 내용입니다.
* 참고!
OllyDbg 의 Memory Window (<Fig. 10> 의 빨간 테두리 부분) 의 보기 옵션을 “Integer -> Address” 로 변경하면 <Fig. 10> 과 같이 [Address & API Name] 형식으로 볼 수 있습니다.
# IAT Hooking
이제 실제로 IAT 를 후킹하는 코드 입니다.
<Fig. 11>
10001117 주소의 MOV DWORD PTR DS:[ESI], EDX 명령이 바로 앞에서 구한 IAT 에서 SetWindowTextW 위치(01001110)에 후킹 함수 MySetWindowTextW 주소(10001000)를 덮어쓰는 코드입니다.
01001110 주소는 calc.exe 프로세스에서 user32.dll 의 IAT 영역입니다. 원래 이 위치에는 SetWindowTextW 주소값(77D0960E) 이 저장 되어 있었습니다. (<Fig. 10> 참고)
10001117 주소의 명령어에 의해서 user32!SetWindowTextW 주소값(77D0960E)이 hookiat!MySetWindowTextW 주소값(10001000)으로 변경됩니다. (<Fig. 11> 참고)
이제부터 calc.exe 프로세스내의 코드에서 (IAT 를 통해) user32!SetWindowTextW() API 를 호출하면 실제로는 hookiat!MySetWindowTextW() 가 호출됩니다.
MySetWindowTextW()
앞에서 IAT 후킹이 완료되었으므로 OllyDbg 를 실행[F9] 시켜서 계산기(calc.exe) 프로세스가 정상적으로 동작하게 만듭니다.
calc.exe 프로세스에서 user32!SetWindowTextW() API 를 호출하는 코드에 BP를 설치하여 실제로 hookiat!MySetWindowTextW() 함수가 호출되는 상황을 디버깅 해보겠습니다.
먼저 user32!SetWindowTextW() 호출 코드에 BP 를 설치합니다. OllyDbg 의 “Search for – All intermodular calls” 기능을 쓰면 아래와 같은 다이알로그가 나타납니다.
<Fig. 12>
calc.exe 프로세서에서 user32!SetWindowTextW() API 를 호출하는 위치는 총 2 곳입니다. 2 곳 모두 BP 를 설치합니다. (실제로는 01002628 주소의 명령어가 우리가 찾는 위치입니다.)
그리고 계산기 프로그램에 숫자 '1' 을 입력하면 (위에서 설치한) 01002628 주소의 BP 에 걸립니다. (아래 그림 참고)
<Fig. 13>
01001110 주소에는 원래 user32!SetWindowTextW() 주소(77D0960E)가 있었으나, 후킹된 이후에는 <Fig. 13> 에서 보듯이 hookiat!MySetWindowTextW() 주소(10001000)가 있습니다.
MySetWindowTextW() 함수 안으로 디버깅해 들어가면 아래 그림과 같은 코드가 나타납니다.
<Fig. 14>
MySetWindowTextW() 함수의 기능은 "숫자" 문자열을 "한글" 문자열로 변환한 후 원래 함수인 user32!SetWindowTextW() API 를 호출하는 것입니다.
1000107D 주소의 CALL DWORD PTR DS:[1000B6B8] 명령어가 바로 user32!SetWindowTextW() API 를호출하는 코드입니다. 1000B6B8 주소는 hookiat.dll 에서 .data 섹션의 전역변수(g_pOrgFunc) 를 의미합니다. DllMain() 에서 이곳에 미리 SetWindowTextW 주소를 저장해 두었습니다. (<Code 1> & <Fig. 8> 참고)
이것으로써 calc.exe 프로세스에 인젝션 된 hookiat.dll 을 디버깅을 완료하였습니다.
+---+
총 4 회에 걸쳐서 API Hooking 기법 중에서 "Dll Injection" 과 "IAT Hooking" 을 사용해서 계산기 프로세스를 후킹해 보았습니다.
다음 번에는 API Code 를 직접 패치하는 후킹 기법에 대해서 알아보겠습니다. 또한 시스템에서 실행되는 모든 프로세스들과 이후에 실행되는 모든 프로세스들에 대해서 전부 API Hooking 시키는 기술인 global Hooking 기법에 대해서 살펴보겠습니다.
재미 있으실 겁니다. 많이 기대해 주세요~ ^^
감사합니다.
ReverseCore
위 글이 도움이 되셨다면 추천(VIEW ON) 부탁 드려요~
'study' 카테고리의 다른 글
| ASLR(Address Space Layout Randomization) (20) | 2010/01/27 |
|---|---|
| API Hooking - '스텔스' 프로세스 (4) (42) | 2010/01/17 |
| API Hooking - '스텔스' 프로세스 (3) (39) | 2009/12/30 |
| API Hooking - '스텔스' 프로세스 (2) (32) | 2009/12/16 |
| API Hooking – '스텔스' 프로세스 (1) (18) | 2009/12/13 |
| API Hooking - 계산기, 한글을 배우다. (4) (10) | 2009/11/27 |
| API Hooking – 계산기, 한글을 배우다. (3) (35) | 2009/11/20 |
| API Hooking - 계산기, 한글을 배우다. (2) (14) | 2009/11/13 |
| API Hooking - 계산기, 한글을 배우다. (1) (30) | 2009/11/10 |
| API Hooking - 메모장 WriteFile() 후킹 (3) (26) | 2009/11/04 |
| API Hooking - 메모장 WriteFile() 후킹 (2) (3) | 2009/11/03 |
InjectDll.cpp
hookdbg.exe