PE Header – IAT(Import Address Table), EAT(Export Address Table)

IAT는 PE Header 이해에 있어 제일 중요한 부분이라고 할 수 있다. . IAT는 프로그램이 필요로 하는 라이브러리의 함수들의 기술한 테이블이다. 따라서 현재 프로그램이 이용하는 라이브러리를 알 수 있기에 이 부분은 필수로 이해해야 한다

윈도우는 앞서 얘기듯이 서브시스템을 통해 커널 호출할 수 있도록 하였다. 이는 실제 PE 파일 내에 IAT로 구현되어, 여러 프로세스가 공유하도록 설계하여 이 메모리 낭비를 막을 수 있고 파일의 크기도 줄일 수 있게 된다.

조금 더 쉽게 설명하자면, CreateFile을 이용하기 위해, 해당 함수를 파일 내에 포함하는 것이 아닌, 별도 라이브러리(DLL, Dynamic-link library)화된 파일의 해당 함수 위치를 기억해 놓았다가 필요 인자값들과 함께 해당 함수를 호출하게 된다. 그리하여 DLL이라는 개념이 나오게 되었다.

그래서 프로그램을 실행시 IAT에 기술된 함수의 위치를 확인하여, PE 파일에서 필요로 하는 DLL의 함수들의 주소값들을 조사하여 갱신한다.

그리고 프로그램 종료시 주소값을 프로그램내 별도로 저장하지 않고, 해제하는 이유는 윈도우 마다 DLL의 주소가 다를 수 있고, DLL의 메모리 위치가 변경될 수 있기 때문이다.

예로, Regedit의 GetStartupInfoA를 호출하는 과정을 보면, 프로그램이 실행 시 실행 중인 윈도우의 해당 함수 위치를 찾아 IAT를 먼저 갱신하게 된다. 그리고 IAT의 특정 주소에 GetStartupInfoA라는 함수의 주소값이 지정되며, 프로그램은 단지 IAT의 주소 값을 호출하는 방식이다.

Ollydbg를 이용해 실제 Call 명령문을 확인해 보았다. 프로그램 내에서는 단지 특정 IAT의 주소값을 가리키는 것으로 실행되는 것을 확인할 수 있다.

 

[그림] GetStartupInfoA를 호출하는 주소는 IAT 테이블 주소이다

 

EAT(Export Address Table)은 반대로 자신이 라이브러리가 되어, 다른 프로그램들에게 자신의 함수를 제공하게 된다.

그럼 IAT에 대해 보다 자세히 알아보기 위하여, IAT에서 사용되는 구조체 IMAGE_IMPORT_DESCRIPTOR와 IMAGE_IMPORT_BY_NAME를 확인해 보자.

 

typedef
struct _IMAGE_IMPORT_DESCRIPTOR {

union {

DWORD Characteristics;                // 0 for terminating null import descriptor

DWORD OriginalFirstThunk;            // RVA to original unbound IAT (PIMAGE_THUNK_DATA)

} DUMMYUNIONNAME;

DWORD TimeDateStamp;                // 0 if not bound,

        // -1 if bound, and real date\time stamp

        // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)

        // O.W. date/time stamp of DLL bound to (Old BIND)

DWORD ForwarderChain;         // -1 if no forwarders

DWORD Name;

DWORD FirstThunk;         // RVA to IAT (if bound this IAT has actual addresses)

} IMAGE_IMPORT_DESCRIPTOR;

typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

 

IMAGE_IMPORT_DESCRIPTOR 구조체는 다른 이름으로도 불리는데, IMPORT Directory Table이라고는 이름으로도 불리니, 용어에 혼동이 없기를 바란다(PEview 에서 IMPORAT Directory Table로 나타난다).

Name:

라이브러리(DLL)의 이름값이 들어 있는 주소를 확인할 수 있다.

OriginalFirstThunk:

이 값을 통해 아래 IMAGE_IMPORT_BY_NAME 구조체를 가리켜, 함수 이름 문자열을 확인하고, 각 값들을 얻어오게 되는데, IMAGE_IMPORT_BY_NAME의 구조체 정보는 아래와 같다.

 

typedef
struct _IMAGE_IMPORT_BY_NAME {

WORD Hint;

BYTE Name[1];

} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

 

FirstThunk:

라이브러리의 주소값이 들어있는 주소를 가리키게 되며 NULL을 만날 때까지 해당 라이브러리 안의 함수 주소값을 가져오게 된다.

다음 그림을 통해 최종 IMAGE_IMPORT_BY_NAME을 알아내는 과정의 이해를 조금 쉽게 했으면 한다.

 

[그림] IAT 구조체의 연결 관계

 

즉 OriginalFirstThunk를 통해 함수 이름을 확인하고,
FirstThunk를 이용해
해당 함수를 주소를 확인하다는 것이 핵심이다. 아래 그림은 Kernel32.dll의 IMPORT Directory Table의 내용이다.

 

[그림] PEview로 확인한 kernel32.dll의 IAT 정보

 

EAT는 IAT와 다르게 여러 구조체가 아닌 하나의 구조체로 구성되어 있다.

여기서 EAT에 대해서는 자세히 언급하지는 않지만, 기본적으로 IAT와 크게 다르지 않으므로 IAT를 이해했다면, EAT를 이해하는 건 크게 어렵지 않을 것이다. 그럼 EAT의 구조체를 확인해 보자.

 

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;

출처 : Microsoft SDK WinNT.h

 

먼저 Name을 통해 해당 DLL의 이름을 얻는다, 그리고 NumberOfFunctions을 통해 제공하는 함수의 개수를 확인하고, NumberOfNames를 통해 이름있는 함수의 개수를 확인한다.

그후, AddressOfFunctions를 통해 함수의 주소 위치 값을 RVA(상대주소)로 확인하고, 해당 주소의 이름은 AddressOfNames를 통해 확인하게 된다. 마지막 AddressOfNameOrdinals은 제공함수들의 번호로 마지막 번호가 NumberOfNames와 같다.

 

위와 같은 IMAGE_EXPORT_DIRECTORY 구조는 아래 그림과 같이 자신이 제공할 수 있는 함수들의 위치를 표시한다.

 

[그림] EAT 구조

 

그럼 예제를 통해 확인해 보자

AddressOfNames를 통해 함수 이름을 확인하고, AddressOfFunctions를 통해
해당 함수를 주소를 확인한다는 것이 핵심이다.

 

[그림] PEview로 확인한 kernel32.dll의 EAT 정보

 

이제 PE Header를 통해 프로그램의 전체적인 정보를 확인할 수 있게 되었다. 이로써 프로그램 분석을 위한 기본적인 준비가 완료되었다. 여기서 확인한 내용들은 차후 파일 분석이 큰 도움이 될 것이다. 이제 실제 프로그램을 통해, 지금 배운 내용을 사용해 보도록 하자.

Facebook Comments

Leave A Reply

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.