본문 바로가기
게임해킹/리버싱 핵심원리(나뭇잎책)

13장. IAT - 실습 해보기

by HHack 2025. 1. 22.
반응형

13.5.5 notepad.exe를 이용한 실습

목표 : 특정 DLL의 Import Table에서 함수를 찾고, 디버거로 실제 함수 위치 찾아보기

1. IMAGE_IMPORT_DESCRIPTOR 구조체 배열 찾기

실제 IMAGE_IMPORT_DESCRIPTOR 구조체는 PE 헤더가 아닌 PE 바디에 위치합니다. 그곳을 찾아가기 위한 정보는 역시 PE헤더에 있습니다. IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress 값이 실제 IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소입니다.

[그림1] notepad.exe의 IMAGE_HEADER32.DataDirectory[1]

[그림 1]의 구조체 배열 정보를 보기 쉽게 나타내면 아래와 같습니다.

offset value description
00000158 00000000 RVA of EXPORT Directory
0000015C 00000000 size of EXPORT Directory
00000160 00007604 RVA of IMPORT Directory
00000164 000000C8 size of IMPORT Directory
00000168 0000B000 RVA of RESOURCE Directory
0000016C 00008304 size of RESOURCE Directory
  • 이 PE 파일에는 Export Directory가 없고,
  • Import Directory RVA 0x7604에 있으며, 크기는 200 바이트입니다.
  • 또한, Resource Directory RVA 0xB000에 있으며, 크기는 33,028 바이트입니다.

위 정보에서 RVA가 7604이므로 File Offset은 6A04입니다. File Offset을 구하기 위해선 IMAGE_SECTION_HEADER를 통해 각각 섹션의 정보를 알아야 합니다.


STEP 1 : DOS Header 분석

PE 파일은 IMAGE_DOS_HEADER로 시작됩니다. 중요한 정보는 다음과 같습니다:

  • 0x3C 위치: e_lfanew 값으로, PE Header의 시작 오프셋을 나타냅니다.
    • HEX 데이터: 0x3C = E0 00 00 00 → e_lfanew = 0xE0

STEP 2 : PE Header 확인

e_lfanew에서부터 PE Header가 시작됩니다.

  • PE Header의 Signature는 PE\0\0입니다.
    • HEX 데이터: 0xE0 = 50 45 00 00 (PE\0\0)

PE Header는 다음 섹션을 포함합니다:

  1. File Header (IMAGE_FILE_HEADER): PE Header에서 바로 뒤에 위치합니다.
    • Machine (2바이트): 4C 01 (Intel x86)
    • NumberOfSections (2바이트): 03 00 (섹션의 개수 = 3)
    • SizeOfOptionalHeader (2바이트): E0 00 (Optional Header의 크기 = 0xE0)
  2. Optional Header (IMAGE_OPTIONAL_HEADER):
    • 이 Header는 PE Header에서 중요한 데이터(RVA, 섹션 정보 등)를 포함합니다.
    • 크기는 0xE0로 주어졌습니다.

STEP 3 : Section Header 위치 계산

Section Header는 Optional Header 바로 뒤에 위치합니다.

Section Header 위치 = e_lfanew + 24 + SizeOfOptionalHeader

  • e_lfanew = 0xE0
  • 24 (PE Header 크기)
  • SizeOfOptionalHeader = 0xE0
  • Section Header 위치 = 0xE0 + 24 + 0xE0 = 0x1D8

[그림 2] IMAGE_SECTION_HEADER

STEP 4 : IMAGE_SECTION_HEADER에서 정보 추출

각 IMAGE_SECTION_HEADER는 40바이트입니다. NumberOfSections = 3이므로, 3개의 섹션을 분석해야 합니다. 데이터는 다음과 같은 필드를 포함합니다:

  1. Name (8바이트): 섹션 이름
  2. VirtualSize (4바이트): 메모리에서 섹션의 크기
  3. VirtualAddress (4바이트): 섹션의 시작 RVA
  4. SizeOfRawData (4바이트): 파일에서 섹션의 크기
  5. PointerToRawData (4바이트): 파일에서 섹션의 시작 위치
  6. 기타 정보(16바이트)

1. 첫 번째 섹션 (.text)

  • Name: .text → 74 65 78 74 00 00 00
  • VirtualSize: 0x00487700 → 0x00007748
  • VirtualAddress (RVA): 0x00001000
  • SizeOfRawData: 0x00007800
  • PointerToRawData: 0x00000400
  • Characteristics: 0x60000020
    • IMAGE_SCN_CNT_CODE
    • IMAGE_SCN_MEM_EXECUTE
    • IMAGE_SCN_MEM_READ

2. 두 번째 섹션 (.data)

  • Name: .data → 2E 64 61 74 61 00 00 00
  • VirtualSize: 0x00001BA8
  • VirtualAddress (RVA): 0x00009000
  • SizeOfRawData: 0x00000800
  • PointerToRawData: 0x00007C00
  • Characteristics: 0xC0000040
    • IMAGE_SCN_CNT_INITIALIZED_DATA
    • IMAGE_SCN_MEM_READ
    • IMAGE_SCN_MEM_WRITE

3. 세 번째 섹션 (.rsrc)

  • Name: .rsrc → 2E 72 73 72 63 00 00 00
  • VirtualSize: 0x00008304
  • VirtualAddress (RVA): 0x0000B000
  • SizeOfRawData: 0x00008400
  • PointerToRawData: 0x00008400
  • Characteristics: 0x40000040
    • IMAGE_SCN_CNT_INITIALIZED_DATA
    • IMAGE_SCN_MEM_READ

※ 정리된 섹션 정보

