https://www.bpsend.net/thread-444-1-1.html

选项头 IMAGE_OPTIONAL_HEADER:以供操作系统加载PE文件使用,32位必选。

重要字段:
DWORD AddressOfEntryPoint;   入口点
DWORD ImageBase                          建议模块地址
DWORD SectionAlignment;       内存对齐值
DWORD FileAlignment;               文件对齐值,
DWORD SizeOfImage;                 模块在内存中总大小,与 SectionAlignment 对齐
DWORD SizeOfHeaders;             PE头总大小,与 FileAlignment 对齐

IMAGE_OPTIONAL_HEADER 结构体

typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD  Magic;  // 32位PE: IMAGE_NT_OPTIONAL_HDR32_MAGIC  ,   0x10b. 
                // 以 _IMAGE_OPTIONAL_HEADER  结构体解析      
                // 64位PE: IMAGE_NT_OPTIONAL_HDR64_MAGIC  ,   0x20b.
                // 以 _IMAGE_OPTIONAL_HEADER64  结构体解析        
  BYTE  MajorLinkerVersion; // 主链接器版本号 (无用)
  BYTE  MinorLinkerVersion; // 副链接器版本号  (无用)   //系统分配内存不看着3个值,但是对于调试器有影响(影响反汇编所用内存大小,OD是机器码个数*2,字节数是通过SizeOfCode 得到)   
  DWORD SizeOfCode;             // 代码所占空间大小  (没啥用)
  DWORD SizeOfInitializedData;  // 已初始化数据所占空间大小 (没啥用)
  DWORD SizeOfUninitializedData;// 未初始化数据所占空间大小 (没啥用)   DWORD AddressOfEntryPoint;    // *oep:原本的程序入口点(实际为偏移,+模块基址=实际入口点)
                                // ep: 被加工后的入口点
                                //这个值可以修改,但是修改过后必须跳转到在该偏移处跳转到真正入口   DWORD BaseOfCode; // 代码基址  (无用)
  DWORD BaseOfData; // 数据基址   (无用)   DWORD ImageBase;  // *建议模块地址:exe映射加载到内存中的首地址= PE 0处,即实例句柄hInstance
                    // 一般而言,exe文件可遵从装载地址建议,但dll文件无法满足 (开了随机基址可能也不是这个值,是通过重定位表得到)
                    //这个值最好不要改,改的话要改动大量地方,因为函数和全局变量的地址也需要跟着改变   DWORD SectionAlignment;   //内存对齐值,数据在内存的对齐值,很多内存地址,大小都要依赖他来计算
                            //默认1000h 一个分页大小,系统管理内存是以分页为单位
  DWORD FileAlignment;      //文件对齐值, 200h,磁盘的一个扇区大小  (vc6是1000h)
                            //文件的起始位置和大小都是跟文件对齐值对齐的 
                            // 对齐值都是2的倍数 如果把所有节合并了 就可以设为1,否则不可以随便修改因为要配合节检查    //主副系统相关版本号   除了  MajorSubsystemVersion 不可修改,其他5个可以
  WORD  MajorOperatingSystemVersion;
  WORD  MinorOperatingSystemVersion;
  WORD  MajorImageVersion;
  WORD  MinorImageVersion;
  WORD  MajorSubsystemVersion;   //主子系统版本号   不可以修改   这里改成4可以再xp运行
  WORD  MinorSubsystemVersion;   DWORD Win32VersionValue;   // win32版本值  xp上不可以改 ,win7和win10可以修改   //改值是通过节表计算得到的   
  DWORD SizeOfImage;     //模块在内存中总大小,与 SectionAlignment 对齐,改的话不可以改变分页数量(但最好对齐)
  DWORD SizeOfHeaders;   // PE头总大小,与 FileAlignment 对齐   DWORD CheckSum;       //校验值  3环程序随便改,0环程序会检查,不允许改 可以用 MapFileAndCheckSum 计算值   WORD  Subsystem;      //子系统  不允许修改    /subsystem   WORD  DllCharacteristics;   //描述应用程序的一些相关信息(例如是否开了随机基址等),可以改,但不能随便改   //这四个值可以改,但是不能改得太离谱
  DWORD SizeOfStackReserve;  //栈保留
  DWORD SizeOfStackCommit;   //栈提交
  DWORD SizeOfHeapReserve;   //堆保留
  DWORD SizeOfHeapCommit;    //堆保留   DWORD LoaderFlags;     //跟调试相关,目前用不到,值可以随便改
  DWORD NumberOfRvaAndSizes;   //下面数组个数(最小可以改为2,最大为16,前2个表是导入,导出表,必须要有)
 //数据目录表(成员2个 dword 第一个是内存偏移,第二个大小 )  
 //描述PE中各种个样的表的位置和大小,每个下标对应一个固定的表(前2个不能改,导入,导出表,改了无法调API)
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];  //柔性数组 个数由上面值决定,但是总大小为16个
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

