본문 바로가기

Security/Reversing

리버싱 핵심원리 13장(2)

13장(2) PE헤더의 핵심 IAT(Import Address Table), EAT(Export Address Table)

 

IAT(Import Address Table)에는 Windows 운영체제의 핵심 개념인 process, memory, DLL 구조 등에 대한 내용이 함축되어있음. IAT란 쉽게 말해 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블이다.

 

IAT(Import Address Table)

1. DLL(Dynamic Linked Library) 

DLL 로딩 방식

프로그램에서 사용되는 순간에 로딩하고 사용이 끝나면 메모리에서 해제하는 방법(Explicit Linking)

프로그램 시작과 함께 로딩되어 프로그램이 종료될 때 메모리에서 해제하는 방법(Implicit Linking)

IAT는 Implicit Linking에 대한 매커니즘을 제공하는 역할을 한다.

 

모든 API호출은 호출할 때 직접 호출하지 않고 지정된 주소에 있는 값을 가져와서 호출하는 방식이다. 여기에 명시되어 있는 주소의 값을 주소로 쓰는 것이 함수의 주소이다. 한번에 함수의 주소를 써주면 편할텐데 라는 의문이 들텐데 그건 앞에서 설명한 DOS시절의 방식이다.

프로그램의 제작자가 프로그램을 컴파일(생성)하는 순간엔 이 프로그램이 어떤 운영체제, 언어, Service Pack에서 실행될지 알 수 없다. 위에서 열거한 모든 환경에서 kernel32.dll의 버전이 달라지고 함수의 위치(주소)가 달라진다. 모든 환경에서 함수 호출을 보장하기 위해선 함수의 실제 주소가 저장될 위치를 준비하고 call dword ptr ds:[**] 형식의 명령어를 적어두기만 한다. 그러다 파일이 실행되는 순간 PE로더가 주소의 위치에 함수의 실제 주소를 입력해주는 방식이다.

컴파일러가 함수의 실제 주소를 쓰지 못하는 또 다른 이유는 DLL Relocation때문이다. 일반적인 DLL 파일의 ImageBase 값은 10000000이다. 예를 들어 어떤 프로그램이 a.dll과 b.dll을 사용한다고 했을 때 PE로더는 a.dll을 먼저 ImageBase값인 메모리 10000000에 로딩하고 b.dll을 ImageBase에 로딩하려고 봤더니 그 주소는 a.dll이 사용하고 있어 PE로더는 다른 비어있는 메모리 공간을 찾아서 b.dll을 로딩시켜 준다. 이것이 DLL Relocation이며 실제 주소를 하드 코딩할 수 없는 이유이다. 또한 PE헤더에서 주소를 나타낼 때 VA를 쓰지 못하고 RVA를 써야 하는 이유이기도 하다.

*DLL은 PE헤더에 명시된 ImageBase에 로딩된다고 보장할 수 없는데 반해 process 생성 주체가 되는 EXE 파일은 자신의 ImageBase에 정확히 로딩된다.(자신만의 가상 메모리 공간을 가지고 있기 때문)

 

2. IMAGE_IMPORT_DESCRIPTOR(IID = IMPORT Directory Table)

이 구조체는 PE파일 자신이 어떤 라이브러리(DLL)을 Import(제공)받고 있는지에 대한 정보를 담고 있는 구조체

- Import : PE입장에서 library에게 함수를 제공받는 일

- Export : library입장에서 다른 PE파일에게 함수를 제공하는 일

IMAGE_IMPORT_DESCRIPTOR 구조체

 

일반적인 프로그램에서는 여러 개의 라이브러리를 Import하기 때문에 라이브러리의 개수만큼 위 구조체의 배열 형식으로 존재하고 구조체 배열의 마지막은 NULL 구조체로 끝나게 됨.(Import하는 라이브러리의 개수보다 1개 더 많은 수로 배열로 존재)

 

2-1. Original First Thunk(Import Name Table RVA)

 INT(Import Name Table)의 주소(RVA)

 

2-2. Name RVA

 Library 이름 문자열의 주소(RVA)

 

2-3. First Thunk(Import Address Table RVA)

 IAT(Import Address Table)의 주소(RVA)

 

- PE헤더에서 Table은 배열을 뜻함.

- INT와 IAT는 long type(4바이트 자료형) 배열이고 NULL로 끝남.(크기가 명시되어 있지 않음)

- INT에서 각 원소의 값은 IMAGE_IMPORT_BY_NAME 구조체 포인터이다.(IAT도 같은 값을 가지는 경우도 있음)

- INT = IAT 이어야함

notepad.exe의 kernel32.dll에 대한 IID구조

 

 

PE로더가 Import함수 주소를 IAT에 입력하는 순서

 

 

EAT(Export Address Table)

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

EAT는 라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 사용할 수 있도록 해주는 핵심 매커니즘이다. 즉 EAT를 통해서만 해당 라이브러리에서 Export하는 함수의 시작 주소를 정확히 구할 수 있다. IAT처럼 PE파일 내의 특정 구조체(IMAGE_EXPORT_DIRECTORY)에 EXPORT정보를 저장하고 있다. 라이브러리의 EAT를 설명하는  IMAGE_EXPORT_DIERECTORY 구조체는 PE파일에 하나만 존재(PE파일은 여러개의 라이브러리를 동시에 Import할 수 있기 때문에 IAT는 여러개의 멤버를 가진 배열 형태로 존재함)

 

1. IMAGE_EXPORT_DIRECTORY(IED)

IMAGE_EXPORT_DIRECTORY 구조체

 

1-1. Number Of Functions

 실제 Export 함수 개수

 

1-2. Number Of Names

 Export 함수 중에서 이름을 가지는 함수 개수 (개수가 Number Of Functions 보다 작거나 같다)

 

1-3. Address Of Functions(Address Table RVA)

 Export 함수 주소 배열 (배열의 원소 개수는 Number Of Functions의 값과 같다)

 

1-4. Address Of Names(Name Pointer Table RVA)

 함수 이름 주소 배열 (배열의 원소 개수는 Number Of Names의 값과 같다)

 

1-5. Address Of Name Ordinals(Ordinal Table RVA)

 Ordinal 주소 배열 (배열의 원소 개수는 Number Of Names의 값과 같다)

 

kernel32.dll 파일의 IED 구조체와 EAT 전체 구조

 

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

GetProcAddress() 동작 원리

'Security > Reversing' 카테고리의 다른 글

리버싱 핵심원리 15장  (0) 2023.03.27
리버싱 핵심원리 14장  (0) 2023.03.27
리버싱 핵심원리 13장(1)  (0) 2023.03.27
리버싱 핵심원리 10장  (0) 2023.03.27
리버싱 핵심원리 8장  (0) 2023.03.27