[原创]手写一个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;
}

 工程下载:https://files.cnblogs.com/tk091/PECheck.rar

原文地址:https://www.cnblogs.com/tk091/p/2457464.html