AddressOfEntryPoint EP

OEP 程序入口点 - Old Entry Point

如果 EP 没有被修改的话 OEP = EP ,但是很多时候为了隐藏程序入口点 通常会修改 EP 的值

例如 原本 AddressOfEntryPoint 的值为1000 可以改成 1100 ,那么模块基址 + 1100 的地方就成了程序入口点 再到 该地址 执行跳转指令 ,可以挑战转到 偏移 为 1000 处或者 跳转到其他地方在跳回 偏移 1000处

ImageBase 建议模块基址

命令行编译的时候可以通过 /base修改

link /subsystem:windows /base:0x01000000 pe.obj

名称

开了随机基址,PE里面就会有一个重定位表,记录了所有需要修改的地方,没开没有

节表 IMAGE_SECTION_HEADER

●描述PE文件与内存之间的映射关系,说明PE文件的指定内容拷贝至内存的哪个位置、拷贝大小及内存属性的设置。

●一个结构体总大小为 0x 28 (40) 字节

如何定位节表

●节表置于选项头之后,位置 = 选项头( IMAGE_OPTIONAL_HEADER) 的地址


+ 选项头( IMAGE_OPTIONAL_HEADER)的大小。

节表字段的意义

// IMAGE_SECTION_HEADER 节表结构体,大小40B
typedef struct _IMAGE_SECTION_HEADER {   BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];  // 节表名称:描述性字段  2个字节   // 下方4个字段:从文件S1处开始,拷贝S2大小的数据,到内存S3处,有效数据占用内存S4大小
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize;          // S4:内存大小
  } Misc;
  DWORD VirtualAddress;         // S3:内存地址:基于模块基址,与SectionAlignment对齐(0x1000)
  DWORD SizeOfRawData;          // S2:文件大小,与FileAlignment对齐(0x200)
  DWORD PointerToRawData;       // S1:文件偏移,与FileAlignment对齐(0x200)   //跟调试相关  
  DWORD PointerToRelocations;   // 无用
  DWORD PointerToLinenumbers;   // 无用
  WORD  NumberOfRelocations;    // 无用
  WORD  NumberOfLinenumbers;    // 无用   DWORD Characteristics;        // 节内存属性,取值IMAGE_SCN_...系列宏  分低位和高位
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
空节

文件大小和偏移都为0

编译器一般用来存放未初始化的数据

节表与OD复制到文件功能的关系
  • 由于内存是基于分页管理,所以要注意区块与页的关系。
节表字段的关系
  • 关系:各个节之间是连续不间断的(除特殊节.textbss)。但是系统检查没那么严格改成不对齐也没事(修改 VirtualSize 的值或 SizeOfRawData ),但是不能改变分页情况,最好连续
    • 下一个节的PointerToRawData = 上一个节的PointerToRawData+SizeOfRawData。
    • 下一个节的VirtualAddress= 上一个节的VirtualAddress+VirtualSize对齐后的值(+0x1000)。
  • SizeofImage PE文件在进程内存中的总大小=
    • 算法1:最后一个节的virtualAddress + VirtualSize对齐后的值(+0x1000)
    • 算法2:各个节占内存大小的总和+PE头大小(对齐后0x1000)。

最后一个节点数据不能随便改,再便宜为 0c处的 存的是资源信息表

地址转换 FA,VA和RVA的转换

FA --- file Address 文件地址(文件偏移)

VA --- virtual Address 虚拟内存(内存中的绝对地址)

RVA --- relative virtual Address 相对虚拟地址(基于模块基质的偏移,内存偏移)

注意:

通过FA 一定可以找到对应的 VA地址 因为文件对齐位200h,比内存偏移1000h小

VA地址不一定可以找到对应的 FA 地址 内存偏移 大于 当前节 200h是 , 文件中没办法找到对应偏移

VA 转 RVA
  • 相对虚拟地址 = 绝对虚拟地址 - 基地址

RVA = VA - ImageBase

RVA 转 FA
VA -> FA
  1. 计算相对虚拟地址:RVA = VA - ImageBase
  2. 定位RVA所在的节,查节表,定位所在节
  3. 计算节内偏移:= RVA - 所定位节表virtualAddress字段的值
  4. 计算FA = 节内偏移 + PointToRawData

特殊的节:文件大小和偏移都为空,这叫空节,虽然不映射文件,但是会申请空间。

