您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列。因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全、逆向分析和恶意代码检测,“系统安全”系列文章会更加聚焦,更加系统,更加深入,也是作者的慢慢成长史。换专业确实挺难的,逆向分析也是块硬骨头,但我也试试,看看自己未来四年究竟能将它学到什么程度,漫漫长征路,偏向虎山行。享受过程,一起加油~
作者前文介绍了三个漏洞,包括Chrome浏览器保存密码渗透解析、通过Chrome浏览器实现Win10蓝屏、音乐软件解密功能复现。这篇文章将介绍基础知识,详细讲解PE文件格式,熟悉各种PE编辑查看工具,针对目标EXE程序新增对话框等,这也为后续PE病毒和恶意代码的攻防打下扎实基础。这些基础性知识不仅和系统安全相关,同样与我们身边的APP、常用软件及系统紧密联系,希望这些知识对您有所帮助,更希望大家提高安全意识,安全保障任重道远。
使用工具包括:
从2019年7月开始,我来到了一个陌生的专业——网络空间安全。初入安全领域,是非常痛苦和难受的,要学的东西太多、涉及面太广,但好在自己通过分享100篇“网络安全自学”系列文章,艰难前行着。感恩这一年相识、相知、相趣的安全大佬和朋友们,如果写得不好或不足之处,还请大家海涵!
接下来我将开启新的安全系列,叫“系统安全”,也是免费的100篇文章,作者将更加深入的去研究恶意样本分析、逆向分析、内网渗透、网络攻防实战等,也将通过在线笔记和实践操作的形式分享与博友们学习,希望能与您一起进步,加油~
- 推荐前文:网络安全自学篇系列-100篇
作者的github资源:
前文分析:
声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。该样本不会分享给大家,分析工具会分享。(参考文献见后)
什么是PE文件?
PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件(可能是间接被执行,如DLL)。
EXE文件格式:
为什么要重点学习这种文件格式呢?
可执行程序是具有不同的形态的,比如用户眼中的QQ如下图所示。
本质上,QQ如下图所示。
PE文件格式总体结构
接着让我们来欣赏下PE文件格式总体结构图,包括:MZ头部、DOS stub、PE文件头、可选文件头、节表、节等。
本文的第二部分我们将对PE文件格式进行详细解析。比如,MZ头文件是定位PE文件头开始位置,用于PE文件合法性检测。DOS下运行该程序时,会提示用户“This Program cannot be run in DOS mode”。
PE文件格式与恶意软件的关系
PE文件解析常用工具包括:
该部分实验内容:
PE文件结构如下图所示,我推荐大家使用010Editor工具及其模板来进行PE文件分析。
MZ头部+DOS stub+PE文件头+可选文件头+节表+节
(1) 使用010Editor工具打开PE文件,并运行模板
该PE文件可分为若干结构,如下图所示。
(2) MZ文件头(000h-03fh)
下图为hello-2.5.exe的MZ文件头,该部分固定大小为40H个字节。偏移3cH处字段Offset to New EXE Header,指示“NT映象头的偏移地址”,其中000000B0是NT映象头的文件偏移地址,定位PE文件头开始位置,用于PE文件合法性检验。
000000B0指向PE文件头开始位置。
(3) DOS插桩程序(040h-0afh)
DOS Stub部分大小不固定,位于MZ文件头和NT映象头之间,可由MZ文件头中的Offset to New EXE Header字段确定。下图为hello-2.5.exe中的该部分内容。
(4) PE文件头(0b0h-1a7h)
该部分包括PE标识、映像文件头、可选文件头。
对应解析如下图所示,包括PE标识、X86架构、3个节、文件生成时间、COFF便宜、可选头大小、文件信息标记等。
010Editor使用模板定位PE文件各节点信息。
PE文件可选文件头224字节,其对应的字段信息如下所示:
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; /*机器型号,判断是PE是32位还是64位*/
BYTE MajorLinkerVersion; /*连接器版本号高版本*/
BYTE MinorLinkerVersion; /*连接器版本号低版本,组合起来就是 5.12 其中5是高版本,C是低版本*/
DWORD SizeOfCode; /*代码节的总大小(512为一个磁盘扇区)*/
DWORD SizeOfInitializedData; /*初始化数据的节的总大小,也就是.data*/
DWORD SizeOfUninitializedData; /*未初始化数据的节的大小,也就是 .data ? */
DWORD AddressOfEntryPoint; /*程序执行入口(OEP) RVA(相对偏移)*/
DWORD BaseOfCode; /*代码的节的起始RVA(相对偏移)也就是代码区的偏移,偏移+模块首地址定位代码区*/
DWORD BaseOfData; /*数据结的起始偏移(RVA),同上*/
DWORD ImageBase; /*程序的建议模块基址(意思就是说作参考用的,模块地址在哪里)*/
DWORD SectionAlignment; /*内存中的节对齐*/
DWORD FileAlignment; /*文件中的节对齐*/
WORD MajorOperatingSystemVersion; /*操作系统版本号高位*/
WORD MinorOperatingSystemVersion; /*操作系统版本号低位*/
WORD MajorImageVersion; /*PE版本号高位*/
WORD MinorImageVersion; /*PE版本号低位*/
WORD MajorSubsystemVersion; /*子系统版本号高位*/
WORD MinorSubsystemVersion; /*子系统版本号低位*/
DWORD Win32VersionValue; /*32位系统版本号值,注意只能修改为4 5 6表示操作系统支持nt4.0 以上,5的话依次类推*/
DWORD SizeOfImage; /*整个程序在内存中占用的空间(PE映尺寸)*/
DWORD SizeOfHeaders; /*所有头(头的结构体大小)+节表的大小*/
DWORD CheckSum; /*校验和,对于驱动程序,可能会使用*/
WORD Subsystem; /*文件的子系统 :重要*/
WORD DllCharacteristics; /*DLL文件属性,也可以成为特性,可能DLL文件可以当做驱动程序使用*/
DWORD SizeOfStackReserve; /*预留的栈的大小*/
DWORD SizeOfStackCommit; /*立即申请的栈的大小(分页为单位)*/
DWORD SizeOfHeapReserve; /*预留的堆空间大小*/
DWORD SizeOfHeapCommit; /*立即申请的堆的空间的大小*/
DWORD LoaderFlags; /*与调试有关*/
DWORD NumberOfRvaAndSizes; /*下面的成员,数据目录结构的项目数量*/
IMAGE_DATA_DIRECTORY DataDirectory[16]; /*数据目录,默认16个,16是宏,这里方便直接写成16*/
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
(5) 节表(1a8h-21fh)
该结构包括3个节,对应上图的3个struct IMAGE_SECTION_HEADER,即“.test”、“.rdata”、“.data”节,其偏移地址对应下图紫色区域,分别是400、600、800的位置。
(6) 3个节
注意,代码节“.text”前46H为数据,后面全是0位填充值,为了实现文件的200H对齐,所以代码节是400H到5ffH。
(7) 引入函数节
⽤来从其他DLL中引⼊函数,引入了kernel32.dll和user32.dll,这个节一般名为“.rdata”。引入函数是被某模块调用的但又不在调用者模块中的函数,用来从其他(系统或第三方写的)DLL中引入函数,例如kernel32.dll、gdi32.dll等。
010Editor打开如下图所示:
详细标注信息如下图所示:(图引自HYQ同学,再此感谢)
(8) 数据节
数据节实际大小58h,对齐后大小200h,地址为800h-9ffh,包括对话框弹出的具体内容。
使用Ollydbg对该程序进行初步调试,了解该程序功能结构,在内存中观察该程序的完整结构。注意,内存对齐单位和文件对齐单位的不同,内容和文件中IAT表内容的不同。我们以hello-2.5.exe程序作为示例进行调试。
第一步,打开OD加载PE文件。
OD是一款PE文件动态调试器,此时程序断点自动停止在程序入口点00401000H位置。
在010Editor中,我们可以看到,该PE程序基地址是400000h,程序入口地址是1000h,两个相加为加载至内存中的地址,即401000h。
第二步,动态调试程序。
当我们双击地址位置,则可以下断点且变红,比如0040100Fh。
接着查看对应调试快捷键,F7是单步步入,F8是单步步过。
我们直接按F8单步步过,此时的位置会CALL一个MessageBoxA函数。
直接单步步过,此时会弹出第一个对话框,点击“确定”按钮。
第三步,动态调试程序之数据跟随。
接着我们看左下角部分的内存数据,在该区域按下“Ctrl+G”在数据窗口中跟随,输入基地址400000。
此时可以看到加载到内存中的数据,可以看到该数据与010Editor打开的PE文件数据一致的。
接着继续按F8单步步过弹出第二个窗口。
右上角是它寄存器的值,包括各个寄存器中的数据,我们实验中主要使用的寄存器包括EAX、ECX、EDX、EBX等。
接着步过0040102E,它是退出进程ExitProcess的位置,此时进程已经终止,如下图所示。
实验讲到这里,使用OD动态调试的PE文件的基础流程就讲解完毕,后续随着实验深入,我们还会使用该工具。
使用010Editor修改该程序,使得该程序仅弹出第二个对话框。思路为:修改程序的入口点地址。
第一步,确定程序入口点地址。
前面我们用010Editor(或PEView)确定了可选文件头中Adress od Entry Point这一项,里面的值是1000h,这个是RVA相对便宜地址,偏移在D8处大小为4字节。装载到内存后,程序入口点应该是401000h。
第二步,确定第二个对话框的偏移地址。
然后看到OD里,为第二个call做传入参数的push语句从401016开始,故我们将1000改为1016即可弹出第二个窗口。
第三步,利用PE编辑工具修改程序入口地址。
注意,可以在PE模板中修改该值,将AddressOfEntryPoint修改为1016h。
第四步,双击运行仅弹出第二个对话框。
实验内容如下:
在文件中的引入表结构:IMAGE_IMPORT_DESCRIPTOR
在内存中的引入表结构:IMAGE_IMPORT_DESCRIPTOR
(1) 使用PEView查看文件属性
PEview.exe工具是一款可以进行PE文件解析的强大PE文件解析工具,通过PEview软件可以分析PE文件的详细格式。如下图所示,左边是相应结构,右边是对应的数据。PEView虽然能读写PE文件的数字化模板,但作者更推荐使用010Editor编辑。
ImageBase: PE文件在内存中的优先装载地址。
RVA地址:Relative Virtual Address,相对虚拟地址,它是相对内存中ImageBase的偏移位置。
比如PE文件头(IMAGE_NT_HEADERS)中,AddressOfEntryPoint为1000h,基地址为400000h。
下面是导入表信息:
第一条指令在内存中的地址计算方法如下图所示:
(2) 使用stud_PE查看文件属性
该软件用于显示头部、DOs、区段、函数等信息,包括导入表、导出表等。
显示对应的3个节信息。
显示该EXE程序加载的两个DLL文件及函数。
user32.dll是Windows用户界面相关应用程序接口,用于包括Windows处理、基本用户界面等特性。这里采用PEview+STUD_PE方法分析,找到系统System32目录下的user32.dll文件,用010Editor打开并分析该文件引出表,找出函数MessageBoxA的地址,并验证该地址是否正确。
第一步,使用PEView打开user32.dll,查找MessageBoxA的RVA。
找到系统System32目录下的user32.dll文件并打开,在AddressOfNames数组(Name Pointer Table)中找到MessageBoxA字符串,其数组序号为01DD。
在Ordinal Table找到序号(Value)01DD项,Data为01DC;而由于Ordinal Table中Data从0开始,故在Address Table中找到第01DC+1,即01DD项。
Address Table中01DD项的Data为407EA,也即MessageBoxA的RVA(相对虚拟地址)。
第二步,得到RVA为38D9后,用Stud_PE打开user32.dll,使用Raw与RVA转换功能,得其VA为77D507EA。
第三步,用OD打开hello-2.5.exe,查看MessageBoxA,两个值并不一致。
用OD调试程序的时候发现其VA地址其实是74DA0F40,与计算出的77D507EA位置有所偏差,然后查找资料发现这应该是操作系统处于安全原因改变了真实地址。
之后我在虚拟机的XP环境下进行了相同的实验,其值为77D507EA,完全一致。注意,user32.dll来自XP操作系统。
最终得出结论:XP之后的操作系统对真实地址做了相关的保护。
实验内容是手工修改hello-2.5.exe程序,使得其可以弹出第三个对话框(提示框标题为“武汉大学信安病毒实验”,内容为:你的姓名+日期)。基本思路为:
第一步,使用OD打开目标PE文件。
如下图所示,包括第一个对话框、第二个对话框和退出进程。
第二步,修改数据区“.data”。
注意,从下图所示的位置开始修改,地址为0040 3059,需要留一个00字符,表示截断符。同样,title和text之间也要用00隔开。
右键选择“二进制”->“编辑”,其快捷键为Ctrl+E。
输入标题“武汉大学信安病毒实验”。
此时标题已经成功写入,接着空一个00,从下图所示位置接着写入正文内容,地址为0040 306A。
接着写入内容,如下图所示。
增加内容如下图所示:
注意,现在我们只是写入内存,而没有写入PE文件中。
第三步,选中新增内容,右键点击“复制到可执行文件”。
此时显示如下图所示,成功复制到PE文件中。
第四步,修改代码段。
我们需要在代码段第二个对话框之后插入新的数据,从而弹出第三个对话框。需要将下图阴影部分整体后移。
首先,在0040101B位置进行数据跟随。
数据跟随显示如下图所示,代码段中每个弹框是22字节,从“68 40”开始,共计两个弹框。
接着我们将退出函数的所有字节复制出来,为了整体后移。
6A 00 E8 01 00 00 00 CC FF 25 00 20 40 00 FF 25 0C 20 40 00 FF 25 08 20 40
接着空22字节用于填写第三个对话框信息,将复制的二进制数据粘贴出来(Ctrl+E),如下图所示。
将第一个对话框22字节代码复制至第三个对话框,如下图所示:
第五步,修改.text段。
第二个栈的参数是它的Title,需要将偏移地址设置为对应的位置。
右键“数据窗口中跟随”->“立即数”。
第三个窗口标题的地址修改为“00403059”。
Ctrl+E修改为59。
修改第三个窗口Text地址为“0040306A”。
第六步,修改CALL对应的地址。
注意,三个对话框CALL对应的E8值需要以00401056(user32.MessageBoxA)为基址。
修改如下图所示:
第七步,保存文件。
选择“复制到可执行文件”->“所有修改”按钮。
点击“全部复制”。
最终保存修改的PE文件。
运行PE文件,成功弹出第三个框。
新增的内容如下图所示:
注意:
文章写到这里,就介绍完毕,本文主要讲解PE文件解析、PE编辑工具使用和PE结构修改,属于系统安全和PE逆向相关知识,希望对您有所帮助。内容包括:
后续将学习PE文件图标修改、对话框分析、EXE解析、加壳解密等。希望这系列文章对您有所帮助,同时真的感觉自己技术好菜,要学的知识好多。未知攻焉知防,人生漫漫其路远兮,作为初学者,自己真是爬着前行,感谢很多人的帮助,继续爬着,继续加油!
欢迎大家讨论,是否觉得这系列文章帮助到您!如果存在不足之处,还请海涵。任何建议都可以评论告知读者,共勉~
学安全一年,认识了很多安全大佬和朋友,希望大家一起进步。这篇文章中如果存在一些不足,还请海涵。作者作为网络安全和系统安全初学者的慢慢成长路吧!希望未来能更透彻撰写相关文章。同时非常感谢参考文献中的安全大佬们的文章分享,深知自己很菜,得努力前行。编程没有捷径,逆向也没有捷径,它们都是搬砖活,少琢磨技巧,干就对了。什么时候你把攻击对手按在地上摩擦,你就赢了,也会慢慢形成了自己的安全经验和技巧。加油吧,少年希望这个路线对你有所帮助,共勉。
2020年8月18新开的“娜璋AI安全之家”,主要围绕Python大数据分析、网络空间安全、人工智能、Web渗透及攻防技术进行讲解,同时分享CCF、SCI、南核北核论文的算法实现。娜璋之家会更加系统,并重构作者的所有文章,从零讲解Python和安全,写了近十年文章,真心想把自己所学所感所做分享出来,还请各位多多指教,真诚邀请您的关注!谢谢。
(By:Eastmount 2021-01-25 星期一 夜于武汉 http://blog.csdn.net/eastmount/ )
参考文献:
[1] 武大《软件安全》课程
[2] 师姐PPT、师弟师妹PPT及视频
[3] PE文件格式分析 - Erio
[4] PE文件学习系列一为什么是PE - Egojit
[5] 第二章PE文件结构解析 - 百度文库