IAT = Import Address Table
DLL 에서 가져다 쓴 함수들에대한 정보들을 가지고 있다고함
묵시적인 로딩인 경우에만 남아있는다
PE (portable excutable) 파일 구조
https://github.com/corkami/pics/blob/master/binary/pe101/pe101.pdf
RVA (Relative Virutal Address)
dll 이 가상메모리에 로딩이 되고 어떤 함수가 어디에 써있느냐를 나타낼떄 RVA를 통해 나타낸다.
IAT hook 기술 개요
- 실행 파일이 가지고 있는 IAT 영역 정보를 직접 조작하는 방식으로 피호출 API함수를 변경하는 기법
- C 언어 기반 응용 프로그램의 주요 함수들은 별도의 C Runtime Library(DLL) 로 구현되어 있으며 프로그램에서 호출 시 관련 정보가 IAT에 기술되어 있다.
- IAT에 기술된 정보는 특정 라이브러리(DLL)에 대한 묵시적 로딩 방식 적용시에 생성되는 것이다.
묵시적 로딩을 통해 메모리에 dll들이 다 로딩이 되면 IAT에 해당 function으로 갈 수 있는 주소가 저장된다.
실제 런타임에 puts 를 호출하기 위해서 IAT에서 메모리 주소를 찾고 DLL로 가서 해당 함수를 실행하고 다시 testFunc로 돌아오게 된다.
IAT에 담긴 주소를 다르게 담게 하여서 전혀 다른 DLL 을 호출하게 만들 수 있다.
PIMAGE_IMPORT_DESCRIPTOR getImportTable()
{
LPVOID baseAddress = GetModuleHandle(NULL);
PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)baseAddress;
PIMAGE_NT_HEADERS ntHeader =
(PIMAGE_NT_HEADERS)((DWORD_PTR)baseAddress + dosHeaders->e_lfanew);
IMAGE_DATA_DIRECTORY importTableDesc =
ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
PIMAGE_IMPORT_DESCRIPTOR importTable = NULL;
importTable = (PIMAGE_IMPORT_DESCRIPTOR)
((DWORD_PTR)baseAddress + importTableDesc.VirtualAddress);
return importTable;
}
null을 인자로 주면 현재 process 에서 진행중인 exe (pe)를 불러온다. 이후 pe 구조대로 내부 정보들을 읽어온다
optional header를 기준으로 타고들어가서 import table (iat) 주소를 알아낸다.
int hookIAT(const char *pszfuncName, LPVOID fphookFunc,
PIMAGE_IMPORT_DESCRIPTOR importTable)
{
LPVOID pBaseAddress = GetModuleHandle(NULL);
while (importTable->Name != NULL)
{
PIMAGE_THUNK_DATA impNameTable = (PIMAGE_THUNK_DATA)
((DWORD_PTR)pBaseAddress + importTable->OriginalFirstThunk);
PIMAGE_THUNK_DATA impAddrTable = (PIMAGE_THUNK_DATA)
((DWORD_PTR)pBaseAddress + importTable->FirstThunk);
while (impNameTable->u1.AddressOfData != NULL)
{
PIMAGE_IMPORT_BY_NAME importByName =
(PIMAGE_IMPORT_BY_NAME)
((DWORD_PTR)pBaseAddress + impNameTable->u1.AddressOfData);
cout << importByName->Name << endl;
if (strcmp(importByName->Name, pszfuncName) == 0)
{
DWORD oldProtect;
::VirtualProtect(&impAddrTable->u1.Function,
sizeof(DWORD_PTR), PAGE_READWRITE, &oldProtect);
impAddrTable->u1.Function = (DWORD_PTR)fphookFunc;
}
impNameTable++;
impAddrTable++;
}
importTable++;
}
return 0;
}
import table에서 내가 원하는 함수가 나올 때까지 계속 while을 돌게 된다.
원하는 함수이름을 찾으면 impAddrtTable -> u1.Function 을 통해 주소를 fphooFunc로 바꿔치기한다.
#include <iostream>
#include <Windows.h>
using namespace std;
int MyMessageBox(
HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
MessageBoxW(NULL, L"MyMessageBox", L"*** Hooked ***", MB_OK);
return 0;
}
PIMAGE_IMPORT_DESCRIPTOR getImportTable()
{
LPVOID baseAddress = GetModuleHandle(NULL);
PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)baseAddress;
PIMAGE_NT_HEADERS ntHeader =
(PIMAGE_NT_HEADERS)((DWORD_PTR)baseAddress + dosHeaders->e_lfanew);
IMAGE_DATA_DIRECTORY importTableDesc =
ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
PIMAGE_IMPORT_DESCRIPTOR importTable = NULL;
importTable = (PIMAGE_IMPORT_DESCRIPTOR)
((DWORD_PTR)baseAddress + importTableDesc.VirtualAddress);
return importTable;
}
int hookIAT(const char *pszfuncName, LPVOID fphookFunc,
PIMAGE_IMPORT_DESCRIPTOR importTable)
{
LPVOID pBaseAddress = GetModuleHandle(NULL);
while (importTable->Name != NULL)
{
PIMAGE_THUNK_DATA impNameTable = (PIMAGE_THUNK_DATA)
((DWORD_PTR)pBaseAddress + importTable->OriginalFirstThunk);
PIMAGE_THUNK_DATA impAddrTable = (PIMAGE_THUNK_DATA)
((DWORD_PTR)pBaseAddress + importTable->FirstThunk);
while (impNameTable->u1.AddressOfData != NULL)
{
PIMAGE_IMPORT_BY_NAME importByName =
(PIMAGE_IMPORT_BY_NAME)
((DWORD_PTR)pBaseAddress + impNameTable->u1.AddressOfData);
cout << importByName->Name << endl;
if (strcmp(importByName->Name, pszfuncName) == 0)
{
DWORD oldProtect;
::VirtualProtect(&impAddrTable->u1.Function,
sizeof(DWORD_PTR), PAGE_READWRITE, &oldProtect);
impAddrTable->u1.Function = (DWORD_PTR)fphookFunc;
}
impNameTable++;
impAddrTable++;
}
importTable++;
}
return 0;
}
int main(void)
{
MessageBoxA(NULL, "Before IAT hook", "Hook test", MB_OK);
PIMAGE_IMPORT_DESCRIPTOR importTable = getImportTable();
hookIAT("MessageBoxA", (LPVOID)MyMessageBox, importTable );
MessageBoxA(NULL, "After IAT hook", "Hook test", MB_OK);
return 0;
}
MyMessageBox에서는 MessageBoxW 를 사용했는데 이는 stackoverflow와 손 쉬운 main return을 위해서 인것 같다.
IAT hook의 한계
- 비교적 안정적인 기법이나 묵시적 로딩 방식을 채택하고 있는 경우에 대해서만 적용이 가능하다.
- LoadLibrary 함수를 이용하는 Dynamic 방식을 채택한 실행 코드에 대해서 사실상 무의미하다.
'Operating System > 이해하면 인생이 바뀌는 Windows API hook' 카테고리의 다른 글
DLL injection (0) | 2024.12.13 |
---|---|
Inline hook (0) | 2024.12.02 |
사전지식 - 두 번째 (0) | 2024.11.27 |
사전 지식 - 첫 번째 (0) | 2024.11.22 |