PE导出导出表

本文详细介绍了在C++程序中如何通过提供的入口点函数实现导出函数的功能,包括检查模块头文件和导入库,以及如何通过模块地址和函数名获取实际的函数地址。

// export_test.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
int export_jixi(unsigned long pmodule ,char *function)
{
 if(pmodule == 0)
  return -1;
 if(IMAGE_DOS_SIGNATURE != *(unsigned short*)pmodule)
  return -1;
 unsigned long peheader=pmodule + *(unsigned long*)(pmodule + 0x3C);
 if(IMAGE_NT_SIGNATURE != *(unsigned long*)peheader)
  return -1;
 unsigned long export=*(unsigned long*)(peheader + 0x78) + pmodule;
 if(export == pmodule)
  return -1;
 unsigned long base=0;
 unsigned long numoffunction=0;
 unsigned long numofname=0;
 unsigned long *addrofname=NULL;
 unsigned long *addroffunction=NULL;
 unsigned short *addroforgname=NULL;
 //printf("base:%d name:%s\r\n" ,*(unsigned long*)(export+0x10) ,*(unsigned long*)(export+0x0C)+pmodule);
 numoffunction = *(unsigned long*)(export + 0x14);
 numofname = *(unsigned long*)(export + 0x18);
 //printf("numoffunction :%d ,numofname :%d \r\n" ,numoffunction ,numofname);
 addrofname = (unsigned long*)(*(unsigned long*)(export + 0x20) + pmodule);
 addroforgname = (unsigned short*)(*(unsigned long*)(export + 0x24) + pmodule);
 addroffunction = (unsigned long*)(*(unsigned long*)(export + 0x1C) + pmodule);
 int index=0;
 for (int i=0 ;i<numofname ;i++)
 {
  if(!strncmp(function ,(char*)(addrofname[i]+pmodule) ,strlen(function)))
  {
    index =base + addroforgname[i];
    return addroffunction[index] + pmodule;
  }
 }
 return 0;
 
}
int main(int argc, char* argv[])
{
 HMODULE hlib=LoadLibrary("kernel32");
 printf("%x\r\n" ,export_jixi((unsigned long)hlib ,"GetProcAddress"));
 return 0;
}


PE导出(Export Table)是Windows可执行文件中的一个结构,记录了可执行文件中某些函数或变量的名称和地址,这些名称和地址可供其他程序调用或使用。当PE文件执行时,Windows装载器将文件装入内存并将导入中登记的DLL文件一并装入,再根据DLL文件中函数的导出信息对可执行文件的导入(IAT)进行修正 [^1]。 导出的结构体定义如下: ```c typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; // DLL名称的RVA DWORD Base; // 导出函数序号的起始值 DWORD NumberOfFunctions; // 实际导出的函数数量 DWORD NumberOfNames; // 按名称导出的函数数量 DWORD AddressOfFunctions; // 函数地址数组的RVA DWORD AddressOfNames; // 函数名称数组的RVA DWORD AddressOfNameOrdinals; // 函数序号数组的RVA } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; ``` 其中,`TimeDateStamp` 是导出生成的时间戳;`Name` 为模块名;`Base` 是序号的基数,导出函数的序号值从 `Base` 开始递增;`NumberOfFunctions` 示所有导出函数的数量;`NumberOfNames` 是按名字导出函数的数量;`AddressOfFunctions` 指向函数地址RVA;`AddressOfNames` 指向函数名RVA;`AddressOfNameOrdinals` 指向函数名索引RVA [^2][^4]。 导出的大小比较固定,比较关键的是最后三个字段,分别指向了3个不同的,而这3个之间也有关联。`AddressOfNames` 和 `AddressOfNameOrdinals` 是按照顺序一一对应的(每个中单元数量也是由 `NumberOfNames` 决定的),`AddressOfNamesOrdinals` 中的序号值示在 `AddressOfFunction` 中对应函数的行号(从0开始),其值加上 `base` 值等于在 `.def` 文件中定义的真正的函数的导出序号值。`AddressOfFunction` 是按照在 `.def` 文件中的函数顺序排列的,只不过中间多了一些 `NULL` 值。这样其他程序就可以通过函数名称地址,找到函数序号,然后对应找到函数具体代码内容 [^5]。 在分析时,用工具找出的导出位置通常是RVA(内存偏移地址)的值,要转化成FOA(文件偏移地址)才能分析 [^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值