PE Viewer 개발 (1) - IMAGE_DOS_HEADER
- 리버싱/리버싱
- 2020. 2. 9. 00:27
C언어로 개발하는 PE Viewer(PE 뷰어) 첫번째 포스팅에서는 DOS 헤더의 IMAGE_DOS_HEADER 구조체를 분석하고 파싱해볼 것이다. 한꺼번에 작성하려니, 포스팅 길이가 너무 길어져서 부득이하게 분할하게 되었다. IMAGE_NT_HEADERS 구조체를 포함한 다른 헤더나 섹션 관련은 이 다음 포스팅들을 확인해주기 바란다.
시작하기에 앞서
PE Viewer(PE 뷰어) 개발의 본격적인 시작인 IMAGE_DOS_HEADER 파싱에 앞서 간단히 부수적? 요소들을 설명하고 시작하려 한다.
PE 파일 체크 및 x32/x64 체크
제일 먼저, PE 파일인지에 대한 확인과 x32와 x64 체크를 수행하는데 이는 IMAGE_FILE_HEADER의 Machine을 이용해 '0x14c -> 32bit, 0x8664/0x2000 -> 64bit' 인 것으로 판단했다(IMAGE_OPTIONAL_HEADER의 Magic을 사용해도 무방하다).
또한, 해당 PEViewer는 옵션에 따라 콘솔 출력/파일 저장 두 부분으로 나뉘어져 있는데 본 포스팅에서의 코드는 x32와 콘솔 출력을 기반으로 작성한다. (아래는 파일 출력 결과)
pe_structure.h 헤더 정의
pe_structure.h는 MSDN에서 정의한 각 헤더의 구조체가 정의되어 있는 헤더 파일로, MSDN에 관련 헤더들이 이미 정의되어 있다.
DOS Header (IMAGE_DOS_HEADER)
PE 헤더의 제일 앞부분에는 IMAGE_DOS_HEADER가 존재하며 크기는 60바이트 (0x40)이다.
- 시작 위치: PE 헤더의 제일 앞 부분
- 크기: 0x40(60바이트)
IMAGE_DOS_HEADER 정의
IMAGE_DOS_HEADER는 아래와 같이 정의되어 있으며, 중요하게 여겨지는 멤버로는 e_magic과 e_lfanew가 있다. 보통 PE 파일은 'MZ'라는 Dos Signature을 항상 갖고 있기 때문에 보통 이 시그니쳐의 여부로 PE파일임을 체크한다. 그리고 이 뒤에서 다룰 IMAGE_NT_HEADER의 경우 시작 오프셋이 가변적인데 그 값이 e_lfanew에 저장되어 있기 때문에 이것을 잘 체크해야 한다.
- e_magic : Dos Signature(‘MZ’)
- e_lfanew : IMAGE_NT_HEADERS 구조체의 시작 오프셋 값
typedef struct _IMAGE_DOS_HEADER
{
WORD e_magic; // MZ
WORD e_cblp;
WORD e_cp;
WORD e_crlc;
WORD e_cparhdr;
WORD e_minalloc;
WORD e_maxalloc;
WORD e_ss;
WORD e_sp;
WORD e_csum;
WORD e_ip;
WORD e_cs;
WORD e_lfarlc;
WORD e_ovno;
WORD e_res[4];
WORD e_oemid;
WORD e_oeminfo;
WORD e_res2[10];
LONG e_lfanew; // // IMAGE_NT_HEAERS 구조체의 시작 오프셋 값
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
IMAGE_DOS_HEADER 변수 선언
buffer는 fread로 읽어 들인 파일의 크기만큼의 메모리가 할당되어 있는 포인터 변수이다. 첫번째로 IMAGE_DOS_HEADER 구조체의 포인터 변수 idh를 선언하고 해당 구조체의 크기만큼 메모리를 할당한다. IMAGE_DOS_HEADER는 PE 헤더의 제일 앞부분에 위치하므로 ‘idh=buffer’가 된다.
int pe_print_32(char* file, char* option)
{
// 코드...
/* 구조체 포인터 변수 선언 */
/* 각 헤더 구조체의 포인터 변수를 선언하고 buffer를 기점으로 (buffer + ~) 헤더 시작 위치를 파악함 */
// IMAGE_DOS_HEADER
struct _MY_IMAGE_DOS_HEADER *idh = buffer;
// 코드...
}
IMAGE_DOS_HEADER 파싱 및 출력
IMAGE_DOS_HEADER 파싱 결과 파일 출력은 아래와 같이 이루어진다.
#include <stdio.h>
#include <string.h>
#include <Windows.h>
#include "pe_structure.h"
// 32 exe print option _ check v
int pe_print_32(char* file, char* option)
{
// 코드...
// IMAGE_DOS_HEADER
printf("********************************* [IMAGE_DOS_HEADER] ************************************\n\n");
printf("- e_magic : %02X\n", idh->e_magic);
printf("- e_cblp : %02X\n", idh->e_cblp);
printf("- e_cp : %02X\n", idh->e_cp);
printf("- e_crlc : %02X\n", idh->e_crlc);
printf("- e_cparhdr : %02X\n", idh->e_cparhdr);
printf("- e_minalloc : %02X\n", idh->e_minalloc);
printf("- e_maxalloc : %02X\n", idh->e_maxalloc);
printf("- e_ss : %02X\n", idh->e_ss);
printf("- e_sp : %02X\n", idh->e_sp);
printf("- e_csum : %02X\n", idh->e_csum);
printf("- e_ip : %02X\n", idh->e_ip);
printf("- e_cs : %02X\n", idh->e_cs);
printf("- e_lfarlc : %02X\n", idh->e_lfarlc);
printf("- e_ovno : %02X\n", idh->e_ovno);
for (i = 0; i < 4; i++)
{
printf("- e_res[i] : %02X\n", i, idh->e_res[i]);
}
printf("- e_oemid : %02X\n", idh->e_oemid);
printf("- e_oeminfo : %02X\n", idh->e_oeminfo);
for (i = 0; i < 10; i++)
{
printf("- e_res2[i] : %02X\n", idh->e_res2[i]);
}
printf("- e_lfanew : %08X\n\n", i, idh->e_lfanew);
// 코드...
}
IMAGE_DOS_HEADER 출력 결과는 아래와 같다.
'리버싱 > 리버싱' 카테고리의 다른 글
[원데이 취약점] GOM Player 2.0.12.3375 - '.asx' Local Stack Overflow (0) | 2020.03.11 |
---|---|
IDA 사용법 및 분석 시 유용한 팁 (0) | 2020.03.07 |
바이너리 디핑을 위한 bindiff 설치 및 사용법 (0) | 2020.02.21 |
PE Viewer 개발 (3) - IMAGE_SECTION_HEADER (0) | 2020.02.11 |
PE Viewer 개발 (2) - IMAGE_NT_HEADERS (0) | 2020.02.09 |