IAT
IAT(Import Address Table)에는 Windows 운영체제의 핵심 개념인 process, memory, DLL 구조 등에 대한 내용이 함축되어 있다.
즉, 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블로 IAT를 잘 이해하면 Windows 운영체제의 근간을 이해할 수 있다.
우선 IAT를 알아보기 전에 DLL에 대해 먼저 알아보겠다.
DLL
DLL이란 Dynamic Linking library(동적 연결 라이브러리)의 약어로 windows에서 사용되는 공유 라이브러리이다.
DLL의 특징은 다음과 같다.
- 프로그램에 라이브러리를 포함시키지 않고 별도의 파일로 구성하여 필요할때마다 불러쓸 수 있다.
- 한번 로딩된 DLL의 코드, 리소스는 Memory Mapping 기술로 여러 process에서 공유해 쓸 수 있다.
- 라이브러리가 업데이트되었을 때 해당 DLL 파일만 교체할 수 있다.
실제 DLL 로딩 방식은 2가지인데, 다음과 같다.
- 프로그램에서 사용되는 순간에 로딩하고 사용이 끝나면 메모리에서 해제되는 방법(Explict Linking)
- 프로그램 시작할 때 같이 로딩되어 프로그램 종료할 때 메모리에서 해제되는 방법(Implict Linking)
IAT는 바로 Implict Linking에 대한 메커니즘을 제공하는 역할을 한다.
메커니즘의 확인을 위해 Ollydbg로 notepad.exe를 열어본다.
위 그림을 보면 CreateFileW를 호출할 때 직접 호출하지 않고 101A150 주소에 있는 값을 가져와서 호출한다.
이렇게 호출하는 이유는 Kernel32.dll의 버전이 틀려지고, CreateFileW 함수의 위치가 틀려지는 모든 환경에서
CreateFileW 함수 호출을 보장하기 위해서이다.
컴파일러는 CreateFileW 의 실제 주소가 저장될 위치를 준비하고 CALL DWORD PTR DS:[&KERNEL32.CreateFileW] 형식의 명령어를 적어두기만 한다.
또 다른 이유는 DLL Relocation 때문이다.
어떤 프로그램이 a.dll 과 b.dll 을 사용한다고 했을 때, PE Loader는 먼저 a.dll 을 ImageBase 값인 메모리 10000000h 에 로딩한다. (일반적인 DLL 파일의 ImageBase 값은 10000000h 이다.) 그 다음 b.dll 을 ImageBase 값인 메모리 10000000h 에 로딩하려고 봤더니, 이미 그 주소는 a.dll 이 사용하고 있었다.
그래서 PE Loader 는 다른 비어있는 메모리 공간을 찾아서 b.dll을 로딩시켜 준다.
이것이 DLL Relocation 이며 실제 주소를 하드코딩 할 수 없는 이유이다.
또한 PE Header 에서 주소를 나타낼 때 VA 를 쓰지 못하고 RVA 를 쓰는 이유이기도 하다.
IMAGE_IMPORT_DESCRIPTOR
PE 파일은 자신이 어떤 라이브러리를 임포트(Import) 하고 있는지 IMAGE_IMPORT_DESCRIPTOR 구조체에 명시하고 있다.
- Import : library에게 서비스(함수)를 제공받는 일
- Export : library 입장에서 다른 PE 파일에게 서비스를 제공하는 일
IMAGE_IMPORT_DESCRIPTOR 구조체는 다음과 같다.
출처 - k3y6reak's Blog
일반적인 프로그램에서는 보통 여러 개의 라이브러리를 임포트하기 때문에 라이브러리의 개수만큼 위 구조체의 배열 형식으로 존재하며, 구조체 배열의 마지막은 NULL 구조체로 끝나게 된다. IMAGE_IMPORT_DESCRIPTOR 구조체에서 중요한 멤버는 다음과 같다.
항목 | 의미 |
OriginalFirstThunk | INT(Import Name Table)의 주소(RVA) |
Name | Library 이름 문자열의 주소(RVA) |
FirstThunk | IAT(Import Address Table)의 주소(RVA) |
PE Loader가 Import 함수 주소를 IAT 에 입력하는 기본적인 순서를 정리해보면
- IID 의 Name 멤버를 읽어서 라이브러리의 이름 문자열("kernel32.dll")을 얻는다.
- 해당 라이브러리("kernel32.dll")를 로딩한다.
- IID 의 OriginalFirstThunk 멤버를 읽어서 INT 주소를 얻는다.
- INT 에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME 주소 (RVA)를 얻는다.
- IMAGE_IMPORT_BY_NAME 의 Hint(ordinal) 또는 Name 항목을 이용하여 해당 함수의 시작 주소를 얻는다.
- IID 의 FirstThunk(IAT) 멤버를 읽어서 IAT 주소를 얻는다.
- 해당 IAT 배열 값에 위에서 구한 함수 주소를 입력한다.
- INT 가 끝날때까지 (NULL 을 만날때까지) 위 4~7 과정을 반복한다
'컴퓨터와 보안 > 리버싱' 카테고리의 다른 글
PE File Format - pestudio (0) | 2021.04.02 |
---|---|
PE File Format4 (0) | 2021.04.01 |
PE File Format (0) | 2021.03.29 |
Hello World! 리버싱3 (0) | 2021.03.28 |
Hello world! 리버싱2 (0) | 2021.03.28 |