Global API Hooking 에 대한 기본 개념을 정리합니다. 또한 ntdll!ZwResumeThread() API 의 후킹을 통한 Global API Hooking 기법의 원리를 살펴봅니다.
<Global API Hooking>
본 내용을 읽기 전에 이전 포스트를 참고하세요. 같이 이어지는 내용입니다.
* 참고!
모든 소스 코드는 MS Visual C++ 2008 Express Edition 으로 개발 되었으며, Windows 7 32bit & IE 8 에서 테스트 되었습니다.
Global API Hooking
Global API Hooking 에 대해서 다시 한번 간단히 정리하고 넘어가겠습니다.
지금까지의 강좌를 통해서 우리는 특정 프로세스에 대해 원하는 API 의 후킹을 간단하게 구현할 수 있게 되었습니다.
* 참고
#1. 일반적인 API Hooking
일반적인 API 후킹의 문제는 후킹을 원하는 프로세스가 생성될 때마다 매번 API 후킹을 해줘야 한다는 것입니다. 아래 그림은 DLL Injection 기법을 이용한 일반적인 API Hooking 을 표현한 것입니다.
<Fig. 1>
위 그림에서 후킹 대상 프로세스는 Test.exe(PID:2492) 입니다. InjDll.exe 프로그램을 이용해서 Hook.dll 을 Test.exe 프로세스에 인젝션 시켜서 원하는 API 를 후킹 하였습니다. (1)
그런데 이후에 또 다른 Test.exe(PID:3796) 프로세스가 생성되었다면 이 프로세스에도 역시 Hook.dll 을 인젝션 시켜줘야 (PID:3796 프로세스에 대한) 정상적인 API 후킹이 이루어 질 것입니다. (2)
즉, 후킹 대상 프로세스가 새로 생성 될 때마다 계속해서 수동으로 API 후킹을 시켜야 합니다.
#2. Global API Hooking
이번에는 Global API Hooking 을 살펴보도록 하겠습니다.
<Fig. 2>
InjDll.exe 는 Windows 운영체제의 기본 쉘(Shell) 인 Explorer.exe 프로세스에 gHook.dll 을 인젝션 시킵니다. (후킹하고자 하는 Test.exe 가 아닌, Test.exe 를 실행시켜주는 프로세스인 Explorer.exe 를 후킹한다는 것이 핵심입니다.)
gHook.dll 은 <Fig. 1> 의 Hook.dll 의 기능에다가 자식 프로세스 생성에 관련된 API 를 후킹하여 자식 프로세스를 생성할 때마다 자신(gHook.dll)을 인젝션 시키는 기능을 가지고 있습니다. (위 <Fig. 2> 참조)
따라서 Windows 쉘인 Explorer.exe 프로세스에 gHook.dll 을 한번 인젝션 시켜놓으면 그 이후 Explorer.exe 에서 생성되는 모든 자식 프로세스들에게 자동으로 gHook.dll 이 인젝션 됩니다.
이것이 자동 API Hooking 의 기본 개념이며, 이를 시스템에 실행중인 모든 프로세스를 대상으로 확장한 것이 바로 Global API Hooking 입니다.
* 참고!
Explorer.exe 외에 다른 프로세스들도 자식 프로세스를 생성할 수 있습니다. 따라서 원칙적으로 Global API Hooking 을 완벽히 구현하려면 현재 실행중인 모든 프로세스들을 후킹 해야 합니다.
하지만 시스템 안정성과 불필요한 오버헤드를 막기 위해 작업 내용에 따라서 특정 프로세스만 후킹 하는 경우도 있습니다. (제가 실습 예제로 준비한 IE 후킹이 대표적인 경우입니다.)
이상으로 Global API Hooking 개념에 대해서 정리해 보았습니다.
이제부터 어떤 API 를 후킹해야 Global API Hooking 을 쉽게 구현할 수 있는지 알아보도록 하겠습니다.
ntdll!ZeResumeThread() API
자식 프로세스를 생성하는 API 에 대해서 생각해 보도록 하겠습니다.
프로세스를 생성하는 API 는 단연 kernel32!CreateProcess() API 가 대표적입니다.
* 참고 – kernel32!CreateProcess() API 를 이용한 Global API Hooking 방법
CreateProcess() API 의 디버깅을 위하여 다음과 같이 간단한 프로그램을 만들어 보겠습니다.
// cptest.cpp
#include "windows.h"
#include "tchar.h"
void main()
{
STARTUPINFO si = {0,};
PROCESS_INFORMATION pi = {0,};
TCHAR szCmd[MAX_PATH] = {0,};
si.cb = sizeof(STARTUPINFO);
_tcscpy(szCmd, L"notepad.exe");
if( !CreateProcess(NULL, // lpApplicationName
szCmd, // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // bInheritHandles
NORMAL_PRIORITY_CLASS, // dwCreationFlags
NULL, // lpEnvironment
NULL, // lpCurrentDirectory
&si, // lpStartupInfo
&pi) ) // lpProcessInformation
return;
if( pi.hProcess != NULL )
CloseHandle(pi.hProcess);
}
위 코드로 빌드 시킨 프로그램입니다.
cptest.exe 를 디버깅 해보면 프로세스 생성과 관련된 API 들의 호출 흐름을 알 수 있습니다.
아래 그림은 cptest.exe 의 kernel32!CreateProcessW() 호출 코드입니다.
* 참고 : CreateProcessW 는 CreateProcess 의 Wide Character (유니코드)버전입니다.
<Fig. 3>
kernel32!CreateProcessW() 내부로 따라 들어가면 아래 그림과 같이 kernel32!CreateProcessInternalW() 호출 코드를 볼 수 있습니다.
<Fig. 4>
위 그림에서 아래쪽의 스택 메모리를 보시면 <Fig. 3> 의 스택(함수 파라미터)이 거의 동일하게 넘어온걸 알 수 있습니다.
kernel32!CreateProcessInternalW() 내부로 들어가 보겠습니다.
<Fig. 5>
kernel32!CreateProcessInternalW() 는 상당히 큰 함수입니다. 아래로 쭉 스크롤을 내리면 아래 그림과 같이 ntdll!ZwCreateProcess() 를 호출하는 코드가 나타납니다.
<Fig. 6>
위 그림에서 아래쪽의 스택을 보시면 <Fig. 4> 에서의 스택과는 많이 다른 형태인 것을 알 수 있습니다. 2번째 파라미터(Arg2)는 어떤 구조체 인데 왼쪽의 Hex dump 창을 보시면 구조체 멤버 중에 12F950 주소의 12FD38(“notepad”) 문자열 주소를 확인 하실 수 있습니다. (<Fig. 3> 스택 참조)
Ntdll!ZwCreateUserProcess() 가 호출되면 자식 프로세스가 SUSPEND 모드로 실행됩니다.
<Fig. 7>
notepad.exe 프로세스는 생성되었지만 아직 EP 코드가 실행되지 않은 상태입니다.
<Fig. 6> 코드에서 계속 아래로 진행하면 ntdll!ZwResumeThread() API 호출 코드가 나타납니다.
<Fig. 8>
ntdll!ZwResumeThread() 는 함수 이름 그대로 스레드를 RESUME 시켜줍니다. 이 스레드가 바로 자식 프로세스(notepad.exe) 의 메인 스레드입니다. 따라서 이 API 가 호출되면 비로소 자식 프로세스의 EP 코드가 실행됩니다.
<Fig. 9>
지금까지의 API 호출 흐름을 정리하면 아래와 같습니다.
kernel32!CreateProcessW
kernel32!CreateProcessInternalW
ntdll!ZwCreateUserProcess // 프로세스 생성됨 (메인 스레드는 suspend 상태)
ntdll!ZwResumeThread // 메인 스레드 resume 시킴 (프로세스 실행됨)
자식 프로세스 생성에 있어서 가장 마지막에 호출되는 API 가 바로 ntdll!ZwResumeThread() 입니다. 따라서 우리는 이 API 를 후킹함으로써 자식 프로세스의 EP 코드가 실행되기 직전에 제어를 가로챈 후 원하는 API 를 후킹시킬 수 있습니다.
ntdll!ZwResumeThread() 는 undocumented API 이며, 함수 정의는 아래와 같습니다.
NTSTATUS NtResumeThread(
IN HANDLE ThreadHandle,
OUT PULONG SuspendCount OPTIONAL
);
* 출처
* 참고
User Mode 에서 ntdll!ZwResumeThread() API 와 ntdll!NtResumeThread() API 는 동일합니다.
위에서 소개한 4 개의 API (CreateProcessW, CreateProcessInternalW, ZwCreateUserProcess, ZwResumeThread) 중에서 어떤걸 후킹해도 Global API Hooking 이 가능합니다.
다만 상위에 위치한 CreateProcessW() 함수를 후킹하면 특정 경우(CreateProcessInternalW 를 직접 호출하는 경우)에 후킹이 되지 않을 수 있습니다. 따라서 CreateProcessInternalW() 이하를 후킹하는 것이 더 좋은 방법입니다. (각자 장단점이 있을 수 있으므로 모두 연습해 두시는 것이 좋을 것 같습니다.)
다음 시간에는 Global 후킹 실습을 한 후 실제 코드를 살펴보도록 하겠습니다.
ReverseCore
위 글이 도움이 되셨다면 추천(VIEW ON) 부탁 드려요~
'study' 카테고리의 다른 글
| Code Injection 기법 (3) (7) | 2010/06/24 |
|---|---|
| Code Injection 기법 (2) (14) | 2010/06/23 |
| Code Injection 기법 (1) (8) | 2010/06/22 |
| Advanced Global API Hooking – IE 접속 제어 (4) (27) | 2010/05/17 |
| Advanced Global API Hooking – IE 접속 제어 (3) (10) | 2010/05/07 |
| Advanced Global API Hooking – IE 접속 제어 (2) (10) | 2010/04/25 |
| Advanced Global API Hooking – IE 접속 제어 (1) (21) | 2010/03/29 |
| DLL Injection in Windows 7 (3) (32) | 2010/02/23 |
| DLL Injection in Windows 7 (2) (15) | 2010/02/21 |
| DLL Injection in Windows 7 (1) (8) | 2010/02/20 |
| Session in Windows 7 (14) | 2010/02/14 |
API,
CreateProcess,
CreateProcessInternal,
Global API Hooking,
IE,
Internet Explorer,
InternetConnect,
it,
kernel32.dll,
NtCreateUserProcess,
NtResumeThread,
redirect,
redirection,
Reverse Code Engineering,
Reverse Engineering,
ReverseCore,
Reversing,
wininet.dll,
ZwCreateUserProcess,
ZwResumeThread,
리버스 엔지니어링,
리버싱,
소프트웨어 역공학,
우회,
후킹
cptest.exe
