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

2부. 13장 PE File Format(3)

by HHack 2025. 1. 21.
반응형

13.6 EAT(Export Address Table)

  라이브러리란 다른 프로그램에서 불러 쓸 수 있도록 관련 함수들을 모아놓은 파일(DLL/SYS)입니다. Win32 API가 대표적인 라이브러리이며, 그 중에서도 kernel32.dll 파일이 가장 핵심적인 라이브러리 입니다.

  EAT(Export Address Table)은 라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 사용할 수 있도록 해주는 핵심 메커니즘 입니다. 즉, EAT를 통해서만 해당 라이브러리에서 익스포트하는 함수의 시작 주소를 정확히 구할 수 있습니다. IAT와 마찬가지로 PE 파일 내의 특정 구조체(IMAGE_EXPORT_DIRECTORY)에 익스포트 정보를 저장하고 있습니다. IMAGE_EXPORT_DIRECTORY 구조체는 PE파일에 하나만 존재합니다.

  참고로 IAT를 설명하는 IMAGE_IMPORT_DECRIPTOR 구조체는 여러 개의 멤버를 가진 배열 형태로 존재합니다. 왜냐하면 PE파일은 여러 개의 라이브러리를 동시에 임포트 할 수 있기 때문입니다.

kernel32.dll의 Hex값

PE 파일에서 IMAGE_EXPORT_DIRECTORY 구조체 위치는 PE 헤더에서 찾을 수 있습니다. IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress 값이 실제 IMAGE_EXPORT_DIRECTORY 구조체 배열의 시작 주소 입니다.

그림 13.20 IMAGE_OPTIONAL_HEADER32.DataDirectory[0]

Offset 데이터 설명
0x0168 2C 26 00 00 VirtualAddress (RVA)
0x016C FD 6C 00 00 Size (테이블 크기)

1. VirtualAddress (RVA)

  • 값: 0x0026262C (리틀 엔디언 방식이므로 0x262C로 해석)
  • 설명: Export Table이 메모리에서 RVA 0x262C 위치에 로드됩니다.
  • RVA 값이 262C이므로 FileOffset은 1A2C입니다.

2. Size

  • 값: 0x00006CFD (리틀 엔디언 방식이므로 **0x6CFD**로 해석)
  • 설명: Export Table의 크기는 0x6CFD 바이트입니다.

13.6.1 IMAGE_EXPORT_DIRECTORY

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD Characteristics;          // 예약된 필드 (일반적으로 0)
    DWORD TimeDateStamp;            // 생성 시간 (UNIX 타임스탬프)
    WORD  MajorVersion;             // 주요 버전 (일반적으로 0)
    WORD  MinorVersion;             // 부 버전 (일반적으로 0)
    DWORD Name;                     // DLL 이름의 RVA
    DWORD Base;                     // Ordinal의 시작 번호
    DWORD NumberOfFunctions;        // ★실제 Export 함수 개수
    DWORD NumberOfNames;            // ★Export 함수 중 이름을 가지는 함수 개수
    DWORD AddressOfFunctions;       // ★Export 함수 주소 배열(배열의 원소 개수 = NOF)
    DWORD AddressOfNames;           // ★함수 이름 주소 배열(배열의 원소 개수 = NON)
    DWORD AddressOfNameOrdinals;    // ★Ordinal 테이블의 RVA(배열의 원소 개수 = NON)
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

라이브러리에서 함수 주소를 얻는 API는 GetProcAddress()입니다. 이 API가 바로 EAT를 참조해서 원하는 API 주소를 구하는 것입니다.

예시: "MessageBoxA"를 검색하는 과정

GetProcAddress()를 사용해 DLL에서 "MessageBoxA()"의 주소를 찾는 과정을 설명하겠습니다. kernel32.dll을 예로 들겠습니다.

FARPROC GetProcAddress(
    HMODULE hModule,    // DLL의 모듈 핸들
    LPCSTR  lpProcName  // 함수 이름 또는 Ordinal
);

과정 설명

  1. DLL 로드 및 Export Table 접근
    • hModule이 kernel32.dll의 메모리 베이스 주소를 가리킨다고 가정.
    • LoadLibrary("kernel32.dll")로 kernel32.dll을 메모리에 로드하고, 베이스 주소(Base Address)를 가져옵니다.
    • Export Table의 시작 위치를 찾습니다:
      • PE HeaderOptional Header → Data Directory[0]를 따라가면 Export Directory Table에 접근할 수 있습니다.
  2. AddressOfNames 배열로 이동
    • AddressOfNames는 Export Name Table의 시작 위치를 나타냅니다.
    • 이 배열에는 함수 이름(예: "MessageBoxA") 문자열들의 RVA 목록이 들어 있습니다.
  3. 함수 이름 비교
    • "lpProcName**이 문자열인 경우
      1. Export Name Table에서 각 이름의 RVA를 통해 문자열을 확인합니다.
      2. strcmp("MessageBoxA", ExportName[i])로 원하는 이름을 찾습니다.(이진 탐색 사용)
      3. 찾은 이름의 인덱스(name_index)를 저장합니다.
  4. Ordinal 검색
    • "lpProcName"이 숫자(Ordinal)로 전달된 경우:
      1. Export Directory Table의 Base 필드를 사용하여 실제 Ordinal 번호를 계산.
      2. 계산된 Ordinal 번호를 EAT의 인덱스로 사용.
      3. EAT에서 함수의 RVA를 가져옵니다.
  5. AddressOfNameOrdinals 배열로 이동
    • AddressOfNameOrdinals는 함수 이름과 Export Address Table (EAT)의 인덱스를 연결합니다.
    • name_index를 사용해 Ordinal 값을 가져옵니다.
  6. Export Address Table 접근
    • AddressOfFunctions는 EAT(Export Address Table)의 시작 위치를 나타냅니다.
    • Ordinal 값을 사용해 EAT에서 해당 함수의 RVA를 가져옵니다.
  7. RVA를 실제 주소로 변환
    • DLL의 Base Address와 RVA를 더해 MessageBoxA 함수의 실제 메모리 주소를 계산합니다.
  8. 주소 반환
    • 계산된 MessageBoxA()의 메모리 주소를 반환합니다.

그림 13.21 EAT

13.6.2 kernel32.dll를 이용한 실습

실습 내용은 따로 포스팅하여 실습해보도록 하겠습니다.

13.7 Advanced PE

13.7.1 PEView.exe

13.7.2 Patched PE

13.8 마무리

 

반응형