吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1603|回复: 3
收起左侧

[原创] PE导出表分析

  [复制链接]
lisir1 发表于 2022-12-6 23:55
本帖最后由 lisir1 于 2022-12-7 21:25 编辑

1.导出表的定义
导出表大多存在于DLL中,目的就是用来导出其他exe运行的函数。不管exe或者DLL的本质都是PE文件。

2.准备工作
写一个DLL文件并简单调用一下。

2.1主要代码
2.1.1   .C文件

[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文件


[C++] 纯文本查看 复制代码
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   测试代码


[C++] 纯文本查看 复制代码
#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  结果


2.2导出函数定义声明


[C#] 纯文本查看 复制代码
> EXPORTS
    Add @1
    Div @2
    Mul @3
    Sub @4


3.导出表的位置
导出表位于扩展PE头的DIRECTORY DataDirectory[10]里,该数组有10个成员,第一个就是导出表,即DIRECTORY DataDirectory[0]。
[C++] 纯文本查看 复制代码
> typedef struct _IMAGE_DATA_DIRECTORY
>  {
> DWORD   VirtualAddress;        //导出表开始的地址       **注意是RVA的值**
> RVADWORD   Size;                //导出表大小
> }
> IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;


关于PE文件结构前面提到很多,这里不再赘述,直接用StudyPE+工具找出,如图:

选中区域就是数组里的内容,
前四个字节就是导出表的位置:0x00006000是RVA(内存偏移地址)的值,要转化成FOA(文件偏移地址)才能分析;

这里简单说一下,RVA转FOA的步骤:
1.判断RVA是否位于PE头中,如果在,那么RVA==FOA;
2.判断RVA的值在哪一个区段中,求出RVA与该区段开始地址的差值;
3.FOA=节.PointerToRawData(节区在文件中开始地址)+差值


我们来手动算一下,

1.节表开始的位置在0x00000178,则RVA不属于PE头

2.观察可知RVA介于.edata和.idata区段之间,

(


差值为:0x00006000-0x00006000=0x0


3.FOA=0x2000+0x0=0x2000

接着四个字节就是导出表的大小:0x00000069;
4.导出表的结构
现在我们找到了导出表的文件地址,贴上导出表的数据结构和对应的二进制代码:


>
[C] 纯文本查看 复制代码
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**;




字符串以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):



> 55 89 E5 8B 55 08 8B 45 0C 01 D0 5D C3

利用OD分析一下代码:


[Asm] 纯文本查看 复制代码
89E5          mov ebp,esp
8B55 08       mov edx,dword ptr ss:[ebp+0x8]           ;  ipmsg.<ModuleEntryPoint>
8B45 0C       mov eax,dword ptr ss:[ebp+0xC]
01D0          add eax,edx                              ;  ntdll.KiFastSystemCallRet
5D            pop ebp                                  ;  kernel32.7C817077
C3            retn                                 ;  


很容易分析出是加法操作即Add函数;
其他函数类似,至此,PE表分析结束。
## 总结:


PE导出表的分析难点主要在于后三个表,以及它们之间的关系,建议自己手动操作一遍,慢慢体会。

免费评分

参与人数 5吾爱币 +11 热心值 +5 收起 理由
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
笙若 + 1 + 1 谢谢@Thanks!
zhczf + 1 + 1 用心讨论,共获提升!
Panel + 1 + 1 用心讨论,共获提升!
飘零星夜 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

zhczf 发表于 2022-12-8 11:42
楼主辛苦了。想要加一下分,发现无分了,以后再评分。
rgzz 发表于 2022-12-11 21:32
Hmily 发表于 2022-12-29 18:24
@lisir1 论坛支持markdown格式,也可以用md格式来编写文章。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-24 03:43

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表