这个头文件包含了所有PE相关的结构
首先创建一个小的可执行程序
选择一个小的应用程序
编译好之后,我们看一下他的区段(text是放代码的,rdata段是放导入表的,data用来放全局函数的)
把头文件放到编译的目录下可以精简我们的程序
只要在源码中包含一下这个头文件即可
右键查看头文件
合并区段的部分被注释掉了
加上合并区段的代码
编译出来会发现应用程序更小了,查看一下区段,会发现区段被合并了
进入正题
这是PE的整体架构
我们一个一个分开看
为什么要将MZ当做DOS签名呢?
这个是我手写PE程序的DOS头,有64字节(PE只是个参考,有些结构体成员可有可无)
计算一下大小,验证一下
DOS存根的概念
40到4D是16位的汇编指令,在DOS环境下才会执行这些代码,32为的Windows中不会运行。
这是手写PE的DOS存根,总共112字节 我将它的位置留出来方便讲解,也可以不留(DOS存根大小一般为112字节,大小不固定)
NT头(包含了文件头和可选头)
NT头有4个字节的标识,前面空出的8字节是为了内存对其需要,必须得填充NULL
文件头
文件头是表现文件大致属性的结构体
注意一个特殊的例子:.obj和resource DLL的characteristics值不是0002h,他们都不可执行
手写PE的NT头
可选头(PE头中最大的结构体)
必须有的成员:否则可执行文件会出错
手写PE的可选头
节区头的概念
节区头结构体
重要成员
Characterisitics是由下面这些值组合起来的(相加)
手写PE的节区头(区段头)
RVA和RAW之间的转换
文件偏移-文件中节区的起始位置=内存偏移-内存中节区的起始位置
文件偏移=内存偏移-内存中节区的起始位置+文件中节区的起始位置
创建一个文件,用C32打开
认识数据的存储方式
写好PE签名,然后剩下成员用58个00填充
写好了
参考编译器生成的程序
把存根的位置留出来
把NT头的PE标识写在后面