앙 귬모띠~ 

 

 

PE File(2)

어제에 이어서 PE File 공부를 마저해보자!

어제는 NT Header에 대해서 안에 멤버들을 들여보다가 Image File Header 까지 봤었다.

오늘은 Image Optional Header로 가자

 

우선 까먹었을것 같으니 

가즈아

 

어제껄 복습한다.

다음의 블록이 NT Heaer - image file header 부분

 

offset		value		desc
---------------------------------------
000000DC	014c		machine
000000DE	0004		numeber of sections
000000E0	559EA6FF	time date stamp
000000E4	00000000	offset to symbol table
000000E8	00000000	number of symbols
000000EC	00E0		sie of optional header
000000EE	0102		characteristic
						IMAGE_FILE_EXECUTABLE_IMAGE
						IMAGE_FILE_32BIT_MACHINE

view 확인결과 실제와 동일 ㅋ

NT Header - image file header까지 보았다.

이제 구조체중에 가장 큰 NT Header - Optional Header를 살펴보자.

 

[IMAGE_OPTIONAL_HEADER32]

typedef struct _IMAGE_OPTIONAL_HEADER {  
 
   /* Standard fields */
   WORD  Magic; /* 0x10b or 0x107 */ /* 0x00 */
   BYTE  MajorLinkerVersion;
   BYTE  MinorLinkerVersion;
   DWORD SizeOfCode;
   DWORD SizeOfInitializedData;
   DWORD SizeOfUninitializedData;
   DWORD AddressOfEntryPoint;        /* 0x10 */
   DWORD BaseOfCode;
   DWORD BaseOfData;
 
   /* NT additional fields */
   DWORD ImageBase;
   DWORD SectionAlignment;       /* 0x20 */
   DWORD FileAlignment;
   WORD  MajorOperatingSystemVersion;
   WORD  MinorOperatingSystemVersion;
   WORD  MajorImageVersion;
   WORD  MinorImageVersion;
   WORD  MajorSubsystemVersion;      /* 0x30 */
   WORD  MinorSubsystemVersion;
   DWORD Win32VersionValue;
   DWORD SizeOfImage;
   DWORD SizeOfHeaders;
   DWORD CheckSum;           /* 0x40 */
   WORD  Subsystem;
   WORD  DllCharacteristics;
   DWORD SizeOfStackReserve;
   DWORD SizeOfStackCommit;
   DWORD SizeOfHeapReserve;      /* 0x50 */
   DWORD SizeOfHeapCommit;
   DWORD LoaderFlags;
   DWORD NumberOfRvaAndSizes;
   IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */
 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

그렇다면 몇가지 중요한(?) 멤버들에 대해서 살펴봅시다.

 

#1. Magic

Magic은 32비트인지 64비트인지 구분할 때 쓰인다고 한다

 

- IMAGE_OPTIONAL_HEADER32 : 10B

- IMAGE_OPTIONAL_HEADER64 : 20B

 

#2. AddressOfEntryPoint

AddressOfEntryPoint는 EP의 RVA 값을 가지고 있습니다.

최초로 프로그램이 실행되는 코드의 시작주소로 ★매우 중요한 값★을 가지고 있음.

 

#3. ImageBase('중요')

메모리에서 PE파일이 어디에 로딩이 되는지 메모리에 로딩되는 시작 주소를 알려주는 부분.

exe, dll은 user memory 영역인 0~7FFFFFFF 범위에 로딩되어지며, sys 파일은 kernal memory 영역인 80000000~FFFFFFFF에 로딩되어진다고 합니다.

- 일반적으로 개발도구들이 만들어내는 .exe은 00400000이고, .dll은 100000000 입니다.

 

#4. SectionAlignment, FileAlignment

PE file의 Body는 섹션으로 나뉘어져 있다. file에서의 섹션의 최소단위가 filealignment이고 메모리에서의 섹션의 최소단위가 sectionalignment이다. 이 둘은 같을 수도 다를수도 있습니다.(읍읍;;) 파일/메모리의 섹션 크기는 반드시 sectionalignment / filealignment의 배수가 되어야 합니다.

 

#5. SizeOfImage

SizeofImage는 PE가 메모리에 로딩되었을 때에 가상메모리에서 PE image가 차지하는 크기를 말합니다.

실행되기 전과 후가 다르다고 하네요!

 

#6. SizeOfHeader

SizeofHeader는 PE Header의 전체 크기를 나타냅니다. 이 값또한 filealignment의 배수여야 하며, 파일시작에서 SizeOfHeader의 옵셋만큼 떨어진 위치에 첫번째 섹션에 존재

(DoS + NT + Section Header)

#7. Subsystem

이걸 보고서 .sys인지 .exe인지 .dll인지 구분할수 있다고 함.

참고.

#8. NumberOfRvaAndSizes

NumberOfRvaAndSizes는 IMAEG_OPTIONAL_HEADER32 구조체의 마지막 멤버인 DataDictionary 배열의 개수를 나타냄. 이값을 보고 PE 로더가 배열의 크기를 인식함 (꼭 16이 아닐수 있음. 16은 구조체 정의에 명시된 숫자)

 

#9. DataDirectory

DataDirectory는 IMAGE_DATA_DIRECTORY 구조체의 배열로, 각 항목마다 정의된 값을 가짐.

#define IMAGE_DIRECTORY_ENTRY_EXPORT        0
#define IMAGE_DIRECTORY_ENTRY_IMPORT        1
#define IMAGE_DIRECTORY_ENTRY_RESOURCE      2
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION     3
#define IMAGE_DIRECTORY_ENTRY_SECURITY      4
#define IMAGE_DIRECTORY_ENTRY_BASERELOC     5
#define IMAGE_DIRECTORY_ENTRY_DEBUG         6
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT     7
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR     8   /* (MIPS GP) */
#define IMAGE_DIRECTORY_ENTRY_TLS           9
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG   10
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT  11
#define IMAGE_DIRECTORY_ENTRY_IAT           12  /* Import Address Table */
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT  13
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR    14
#define IMAGE_DIRECTORY_ENTRY Reserved Directory 15

EXPORT, IMPORT, RESOURCE, TLS를 기억할 것.

IAT와 EAT의 개념에 대해서 잠깐 정리를 해보면 

 

IAT : 내가 프로그램에서 어떠한 함수를 사용할지를 모아둠

EAT : 내가 라이브러리가 되어서 다른 프로그램에 제공하는 함수목록을 모아둠.

 

여기까지가 IMAGE_OPTIONAL_HEADER 입니다.

VIEW 밑에 짤렸;

NT Header 끝!!

 

섹션 헤더로 출발!

섹션 헤더

- 각 섹션의 속성을 정의한 것이 섹션 헤더라고 합니다.

PE 파일은 code, data, resouce 등을 각각의 섹션으로 나뉘어서 저장되어 집니다.

이유 ? 프로그램의 안정성을 위해서. 안나뉘어 있으면 데이터 쓰다가 overflow나면 code영역을 침범함 ㅋㅋ

 

섹션의 속성에는 file/memory에서의 시작 위치, 크기, 엑세스 권한 등이 있어야겠고, 각각의 섹션마다 특성, 접근권한을 다르게 설정할 필요가 있어짐.

종류 엑세스 권한
code 실행, 읽기 권한
data 비실행, 읽기, 쓰기 권한
resource 비실행, 읽기 권한

IMAGE_SECTION_HEADER

섹션 헤더는 각 섹션별 IMAGE_SECTION_HEADER 구조체의 배열로 되어 있음.

typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize;
  } Misc;
  DWORD VirtualAddress;
  DWORD SizeOfRawData;
  DWORD PointerToRawData;
  DWORD PointerToRelocations;
  DWORD PointerToLinenumbers;
  WORD  NumberOfRelocations;
  WORD  NumberOfLinenumbers;
  DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

