minhui study

32bit로 IAT, EAT 로딩 과정 본문

Hacking/Reversing

32bit로 IAT, EAT 로딩 과정

minhui 2020. 10. 12. 04:53

IAT(Import Address Table)

프로그램에서 어떤 라이브러리의 어떤 함수를 참조하고있는지 가르쳐주는 테이블

 

IMAGE_IMPORT_DESCRIPTOR

PE파일은 자신이 어떤 라이브러리를 임포트하고 있는지 IMAGE_IMPORT_DESCRIPTOR구조체에 명시하고 있다.

 

< IAT 로딩 과정 예시 >

1. IID Name 멤버를 읽어서 라이브러리의 이름 문자열을 얻는다
2. 해당 라이브러리를 로딩한다.
→ LoadLibrary(“ADVAPI32.dll”)
3. IID OriginalFirstThunk 멤버를 읽어서 INT 주소를 얻는다.
4. INT에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME주소(RVA)를 얻는다.
5. IMAGE_IMPORT_BY_NAME Hint or Name의 이용하여 해당 함수의 시작 주소를 얻는다.
 → GetProcAddress(“OpenProcessToken”)
6.IID FirstThunk 멤버를 읽어서 IAT의 주소를 얻는다.
7.해당 IAT 배열 값에 위에서 구한 함수 주소를 입력한다.
8. INT가 끝날 때까지 (NULL을 만날 때까지) 4~7의 과정을 반복한다

 

 

notepad.exe를 이용한 실습

실제 IMAGE_IMPORT_DESCRIPTOR구조체 배열은 PE파일의 어느 곳에 존재할까? 

PE바디에 위치하는데 그곳을 찾아가기 위한 정보는 PE헤더에 있다. IMAGE_IMPORT_DESCRIPTOR 구조체 배열 정보는 다음과 같다.

RVA가 234B8이므로 Import Directory 시작 offset은 다음과 같다.

* RAW = RVA - VirtualAddress + PointerToRawData

VA 프로세스 가상 메모리의 절대주소
RVA ImageBase로부터의 상대주소(메모리에 로딩된 상태)
RAW PE 파일이 로딩되기 전에 FileOffset

RAW = 234B8 - 23000 + 20800 = 20CB8 -> Import Directory 시작 offset

20CB8 + 370 = 21028 -> IMAGE_IMPORT_DESCRIPTOR 끝나는 부분

 

File Offset Member RVA RAW
20FD8 OriginalFirstThunk(INT) 00023828 00021028
20FDC TimeDateStamp 00 00 00 00 -
20FE0 ForwarderChain 00 00 00 00 -
20FE4 Name 0002539E 00022B9E
20FE8 FirstThunk(IAT) 00023000 00020800

 

(1) 라이브러리 이름(Name)

Name 항목은 임포트 함수가 소속된 라이브러리 파일의 이름 문자열 포인터이다. 

RVA:2539E -> RAW:22B9E 

file offset 22B9E 에 "COMCTL32.dll"문자열이 있는 것을 확인할 수 있다.

 

(2) OriginalFirstThunk(INT)_Import Name Table

INT는 임포트하는 함수의 정보가 담긴 구조체 포이터 배열이다. 이 정보를 얻어야 프로세스 메모리에 로딩된 라이브러리에서 해당 함수의 시작 주소를 정확히 구할 수 있다. 

RVA:23828 -> RAW:21028

INT는 주소 배열 형태로 되어 있고 주소 값 하나하나가 각각의 IMAGE_IMPORT_BY_NAME구조체를 가리키고 있다. 배열의 첫번 째 값인 25388(RVA)를 따라가 보자 

 

 

(3) IMAGE_IMPORT_BY_NAME

RVA:25388 -> RAW:22B88

file offset 22B88의 최초 2바이트 값 (000C)는 라이브러리에서 함수의 고유번호이고, 그 뒤로 CreateStatusWindowW함수 이름 문자열이 보인다. (문자열의 마지막은 NULL로 끝난다.)

정리하자면 INT는 IMAGE_IMPORT_BY_NAME구조체 포인터 배열이라고 할 수 있다. 배열의 첫번째 원소가 가리키는 함수의 고유번호는 000C이고 함수의 이름은 CreateStatusWindowW이다.

 

 

(4) FirstThunk - IAT(Import Address Table)

RVA:23000 -> RAW:20800

INT와 마찬가지로 구조체 포인터 배열 형태로 되어 있다. IAT의 첫번 째 원소 값은 025388로 notepad.exe파일이 메모리에 로딩될 때 이 값은 정확한 주소 값으로 대체된다.

 

 

 

 


EAT(Export Address Table)

프로그램에서 어떤 라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 사용할 수 있도록 해주는 핵심 메커니즘이다. 즉 EAT를 통해서만 해당 라이브러리에서 익스포트하는 함수의 시작 주소를 정확히 구할 수 있다.

 

- IAT와 마찬가지로 PE 파일 내의 특정 구조체 IMAGE_EXPORT_DIRECTORY에 익스포트 정보를 저장하고 있으며 라이브러리의 EAT를 설명하는 이 구조체는 PE파일에 하나만 존재한다.

 

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

 

< IMAGE_EXPORT_DIRECTORY >

