4-1공부/리버스엔지니어링

[기말고사] 10-11. PE & IAT

KGW2027 2023. 6. 18. 04:46
728x90
반응형

 PEPortable Executable의 약어로 exe, dll 확장자를 가진 윈도우의 실행 파일의 형식이다.
PE에는 메모리맵 구조, IAT, EAT, 프로그램 코드 등의 정보가 포함되어 있으며, PE 포맷을 가진 프로그램을 실행하면 이에 맞게 메모리를 로드하게 된다. Windows에서 실행되는 대부분의 파일은 PE 포맷으로 작성된다.

  PE 파일을 실행할 때, 파일 저장장치의 블록 크기와 메모리 페이지 크기의 차이로 인해, 메모리에 로드하는 과정에 기존보다 더 큰 크기의 공간을 차지하게 되는데, 이 때 오프셋을 유지하기 위해 Alignment를 사용한다. 이로 인해 PE 파일 내에는 'PE 파일에서 사용하는 오프셋'과 '메모리에서 사용하는 오프셋'에 대한 두 개의 Alignment 정보가 포함되어 있고, 이러한 정보는 RAW(PE 파일 기반 포인터)RVA(상대 가상 주소, 메모리)로 나눠진다.

 기초적인 지식을 살펴보았으니, PE 헤더를 살펴보자.
PE 헤더는 크게 DOS 헤더, NT 헤더, 섹션 헤더의 3개로 나눠진다.

 DOS 헤더는 DOS에서 프로그램을 실행할 때 사용하는 것으로, 반드시 시작에 4D 5A라는 시그니처가 있어야 한다. 이 시그니처가 없으면 DOS 헤더로 인식하지 않으며, 작동에 실패한다. DOS 헤더는 64바이트의 크기를 가지며, 마지막 4바이트에 NT 헤더의 위치 정보를 가진다. 그 외에는 DOS 환경에서 프로그램을 실행할 일은 거의 없으므로 생략한다.

 NT 헤더50 45 00 00라는 4바이트의 PE 그니처를 가진다. 바로 뒤의 20바이트파일 헤더 속성인데, 앞의 4바이트와 뒤의 4바이트의 값이 중요하다. 먼저, 앞의 4바이트는 Machine(목표 아키텍처)NumberOfSections(PE 파일 섹션 수)를 나타낸다. 일반적으로 Machine의 값은 64 86 (0x8664)x64 아키텍처를 목표로 하며, 섹션 수는 섹션 수다. 뒤의 4바이트는 옵션 헤더의 크기와 속성에 대한 비트 플래그를 각각 2바이트씩 나타내는데, 크게 중요한 정보는 아니다.
 그 다음으로 이어지는 것은 옵션 헤더다. 가장 처음의 2바이트Magic으로 PE 파일의 버전을 의미한다. 16바이트 오프셋부터 4바이트AddressOfEntryPoint로 코드 시작 위치를 나타낸다. 32바이트 오프셋부터 4바이트는 Section Alignment, 그 뒤의 4바이트 File Alignment로 각각 메모리와 파일의 블록 크기를 나타낸다. 일반적으로 각각 00 10 00 00(0x00001000), 00 02 00 00(0x00000200) 으로, 4096바이트, 512바이트이다. 112바이트 오프셋(7줄 아래)부터는 DataDirectory로 중요한 데이터 구조를 나타내는데, 앞의 16바이트(4x4)Export RVA, Export Size, Import RVA, Import Size가 중요하다.

 옵션 헤더가 끝나면 섹션 정보가 나온다. 섹션 정보는 NumberOfSections * 40바이트의 크기를 갖는다. 구조는 Name(8), V_Size(4), VA(4), R_Size(4), RAW(4), (12), Characteristics(4)이다. 각 섹션은 PE 파일에서는 RAW ~ RAW + R_Size의 공간을 가지고, 메모리에서는 시작주소+VA ~ 시작주소+VA+V_Size의 공간을 가진다. 이 때, 양쪽에서 사용하는 공간은 Section Alignment와 File Alignment 값에 따라 올림된 크기를 가진다.
