PE文件的基本结构图
- 第一个字段4D 5A被定义成字符“MZ”作为识别标志,后面的一些字段指明了入口地址、堆栈位置和重定位表位置等。
- 对于PE文件来说,有用的是最后的e_lfanew字段,这个字段指出了真正的PE文件头在文件中的位置,这个位置总是以8字节为单位对齐的。
判断是否是PE文件
1.使用Winhex工具,从文件头4D 5A(MZ)开始,跳转3C(即偏移3C);
2.跳转后可读取到数据为0000 00B0;
3.再跳转到地址0000 00B0;
4.若该地址数据为50 45(即PE)就为PE文件。
导入表
导入表结构:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk; // 指向 导入名称表INT(ImportNameTable)的RVA
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name; // 指向 DLL名称的地址 RVA
DWORD FirstThunk; // 指向 导入地址表IAT(ImportAddressTable)的RVA
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
导入表&桥1&桥2位置
注意!WinHex右往左读
如:
读值:2084
使用软件:PEditor、WinHex
1.WinHex打开,PE头偏移80H得到导入表的RVA。
2.根据节表(可用PEditor查看),将RVA转成FOA,WinHex跳转该FOA得到一串20个字节描述的一个dll文件,如下图蓝色部分。
FOA=(RVA-Virtual Offset)+Raw Offset
节表结构
如:
3.第一个字节为桥1的RVA,计算其FOA并跳转得到桥1的数组,每四个字节为一组,到四个字节均为00结束。每一组的FOA跳转得到对应函数。
4.第四个字节,计算其FOA跳转得到winresult.dd.
5.第五个字节为桥2RVA,计算其FOA并跳转得到静态指向桥2的数组。
注意!桥2动态和静态指向数组不一样
动态时桥2组数里是user32.dll的函数地址,所以指向桥2的地址会是
导出表
导出表结构:
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
导出表位置
软件:WinHex
1.PE偏移78H,得到导出表RVA。
2.根据节表将RVA转成FOA,并跳转该FOA。
3.从这开始对应导出表的数据结构写出对应名称的数值。
其中Address of Functions要计算FOA,跳转得到该值为第一个导出函数RVA,依次往下分别为其余三个函数地址的RVA值,再转FOA跳转得到函数名。
Address of NameOrdinal与Address of Functions方式相同。
实现两种方式的导出函数覆盖
达到.exe以动画打开效果
(修改导出结构中函数入口地址)
1.先找到两个函数FadeInOpen和AnimatOpen的入口地址;
2.将两个地址进行交换(exe文件想要调用FadeInOpen时实际是调用了AnimateOpen)
使.exe运行会弹出messagebox
(覆盖fadeinopen函数的指令代码)
1.在winhex找到fadeinopen的地址从那里开始修改
2.保存原始栈基地址
3.维持栈平衡的返回命令
4.到OD单步执行user32.messageboxA看缓冲区的地址,将其地址覆盖到该函数最好四个字节。
练习题
1.某变量的FOA为410H,试分析其位于哪一节(给出分析过程。)?该变量的RVA为多少(给出计算过程)?
由节表可知,.text的起始为00000400,大小为00000200;所以变量的FOA为410H属于.text节;
因为400<410<600
RVA = (410-400)+1000=1100H
2.查阅资料,简述病毒获取Kernel32模块基地址的重要性
答:获取了kernel32.dll基址后就可以获取LoadLibrary()和GetProcAddress()的地址,有了它们之后我们就不用大费周章的去找API了。使用Kernel32.DLL中的LoadLibrary 和GetProcAddress 两个API可以获取任意DLL中导出的任意函数在进程空间中的位置即可进行入侵。
3.利用PEditor打开firstwindow.exe,分析该PE的导入表。该PE文件描述导入表的数据目录项的偏移是多少?导入表的VA和大小分别是多少?该EXE用到多少个DLL文件。验证与PEditor中查看到的是否一致?
导入表的数据目录项的偏移是:80H;
导入表的VA和大小分别是:00402084(RVA是2084;基址是00400000;VA=RVA+基址),大小FOA=RVA-virtual offest+Raw offest=684;跳到FOA(文件偏移处)可以看见;
该EXE用到3个DLL文件,因为在winhex打开找到导入表对应数组时连续3个20个字节才到全为0的20字节。
对比PEditor:一致!
4.利用Winhex查看Winresult.DLL 并分析其提供函数的名字及对应入口地址。画出导出表结构图(类似图2)
分析:
导出表:
RVA:00002140,Vitual Offset:00002000,Raw Offset:800
因此FOA:940,跳到地址940既是导出表的地址:
标志:00000000
时间日期记录:4D510EEE
Dll的首版本号:0000
Dll的次版本号:0000
Dll的模块名(RVA):00002190,FOA:990,跳转可得文件名为winresult.dll
基数:00000010
函数的总数:00000004
有名函数的总数:00000004
FAT表RVA值(AddressOfFunctions):00002168 FOA(968),跳转的得到968地址值为00001183
该值为第一个导出函数RVA,依次往下分别为其余三个函数地址的RVA值:00001022、00001282、00001323
与Peditor查看到的一致:
FAT表RVA值(AddressOfNames):00002178 FOA:(978)
跳转到978地址得到地址为0000219E
第一个导出函数RVA值:0000219E,FOA=99E
第二个导出函数RVA值:000021AB,FOA=9AB
第三个导出函数RVA值:000021B7,FOA=9B7
第四个导出函数RVA值:000021C2,FOA=9C2
查看对应文件偏移量处的函数名:
5.pedtior打开本机的kernel32.DLL,其导出表RVA?导出表大小?找到无导入表情况下需要的两个函数的名称及其入口地址。 (本机的kernel32.DLL用winhex存到桌面)
导出表RVA:00092D30导出表大小:0000DC14
在得到kernel32.dll的基地址后,就可以获得LoadLibray()函数来继续加载其他的动态链接库,再通过GetProcAdress()函数来获得相应需要的api函数地址,这样也就做到了可移植性的要求。
LoadLibray:
GetProcAddress:
6.实现两种方式的导出函数覆盖-1
修改winResult.dll导出结构中的函数入口地址,达到firstwindow.EXE以动画方式打开的效果。描述你所做的工作。
1.先找到两个函数FadeInOpen和AnimateOpen的入口地址:00001022和0001282
2.将两个地址进行交换(exe文件想要调用FadeInOpen时实际是调用了AnimateOpen)
7.实现两种方式的导出函数覆盖-2 (P166)
覆盖fadeinopen函数的指令代码:使得运行firstwindow.EXE程序时会弹出messagebox(OD打开看地址)。如下图所示:
1.Winhex打开winResult.dll,修改函数FadeInOpen(文件起始偏移0x0682)定义部分:保存原始栈基地址、维持栈平衡的返回命令:
原始得:
修改后:
红框部分是messageboxA在user32.dll的地址,可以通过OD打开其他导入了messageboxA的文件查看其所在的地址,例如上次作业的Helloworld.exe,注意在Winhex上使用小端方式!
运行:
以上是学习PE文件的一些笔记,欢迎大家查阅指正~