Name Virtual Size Virtual Address (RVA) Size of Raw Data Pointer to Raw Data Characteristics
.text 0x7748 0x1000 0x7800 0x0400 Code, Execute, Read
.data 0x1BA8 0x9000 0x0800 0x7C00 Initialized Data, RW
.rsrc 0x8304 0xB000 0x8400 0x8400 Initialized Data, Read

2. IMAGE_IMPORT_DESCRIPTOR 구조 이해하기

RVA가 7604의 섹션은 .text에 속하므로, RAW값은 7604 - 1000 + 400을 계산하여 6A04가 나오게 됩니다.

파일에서 6A04를 보겠습니다.

[그림 3] notepad.exe의 IMAGE_IMPORT_DESCRIPTOR 구조체 배열

IMAGE_IMPORT_DESCRIPTOR 구조체는 다음 필드로 구성됩니다.

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    DWORD OriginalFirstThunk;  // IAT의 RVA (INT로도 불림)
    DWORD TimeDateStamp;       // Time/Date 스탬프 (미사용 시 0 또는 -1)
    DWORD ForwarderChain;      // Forwarder Chain (미사용 시 -1)
    DWORD Name;                // DLL 이름의 RVA
    DWORD FirstThunk;          // IAT의 RVA
} IMAGE_IMPORT_DESCRIPTOR;

위 구조체 필드를 표로 정리하면 아래와 같습니다.

 

File Offset Member RVA RAW
6A04 OriginalFirstThunk(INT) 00007990 00006D90
6A08 TimeDateStamp FFFFFFFF -
6A0C ForwarderChain FFFFFFFF -
6A10 Name 00007AAC 00006EAC
6A14 FirstThunk(IAT) 000012C4 000006C4

1. 라이브러리 이름(Name)

Name 항목은 임포트 함수가 소속된 라이브러리 파일의 이름 문자열 포인터 입니다.

RVA : 7AAC -> RAW : 6EAC

파일 Offset 6EAC에 "comdlg32.dll" 문자열이 보입니다.

[그림 3] "comdlg32.dll" 문자열

2. OriginalFirstThunk - INT(Import Name Table)

INT는 임포튼 하는 함수의 정보(Ordinal, Name)가 담긴 구조체 포인터 배열입니다. 이 정보를 얻어야 프로세스 메모리에 로딩된 라이브러리에서 해당 함수의 시작 주소를 정확히 알 수 있습니다.

OriginalFirstThunk 멤버를 따라갑니다.(RVA : 7990 -> RAW : 6D90)

[그림 4] INT

주소 배열 형태로 되어있습니다. 주소 값 하나하나가 각각의 IMAGE_IMPORT_BY_NAME 구조체를 가리키고 있습니다. 첫 번째 값인 7A7A를 따라가보면 임포는 하는 API 함수 이름 문자열이 나타날겁니다.

3. IMAGE_IMPORT_BY_NAME

RVA : 7A7A는 RAW : 6E7A 입니다.

[그림 5] IMAGE_IMPORT_BY_NAME

파일 오프셋 6E7A의 최초 2바이트 값(000F)은 Ordinal로, 라이브러리에서 함수의 고유 번호입니다. 그 뒤로 "PageSetupDlgW" 함수 이름 문자열이 보입니다.

INT는 IMAGE_IMPORT_BY_NAME 구조체 포인터 배열입니다. 즉, 배열의 첫 번째 원소가 가리키는 함수의 Ordinal 값은 000F이고 함수의 이름은 "PageSetupDlgW" 입니다.

4. FirstThunk - IAT(Import Address Table)

FirstThunk 또는 IAT (Import Address Table)는 실행 시에 함수의 실제 주소가 저장되는 위치를 나타냅니다.

 

IAT의 RVA : 12C4는 RAW : 6C4입니다.

[그림 6] FirstThunk - IAT

IAT의 크기는 DLL에서 가져올 함수의 개수에 따라 결정됩니다. 각 함수는 IAT에서 IMAGE_THUNK_DATA 구조체로 표현되며, 각 엔트리는 4바이트(32비트) 또는 8바이트(64비트)를 차지합니다.

 

6C4의 값들은 comdlg32.dll에서 참조된 함수들을 나타내며, 크기는 다음과 같이 정의됩니다:

  • 가져올 함수의 개수: 6E10 ~ 6EAB에서 정의된 9개의 함수.

IAT의 크기는 다음 식으로 계산됩니다

  • 크기 = (가져올 함수의 개수 + 1) × 엔트리 크기

따라서 [그림 6]에서 파일 오프셋 6C4 ~ 6EB 영역이 "comdlg32.dll" 라이브러리에 해당하는 IAT 배열 영역입니다.

INT와 마찬가지로 구조체 포인터 배열 형태로 되어있으며, 배열은 NULL로 끝납니다.

IAT의 첫 번째 원소 값은 이미 '76324906'으로 하드코딩 되어있습니다. 이 값은 의미 없는 값으로 notepad.exe 파일이 메모리에 로딩될 때 이 값은 정확한 주소 값으로 대체됩니다.

 

디버거를 이용해서 notepad.exe의 IAT를 확인해보겠습니다.

[그림 7] notepad.exe의 IAT

notepad.exe의 ImageBase 값은 01000000입니다. 따라서 IAT 주소는 010012C4이며, 그 값은 정확한 API의 시작 주소 값이 들어와 있습니다.

 

OS별로 다른 값이 세팅되어 있을 수 있습니다. 디버거에 들어와있는 값이 정확한 API 값이 들어와있습니다.

 

해당주소 75E66430으로 가면 아래의 그림과 같이 comdlg32.dll의 PageSetupDlgW 함수 시작이 나타납니다.

[그림 8] comdlg32.PaseSetupDlgW

반응형