흰싸라기 2022. 3. 24. 23:08

 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

 

 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

  • 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)