首页 > 其他分享 >手写PE文件

手写PE文件

时间:2023-06-20 13:47:11浏览次数:25  
标签:文件 00 填充 IMAGE HEADER 导入 PE 手写

构造PE文件需要将所需的结构逐一构建出来,即需要将IMAGE_DOS_HEADER、IMAGE_FILE_HEADER、IMAGE_OPTIONAL_HEADER、IMAGE_SECTION_HEADER、IMAGE_IMPORT_DESCRIPTOR和数据节构造好,进而完成整个PE文件的代码。

1、构造IMAGE_DOS_HEADER结构

在PE文件格式中以IMAGE_DOS_HEADER结构开始,根据对其结构题进行分析可知其大小为40h字节(十进制中的64字节)。此时可以都打开C32Asm编辑器,选择“文件”、“新建十六进制文件”,在所弹出的“新建文件”对话框中设置“新文件大小”为“64”,即如下所示:

设置好“新文件大小”后,单击“确定”按钮,在C32Asm中即可插入有64字节的文件,即如下所示:

通过上述步骤可创建一个全0的64字节的数据,根据IMAGE_DOS_HEADER结构体对该64字节数据进行修改。在IMAGE_DOS_HEADER结构体中,关键字段只有两个,分别为e_magic和e_lfanew,其取值如下表所示:

字段

取值

备注

e_magic

4D 5A

MZ头标识

e_lfanew

40 00 00 00

指向PE标识符的偏移

在IMAGE_DOS_HEADER中,只须为这两个字段赋值,其余字段值均为0,赋值后的数据情况如下图所示:

 

 在IMAGE_DOS_HEADER结构体中,最后4字节指向PE标识符的偏移,由于手写PE文件不会像编译器一样插入DOS Stub所以在DOS头后紧跟着PE标识符,此处所给出的值为0x00000040。

2、构造PE标识符

构造完DOS头后则需要构建PE标识符,PE标识符占4字节,因此,C32Asm中需要新增4字节的数据。在C32Asm中选择“编辑”、“插入数据”选项,在所弹出的“插入数据”对话框中设置“插入数据大小”为“4”点击“确定”即可,此时所插入的4字节十六进制数据均为“0”。已知PE标识符对应的十六进制数据为“50 45 00 00”,因此,需要将插入的4字节数据的前两字节修改为“50 45”,修改后的数据情况如下图所示:

3、构造IMAGE_FILE_HEADER结构体

构造好PE标识符后则需要构造IMAGE_FILE_HEADER结构体,该结构体的大小为14h字节(十进制的20字节),同样需要在C32Asm中插入20字节的全0数据,并根据需求进行相应的数据修改,其中IMAGE_FILE_HEADER结构体的关键字段取值如下表所示:

字段

取值

备注

Machine

4C 01

表示i 386类型的CPU

NumberOfSections

03 00

该PE文件共计3字节

SizeOfOptionalHeader

E0 00

在Win32环境下可选头的大小

Characteristics

03 01

没有重定位信息的32位平台的可执行文件

填充数据时,SizeOfOptionalHeader字段值大小与32位平台的IMAGE_OPTIONAL_HEADER结构体的大小相同,Characteristics字段的值由多个值组合而成。填充的数据均为word类型,其余没有填充的字段值均为0,填充数据后的情况如下图所示:

4、构造IMAGE_OPTIONAL_HEADER结构体

IMAGE_OPTIONAL_HEADER结构体是PE文件中最为重要且体量较大的结构体之一,该结构体划分为32位和64位两个版本,此处以32位版本为例。该结构体大小为0E0h字节,即十进制224字节,因此,需要在C32Asm中填充224字节的全0十六进制数。

由于没有填充数据目录,所以根据IMAGE_OPTIONAL_HEADER结构体中字段的取值可先将结构体中的字段填充一半,填充数据是将0的部分同时在表格中进行展示,其具体填充数据情况如下图所示:

填充完IMAGE_OPTIONAL_HEADER结构体的基础数据部分后,话需要填充其数据目录部分。由于此处为手写一个EXE文件,所以数据目录只须存在两项,分别为第1个数据目录项和第13个数据目录项。其中数据目录的第1项是导入表,第13项是导入地址表

