当前位置 : 主页 > 编程语言 > java >

滴水逆向-导出表-课堂笔记

来源:互联网 收集:自由互联 发布时间:2022-06-23
相关知识点 导出表: 导出表结构分析 1、如何定位导出表: 数据目录项的第一个结构,就是导出表. typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DA

滴水逆向-导出表-课堂笔记_逆向
滴水逆向-导出表-课堂笔记_逆向_02

相关知识点

导出表:

导出表结构分析

1、如何定位导出表:

数据目录项的第一个结构,就是导出表.

typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

VirtualAddress 导出表的RVA

Size 导出表大小

2、导出表结构

上面的结构,只是说明导出表在哪里,有多大,并不是真正的导出表.

如何在FileBuffer中找到这个结构呢?在VirtualAddress中存储的是RVA,如果想在FileBuffer中定位

必须要先将该RVA转换成FOA.

真正的导出表结构如下:


typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 未使用
DWORD TimeDateStamp; // 时间戳
WORD MajorVersion; // 未使用
WORD MinorVersion; // 未使用
DWORD Name; // 指向该导出表文件名字符串
DWORD Base; // 导出函数起始序号
DWORD NumberOfFunctions; // 所有导出函数的个数
DWORD NumberOfNames; // 以函数名字导出的函数个数
DWORD AddressOfFunctions; // 导出函数地址表RVA
DWORD AddressOfNames; // 导出函数名称表RVA
DWORD AddressOfNameOrdinals; // 导出函数序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;


导出函数两种方式:
(1)名字导出 (2)序号导出

下面是重要参数选项:

AddressOfFunctions AddressOfNameOrdinals AddressOfNames

0 Fn1地址 0 3 0 0x12345678
1 Fn2地址 1 1 1 0x12345678
2 Fn3地址 2 4 2 0x12345678
3 Fn4地址 3 7 3 0x12345678
4 Fn5地址 4 8 4 0x12345678
Fn6地址 6 0x12345678
Fn7地址 7 0x12345678
.. 0x12345678
.. 0x12345678
.. 0x12345678
.. 0x12345678
.. 0x12345678
Fnx地址 N 0x12345678

宽度4 宽度2 宽度4

数量:NumberOfFunctions 数量:NumberOfNames 数量:NumberOfNames

3、AddressOfFunctions说明:

该表中元素宽度为4个字节

该表中存储所有导出函数的地址

该表中个数由NumberOfFunctions决定

该表项中的值是RVA, 加上ImageBase才是函数真正的地址

定位:

IMAGE_EXPORT_DIRECTORY->AddressOfFunctions 中存储的是该表的RVA 需要先转换成FOA

4、AddressOfNames说明:

该表中元素宽度为4个字节

该表中存储所有以名字导出函数的名字的RVA

该表项中的值是RVA, 指向函数真正的名称

AddressOfNames 特别说明:

0x12345678 1、函数的真正的名字在文件中位置是不确定的
0x12345678 DXXXXXXXXX
0x12345678 2、但函数名称表中是按名字排序的
0x12345678
0x12345678 AXXXXXXXXXXX 也就是说,A开头的函数在AddressOfNames排在最前面.
0x12345678
0x12345678 CXXXXXX 但AXXXXXX这个真正的名字,可能排在BXXXXX后面
0x12345678
0x12345678 BXXXXXXXXX 3、如果想打印名字,要先将AddressOfNames转换为FOA
0x12345678
0x12345678
0x12345678
0x12345678


5、AddressOfNameOrdinals

该表中元素宽度为2个字节
该表中存储的内容 + Base = 函数的导出序号


总结:

为什么要分成3张表?

1.函数导出的个数与函数名的个数未必一样.所以要将函数地址表和函数名称表分开.

2.函数地址表是不是一定大于函数名称表?

未必,一个相同的函数地址,可能有多个不同的名字.

3.如何根据函数的名字获取一个函数的地址?
函数VA = ImageBase + 0x1234
函数名称表 函数序号表 函数地址表

4.如何根据函数的导出序号获取一个函数的地址?

假设导出序号是10

Base 的值是5

函数地址:10 - 5 = 5

找出下标为5的函数地址即可;




课后练习

1.编写程序打印所有的导出表信息;

typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 未使用
DWORD TimeDateStamp; // 时间戳
WORD MajorVersion; // 未使用
WORD MinorVersion; // 未使用
DWORD Name; // 指向该导出表文件名字符串
DWORD Base; // 导出函数起始序号
DWORD NumberOfFunctions; // 所有导出函数的个数
DWORD NumberOfNames; // 以函数名字导出的函数个数
DWORD AddressOfFunctions; // 导出函数地址表RVA
DWORD AddressOfNames; // 导出函数名称表RVA
DWORD AddressOfNameOrdinals; // 导出函数序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;


