PE File Format(2)에서 동적 연결 라이브러리에 대해서 이야기 해온 마당에 이제와서 라이브러리에 대한 정의를 내리기엔 좀 그렇지만, 라이브러리는
"다른 프로그램에서 불러서 사용할수 있도록 관련 함수들을 모아놓은 파일"이다.
Win32 API가 대표적이고 그 중에서 kernel32.dll이 핵심적이라고 한다.
EAT는 (Export Address Table)로서 라이브러리 함수를 다른 프로그램에서 가져다 쓸 수 있도록 해주는 하나의 중요한 매커니즘이다.
앞서 PE File Format(2)에서 IAT를 설명할 때 이야기 했었는데 EAT를 통해서 Export하는 함수의 시작 주소를 정확히 구할 수 있다.
IAT처럼 IMAGE_EXPORT_DIRECTORY라는 구조체에 정보를 저장하고 있다.
IMAGE_IMPORT_DIRECTORY구조체는 라이브러리의 개수만큼 존재한다고 했었는데,
IED 구조체는 PE파일에 단 하나만 존재한다고 한다.
PE파일에서 IMAGE_EXPORT_DIRECTORY구조체는 PE 헤더에 위치하고 있다.
IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress 값이 IED 구조체의 시작 주소다.
이전에 IID의 시작 주소는
IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress라고 한 적이 있다.

드래그한 부분의 첫 번째 4바이트(0000262C)가 VirtualAddress가 될 것이고, 그 뒤의 4바이트(6CFD)가 Size가 될 것이다.
File Offset을 찾아보자, 공식에 대입하면
RAW = 262C - 1000 + 400 = 1A2C으로 IMAGE_EXPORT_DIRECTORY구조체의 파일에서의 위치는 1A2C가 된다.
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
IMAGE_EXPORT_DIRECTORY구조체의 코드다.
.
| 항목 | 의미 |
|---|---|
| NumberOfFunctions | 실제 Export 함수 개수 |
| NumberOfNames | Export 함수 중에서 이름을 가지는 함수 개수 |
| AddressOfFunctions | Export 함수 주소 배열 |
| AddressOfNames | 함수 이름 주소 배열 |
| AddressOfNameOrdinals | ordinal 배열 |

함수 주소를 얻는 과정을 살펴보며 위 멤버들의 용도를 알아보자.
GetProcAddress()가 라이브러리에서 함수 주소를 얻어내는 API다.
Cotton이라는 함수를 찾는다고 해 보자.
GetProcAddress("Cotton");
13.21 그림은 "kernel32.dll"파일에 대한 경우다. DLL파일은 "kernel32.dll"파일처럼 모든 함수에 이름이 존재하고, index와 ordinal이 같은 형태로 되어 있을 수도 있지만 모든 DLL 파일이 이와 같지는 않고 이름이 존재하지 않는 함수도 있고, index != ordinal인 경우도 있기에 위와 같은 순서를 거쳐야만 한다.

실제로 헥사 에디터를 이용해서 살펴보자.
위에서 구해둔 File Offset인 1A2C로 이동해보았다.
| File Offset | Member | Value | Raw |
|---|---|---|---|
| 1A40 | NumberOfFunctions | 000003B9 | - |
| 1A44 | NumberOfNames | 000003B9 | - |
| 1A48 | AddressOfFunctions | 00002654 | 1A54 |
| 1A4C | AddressOfNames | 00003538 | 2938 |
| 1A50 | AddressOfNamesOrdinals | 0000441C | 381C |
위에서 했던 과정을 따라가서 'AddAtomW'함수를 찾아보자..
'함수 이름 배열'을 찾아 보자.
AddressOfNames의 값이 00003638(RVA) 오프셋은 2938이다. 2938로 이동해보자

함수 이름 배열의 원소들은 문자열에 대한 주소라고 설명했다.(4바이트RVA)
GetProcAddress함수는 strcmp를 통해서 "AddAtomW" 문자열을 비교해본다.
지금 "AddAtomW"문자열은 배열의 3번째 원소에 있다. (2940의 4BB3값)
4BB3(RVA) -> 3FB3(RAW)

문자열 비교를 통해서 AddAtomW()가 배열의 3번째 원소이고 인덱스는 2라는 것을 알아냈다.
AddAtomW()의 Ordinal값을 찾아 낼 차례다.AddressOfNameOrdinals멤버의 값은 441C (RAW:381C)다
이와 같은 2바이트 ordinal 배열이 나타난다.
2번에서 얻은 인덱스 값 2를 위 배열에 적용하게 되면 ordinal 2를 구할 수 있다.
이제 함수 주소 배열의 주소를 가지고 있는AddressOfFunctions값을 보자
해당 멤버는 2654(RAW 1A54)값을 가지고 있다. 이동해 보자.

4바이트 주소 배열이 나타난다 . 이것이 Export함수 주소들이다.
이렇게 구한 주소를 디버거로 한번 살펴보자.

일단 kernel32.dll파일의 imageBase 값을 PEview로 살펴보니 7C800000이 나온다
즉 실행시 AddAtomW의 주소는 7C800000 + 000326D9 = 7C8326D9가 된다.

ollydbg(디버거)로 주소를 확인해 보니 AddAtomW함수가 나오는 모습이다.