根据对PE文件的规划可在0x00003000起始处存放导入表的相关内容,在0x00003000中先存放导入地址表,即数据目录的第13项,再在导入地址表后存放导入表,即数据目录的第1项。导入地址表占用16字节,即从0x00003000处起始,在0x0000300f处结束。又导入表从0x00003010处起始。按照该布局在数据目录中输入导入表和导入地址表的RVA。此时IMAGE_OPTIONAL_HEADER数据目录部分的填充数据情况如下所示:

IMAGE_OPTIONAL_HEADER结构体的字段相对较多,可将其划分为普通字段信息和数据两个部分进行数据填充。

5、构造IMAGE_SECTION_HEADER结构

构造完IMAGE_OPTIONAL_HEADER结构体后则需要构造节表,节表中一共包含3个节表项,也就是需要构造3个IMAGE_SECTION_HEADER结构,每个IMAGE_SECTION_HEADER结构体的大小为40字节,由于需要构造3个节表项,因此,节表大小为120字节,节表中字段的填充情况如下表所示:

Name

.text

.data

.idata

VirtualSize

0x00001000

0x00001000

0x00001000

VirtualAddress

0x00001000

0x00002000

0x00003000

SizeOfRawData

0x00001000

0x00001000

0x00001000

PointerToRawData

0x00001000

0x00002000

0x00003000

Characteristics

0x60000020

0xC0000040

0xC0000040

 在C32Asm中填充120字节的全0数据并根据上表填充,填充完成后的数据情况如下所示:

6、0数据的填充

PE文件格式的头部到此填充完毕,在IMAGE_OPTIONAL_HEADER结构体结构体中,SizeOfHeader字段的值是0x00001000。因此,为了对齐粒度需要将头部的大小用0字节补足。目前已填充的PE文件格式头部的大小为432字节,在C32Asm中,将光标移动到最后一字节处,可以看到C32Asm右下角“光标”的值为“000001B0”,“文件长度”为432 bytes,即如下图所示:

由于需要按照0x00001000长度进行对齐,因此用0x1000-0x01B0=0x0E50,即十进制数的3664。在C32Asm中插入“3664”个“0”字符将PE文件头部按照IMAGE_OPTIONAL的SizeOfHeader对齐。插入3664个0字符后,文件的结束偏移地址是0x00000FFF。在填充PE文件头部后,需要继续填充0x00001000字节的0字符,该0x00001000字节的数据用来存放.text节的内容,即代码节内容。继续使用C32Asm插入4096个0字符。

7、填充.data节的数据

.data节用来保存程序运行时弹出的提示对话框中显示的字符串。提示对话框使用MessageBox函数来实现。MessageBox函数的第二个参数和第三个参数分别是两个字符串,第二个参数lpText是提示对话框中用于显示的字符串,第三个参数lpCaption是提示对话框中标题显示的字符串。

lpText显示的字符串是“Hello,PE 文件”,lpCaption显示的字符串是“Binary Diy”。在C32Asm中先插入4096个0字符后,再在0x00002000地址处写入lpText的值,在0x00002020地址处写入lpCaption的值,即如下所示:

8、填充.idata节的数据

.idata节用于保存PE文件中重要的两个部分,即导入表和导入地址表。在填充.idata节的数据之前,先来对.idata节的数据进行分析。

导入表和导入地址表地址分别由数据目录给出,在数据目录中,导入表的RVA为0x00003010,导入地址表的RVA为0x00003000。由于此所构造的PE文件的RVA与FOA地址相同,故不需要进行转换,RVA即为FOA。因此,导入地址表的偏移地址为0x00003000,而导入表的偏移地址为0x00003010。

在本例中需要导入两个DLL文件,因此需构造3个IMAGE_IMPORT_DESCRIPTOR,因为导入表需要由一个全0的IMAGE_IMPORT_DESCRIPTOR来结束。因为导入表中的字段大部分是具体的RVA值,所以先来构造一个占位用的导入表,导入表中字段的填充表如下所示:

OriginalFirstThunk

AA AA AA AA

BB BB BB BB

CC CC CC CC

TimeDateStamp

00 00 00 00

00 00 00 00

CC CC CC CC

ForwarderChain

00 00 00 00

00 00 00 00

CC CC CC CC

Name

AA AA AA AA

BB BB BB BB

CC CC CC CC

FirstThunk

AA AA AA AA

BB BB BB BB

CC CC CC CC

 按照上表可以在文件偏移地址0x00003010处构造占位用的导入表,即如下图所示:

在本例中导入了两个DLL文件,分别是user32.dll和kernel32.dll。在user32.dll中调用了MessageBoxA函数,在kernel32.dll中调用了ExitProcess函数。先构造user32.dll的导入信息,按照IMAGE_IMPORT_DESCRIPTOR结构体来进行构造。

