알쓸전컴(알아두면 쓸모있는 전자 컴퓨터)
[reversing] EAT(Export Address Table) (기타 = 세션 찾기 64bit pe viwer) 본문
EAT (Export Address Table)
Windows 운영체제에서 라이브러리(Library) 란 다른 프로그램에서 불러 쓸 수 있도록
관련 함수들을 모아놓은 파일(DLL/SYS)입니다.
Win32 API 가 대표적인 Library 이며, 그 중에서도 kernel32.dll 파일이 가장 대표적인 Library 파일이라고 할 수 있습니다.
EAT(Export Address Table) 은 라이브러리 파일에서 제공하는 함수를
다른 프로그램에서 가져다 사용할 수 있도록 해주는 매커니즘 입니다.
앞서 설명드린 IAT 와 마찬가지로 PE 파일내에 특정 구조체(IMAGE_EXPORT_DIRECTORY)에 정보를 저장하고 있습니다.
라이브러리의 EAT 를 설명하는 IMAGE_EXPORT_DIRECTORY 구조체는 PE 파일에 하나만 존재합니다.
PE 파일내에서 IMAGE_EXPORT_DIRECTORY 구조체의 위치는 PE Header 에서 찾을 수 있습니다.
IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress 값이
실제 IMAGE_EXPORT_DIRECTORY 구조체 배열의 시작 주소 입니다. (RVA 값입니다.)
출처 : http://reversecore.com/24
저의 컴퓨터의 경우 kernel32.dll 가 64bit 라서
해당 프로그램 사용이 불가 합니다.
그래서 다른 view 프로그램을 받아서 사용 하였습니다.
http://www.codedebug.com/php/Products/Products_NikPEViewer_12v.php
해당 프로그램을 사용하였습니다.
제가 사용한 DLL 은
이거 입니다.
IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress 값을 직접 hax 값으로 자리수 맞춰가며 찾는
방법도 좋으나
Tool 을 이용하게 효율 적이라고 생각 하여 Tool을 사용하여 해당 값을 찾겠습니다.
NikPEViewer.exekernel32.dll 을 open 해서 보면
하지만 1번은 찾아 보겠습니다.
위와 같이 해당 부분 입니다.
0x0008AEE0 이라는 RVA(VirtualAddress) 값이 있고 이 값을 RAW 로 변환하면
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 NumberOfFunctions; // number of functions
DWORD NumberOfNames; // number of names
DWORD AddressOfFunctions; // address of function start addressarray
DWORD AddressOfNames; // address of functino name string array
DWORD AddressOfNameOrdinals; // address of ordinal array
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
* 출처 : Microsoft 의 Visual C++ 에서 제공하는 winnt.h
해당 구조체가 있는 곳이 나옵니다.
RAW 를 구하기 위해서 변환 하겠습니다.
먼저 공식은
RAW = RVA(목표) - VirtualAddress + PointerToRawData
여기서 필요한 값은 RVA = 0x0008AEE0
이고 VirtualAddress + PointerToRawData 은 세션이 어딘지 알아야 구할수 있습니다.
RVA = 0x0008AEE0 는 어디에 포함 되는 세션인지지 구해 보겠습니다.
먼저 세션 테이블을 보겠습니다.
먼저 text 세션은
virtual address = 0x00001000
sizeofrawdata = 0x00071200
이면
text 세션은
0x00001000 ~ (0x00001000+0x00071200 )
가 됩니다.
text 세션에는
0x0008AEE0 값이 포함 되지 않습니다.
그럼 다음
rdata 세션을 보겠습니다.
virtual address = 0x00073000
sizeofrawdata = 0x00030200
rdata 세션은
0x00073000 ~ (0x00073000+ 0x00030200 )
입니다
그러면
rdata 세션에는
0x0008AEE0 값이 포함 됩니다.
RAW = RVA(목표) - VirtualAddress + PointerToRawData
VirtualAddress + PointerToRawData 는 rdata 세션의 값을 사용 하면 됩니다.
이전에 엑셀로 만들어 놓은 곳에 값을 대입해 보겠습니다.
구분 | (16진수) | (10 진수) |
RVA | 08AEE0 | 569056 |
Sestion RVA | 073000 | 471040 |
point to RAW | 071600 | 464384 |
RAW | 894E0 | 562400 |
프로그램에서 EXPORT TABLE 의 offset 주소와 직접 계산한 주소가 일치하니 정확하게 계산 한것이 됩니다.
IMAGE_EXPORT_DIRECTORY 의 각 필드에 대해서 알아 보겠습니다.
NumberOfFunctions : 실제 export 함수 갯수
NumberOfNames : export 함수중에서 이름을 가지는 함수 갯수 (<= NumberOfFunctions)
AddressOfFunctions : export 함수들의 시작 위치 배열의 주소 (배열의 원소개수 = NumberOfFunctions)
AddressOfNames : 함수 이름 배열의 주소 (배열의 원소개수 = NumberOfNames)
AddressOfOrdinals : ordinal 배열의 주소 (배열의 원소개수 = NumberOfNames)
<Fig. EAT 구조>
라이브러리에서 함수 주소를 얻는 API 는 GetProcAddress() 입니다.
GetProcAddress() 함수가 함수 이름을 가지고 어떻게 함수 주소를 얻어내는 순서를 설명드리겠습니다.
2. "함수 이름 배열"은 문자열 주소가 저장되어 있습니다. 문자열 비교(strcmp)를 통하여 원하는 함수 이름을 찾습니다.
이 때의 배열 인덱스를 name_index 라고 하겠습니다.
3. AddressOfNameOrdinals 멤버를 이용해 "ordinal 배열" 로 갑니다.
4. "ordinal 배열" 에서 name_index 로 해당 ordinal_index 값을 찾습니다.
5. AddressOfFunctions 멤버를 이용해 "함수 주소 배열 - EAT" 로 갑니다.
6. "함수 주소 배열 - EAT" 에서 아까 구한 ordinal_index 를 배열 인덱스로 하여 원하는 함수의 시작 주소를 얻습니다.
위 <Fig. EAT 구조> 는 kernel32.dll 의 경우를 보여주고 있습니다.
kernel32.dll 은 export 하는 모든 함수에 이름이 존재하며,
AddressOfNameOrdinals 배열의 값이 index = ordinal 형태로 되어있습니다.
하지만 모든 DLL 파일이 이와 같지는 않습니다.
export 하는 함수 중에 이름이 존재하지 않을 수 도 있으며 (ordinal 로만 export 함)
AddressOfNameOrdinals 배열의 값이 index != ordinal 인 경우도 있습니다.
따라서 위 순서를 따라야만 정확한 함수 주소를 얻을 수 있습니다.
* 참고로 함수 이름 없이 ordinal 로만 export 된 함수의 주소를 찾을 수 도 있습니다.
출처 : http://reversecore.com/24
먼저 addressofname 을 찾아 보겠습니다.
IMAGE_EXPORT_DIRECTORY 값은 프로그램에 직접 파싱 해주지 않으니 직접 찾아야 겠네요
앞에서 말한
이부분의 RAW 쪽입니다.
즉 IMAGE_EXPORT_DIRECTORY 의 헥사 값이지요
이겨서
표시해 둔 부분은 중요하게 사용될
DWORD AddressOfFunctions; // address of function start addressarray
DWORD AddressOfNames; // address of functino name string array
DWORD AddressOfNameOrdinals; // address of ordinal array
AddressOfFunctions = 0x0008AF08
AddressOfNames = 0x0008C85C
AddressOfNameOrdinals = 0x0008E1B0
입니다.
먼저 0x0008C85C 을 RAW 로 바꾸면
구분 | (16진수) | (10 진수) |
RVA | 08C85C | 575580 |
Sestion RVA | 073000 | 471040 |
point to RAW | 071600 | 464384 |
RAW | 8AE5C | 568924 |
그럼 0x08AE5C 를 쫒아 가보겠습니다.
그럼 0x0008EE67 이라는 값이 나옵니다.
그럼 또 다시 RAW 를 변환 하겠습니다.
구분 | (16진수) | (10 진수) |
RVA | 08EE67 | 585319 |
Sestion RVA | 073000 | 471040 |
point to RAW | 071600 | 464384 |
RAW | 8D467 | 578663 |
다시 0x08D467 을 따라가 보겠습니다.
위와 같이 배열의 시작 입니다.
그중에 addAtomA 의 값을 알아보겠습니다.
에서 4바이트 씩 끊어서 정리 해보면
이렇게 되면 addAtomA 는 5번째에 존재 하게 됩니다.
그럼 이제 배열 시작으로 부터 5번째 인것을 알았으니
AddressOfNameOrdinals = 0x0008E1B0 로 가보겠습니다.
구분 | (16진수) | (10 진수) |
RVA | 08E1B0 | 582064 |
Sestion RVA | 073000 | 471040 |
point to RAW | 071600 | 464384 |
RAW | 8C7B0 | 575408 |
WORD 로 2바이트 씩 끊어 서 보면 0x0004이라는 값이 들어 있습니다.
그래서
AddressOfFunctions = 0x0008AF08 의 5번째 배열의 값을 찾아야 됩니다.
그것이 바로 addAtom 의 RAV 값 입니다.
구분 | (16진수) | (10 진수) |
RVA | 08AF08 | 569096 |
Sestion RVA | 073000 | 471040 |
point to RAW | 071600 | 464384 |
RAW | 89508 | 562440 |
0x00021D70 이 RVA 값이라고 나오 네요
그럼 프로그램에서는
값이 이상하네요 해당 프로그램 버그 인듯 합니다.
버그 때문에 기본 개념이 흔들릴뻔 했네요 .....
그래서 다른 PEViwer로 살펴 봤습니다.
해당 값이 맞네요 .
해당 프로그램은
https://www.safer-networking.org/products/filealyzer/ 에서 받아서 설치하여 사용 하였습니다.
'리버싱' 카테고리의 다른 글
ollydbg 대체용 프로그램 x64dbg 소개 (2) | 2018.05.26 |
---|---|
C# 리버싱을 위한 기초 공부 참고 자료 Inside C#_2E.pdf (3) | 2018.05.21 |
[reversing] IAT (0) | 2018.05.11 |
[reversing] RAV RAW 변환 실습 (0) | 2018.05.11 |
windows 10 에서 PE 헤더 Image Base 와 ollydebug (0) | 2018.05.08 |