리얼

여기서 알아야 할 중요 멤버들은 다음과 같습니다.

항목 의미
VirtualSize 메모리에서 섹션이 차지하는 크기
VirtualAddress 메모리에서 섹션의 시작 주소(RVA)
SizeOfRawData 파일에서 섹션이 차지하는 크기
PointerToRawData 파일에서 섹션의 시작 위치
Characteristics 섹션의 속성(bit OR)

VirtualAddress와 PointerToRawData는 아무 값이나 가질 수 없고, 위에서 정의된 Sectionalignment와 Filealignment에 맞게 설정되어짐.

VirtualSize와 SizeOfRawData는 일반적으로 서로 다른 값을 가짐.

즉 파일에서의 섹션크기와 메모리에 로딩되는 섹션의 크기는 서로 다릅니다!

 

해당 부분이 IMAGE_SECTION_HEADER 구조체 배열!

그리고 각각의 .text, .data, .rsrc의 멤버들의 내용은 같습니다.

 

* 여기서 잠깐 이미지란 ?

- PE 파일이 메모리에 로딩되어질 때 파일이 그대로 올라가는게 아니라 섹션 헤더에 정의된 대로 섹션 시작 주소, 섹션 크기 등에 맞춰서 올라가게 됨. 따라서 PE와 메모리에서의 PE는 서로 다른 형태이고, 메모리에 로딩된 상태의 PE를 '이미지' 라고 부는 것임.

 