在0x00003050地址处构造导入表的Name字段的值“user32.dll”。

在0x00003060地址处构造导入表的OriginalFirstThunk字段的值“0x00003070”。OriginalFirstThunk指向一个IMAGE_THUNK_DATA,而IMAGE_THUNK_DATA在高位不为1的情况下指向一个IMAGE_IMPORT_BY_NAME结构体。

在0x00003070地址处根据IMAGE_IMPORT_BY_NAME结构体导入函数的名称。

0x00003000地址处是导入地址表,该值由FirstThunk来指向,当磁盘中时,该值与OriginalFirstThunk相同。因此,在文件偏移地址0x00003000处输入0x00003070。按照构造user32.dll的方式构造kernel32.dll的导入信息,导入表信息的填充情况如下所示:

随后则需要根据如下的导入表进行数据填充,下表如下所示:

OriginalFirstThunk

60 30 00 00

90 30 00 00

00 00 00 00

TimeDateStamp

00 00 00 00

00 00 00 00

00 00 00 00

ForwarderChain

00 00 00 00

00 00 00 00

00 00 00 00

Name

50 30 00 00

80 30 00 00

00 00 00 00

FirstThunk

00 30 00 00

08 30 00 00

00 00 00 00

填充后的导入表情况如下所示: 

在LordPE.EXE中打开该EXE文件查看器导入表信息,其步骤为单击右侧列中的“PE Editor”按钮,找到所需要打开的文件(PE.exe文件),在弹出的对话框中单击“Directories”,在接下来所弹出的对话框中找到“ImportTable”,点击其RVA、Size后的“…”按键打开其输出表,其具体步骤如下图所示:

此时则可以查看器导入表的相关信息,具体情况如下所示:

9、填充.text节数据

手写PE文件的最后一步是填充PE文件.text节的内容,即可执行程序的的代码。将按照前面步骤所构造的PE文件使用OD打开,在反汇编窗口,OD会自动定位到0x00401000地址处,即如下图所示:

由上图可知OD反汇编窗口中“HEX数据”列(第二列)显示的全是0字符,这是因为在构造PE文件是并未对.text节填充任何内容。

随后再查看OD的数据窗口,数据窗口是从0x00402000处开始显示的,即如下所示:

如上图可知OD数据窗口中显示字符串“Hello,PE File!!!”和“Binary Diy”,这是在.data节中填充的数据。

在0D数据窗口中通过快捷键“Ctrl+G”跳转至地址0x00403000处,查看导入表信息,即如下图所示:

由上图可知导入地址的信息已经与构造PE文件时有所差别,这是因为导入地址表在载入内存后其中的值会发生变化,其会被填充为实际的导入地址。在OD数据窗口右键单击,在所弹出的快捷菜单栏中选择“Long”、“Address”选项则可以直观地查看导入地址表信息,即如下所示:

通过上图中“数值”列(第二列)显示的“7709CB80”和“76307F40”即是被填充后的函数地址。

在OD中查看手写PE文件后还需要再次查看手写PE文件,这是因为编写代码时会使用到.data节和.idata节的内容,而此时内存中与磁盘中的地址会发生些许改变,因此需要在OD中再次查看构造的数据。

在OD中查看再次查看构造的数据可以得到如下结果:

①.text节的位置是从0x00401000处起始。

②“Hello,PE File!!!”字符串的地址为0x0040200。

③“Binary Diy”字符串的地址为0x403000。

④“MessageBoxA”函数的导入地址为0x403000。

⑤“ExitProcess”函数的导入地址为0x403008。

综上所述,需要在OD反汇编窗口的0x00401000地址起始依次将9个地址的反汇编代码修改成以下反汇编代码:

push 0

push 00402020

push 00402000

push 0

call 0040101A

push 0

call 00401020

jmp [00403000]

jmp [00403008]

首先,双击0x00401000地址处的“反汇编”列(第三列),即可修改其反汇编代码,依次将上述反汇编代码进行填充修改,最终修改结果如下所示:

将上述反汇编代码写入后选中并右键单击执行快捷菜单栏,选中“Copy to executable”执行“Selection”命令即可进入到文件编辑窗口,在空白处单击右键执行快捷菜单中的“Save File”命令,即可将修改保存到文件之中将其并命名为“ple.ex”。至此,手写可执行文件任务完成。

