일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 해킹
- packet capture
- pwnable
- C언어 패킷캡쳐
- 시스템해킹
- 바이트 오더
- 윈도우 커널 디버깅
- arudino
- Msvcrt.dll
- pcap packet capture
- windows kernel debugging
- vcruntime140.dll
- hacking
- vcruntime.dll
- Windows Kernel Debug
- pcap packet
- 윈도우 커널
- 포너블
- 개발 환경 준비
- 네트워크 바이트 오더
- Windows
- apphelp.dll
- Windows Kernel
- 개발하기
- HackCTF
- Network Byte Order
- 윈도우 커널 드라이버
- Windows Kernel Driver
- ucrtbase.dll
- IAT Hooking
- Today
- Total
미친해커
[Reversing] IAT Hooking Step 1 본문
이번 포스팅부터 본격적으로 IAT Hooking에 대해서 알아보도록 하자
저번 포스팅에서 간단히 PE 구조와 IAT의 구조에 대해서 배웠었다. 이번엔 IAT에서 등록되어 있는 함수들의 이름과 주소를 불러오는 프로그램을 만들어보도록 한다.
Source Code
#include <stdio.h>
#include <windows.h>
int main(int argc, char *argv[])
{
// 현재 프로그램의 ImageBase를 가져온다
ULONGLONG ImageBase = GetModuleHandleA(NULL);
// ImageBase는 Dos Header의 주소와 같기에 포인터로 연결해준다.
IMAGE_DOS_HEADER *DOS = ImageBase;
printf("Image Dos Header : 0x%p\n", DOS);
// Dos Header로부터 NT Header의 주소를 계산한다.
IMAGE_NT_HEADERS *NT = ImageBase + DOS->e_lfanew;
printf("Image Nt Header : 0x%p\n", NT);
/*
NT Header에 멤버 변수중 DataDirectory에 Import Descriptor의 주소가 저장되어 있다.
해당 주소는 VirtualAddress라는 변수에 담겨져 있으며 해당 변수명의 의미는
변수에 담겨져 있는 주소가 상대주소라는 것을 의미한다. 실제 주소를 계산하기 위해서는
IamgeBase + VirtualAdress를 해주면 구할 수 있다.
또 Import Descriptor의 갯수는 미리 알아낼 수 없으며 마지막 Import Descriptor에는
모든 데이터가 NULL로 설정되어 있다. 그 점을 이용해 마지막 Import Descriptor를
구할 수 있다.
*/
IMAGE_IMPORT_DESCRIPTOR (*IMPORT)[1] = ImageBase + NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
printf("Import Address Table : 0x%p\n", IMPORT);
for (int i = 0;; i++)
{
// 현재 Import Descriptor가 마지막인지 확인
if (IMPORT[i]->OriginalFirstThunk == NULL)
break;
printf("[*] Get Module Name : %s\n", ImageBase + IMPORT[i]->Name);
for (int j = 0;; j++)
{
IMAGE_THUNK_DATA *THUNK = ImageBase + IMPORT[i]->OriginalFirstThunk + j * sizeof(void *);
// 해당 Thunk Data가 마지막인지 확인
if (THUNK->u1.AddressOfData == NULL)
{
break;
}
if (THUNK->u1.Ordinal < 0x80000000)
{
IMAGE_IMPORT_BY_NAME *IMPORT_NAME = ImageBase + THUNK->u1.AddressOfData;
printf("[+] Function Name : %s\n", IMPORT_NAME->Name);
}
}
}
}
위 소스코드를 컴파일 해 실행시켜보면 다음과 같이 해당 응용 프로그램이 로드하는 DLL 이름과 API 함수 이름들이 출력된다. (출력되는 DLL 이름과 API 함수 이름들은 아래 예시와 다를 수 있다)
[*] Get Module Name : KERNEL32.dll
[+] Function Name : DeleteCriticalSection
[+] Function Name : EnterCriticalSection
-------------중략-------------
[+] Function Name : fprintf
[+] Function Name : free
[+] Function Name : fwrite
[+] Function Name : malloc
[+] Function Name : memcpy
[+] Function Name : printf
[+] Function Name : signal
[+] Function Name : strlen
[+] Function Name : strncmp
[+] Function Name : vfprintf
How can I get IAT Address?
여기서 해당 함수의 주소를 저장하고 있는 IAT의 주소를 구하기 위해서는 한줄만 추가하면 된다.
printf("[+] Function Name : %s\n", IMPORT_NAME->Name);
printf("[+] IAT Address : 0x%p\n", ImageBase + IMPORT[i]->FirstThunk + j * sizeof(void *));
이렇게 밑에 한줄만 추가하면 해당 함수의 주소를 가지고있는 주소를 구할 수 있다.
Description of source code
소스코드에 대한 간단한 설명은 다음 그림을 보고 이해하자.
GetModuleHandleA 함수는 현재 로르된 모듈들 중, 인자로 들어온 모듈 이름의 ImageBase 주소를 반환해준다. 만약 인자로 NULL 을 주면 현재 exe 프로그램의 ImageBase를 반환한다. 그리고 해당 주소는 IMAGE_DOS_HEADER의 주소이다.
typedef struct _IMAGE_DOS_HEADER
{
WORD e_magic;
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
LONG e_lfanew;
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
IMAGE_DOS_HEADER의 e_lfanew 멤버 변수의 값과 ImageBase의 주소를 더하면 IMAGE_NT_HEADER의 주소를 구할 수 있다.
IMAGE_NT_HEADER는 x86, x64 이렇게 2가지로 나뉜다. x86과 x64의 큰 차이는 없기 때문에 여기서 설명은 x64 하겠다.
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
#define IMAGE_DIRECTORY_ENTRY_TLS 9
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
#define IMAGE_DIRECTORY_ENTRY_IAT 12
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
IMAGE_IMPORT_DESCRIPTOR의 주소를 구하는 방법은 다음 코드와 같다.
IMAGE_NT_HEADER *NT;
IMAGE_IMPORT_DESCRIPTOR *IMPORT = ImageBase + NT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
IMAGE_OPTIONAL_HEADER의 멤버 변수인 IMAGE_DATA_DIRECTORY의 첫번째 인덱스의
VirtualAddress + ImageBase를 계산하면 IMAGE_IMPORT_DESCRIPTOR의 주소가 나온다.
여기서 IMAGE_IMPORT_DESCRIPTOR를 포인터 배열로 선언한 이유는 n개의 갯수가 존재하고 마지막 인덱스에는 모든 값이 0으로 초기화되어 있다. 이것을 기준으로 유효한 개체만 찾으면 된다. 이 외에 INT와 IAT가 존재하는데 각각 Import Name Table과 Import Address Table이라 불린다. 이에 대한건 소스코드와 저번 포스팅에서 설명했기 때문에 여기서의 설명은 생략하도록 하겠다.
후킹할 함수를 찾고 해당 함수의 IAT 주소를 구해서 후킹 함수의 주소로 변경한다. 그러면 IAT Hooking 성공이다. 다음 포스팅에서 그림으로 보다 쉽게 설명하도록 하겠다.
'Reversing > Hooking' 카테고리의 다른 글
[Reversing] Trampoline(Inline) Hooking x86 Step 1 (0) | 2022.04.27 |
---|---|
[Reversing] Trampoline(Inline) Hooking x86 Step 0 (0) | 2022.04.27 |
[Reversing] IAT Hooking Step 2 (0) | 2022.02.03 |
[Reversing] IAT Hooking Step 0 (0) | 2022.01.19 |
[Reversing] 리버싱의 꽃 후킹이란? (0) | 2022.01.18 |