PE导出表分析
本帖最后由 lisir1 于 2022-12-7 21:25 编辑1.导出表的定义
导出表大多存在于DLL中,目的就是用来导出其他exe运行的函数。不管exe或者DLL的本质都是PE文件。
2.准备工作
写一个DLL文件并简单调用一下。
2.1主要代码
2.1.1 .C文件
DLLIMPORT int Add(int a,int b)
{
return a+b;
}
DLLIMPORT int Sub(int a,int b)
{
return a-b;
}
DLLIMPORT int Mul(int a,int b)
{
return a*b;
}
DLLIMPORT int Div(int a,int b)
{
return a/b;
}
DLLIMPORT关键字可以用来修饰某个函数或者时变量就会被导出。
2.1.2 .H文件
DLLIMPORT int Add(int a,int b);
DLLIMPORT int Sub(int a,int b);
DLLIMPORT int Mul(int a,int b);
DLLIMPORT int Div(int a,int b);
2.1.3 测试代码
#include<Windows.h>
#include<stdio.h>
#include<stdlib.h>
int main()
{
typedef int(* FUNT)(int,int); //定义函数指针
HINSTANCE Hint = LoadLibrary("test.dll"); 调用先前写好的DLL
if(Hint==NULL)
{
printf("DLL调用失败"); //测试
return 0;
}
FUNT ADD =(FUNT)GetProcAddress(Hint,"Add");
FUNT SUB =(FUNT)GetProcAddress(Hint,"Sub");
FUNT MUL =(FUNT)GetProcAddress(Hint,"Mul");
FUNT DIV =(FUNT)GetProcAddress(Hint,"Div");
int a=20,b=10;
printf("和为%d\n",ADD(a,b));
printf("差为%d\n",SUB(a,b));
printf("积为%d\n",MUL(a,b));
printf("商为%d\n",DIV(a,b));
FreeLibrary(Hint); //释放
return 0;
}
2.1.4结果
https://img-blog.csdnimg.cn/adf79e8c3aaa4819bae1effa5c7bd9c2.png
2.2导出函数定义声明
> EXPORTS
Add @1
Div @2
Mul @3
Sub @4
3.导出表的位置
导出表位于扩展PE头的DIRECTORY DataDirectory里,该数组有10个成员,第一个就是导出表,即DIRECTORY DataDirectory。
> typedef struct _IMAGE_DATA_DIRECTORY
>{
> DWORD VirtualAddress; //导出表开始的地址 **注意是RVA的值**
> RVADWORD Size; //导出表大小
> }
> IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
关于PE文件结构前面提到很多,这里不再赘述,直接用StudyPE+工具找出,如图:
https://img-blog.csdnimg.cn/7b423e8297d348faa787eb8018211a28.png
选中区域就是数组里的内容,
前四个字节就是导出表的位置:0x00006000是RVA(内存偏移地址)的值,要转化成FOA(文件偏移地址)才能分析;
这里简单说一下,RVA转FOA的步骤:
1.判断RVA是否位于PE头中,如果在,那么RVA==FOA;
2.判断RVA的值在哪一个区段中,求出RVA与该区段开始地址的差值;
3.FOA=节.PointerToRawData(节区在文件中开始地址)+差值
我们来手动算一下,
https://img-blog.csdnimg.cn/fe02c632db9245a3a353142c755d116b.png
1.节表开始的位置在0x00000178,则RVA不属于PE头
2.观察可知RVA介于.edata和.idata区段之间,
(https://img-blog.csdnimg.cn/73d65f74239a4f89a19c1cfe8f61b102.png
差值为:0x00006000-0x00006000=0x0
3.FOA=0x2000+0x0=0x2000
接着四个字节就是导出表的大小:0x00000069;
4.导出表的结构
现在我们找到了导出表的文件地址,贴上导出表的数据结构和对应的二进制代码:
https://img-blog.csdnimg.cn/1360d50e2d794f43bf4b1ecccd5114ae.png
> typedef struct _IMAGE_EXPORT_DIRECTORY {
**0x00**
DWORD Characteristics; //用不到
**0x04**
DWORD TimeDateStamp; //时间戳.编译的时间. 把秒转为时间.可以知道DLL的编译时间.此处和标准PE头的第二个成员一样
**0x08**
WORD MajorVersion;
**0x0a**
WORD MinorVersion;
**0x0c**
DWORD Name; //指向该导出表文件名的字符串,也就是这个DLL的名称 存储的RVA 地址,需要转换
**0x10**
DWORD Base; // 导出函数的起始序号
**0x14**
DWORD NumberOfFunctions; //所有的导出函数的个数
**0x18**
DWORD NumberOfNames; //以名字导出的函数的个数
**0x1c**
DWORD AddressOfFunctions; // 导出的函数地址的 地址表RVA 即函数地址表
**0x20**
DWORD AddressOfNames; // 导出的函数名称表的RVA 即函数名称表
**0x24**
DWORD AddressOfNameOrdinals;// 导出函数序号表的RVA 即函数序号表
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
直接从名字开始分析:导出表文件名所在的地址是:0x6050
差值:0x00006050-0x00006000=0x00000050;
FOA=0x00002000+0x00000050=**0x00002050**;
https://img-blog.csdnimg.cn/ae4de435cf334e81a9c76cd28de0b81a.png
字符串以00结尾,可知该DLL的名字是test.dll;
**导出函数的起始序号(01)稍后分析;**
**导出函数的个数**在0x14位,为4个;
**以名字导出的函数**的个数在0x18位,为4个(说明DLL没有隐藏函数名字,转用序号调用);
导出函数有4个,所以导出函数的地址有4个,是连续的;
**函数地址表**
| 起始序号——>下标 | 函数地址(FOA) | 函数名 (此表中函数名为二进制代码推出)|
|--|--|--|
| 01——>0 | 1450(A50) | Add |
| 02——>1 | 1474(A74) | Div |
| 03——>2 | 1468 (A68)| Mul |
| 04——>3 | 145D (A5D)| Add |
**函数名称表**
| 下标 | 函数名地址(FOA) | 函数名 |
|--|--|--|
| 0 | 6059 (2059)| Add |
| 1 | 605D (205D)| Div |
| 2 | 6061(2061)| Mul |
| 3 | 6065 (2065)| Add |
**导出函数序号(2字节)表**
|下标| 序号 |
|--|--|
| 0 | 00 |
|1|01|
|2|02|
|3|03|
## 下面举一个函数调用过程的例子:
```c
FUNT ADD =(FUNT)GetProcAddress(Hint,"Add");
```
此语句是名称调用,先查询函数名称表,Add函数名对应的下标为0,接下来查询导出函数序号表下标为0的序号,对应的序号为00,再查询函数地址表下标为0,对应的函数地址为1450(A50):
https://img-blog.csdnimg.cn/46c5e392524d41e8a739d9e3659ff167.png
> 55 89 E5 8B 55 08 8B 45 0C 01 D0 5D C3
利用OD分析一下代码:
89E5 mov ebp,esp
8B55 08 mov edx,dword ptr ss: ;ipmsg.<ModuleEntryPoint>
8B45 0C mov eax,dword ptr ss:
01D0 add eax,edx ;ntdll.KiFastSystemCallRet
5D pop ebp ;kernel32.7C817077
C3 retn ;
很容易分析出是加法操作即Add函数;
其他函数类似,至此,PE表分析结束。
## 总结:
PE导出表的分析难点主要在于后三个表,以及它们之间的关系,建议自己手动操作一遍,慢慢体会。 楼主辛苦了。想要加一下分,发现无分了,以后再评分。 学到了,感谢 @lisir1 论坛支持markdown格式,也可以用md格式来编写文章。
页:
[1]