此时双击刚修改后并保存的“pel.exe”文件,此时即可查看到其运行结果如下图所示:

 

 

标签:文件,00,填充,IMAGE,HEADER,导入,PE,手写
From: https://www.cnblogs.com/Auion-idiot/p/17493430.html

相关文章

  • [python][图像切割]给定手写数字图片完成数字切割
    importtorchimporttorch.nnasnnfromtorchvisionimporttransformsfromPILimportImage,ImageOpsimportnumpyasnpimg=Image.open("QQ.png")imgdefCutImage(img):img=img.convert("L")threshold=128img=img.point(......
  • mybatis在mapper.xml中怎么处理大于、小于、不等于号
    第一种方法:使用转义字符大于号>>大于等于号 >= >=小于号 < < 小于等于号 <= <=与 & &amp;双引号 " "单引号 ' &apos;例子: <iftest="beginTimeStr!=null">  andfile.created_at>='${beginTimeStr}'</if>......
  • Windows 找不到文件 gpedit.msc。请确定文件名是否正确后,再试一次
    !!!win11实际操作(以作参考)!!!运行gpedit.msc  运行后报错显示: 解决方案:第一步、新建文本文档,复制以下代码@echooffpushd"%~dp0"dir/bC:\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum>List.txtdir......
  • Apache Superset 身份认证绕过漏洞(CVE-2023-27524)
    漏洞简介ApacheSuperset是一个开源的数据可视化和数据探测平台,它基于Python构建,使用了一些类似于Django和Flask的Pythonweb框架。提供了一个用户友好的界面,可以轻松地创建和共享仪表板、查询和可视化数据,也可以集成到其他应用程序中。由于用户在默认安装过程中,未对SECRET_KEY的默......
  • 解决开发者“兼容”难题,OpenTiny技术探索与应用解读
    简介:OpenTiny是一套企业级组件库解决方案,适配PC端/移动端等多端,涵盖Vue2/Vue3/Angular多技术栈,拥有主题配置系统/中后台模板/CLI命令行等效率提升工具,可帮助开发者高效开发Web应用。6月21日16:30-18:00华为云开源DTSE技术布道师曾令卡将带来**《解决开发者“兼容”......
  • 汽车供应商产品项目开发和质量管理体系过程控制文件汽车供应商产品开发质量管理体系程
    汽车供应商产品项目开发和质量管理体系过程控制文件汽车供应商产品开发质量管理体系程序文件,主机产的开发流程,精心整理,样样齐全,能够轻松应对产品开发和质量工程师工作,请看清楚,电子资料汽车供应商的产品项目开发和质量管理体系是一个包含过程控制文件和程序文件的系统,用于管理汽车......
  • asp.net使用sapnco3.x64,未能加载文件或程序集“XXX”或它的某一个依赖项。试图加载格
    报错原因:引用了sapnco3.x64的dll之后将web应用程序改为了64位,VS启动报错,未能加载文件或程序集“XXX”或它的某一个依赖项。试图加载格式不正确的程序。造成这种故障的原因是,大部分asp.net编译器和容器并没有默认设置为64位,而是以32位兼容模式运行。如果将程序库更换为32位,则运行......
  • 【OpenGL修行】点线面模式设置
    文章目录点点的大小切换线线的宽窄设置虚线绘制多边形多边形正反绘制&反转多边形表面剔除多边形表面镂空通过初始化数组设置镂空样式通过读取bmp文件设置镂空样式点点的大小切换通过glPointSize()函数即可实现点的大小控制,我画了两个大小不同的点,还是挺明显的。voiddisplay1(){......
  • Python中获取路径/文件的父目录
    本教程将讲解在Python中获取一个路径的父目录的各种方法。父目录是指高于或高于给定目录或文件的目录。例如,路径 C:\folder\subfolder\myfile.txt 的父目录是 C:\folder\subfolder。除了根目录外,每个目录都有一个父目录。1、使用 pathlib 模块的 path.parent() 方法获......
  • 【HarmonyOS】如何解决智能穿戴设备中swiper组件右滑与系统退出应用冲突问题(API6 JS)
    【关键字】API6、JS、swiper组件、智能穿戴、setSwipeToDismiss 【问题描述】使用API6JS开发智能穿戴设备HarmonyOS应用,在首页使用swiper组件时,右滑swiper时会退出应用,无法实现swiper右滑效果,效果如下所示:​ 【问题分析与原因】当页面栈只有一个页面时,默认滑动事件分发......