FA - > VA
  1. 定位节
  2. 计算节内偏移 = FA - PointRawData
  3. 计算相对虚拟地址:RVA = 节内偏移 + VirtualAddress
  4. 计算绝对虚拟地址:VA = RVA + ImageBase

WindowsPE文件格式入门02.选项头其它和节表的更多相关文章

  1. 【网络爬虫入门02】HTTP客户端库Requests的基本原理与基础应用

    [网络爬虫入门02]HTTP客户端库Requests的基本原理与基础应用 广东职业技术学院  欧浩源 1.引言 实现网络爬虫的第一步就是要建立网络连接并向服务器或网页等网络资源发起请求.urllib是 ...

  2. CSS3基础入门02

    CSS3 基础入门02 边框相关属性 border-radius 通过这个属性我们可以设置边框圆角,即可以将四个角设置为统一的圆角,也可以单独的设置具体的某一个角的圆角. grammer: borde ...

  3. PE文件格式详解,第三讲,可选头文件格式,以及节表

    PE文件格式详解,第三讲,可选头文件格式,以及节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶可选头结构以及作 ...

  4. JavaScript基本入门02

    目录 JavaScript基础入门 02 条件语句 if 语句 if .. else 语句 switch 结构 循环语句 while 循环 continue 关键字 do...while语句 for ...

  5. Shell入门02

    Shell入门-02 1.重定向 标准输入(<) 标准输出 标准错误重回定向 程序 = 指令 + 数据 命令   变量 在程序中,数据如何输入?又如何输出? 数据输入:键盘 – 标准输入,但是并 ...

  6. # 095 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 03 封装总结 01 封装知识点总结

    095 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  7. 094 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 04 static关键字(续)

    094 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  8. 093 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 03 static关键字(下)

    093 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  9. 092 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 02 static关键字(中)

    092 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  10. 091 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 02 static关键字 01 static关键字(上)

    091 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

随机推荐

  1. .netCore 使用 Quartz 实例

    一.参考源文链接 1.https://www.likecs.com/show-897836.html 2.https://blog.csdn.net/weixin_43614067/article/d ...

  2. 【问题解决】Jenkins使用File的exists()方法判断文件存在,一直提示不存在的问题

    小剧场 最近为了给项目组提供一个能给Java程序替换前端.后端的增量的流水线,继续写上了声明式流水线. 替换增量是根据JSON配置文件去增量目录里去取再替换到对应位置的,替换前需要判断增量文件是否存在 ...

  3. calico配置报错 kubelet.go:2855] "Container runtime network not ready"

    前言 配置 calico 网络插件时,kubectl get node 报错: NoReady kubectl describe node node Name: node Roles: <non ...

  4. Go语言遍历字符串——获取每一个字符串元素

    遍历字符串有下面两种写法. 遍历每一个ASCII字符 遍历 ASCII 字符使用 for 的数值循环进行遍历,直接取每个字符串的下标获取 ASCII 字符,如下面的例子所示. theme := &qu ...

  5. Win环境下的批处理命令和JScript脚本结合使用笔记

    最近工作有接触到.bat 批处理命令,在Win环境下编写的时候基于以前的编码习惯,觉得批处理语法可读性较差,于是学习了解了一下结合JScript的用法,特此记录. 什么是JScript JScript ...

  6. 【Linux】3.3 关机、重启和用户登录注销

    关机.重启和用户登录注销 1. 关机&重启命令 1.1 shutdown shutdown -h now:表示立即关机 shutdown -h 1:表示1分钟后关机 shutdown -r n ...

  7. 【Java】修饰符

    修饰符(Modifier):是用于限定类型以及类型成员的声明的一种符号. 其用来定义类.方法或者变量,通常放在语句的最前端. 例子: public class Person { default Str ...

  8. “你觉得客户需要”是杀死TA的最后一根稻草 | IPD集成产品开发

    这个米老鼠洗衣机,大家眼熟吗? 相信最近热衷于在网上冲浪的朋友们,对这款形似米老鼠的"懒人洗衣机"并不陌生,甚至算是小小地参与了一下这个产品研发项目.在海尔的周云杰总裁爆火出圈后, ...

  9. 使用Python+SymPy计算无穷级数

    引言 在数学中,级数是指由数列的无限项组成的求和表达式.无穷级数的求和是一个非常重要且具有挑战性的数学问题,特别是在信号处理.物理学和工程学等领域.今天,我们将介绍如何利用 Python 中的 Sym ...

  10. 机器人操作系统ROS2之安装(Ubuntu 24)

    根据官网说明,ROS2是支持MAC的,本来打算在手头的MAC 15.3.2装一个,虽然要自己编译ROS2系统,但是想着比虚拟机性能好,就兴冲冲的开始了,也没在意官网提示尽量还是装安装版.最后折腾了几天 ...