13 PE File Format(5) - EAT
EAT
: EAT(Export Address Table)는 라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 사용할 수 있도록 해주는 핵심 메커니즘
- 라이브러리 : 다른 프로그램에서 불러 쓸 수 있도록 관련 함수들을 모아놓은 파일(DLL/SYS)
- Win32 API가 대표적 라이브러리, 그 중에서도 kerner32.dll 파일이 핵심적인 파일
- EAT를 통해서만 해당 라이브러리에서 익스포트하는 함수의 시작 주소를 정확히 구할 수 있음
- PE 파일 내의 IMAGE_EXPORT_DIRECTORY에 익스포트 정보를 저장
- PE 파일에 하나만 존재
- IMAGE_EXPORT_DIRECTORY 위치 → PE 헤더에서 IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress 값이 시작 주소
kernel32.dll 파일의 IMAGE_OPTIONAL_HEADER32.DataDirectory[0]

offset | value | description |
00000168 | 262C | RVA of EXPORT Directory |
0000016C | 6CFD | size of EXPORT Directory |
- RVA 262C → File Offset 1A2C
- 262C - 1000 + 400 (참고 : https://wh514.tistory.com/24 )
IMAGE_EXPORT_DIRECTORY
- IMAGE_EXPORT_DIRECTORY 구조체
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp; // creation time date stamp
WORD MajorVersion;
WORD MinorVersion;
DWORD Name; // address of library file name
DWORD Base; // ordinal base
DWORD NumberOfFunctions; // number of functions
DWORD NumberOfNames; // number of names
DWORD AddressOfFunctions; // address of function start address array
DWORD AddressOfNames; // address of function name string array
DWORD AddressOfNameOrdinals; // address of ordinal array
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
중요 멤버
항목 | 의미 |
NumberOfFunctions | 실제 Export 함수 개수 |
NumberOfNames | Export 함수 중에서 이름을 가지는 함수 개수 (<=NumberOfFunctions) |
AddressOfFunctions | Export 함수 주소 배열 (배열 원소 개수 = NumberOfFunctions) |
AddressOfNames | 함수 이름 주소 배열 (배열의 원소 개수 = NumberOfNames)_ |
AddressOfNameOrdinals | Ordinal 배열 (배열의 원소 개수 = NumberOfNames) |
라이브러리에서 함수 주소를 얻는 API : GetProcAddress()
GetProcAddress() 동작원리
1. AddressOfNames 멤버를 이용해 '함수 이름 배열'로 이동
2. '함수 이름 배열'은 문자열 주소가 저장, 문자열 비교(strcmp)를 통해 원하는 함수 이름을 찾음(이때의 배열 인덱스를 name_index라 가정)
3. AddressOfNameOrdinals 멤버를 이용해 'ordinal 배열'로 이동
4. 'ordinal 배열'에서 name_index로 해당 ordinal값을 찾음
5. AddressOfFunctions 멤버를 이용해 '함수 주소 배열(EAT)'로 이동
6. '함수 주소 배열(EAT)'에서 아까 구한 ordinal을 배열 인덱스로 하여 원하는 함수의 시작 주소를 얻음
IMPORT_EXPORT_DIRECTIORY 구조체와 EAT 전체 구조

- kernel32.dll의 경우 AddressOfNameOrdinals 배열의 값이 index = ordinal 형태로 되어있음
- 익스포트하는 함수 중에 이름이 존재하지 않을 수 있음. 따라서, index != ordinal인 경우도 있음.
kernel32.dll을 이용한 실습
: kernel32.dll 파일의 EAT에서 AddAtomW 함수 주소를 찾는 실습
IMAGE_EXPORT_DIRECTORY 구조체 RAW는 1A2C : Hex Editor

File Offset | Member | Value | RAW |
1A2C | Characteristic | 00000000 | - |
1A30 | TimeDateStamp | 48025BE1 | - |
1A34 | MajorVersion | 0000 | - |
1A36 | MinorVersion | 0000 | - |
1A38 | Name | 00004B8E | 3F8E |
1A3C | Base | 00000001 | - |
1A40 | NumberOfFunctions | 000003B9 | - |
1A44 | NumberOfNames | 000003B9 | - |
1A48 | AddressOfFunctions | 00002654 | 1A54 |
1A4C | AddressOfName | 00003538 | 2938 |
1A50 | AddressOfNameOrdinals | 0000441C | 381C |
1. 함수 이름 배열
AddressOfNames 값은 RVA = 3538 → RAW = 2938

- 배열의 원소의 개수 = NumberOfNames(03B9)
- RVA 값을 하나하나 따라가면 함수 이름 문자열이 나타남
2. 원하는 함수 이름 찾기
RVA = 4B9B → 3F9B로 가서 'AddAtomW' 찾기

- 배열의 세 번째 원소, 배열 인덱스(index) : 2
3. Ordinal 배열
RVA = 441C → 381C로 가서 'AddAtomW' 함수의 Ordinal 값 알아내기

- 2바이트 ordinal로 이루어진 배열
- index 값(2)을 배열에 적용
- AddressOfNameOrdinals[index] = ordinal (index = 2, ordinal = 2)
4. 함수 주소 배열 - EAT
AddressOfNames 값은 RVA = 2654 → RAW = 1A54

- AddressOfFunctions[ordinal] = RVA (ordinal = 2, RVA = 000326D9)
- AddAtomW 함수 주소 = ImageBase + RVA(00326D9)