RVA, RAW

프로그램이 시작되기전의 주소를 offset 또는 'RAW'라고 부릅니다.

빨강네모!

 

실행되기전의 주소를 말할때는 이 부분을 참고하면 됩니당.

아무튼 다음에 알아볼건 'RVA' 입니다.

RVA는 얼마만큼 떨어져 있는지를 의미합니다 기준점은 어디서요? VA에서요 ^^

 

VA(Virtual Address)는 메모리의 어딘가의 주소이기 때문에 offset과는 전혀다른 개념이에요

window도 memory map file을 사용하기 때문에 파일을 실행하게 되면 가상 메모리 공간에 프로그램이 올라가게 되는데 이 과정을 'mapping'이라고 하는듯.

 

그런데 이 mapping이 되는 위치가 바로 NT Header 중에서 IMAGE_OPTIONAL_HEADER32구조체 안에있는 ImageBase를 의미함. VA는 ImageBase를 기준으로 생성된다고 봐도 무관할 것 같다. 실제 메모리에서는 이 VA주소를 보고 프로그램을 실행시키게 된다.

 

* PE의 대부분 주소가 RVA인 이유 ?

- 프로그램은 하나만 실행되지 않는다. 동시에 여러개의 프로그램을 실행할 때 이미 해당주소에 프로그램이 있을경우 하나의 주소에 두개의 프로그램이 실행될 순 없고, window os는 ASLR을 쓰기 때문에 실행마다 주소가 변경된다 따라서 변경된 주소에서 '얼마만큼 떨어져 있느냐' RVA를 통해 시작주소가 바뀌어도 프로그램은 정상적으로 구동된다.

이를 PE relocation 이라고 한다.

 

* 퀴즈 :-3

RVA = 5000일 때, RAW = ?

 

일단 RVA - VA = RAW - PointerToRawData 이다. (매핑과정에서 주소가 바꼈더라도 떨어진 거리는 같으므로 ??)

RAW = RVA - VA + PointerToRawData

RAW = 5000 - 1000 + 400 = 4400 ^^ (위 그림 참고)

 

★ IAT

- IAT ? 프로그램이 어떤 라이브러리에서 어떤 함수를 사용하고 있는지를 기술한 테이블입니다.

- DLL ? '동적 연결 라이브러리', (프로그램에 library를 포함시키지 않고 별도의 DLL로 구성하여 필요할 때 불러씀)

 

자이언티 - 꺼내먹어요 ~

 

DLL의 로딩방식은 2가지 입니다. 

- Explicit Linking : 프로그램에서 사용되는 순간에 로딩하고 사용이 끝나면 메모리에서 해제

- Implicit Linking : 프로그램 시작과 같이 로딩되고 프로그램 종료시 메모리에서 해제

IAT는 후자의 메커니즘을 제공하는 역할을 합니다.

프로그램은 다른 모듈을 호출할 때 다음과 같은 형식으로 호출

GetCureentProcessId를 호출할 때 직접호출하지 않고 002410CC 주소에 있는 값을 가져와서 호출하는 방식임

제작자가 컴파일시에 실제주소가 002410CC에 올라오면서 실행됨

call dword ptr ds:[api 여기에 넣어라!]

 

'Reversing' 카테고리의 다른 글

리버싱 6  (2) 2019.04.04
PE file(3)  (0) 2019.04.03
PE File(1)  (0) 2019.04.02
리버싱 5  (0) 2019.04.02
리버싱 4  (0) 2019.03.29

+ Recent posts