-->

PE Viewer 개발 (1) - IMAGE_DOS_HEADER

반응형

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바이트)

 

ntdll.dll의 IMAGE_DOS_HEADER

 

 

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 출력 결과는 아래와 같다.

 

IMAGE_DOS_HEADER 파싱 및 출력 결과

 

댓글

Designed by JB FACTORY