Windows编程系列:PE文件结构

news/2024/9/23 18:22:05

最近在参考OpenShell为任务栏设置图片背景时,发现里面使用了IAT Hook,这一块没有接触过,去查资料的时候发现IAT Hook需要对PE文件结构有一定的了解,索性将PE文件结构的资料找出来,系统学习一下。

 

PE文件结构

Portable Executable (PE),可移植的可执行文件。在Windows平台下,所有的可执行文件(包括.exe, .dll, .sys, .ocx, .com等)均使用PE文件结构。这些使用了PE文件结构的可执行文件也称为PE文件。

 

PE结构包含的结构体有DOS头,PE标识 、文件头、可选头、目录头、目录结构、节表等。

整体结构如下

 

从上图可以看出PE结构分为4大部分,其中每个部分又进行了细分。

从数据管理的角度来看,可以把PE文件大致分为两部分,

1、DOS头、PE头和节表属于PE文件的数据管理结构或数据组织结构部分,

2、节表数据才是PE文件真正的数据部分,其中包含着代码、数据、资源等内容。

 

DOS头

DOS头分为“MZ头部”和"DOS存根“。

”MZ头部“是真正的DOS头部,由于其开始处的两个字节为"MZ",因此DOS头也可以叫作MZ头部。

这个我们用十六进制编辑器随便打开一个exe就可以看到

该部分用于程序在DOS系统下加载,它的结构被定义为IMAGE_DOS_HEADER

 

IMAGE_DOS_HEADER定义如下所示:

 1 //大小为: 0x40(64)字节
 2  #define IMAGE_DOS_SIGNATURE                 0x5A4D      // MZ
 3  
 4 typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
 5     WORD   e_magic;                     // MZ标记 0x5a4d 
 6     WORD   e_cblp;                      // 最后(部分)页中的字节数
 7     WORD   e_cp;                        // 文件中的全部和部分页数
 8     WORD   e_crlc;                      // 重定位表中的指针数
 9     WORD   e_cparhdr;                   // 头部尺寸以段落为单位
10     WORD   e_minalloc;                  // 所需的最小附加段
11     WORD   e_maxalloc;                  // 所需的最大附加段
12     WORD   e_ss;                        // 初始的SS值(相对偏移量)
13     WORD   e_sp;                        // 初始的SP值
14     WORD   e_csum;                      // 补码校验值
15     WORD   e_ip;                        // 初始的IP值
16     WORD   e_cs;                        // 初始的SS值
17     WORD   e_lfarlc;                    // 重定位表的字节偏移量
18     WORD   e_ovno;                      // 覆盖号
19     WORD   e_res[4];                    // 保留字
20     WORD   e_oemid;                     // OEM标识符(相对m_oeminfo)
21     WORD   e_oeminfo;                   // OEM信息
22     WORD   e_res2[10];                  // 保留字
23     LONG   e_lfanew;                    // NT头(PE标记)相对于文件的偏移地址
24   } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

 

DOS存根是一段简单的程序,主要用于输出“This program cannot be run in DOS mode.”类似的提示字符串。

 

为什么PE结构的最开始位置有这样一段DOS头部呢?

为了该可执行程序可以兼容DOS系统。通常情况下,Win32下的PE程序不能在DOS下运行,因此保留了这样一个简单的DOS程序用于提示“不能运行于DOS模式下”。

 

DOS头部IMAGE_DOS_HEADER详解

IMAGE_DOS_HEADER的定义在前面我们列出来了,该结构体中需要掌握的字段 只有两个分别是第一个字段 e_magic和最后一个字段e_lfanew。

e_magic:DOS可执行文件的标识,占用2字节,该位置保存着字符是“MZ",该标识符在Winnt.h头文件中有一个宏定义,如下所示:

1 #define IMAGE_DOS_SIGNATURE 0x5A4D

 

我们创建一个简单的控制台程序

1 #include <iostream>
2 
3 int main()
4 {
5     std::cout << "Hello World!\n";
6 }

 

使用16进制编辑器(我这里用的是ImHex,使用个人习惯的软件即可)打开编译出来的二进制文件(.exe)。

可以看到在0x00000000的位置保存着2字节的内容0x5A4D(ASCII的MZ)这里使用的是小尾(小端)方式存储,即高位保存高字节,低位保存低字节,所以上图中写的是4D 5A,这也是适合阅读顺序。

 

说明:

  • 大端模式(Big-Endian)。在内存中,多字节数据类型的高位字节存储在低地址处,而低位字节存储在高地址处。这种模式与我们阅读数字的方式相似,即先读高位,后读低位。

  • 小端模式(Little-Endian)。在内存中,多字节数据的低位字节存储在低地址处,而高位字节存储在高地址处。这种模式与我们阅读数字的方式相反,即先读低位,后读高位。

如0x0102这样一个数据,

使用大端方式存储,存储方式为:01 02

使用小端方式存储,存储方式为:02 01

 

