DOS头和NT头(PE头)结构及移位(小白文详细篇)

DOS头和NT头(PE头)结构及移位(小白文详细篇)

DOS头

一个PE文件起始的部分开始就是DOS头,不要觉得难理解,这和GIF,BMP,JPG,RAR等等都一样,只是在文件起始部位标识这个文件属于什么文件类型。
下面是DOS头在winnt.h中的定义:

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;

定义这些结构就是为了计算在DOS头中这些东西的偏移,要修改PE偏移就需要修改DOS头中的e_lfanew,你可以将文件内容读取到内存中,然后将char*强转为PIMAGE_DOS_HEADER类型,通过指针就可以方便的修改,写回文件即可。
本篇内容因为要更直观的看结构,所以不会使用代码方式修改DOS头或PE头,所以我这里使用C32。
结构图

PE头

PE文件的全称是Portable Executable,所以人们常常把NT头叫PE头。
winnt.h中定义的结构体名称为IMAGE_NT_HEADERS,我个人觉得叫PE头可能还一个原因。就是在内存中NT头的Signature(NT头起始位置)是个0x50 0x45,转为字符串就是PE,所以叫PE头了。文章中我就叫PE头了。

上面的DOS头结尾部分e_lfanew的内容是0x1001,我们知道windows中使用小端存储,e_lfanew不是一个字符串数组,所以要区分低位高位,0x1001存储时实际的long应该为0x0110。
我们得到了DOS头偏移多少是PE头位置,实际上这个偏移不需要计算,因为DOS头本身就是文件开始的位置,0x0+0x110还是0x110,我们直接找到0x110的地址就是PE头了。
在这里插入图片描述

下面是winnt.h中PE头的定义:

//x86
typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
//x64
typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

其中FileHeader中包含了这些:

//x86 x64相同
typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

IMAGE_OPTIONAL_HEADER太长不贴了,有兴趣自己看一眼,其中有链接器版本,(AddressOfEntryPoint) 程序执行入口RVA,(BaseOfCode)代码的区块的起始RVA,(BaseOfData)数据的区块的起始RVA等等。

PE头移位

首先我们知道了PE头的偏移位置,我们还要知道PE头大小,否则移位时到底移哪块内容呢?PE头大小其实就在NT头中的FileHeaderSizeOfOptionalHeader中。经过计算是PE头向后偏移0x14
在这里插入图片描述
因为它是个WORD,所以是2字节,0x00F0=240,我们将PE头开始到之后的240个字节拷贝一下。
在这里插入图片描述
因为我演示的程序是x64,所以在RVA计算时地址大小是8字节,所以我得移动8字节的倍数,事实上4字节也可以,并不影响,但是为了区分x64和x86,我向前移动8个字节。
在这里插入图片描述
完成了移动后,我们还要去修改e_lfanew,因为他记录了PE头偏移还记得吧?
0x110-0x8= 0x108
我们修改为0801
在这里插入图片描述
现在PE头移动就完成了,但是不出意外,这程序是无法运行了,因为我们仅仅改了PE头的位置和DOS头记录的偏移,这将导致IMAGE_SECTION_HEADER的位置(section)出错,因为我们向上偏移了8个字节。
我们现在需要将PE头大小+8,实际上PE头大小没有变,+8的原因是为了让他计算section位置时+8,因为我们向上移动了8个字节。还记得PE头大小在哪里吧?
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

没事干写博客玩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值