地址空间:这个地址空间指的是PE文件被加载到内存的空间,是一个虚拟的地址空间,
之所以不是物理空间是因为数据在内存中的位置经常在变,这样既可以节约内存开支又可以避开错误的内存位置。
这个地址空间的大小为4G,但其中供程序装载的空间只有2G而且还是低2G空间,高2G空间则被用于装载内核DLL文件,
所以也被称作内核空间。

文件映射:PE文件在磁盘上的状态和在内存中的状态是不一样的,我们把PE文件在磁盘上的状态称作FileBuffer,
在内存中的状态称为ImageBuffer。当PE文件通过装载器装入内存是会经过“拉伸”的过程,
所以它在FileBuffer状态下和ImageBuffer状态下的大小是不一样的;

VA:英文全称是Virual Address,简称VA,中文意思是虚拟地址。指的是文件被载入虚拟空间后的地址。

ImageBase:中文意思是基址,指的是程序在虚拟空间中被装载的位置。

RVA:英文全称是Relative Virual Address,简称RVA,中文意思是相对虚拟地址。
可以理解为文件被装载到虚拟空间(拉伸)后先对于基址的偏移地址。
计算方式:RVA = VA(虚拟地址) - ImageBase(基址)。它的对齐方式一般是以1000h为单位在虚拟空间中对齐的(传说中的4K对齐),
具体对齐需要参照IMAGE_OPTIONAL_HEADER32中的SectionAlignment成员;

FOA:英文全称是File Offset Address,简称FOA,中文意思是文件偏移地址。
可以理解为文件在磁盘上存放时相对于文件开头的偏移地址。它的对齐方式一般是以200h为单位在硬盘中对齐的(512对齐),
具体对齐需要参照IMAGE_OPTIONAL_HEADER32中的FileAlignment成员;

下面是判断RVA和FOA的计算方法;

<1> 得到RVA的值,RVA=内存地址-ImageBase(ImageBase是IMAGE_OPTION_HEADER中的成员);
<2> 比较RVA与SizeofHeaders的大小,判断RVA是否位于PE头中,如果是的话,FOA=RVA,(SizeofHeaders是IMAGE_OPTION_HEADER中的成员);
<3> 判断RVA位于哪个节,
  RVA >= 节.VirtualAddress
  RVA <= 节.VirtualAddress + 当前节内存对齐后的大小
  差值 = RVA - 节.VirtualAddress
<4> FOA = 节.PointerToRawData + 差值

总结:
总体来说,首先就是要判断某一个内存地址是否是在SizeOfHeaders里面,如果是,那么RVA=FOA,因为SizeOfHeaders在拉伸前后不变;
而如果不在SizeOfHeaders,里面那么就要判断这个内地址落在哪个节的范围,然后减去落在这个节VirtualAddress的地址,得到她们;
的差值,将得到的这个差值加上这个节中对应在文件中偏移的地址(PointerToRawData)结果就是FOA;

实例计算:
1.判断不带ImageBase地址0x00051EC0在哪个节里面;
2.通过计算查找,发现0x00051EC0是落在第二个节里面,因为:VirtualAddress + VirtualSize > 上面不带ImageBase的地址;
第二个节对应的VirtualAddress和VirtuallSIze 0x00046000+0x0000D74D=0x0005374D > 0x00051EC0;
3.确认在哪个节里面之后就可以根据上面的总结计算,下面是导出表给出的VirtualAddress地址加上ImageBase然后减去ImageBase;
对应的距离,刚好就是0x00051EC0
RVA = 0x00051EC0 + 0x00400000 - 0x00400000 = 0x00051EC0;
4.计算出她们之间的差值
差值 = 0x00051EC0 - 0x00046000 = 0x0000BEC0;
5.计算FOA的值
FOA = 对应节的PointerToRawData + 差值
FOA = 0x00046000 + 0x0000BEC0 = 0x00051EC0;
注意:这里的0x00046000是因为此程序中节里面的PointerToRawData=VirtualAddress=0x00046000;

2.GetFunctionAddrByName(FileBuffer指针,函数名指针)

3.GetFunctionAddrByOrdinals(FileBuffer指针,函数名导出序号)

上面计算FOA的过程验证

验证方式是使用winhex打开存储在硬盘位置的ipmsg.exe和意见打开的ipmsg.exe程序,找到其对应的导出表的VirtualAddress地址;

滴水逆向-导出表-课堂笔记_虚拟空间_03
滴水逆向-导出表-课堂笔记_导出函数_04
滴水逆向-导出表-课堂笔记_导出函数_05
滴水逆向-导出表-课堂笔记_逆向_06
滴水逆向-导出表-课堂笔记_虚拟空间_07

迷茫的人生,需要不断努力,才能看清远方模糊的志向!

上一篇:滴水逆向-静态链接库与动态链接库
下一篇:没有了
网友评论