항목 의미
NumberOfFunctions 실제 Export 함수 개수
NumberOfNames Export 함수 중에서 이름을 가지는 함수 개수
AddressOfFunctions Export 함수 주소 배열 (배열의 원소 개수 = NumberOfFunctions)
AddressofNames 함수 이름 주소 배열 (배열의 원소 개수 = NumberOfNames)
AddressOfNameOrdinals Ordinal 배열 (배열의 원소 개수 = NumberOfNames)

 

아래 사진은 kernel32.dll 파일의 IMAGE_EXPORT_DIRECTORY 구조체와 EAT 전체 구조를 나타내고 있다.

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

 

https://reversecore.com/tag/IMAGE_EXPORT_DIRECTORY

 

< EAT 로딩 과정 >

1. AddressOfNames 멤버를 이용해 '함수 이름 배열'로 이동
2. '함수 이름 배열'은 문자열 주소가 있으므로, 문자열 비교를 통하여 원하는 함수 이름을 찾는다.(name_index)
3. AddrssOfNameOrdinals멤버를 이용하여 'ordinal' 배열로 이동
4. 'ordinal 배열'에서 name_index를 이용하여 해당 ordinal 값을 찾는다.
5. AddrssOfFunctions 멤버를 이용하여 함수 주소 배열(EAT)로 이동
6. EAT에서 ordinal을 배열 인덱스로 하여 원하는 함수의 시작주소를 얻는다.

* RAW = RVA - VirtualAddress + PointerToRawData

 IMAGE_EXPORT_DIRECTORY 의 위치를 찾아보자. 

위에서 보면 알다시피 RVA가 92250이므로 Export Directory의 시작 offset은 다음과 같다.

RAW = 92250 - 80000 + 65000 = 77250 -> Export Directory 시작 offset

77250 + DA48 = 84C98 -> IMAGE_EXPORT_DIRECTORY 끝나는 부분

93B84

Export Directory 시작 offset으로 이동하면 다음과 같다. 네모쳐진 부분은 순서대로 Characteristics, TimeDateStamp, MajorVersion, MinorVersion, Name, Base, NumberOfFunctions, NumberOfNames, AddressOfFunctions, AddressOfNames,AddressOfNameOrdinals이다.

 

표로 정리해보면 다음과 같다.

 

File Offset Member Value RAW
00077250 Characteristics 00000000 -
00077254 TimeDateStamp 59B71F87 -
00077258 MajorVersion 0000 -
0007725A MinorVersion 0000 -
0007725C Name 00096116  
00077260 Base 00000001 -
00077264 NumberOfFunctions 00000643 -
00077268 NumberOfNames 00000643 -
0007726C AddressOfFunctions 00092278 77278
00077270 AddressOfNames 00093B84 78B84
00077274 AddressOfNameOrdinals 00095490 7A490

 

(1) 함수 이름 배열

AddressOfNames 멤버의 값은 RVA = 93B84 이고 RAW = 93B84 - 80000 + 65000 = 78B84이다.

RVA:93B84 -> RAW:78B84

file offset 78B84 에 가보자. 이곳은 4바이트의 RVA로 이루어진 배열로 배열의 원소 개수는 NumberOfNames(643)이다. 모든 RVA값을 하나하나 따라가면 함수 이름 문자열이 나타난다.

 

(2) 원하는 함수 이름 찾기

첫 번째 함수를 찾아보자 RAW = 96182 - 80000 + 65000 7B182

RVA:96182 -> RAW:7B182

해당 오프셋으로 가면 AcquireSRWLockExclusive 함수 이름의 문자열을 확인할 수 있다. 이 함수 이름은 배열의 첫번째 원소이고 배열 인덱스로는 0이다.

 

(3) Ordinal 배열

이제 AcquireSRWLockExclusive 함수의 Ordinal 값을 알아낼 차례이다. AddressOfNameOrdinals 멤버의 값은 

RAW = 95490 - 80000 + 65000 7A490 이다.

RVA:95490 -> RAW:7A490

해당 오프셋으로 이동해보면 다음과 같이 2바이트의 ordinal로 이루어진 배열이 나타난다.

AcquireSRWLockExclusive 함수AddressOfNameOrdinals[0]이므로 ordinal(0)은 다음과 같다.

* index = 0, Ordinal = 3

 

(5) 함수 주소 배열 - EAT

이제 마지막으로 AcquireSRWLockExclusive의 실제 함수 주소를 찾아가보자.

AddressOfFunctions 멤버의 값은 RAW = 92278 - 80000 + 65000 77278이다.

RVA:92278 -> RAW:77278

해당 오프셋으로 이동해보면 다음과 같이 4바이트의 함수 주소 RVA 배열이 나타난다. 이게 바로 Export 함수 주소들이다.

그리고 위에서 AcquireSRWLockExclusive는 ordinal이 3이었으므로 해당 함수의 주소의 RVA는 9619A가 된다.

* AddressOfFunctions[3] = 9619A

 

(6) AcquireSRWLockExclusive함수의 주소

VA(함수의 실제 주소)는 ImageBase와 RVA를 더한 값이므로 다음과 같다.

6B800000 + 9619A = 6B89619A

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

패킹 & UPX ( 툴없이 언패킹하기 )  (0) 2020.10.27
Wargame 'Dreamhack' rev-5  (0) 2020.10.01
Wargame 'Dreamhack' rev-4  (0) 2020.10.01
Wargame 'Dreamhack' rev-3  (0) 2020.09.19
WOW64 file system redirection  (0) 2020.09.17
Comments