[原创]手写一个PE文件
手写一个PE文件,首先要对PE文件有一个基本的了解。这里使用的工具是hex workshop6.5。
一、开始为一个结构体,我们来看一下:
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
首先e_magic应为0x5a4d,ANSI对应为“MZ”。还有一个关键的就是e_lfanew,指向IMAGE_NT_HEADER的开始位置。根据
由于存储的方式是地位存放低字节,高位存放高字节,所以开始本来是0x5A4D,结果变成了4D5A,到D8000000的时候,这个结构体结束。
后面是一段DOS的程序,然后是IMAGE_NT_HEADER结构,D800000000(其实真正的数据时0000‘00d8),指向后面的SIGNATURE,即00004550.ANSI为“PE00”,表示PE的有效性。我们可以使用ctrl+G,然后输入十六进制数据“000000D8”,直接跳转到该位置。
下面,我们再来看看 IMAGE_NT_HEADER的结构
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader; } IMAGE_NT_HEADERS;
为了编程方便,windows为DOS文件标记和PE文件标记都定义了宏标识。
#define IMAGE_DOS_HEADER 0x5A4D //MZ #define IMAGE_NT_SIGNATURE 0x00004550 // PE00
二、了解了PE的文件结构,我们就可以来写一个PE文件
特别要注意位置的相对性,因为hex workshop是可调节大小的。
这样,第一个结构体就差最后一个数据了,那就是e_lfanew的值(我这里写了,为42000000,实际为00000042,即下一行的开始)。
42000000之后的数据就代表了dos程序,虽然是几个0。而42000000(实际是0000‘0042),指向的是下一行的开始,即IMAGE_NT_HEADERS的开始部分。
而且只要这个位置的值是ANSI的PE00,则表示这个文件时一个PE文件:
这样,一个PE文件就完成了,我们保存为pe.exe,等待下一步的验证。
三、PE的验证
在王艳萍的windows程序设计中,有涉及到PE文件的验证,我们就以书中代码设计一个程序,用来验证程序是否是PE文件。我对其中代码进行了修改,代码如下:
void CCheckDlg::OnButton1() { CFileDialog dlg(TRUE); if(dlg.DoModal()!=IDOK) return; HANDLE hFile=::CreateFile(dlg.GetFileName(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(hFile==INVALID_HANDLE_VALUE) { MessageBox("无效文件","validpe",MB_OK); } IMAGE_DOS_HEADER dosHeader; IMAGE_NT_HEADERS32 ntHeader; BOOL bValid=FALSE; DWORD dwRead; ::ReadFile(hFile,&dosHeader,sizeof(dosHeader),&dwRead,NULL); if(dwRead==sizeof(dosHeader)) { if(dosHeader.e_magic==IMAGE_DOS_SIGNATURE) { if(::SetFilePointer(hFile,dosHeader.e_lfanew,NULL,FILE_BEGIN)!=-1) { ::ReadFile(hFile,&ntHeader,sizeof(ntHeader),&dwRead,NULL); if(ntHeader.Signature==IMAGE_NT_SIGNATURE) bValid=TRUE; } } } if(bValid) MessageBox("是一个PE格式的文件","提示",MB_OK); else { MessageBox("不是一个PE格式的文件","提示",MB_OK); } ::CloseHandle(hFile); return; }