예를 들어, VA가 0x00001000이고 V_Size가 0x00013A80이라고 해보자. 점유 시작한 주소가 0이라고 하면 0x00001000 ~ 0x00014A80까지를 점유할 것이다. 하지만 Section Alignment가 0x1000이므로, 0x14A80을 올림한 0x15000까지 점유하게된다.

  이러한 값들을 이용하여, 메모리에서 코드가 위치한 주소를 이용해 PE에서 해당 코드를 찾거나 그 반대도 가능하다.
먼저, '현재 코드 주소 - 현재 섹션 시작 주소'가 해당 섹션에서 코드 위치의 오프셋이 된다. 그리고 '반대쪽 세션 시작 주소 + 코드 오프셋'을 하면 그 위치를 찾을 수 있다.

 프로그램은 일반적으로, 옵션 헤더에 존재하는 ImageBase의 위치에 우선적으로 할당되고자 한다. 그러나, ASLR(Address Space Layout Randomization)을 이용하면 보안을 위해 ImageBase와 다른 랜덤한 위치에 프로그램을 로드할 수 있다. ASLR이 활성화되어 있다면, 프로그램이 메모리 어느 위치에 로드될 지 예상할 수 없으므로, Buffer-Overflow나 ROP 공격을 막는데 효과적이다.

IAT, EAT의 주소는 RVA로 주어지므로, 0x42A104의 섹션을 찾아야한다. RVA로 RAW를 구하는 식은 RVA - VA + PtrToRawData인데, 섹션의 메모리를 보면 0x1000 ~ 0x3350AC가 텍스트, 0x336000 ~ 0x42DCF2가 rdata이다. 그러면 RAW는 0x42A104 - 0x336000 + 0x334600 이므로 0x428704(RAW)에 IAT가 위치한다.

 

 DLL이란 Dynamically Linked Libraries의 약어로 다양한 실행 프로그램이 공유하여 사용하는 라이브러리 파일이다. 주요 장점은 코드의 재사용성을 올려준다는 것이다. DLL을 불러오는 방식은 프로그램 실행 중 불러오고 해제하는 동적 링크(Dynamic Linking)와 PE파일에 불러오기 위한 정보가 포함된 정적 링크(Static Linking)로 구분된다.

 IAT란 PE를 실행할 때 어떤 DLL을 불러와야하는지를 저장하는 테이블로 Import Address Table의 약어다. 이 정보는 옵션 헤더의 마지막인 DataDirectory의 8바이트 오프셋에 위치한다. Import RVA와 Import Size가 있는데, 이 데이터를 이용해 PE 파일 내에의 어디에 로드할 DLL의 정보들이 있는지 알 수 있다. 해당 위치로 이동하면 Image Import Descriptor 타입의 정보가 있는데, 여기서 중요한 것은 초기 4바이트와 마지막 8바이트다.

 IAT에서 DLL를 로드하는 과정은 아래와 같다.
 1. 주소(마지막 8바이트중 앞 4바이트가 주소 문자열의 포인터)를 통해 DLL를 로드한다.
 2. 초기 4바이트에서 가리키는 ILT(Import Lookup Table)를 통해 DLL에서 로드할 함수 목록을 읽는다.
 3. 각 함수의 포인터를 마지막 4바이트에서 가리키는 IAT(Import Address Table)에 채운다.

 

EAT는 시험 범위가 아니므로 생략.

728x90
반응형

'4-1공부 > 리버스엔지니어링' 카테고리의 다른 글

[기말고사] 12. DLL & DLL Injection  (1) 2023.06.18
[기말고사] 9. 호출 규약  (0) 2023.06.18
[기말고사] 7. 제어문  (0) 2023.06.18
[중간고사] asm1  (0) 2023.04.16