PE文件解析(3):导出表的解析

编程入门 行业动态 更新时间:2024-10-04 21:23:57

PE<a href=https://www.elefans.com/category/jswz/34/1771438.html style=文件解析(3):导出表的解析"/>

PE文件解析(3):导出表的解析

文章目录

  • 导出表
    • 导出表的结构体解析
    • 寻找导出表的地址
    • 代码解析导出表

可选NT头的结构体中存储着ExportTable(导出表)和 Import Table(导入表)

导出表

什么是导出表?

在我们写的DLL或者EXE导出的函数,会在程序运行时,把这个API加载入程序的运行内存中。
导出表记录了我们加载的这些API函数的的地址,名称,与序号。


导出表具有的特性:

1.导出表的地址是一个RVA,通过基址+偏移可以得到导出表

2.导出表可以用序号记录导出的函数

3.导出表也可以直接显示函数的名字


导出表的位置:
在IMAGE_OPTIONAL_HEADER结构体,即可选NT头的DataDirectory数组字段保存。 这个字段又叫做 数据目录表,是PE文件的一个非常重要的东西。
导出表位于: DataDirectory[0]的位置,是一个RVA偏移,需要我们进行转换才是真正的地址


导出表的结构体解析

typedef struct _IMAGE_EXPORT_DIRECTORY {DWORD   Characteristics;//没用DWORD   TimeDateStamp;//时间戳WORD    MajorVersion;//没用WORD    MinorVersion;//没用DWORD   Name;//指向导出表文件名 RVA -->FOA+FileBuff=char *name;DWORD   Base;//导出函数起始序号DWORD   NumberOfFunctions;//导出函数个数DWORD   NumberOfNames;//以名称导出函数个数DWORD   AddressOfFunctions;//导出函数地址表 RVA-->FOA +FileBuff=            DWORD   AddressOfNames;    //导出函数名称表     // RVA from base of imageDWORD   AddressOfNameOrdinals; //导出函数序号表 // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, * PIMAGE_EXPORT_DIRECTORY;

重要的几个字段:

  • Name:导出表函数的名称,是一个RVA偏移。
  • NumberOfFunctions:导出函数的个数
  • NumberOfNames:以名称导出函数个数
  • AddressOfFunctions:导出函数地址表
  • AddressOfNames:导出函数名称表
  • AddressOfNameOrdinals:导出函数序号表


010editor解析:

寻找导出表的地址

  1. 根据数据目录表找到导出表(索引0 )的RVA:19060h
  2. 转到区段表:计算这个RVA在哪一个区段内,区段具有virtualaddress:区段的RVA偏移地址,virtualSize:区段的字节大小,pointofRawData:区段的FOA。
  3. 计算可知: 19060h > virtualAddress AND 19060h <virtualAddress +VirtualSize ,所以这个导出表的起始地址位于区段内部,所以利用公式:数据的FOA = 数据的RVA - 区段的RVA + 区段的FOA 可以得到数据的FOA,也就是在文件中的偏移: 19060h - 17000h + (5C00)h = 7C60h,所以7C60就是我们的导出表的FOA,再加上基址即可得到我们的真正地址。
  4. 010editor根据偏移可以直接得到导出表位置,无需加上基址:这就是我们的导出表的位置: 注意:7C60不是真正的地址,7C60+imagebase才是

代码解析导出表

void cPE::GetExportTable()
{/*得到导出表的的信息*/// 第一个存储的就是导出表的偏移地址IMAGE_DATA_DIRECTORY ExPortAddr =  pOptionHeader->DataDirectory[0];// 找到导出表PIMAGE_EXPORT_DIRECTORY ExportTable = (PIMAGE_EXPORT_DIRECTORY)(RvaToFoa(ExPortAddr.VirtualAddress) + FileBuff);if (ExportTable == NULL || ExportTable->NumberOfFunctions == 0){printf("导出表为空!\n");return;}// 打印导出表的名称char* DllName =(RvaToFoa(ExportTable->Name) + FileBuff);	printf("DLL名称:%s\n", DllName);//获取其他函数信息DWORD* FuncAddr = (DWORD*)(RvaToFoa(ExportTable->AddressOfFunctions) + FileBuff);WORD* FuncOrder = (WORD*)(RvaToFoa(ExportTable->AddressOfNameOrdinals) + FileBuff);DWORD* FuncName = (DWORD*)(RvaToFoa(ExportTable->AddressOfNames) + FileBuff);bool NameIsNull{ false };for (UINT i = 0; i < ExportTable->NumberOfFunctions; i++){printf("函数地址:%x\t", *FuncAddr);for (UINT Order = 0; Order < ExportTable->NumberOfNames; Order++){//看看在序号表中有没有等于地址表的索引的if (FuncOrder[Order] == i){//如果序号表存在,则取序号表的索引 i,即取名称表的第i的元素printf("序号:%d\t", FuncOrder[Order]);NameIsNull = false;char* Name = RvaToFoa(FuncName[Order]) + FileBuff;printf("%s\n", Name);break;}else{NameIsNull = true;}}if (NameIsNull){printf("NoName\n");}FuncAddr++;}
}

地址,序号与名称表:
序号表Order和名称Name表的索引是一致的。

例如:我们已知函数地址表中MessageBox的地址(我们此时还不知道名称),如何通过地址找到函数的名称?

  1. AddressFunctions: 索引为6
  2. 根据索引为6,找到序号表中序号等于6的索引,得到索引5
  3. 找到名称表中索引为5的名称,即找到了MessageBoxW的名称。

注: 有关RVA和FOA 和NT头部分解析请看我前面的几篇博客!!!!!!

更多推荐

PE文件解析(3):导出表的解析

本文发布于:2024-02-07 04:50:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1753294.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:文件   PE

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!