PE 헤더를 처음 배울때 최대 난관은 IAT(Import Address Table)이다. IAT에는 Windows 운영체제의 핵심 개념인 process, memory, DLL 구조 등에 대한 내용이 함축되어 있다. 즉 IAT만 잘 이해해도 Windows 운영체제의 근간을 이해한다고 할 수있다. IAT란 쉽게 말해서 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블이다.
1.DLL
IAT를 설명하기 앞서 Windows OS의 근간을 이루는 DLL(Dynamic Linked Library) 개념을 짚고 넘어가야 한다. (DLL은 우리말로 '동적 연결 아리브러리'라고 한다.)
16비트 DOS 시절에는 DLL 개념이 없었다. 그냥 'Library'만 존재하였다. 예를 들면 C 언어에서 printf() 함수를 사용할 때 컴파일러는 C라이브러리에서 해당 함수의 binary 코드를 그대로 가져와서 프로그램에 삽입(포함)시켜 버렸다. 즉 실행 파일에 printf() 함수의 바이너리 코드를 가지고 있다는 것이다. Windows OS에서는 멀티 태스킹(Multi-Tasking)을 지원하기 때문에 라이브러리 포함 방식이 비효율적이 되어버렸습니다. 32비트 Windoes 환경을 제대로 지원하기 위해 기본적으로 매우 많은 라이브러리 함수(process, memory, window, message 등)를 사용해야 한다. 여러 프로그램이 동시에 실행되어야 하는 상황에서 모든 프로그램마다 위와 같이 동일한 라이브러리가 포함되어서 실행된다면 심각한 메모리 낭비를 불러온다. 그래서 Windows OS 설계자들은 (필요에 의해)
아래와 같은 DLL 개념을 고안해냈다.
- 프로그램에 라이브러리를 포함시키지 말고 별도의 파일(DLL)로 구성하여 필요할 때마다 불러쓰자.
- 일단 한 번 로딩된 DLL의 코드, 리소스는 Memory Mapping 기술로 여러 process에서 공유해 쓰자.
- 라이브러리가 업데이트 되었을 때 해당 DLL 파일만 교체하면 되니 쉽고 편해서 좋다.
실제 DLL 로딩 방식은 2가지이다. 프로그램에서 사용되는 순간에 로딩하고 사용이 끝나면 메모리에서 해제되는 방법(Explicit Linking)과 프로그램 시작할 때 같이 로딩되어 프로그램 종료할 때 메모리에서 해제되는 방법(Implicit Linking)이 있다. IAT는 바로 Implicit Linking에 대한 메커니즘을 제공하는 역할을 한다. IAT의 확인을 위해 OllyDbg로 notepad.exe를 열어보자.
Kernel32.dll의 CreateFileW를 호출하는 코드이다. CreateFileW를 호출할 때 직접 호출 하지 않고 01001104 주소에 있는 값을 가져와서 호출한다.(모든 API 호출은 이런 방식으로 되어있다.)
01001104주소는 notepad.exe에서 '.text' 섹션의 메모리 영역이다.(더 자세히는 IAT 메모리 영역이다.) 01001104 주소의 값은 74D7DDE0이며 74D7DDE0 주소가 바로 notepad.exe 프로세스 메모리에 로딩된 kernel32.dll의 CreateFileW 함수 주소이다. 여기서 그러면 한가지 의문이 생긴다.
CALL 74D7DDE0이라고 하는게 더 편하지 않은가?
컴파일러가 CALL 74D7DDE0라고 정확히 써줬다면 더 좋지 않냐는 의문이 들 수 있다만 그건 바로 앞에 설명했던 DOS 시절 방식이다.
Notepad.exe 제작자가 프로그램을 컴파일(생성)하는 순간 이 notepad.exe 프로그램이 어떤 Windows 어떤 언어, 어떤 Service Pack에서 실행될지 알 수 없다. 위에서 열거한 모든 환경에서 kernel32.dll의 버전이 달라지고 CreateFileW 함수의 위치(주소)가 달라진다. 모든 환경에서 CreateFileW 함수 호출을 보장하기 위해서 컴파일러는 CreateFileW의 실제 주소가 저장될 위치(01001104)를 준비하고 CALL DWORD PTR DS:[1001104] 형식의 명령어를 적어두기만 한다. 그러다 파일이 실행되는 순갇 PE 로더가 01001104의 위치에 CreateFileW의 주소를 입력해준다.
컴파일러가 CALL 74D7DDE0라고 쓰지 못하는 또 다른 이유는 DLL Relocation때문이다. 일반적으로 DLL 파일의 ImageBase 값은 10000000이다. 예를 들어 어떤 프로그램이 a.dll과 b.dll을 사용한다고 했을때, PE로더는 먼저 a.dll을 ImageBase 값인 메모리 100000000에 잘 로딩한다. 그 다음 b.dll을 ImageBase에 로딩하려고 했더니 이미 그 주소에는 a.dll이 사용하고 있다. 그래서 PE로더는 다른 비어있는 메모리 공간을 찾아서 b.dll을 로딩시켜 준다.
이것이 DLL Relocation이며 실제 주소를 하드코딩을 할 수 없는 이유이다. 또한 PE 헤더에서 주소를 나타낼 때 VA를 쓰지 못하고 RVA를 써야하는 이유이기도 하다.
--------------------------------------------------------------------------------------------------------------------------
DLL은 PE헤더에 명시된 ImageBase에 로딩된다고 보장할 수 없다. 반면 process 생성 주체가 되는 EXE 파일은 자신의 ImageBase에 정확히 로딩된다.(자신만의 가상 메모리 공간을 가지기 때문.)
'리버스 엔지니어링' 카테고리의 다른 글
Lenas_Reversing_for_Newbies를 크랙해보자 (0) | 2018.05.06 |
---|---|
IAT란? -2 IMAGE_IMPORT_DESCRIPTOR (0) | 2018.01.19 |
RVA to RAW (0) | 2018.01.18 |
섹션 헤더 (0) | 2018.01.16 |
NT 헤더 -2 (0) | 2018.01.16 |