我们可以看到下面这样一段程序

 1 #include <iostream>
 2 #include<Windows.h>
 3 #include<winnt.h>
 4 
 5 int main()
 6 {
 7     
 8     HANDLE hFile = CreateFile(L"a.bin", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 9 
10     BYTE buffer[4] = { 1,2,3,4 };  //写入 1 2 3 4 4个字节
11 
12     DWORD dwHexValue = 0x5D4A; //写入0x5D4A(实际存储是4A 5D)
13     DWORD dwDecValue = 1234;   //写入1234  (0x04D2 实际存储D2 04)
14 
15     LPOVERLAPPED lv{};
16     if (hFile)
17     {
18         WriteFile(hFile, buffer, 4, NULL, NULL);
19         WriteFile(hFile, (PVOID)&dwHexValue, 4, NULL, NULL);
20         WriteFile(hFile, (PVOID)&dwDecValue, 4, NULL, NULL);
21         CloseHandle(hFile);
22     }
23 }

用十六进制编辑器打开可以看到

为什么是这种情况,因为对于 Microsoft Visual C++ 的目标平台(x86、x64、ARM、ARM64),所有本机标量类型都是小字节序。

-------------------------

好的,让我们继续回归主题,在0x0000003C位置处,也就是IMAGE_DOS_HEADER的e_lfanew字段,该字段保存着PE头部的起始位置。

e_lfanew字段是LONG类型,所以这里是4个字节,F8 00 00 00,因为是使用的小端字节序,所以我们可以在0x000000F8位置,看到50 45 00 00,与之对应的ASCII字符为”PE\0\0“,这里就是PE头部开始的位置。

 

IMAGE_DOS_HEADER(e_lfanew字段之后)到"PE\0\0"之间的内容就是DOS存档,可以将该部分删除,然后将PE头部整体向前移动,也可以将一些配置数据保存在此处等。

我们将这里全部填充为0,程序也是可以正常执行的。

 

我写了下面这样一段测试程序:

 1 #include <iostream>
 2 #include<Windows.h>
 3 #include<winnt.h>
 4 
 5 int main()
 6 {
 7     
 8     HANDLE hFile = CreateFile(L"exepath", GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
 9 
10     if (hFile)
11     {
12         SetFilePointer(hFile, 0x00000040, NULL, FILE_BEGIN);
13 
14         BYTE buffer[8] = { 1,2,3,4,5,6,7,8 };
15         WriteFile(hFile, buffer, 8, NULL, NULL);
16 
17         CloseHandle(hFile);
18     }    
19 }

在e_lfanew字段之后写入了8个字节的数据,程序也是可以照常执行的。

 

 

参考资料:

pe format

https://learn.microsoft.com/en-us/windows/win32/debug/pe-format

endian

https://learn.microsoft.com/en-us/cpp/standard-library/bit-enum?view=msvc-170

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/30994.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

错误解决Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds

问题:Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor. 今天运行tomcat项目,发现报错信息如下图原因:45s内无法启动改项目,没有其他错误信息,尝试延长启动…

使用 Docker 部署 WebTop 运行 Linux 系统

1)项目介绍 GitHub:https://github.com/linuxserver/docker-webtopWebTop 它是一个基于 Linux ( Ubuntu 和 Alpine 两种版本)的轻量级容器,具有在浏览器中运行的完整桌面环境,具有基本的窗口管理器、像素完美的渲染分辨率、音频支持、剪贴板支持、屏幕键盘支持,以及用于…

Q-learning 玩maze游戏

import pygame import numpy as np import random import sys# 定义迷宫环境 class Maze:def __init__(self):self.size = 10self.maze = np.zeros((self.size, self.size))self.start = (0, 0)self.goal = (9, 9)self.maze[4, 2:7] = 1 # 添加墙壁self.maze[2, 1] = 1self.cu…

react native 项目使用 Xcode 打包上架 App Store

一、创建证书、标识符和描述文件等: 1. 前提条件 可正常运行和打包的代码、Apple开发者账号点击注册Apple开发者账号注册完进入页面可以看到证书、标识符和描述文件创建入口2. 创建App ID点击Identifiers旁边的加号选择 App IDs,点击 Continue。选择 App,点击 Continue。填写…

35岁测试工程师被辞退,给你们一个忠告

一:前言:人生的十字路口静坐反思 入软件测试这一行至今已经10年多,承蒙领导们的照顾与重用,同事的支持与信任,我的职业发展算是相对较好,从入行到各类测试技术岗位,再到测试总监,再转行入测试讲师做技术分享,每一步都刚刚好。 最近自身的职业发展也遇到了瓶颈,又一个…

虚拟机VMware安装统信UOS,再安装中望CAD

下载并安装VMware虚拟机。 下载统信UOS。https://www.chinauos.com/resource/download-professional 统信UOS桌面专业版AMD64(1070版本) 支持:Intel、AMD、兆芯、海光工作站还可以考虑社区版: https://www.uniontech.com/next/product/desktop-system?edition=CommunityVM…

使用 Python 旋转PDF页面、或调整PDF页面顺序

在将纸质文档扫描成PDF电子文档时,有时可能会出现页面方向翻转或者页面顺序混乱的情况。为了确保更好地浏览和查看PDF文件,本文将分享一个使用Python来旋转PDF页面或者调整PDF页面顺序的解决方案。 要实现Python对PDF页面进行设置,我们需要用到第三方库 Spire.PDF for Pytho…

Testing Egineer note:2024_5_13-day08-part01

肖SIR__数据库之搭建__11.2 数据库之搭建 1、rpm -qa|grep 服务名称 案例:rpm -qa|grep mysql 2、将所有msyql的包删除干净 删除方法: (1)yum remove mysql * 删除linux中的数据库 (2)yum erase 包名 ,删除linux中的数据库 (3)rpm -e --nodeps 包名…