API Hooking이란?
- API의 호출을 가로채는 기술
- 예를 들면, A라는 함수를 호출했는데 내가 만든 B라는 함수를 호출하는 것.
IAT Hook, DLL 함수의 호출 원리
- DLL이 로드되는 위치를 알고 있고, 그 DLL이 가진 함수의 Offset을 알고 있다면?
- 어셈블리 명령으로 call 0x주소로 호출할 수 있을 것이다.
- 그러나 컴파일/링크 시 DLL 안의 함수 주소는 알 수 없다 (ASLR 때문)

ASLR(Address space layout randomization)이란?
ASLR은 실행 시 exe, dll, stack, heap등의 주소를 임의로 변경하는 보안 기법
ASLR을 끄고 MessageBoxA의 주소 덮어쓰기
#include <iostream>
#include <Windows.h>
using namespace std;
typedef UINT(__stdcall* f)(HWND, const char*, const char*, UINT);
UINT __stdcall foo(HWND hwnd, const char* s1, const char* s2, UINT btn)
{
printf("foo : %s, %s\n", s1, s2);
HMODULE hDll = GetModuleHandle(L"user32.dll");
f proc = (f)GetProcAddress(hDll, "MessageBoxA");
return proc(hwnd, s2, s1, btn);
}
int main()
{
//MessageBoxA(0, "API Hook", "aAA", 0);
DWORD old;
VirtualProtect((void*)0x041B098, sizeof(void*), PAGE_READWRITE, &old);
*((int*)0x041B098) = (int)foo;
VirtualProtect((void*)0x041B098, sizeof(void*), old, 0);
MessageBoxA(0, "API HOOK", "AAA", MB_OK);
return 0;
}
- 비주얼스튜디오에서 ASLR을 끄고 MessageBoxA의 주소를 알아내어 테스트해본 결과 MessageBoxA가 아닌 foo가 실행
- VirtualProtect는 페이지 보안 속성을 변경해주는 함수. IAT영역은 보안 속성이 기본적으로 READONLY이기 때문
- 주소를 덮어쓰기 위해서 PAGE_READWRITE로 변경해줌
코드를 이용한 IAT 후킹
- 32비트 기준으로 작성
후킹 전 알아야 할 정보들
- 메모리에 매핑된 실행 파일의 주소를 알아야 함 (그래야 IAT의 주소를 아니까)
- 후킹할 함수가 어떤 DLL에 있는지 알아야 함 (user32.dll에 MessageBox가 있듯이)
- 어떤 함수를 어떤 함수로 바꿀건지 (MessageBox -> TestFunction)
가상 주소에 매핑된 실행 파일의 Import table의 주소를 구하는 방법
=> DbgHelp.dll에 있는 ImageDirectoryEntryToData를 사용하면 IAT의 위치를 구할 수 있음
=> 함수의 반환값으로 IMAGE_IMPORT_DESCRIPTOR의 주소가 반환되는데 그게 IAT
IMPORT 섹션
- IMPORT 섹션은 IMAGE_IMPORT_DESCRIPTOR의 배열로 구성되어 있다.
- IMAGE_IMPORT_DESCRIPTOR는 총 5개 항목으로 구성됨

- 여기서 중요한 건 Name과 FirstThunk(IAT)
- IAT 구조체의 항목은 DLL 하나당 하나씩 만들어짐
- 어떤 구조체가 user32.dll을 관리하는지 찾아야 함
- Name 항목이 DLL의 이름이 있는 주소
- 코드에서는 구조체의 각 Name 멤버를 _stricmp로 user32.dll과 비교하고 있다.
- FirstThunk(IAT)는 DLL의 함수가 시작되는 시작 주소
- FirstThunk(IAT)는 Offset이므로 실행 파일의 주소 + FirstThunk로 봐야 함
- 이 주소부터 4바이트씩 읽어서 그 주소가 MessageBoxA의 주소와 같은지 비교하면 됨
- 찾으면 그 주소의 보호속성을 변경하고 덮어씀
#include <iostream>
#include <Windows.h>
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
using namespace std;
UINT __stdcall TestFunction(DWORD p1, const char* p2, const char* p3, DWORD p4)
{
cout << "Fake!!" << endl;
return 0;
}
int main()
{
ULONG sz;
//실행 파일의 시작 주소
HMODULE hExe = GetModuleHandle(0);
// DbgHelp.dll의 ImageDirectoryEntryToData를 사용해 IAT를 구해온다.
// 반환값은 IMAGE_IMPORT_DESCRIPTOR의 주소로 들어오는데 그게 IAT의 주소
IMAGE_IMPORT_DESCRIPTOR* pImage =
(IMAGE_IMPORT_DESCRIPTOR*)ImageDirectoryEntryToData(
hExe, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &sz
);
printf("Address Import Directory : %p\n", pImage);
for (; pImage->Name; pImage++)
{
char* s = ((char*)hExe + pImage->Name);
if (_strcmpi("user32.dll", s) == 0) break;
}
if (pImage->Name == 0)
{
cout << "user32.dll을 찾지 못했음" << endl;
return 0;
}
// pThunk
// pImage->Name이 0이 아니라면 DLL을 찾은 것이고,
// 그 DLL을 관리하는 IAT 구조체의 Thunk에 접근하는 것
IMAGE_THUNK_DATA* pThunk =
(IMAGE_THUNK_DATA*)((char*)hExe + pImage->FirstThunk);
for (; pThunk->u1.Function; pThunk++)
{
// pThunk->u1.Function이 내가 찾는 함수라면 if문 안으로 진입
if (pThunk->u1.Function == (DWORD)MessageBoxA)
{
// 주소를 구해놓기
DWORD* addr = &(pThunk->u1.Function);
DWORD old;
//IAT의 보호 속성이 READONLY 속성이므로 READWRITE로 변경
VirtualProtect(addr, sizeof(DWORD), PAGE_READWRITE, &old);
// 이거보단 WriteProcessMemory로 하는게 더 좋다.
*addr = (DWORD)TestFunction;
DWORD len;
VirtualProtect(addr, sizeof(DWORD), old, &old);
break;
}
}
MessageBoxA(0, "Caption Test", "Error", MB_OK);
return 0;
}
'운영체제 > [ecourse] Windows Programming' 카테고리의 다른 글
7-1. DllMain 기본 개념 (0) | 2022.09.21 |
---|---|
6-2-3. Critical Section 실습 (0) | 2022.09.20 |
6-2-2. Event 실습 (0) | 2022.09.20 |
6-2-1. Semaphore 실습 (0) | 2022.09.20 |
6-2. 동기화 개념과 Critical Section (1) | 2022.09.20 |
댓글