导出表

1.概述

某个DLL中的函数被EXE或者另外的DLL使用需要借助导出表(有的EXE也有导出表)。数据目录项的第一个结构,就是导出表.。

2.结构

数据目录项的结构

这个结构在OptionalHeader的最后一个结构

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

其中 VirtualAddress 导出表的RVA。Size 导出表的大小
上面这个结构指向导出表的位置和大小。

导出表的结构

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

重点的三个成员构成三个表

三个表的目的

函数导出的个数与函数名的个数未必一样.所以要将函数地址表和函数名称表分开.
一个相同的函数地址,可能有多个不同的名字.

AddressOfFunctions:导出函数地址RVA

该表中元素宽度为4个字节
该表中存储所有导出函数的地址
该表中个数由NumberOfFunctions决定
该表项中的值是RVA, 加上ImageBase才是函数真正的地址

AddressOfNames:导出函数名称表

该表中元素宽度为4个字节
该表中存储所有以名字导出函数的名字的RVA
该表项中的值是RVA, 指向函数真正的名称
该表中个数由NumberOfNames决定
在这里插入图片描述

AddressOfNameOrdinals:导出函数序号表

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

3.导出过程

根据函数名字获取函数的地址

1.通过名称反找AddressOfNames表,然后遍历函数名称表得到匹配的函数的索引(与函数序号索引一直)。
2.函数序号表找到对应索引的元素取出值为序号数。
3.通过函数序号访问函数地址表AddressOfFunctions访问函数的地址。
在这里插入图片描述

代码演示

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>


LPVOID ReadPEFile(LPSTR lpszFile)//读取文件
{
	FILE *pFile = NULL;
	DWORD fileSize = 0;
	LPVOID pFileBuffer = NULL;



	//打开文件
	pFile = fopen(lpszFile,"rb");
	if(!pFile)
	{
		printf("无法打开EXE文件!");
		return NULL;
	}

	//读取文件大小
	fseek(pFile,0,SEEK_END);
	//int fseek(FILE *stream, long offset, int fromwhere);
	//函数设置文件指针stream的位置。如果执行成功,stream将指向以fromwhere为基准偏移offset(指针偏移量)个字节的位置。
	fileSize = ftell(pFile);
	fseek(pFile,0,SEEK_SET);

	//分配缓冲区
	pFileBuffer = malloc(fileSize);
	if(!pFileBuffer)
	{
		printf("分配空间失败!");
		fclose(pFile);
		return NULL;
	}

	//将文件读取到缓冲区中
	size_t n = fread(pFileBuffer,fileSize,1,pFile);
	//pFileBuffer流
	//1 对象个数
	//pFile 输入流
	if(!n)
	{
		printf("读取数据失败!");
		free(pFileBuffer);
		fclose(pFile);
		return NULL;
	}

	//关闭文件
	fclose(pFile);
	return pFileBuffer;
}
DWORD GetFunctionAddrByName(LPSTR FunctionName)
{

    LPVOID pFileBuffer = NULL;
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNtHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
    PIMAGE_EXPORT_DIRECTORY pExport;
    LPSTR FilePath = "D://test//Dym.dll";
    //将文件读入内存
    pFileBuffer = ReadPEFile(FilePath);
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    printf("*********************DOS头*********************\n");
	printf("MZ标志:%x\n",pDosHeader->e_magic);
	printf("PE偏移:%x\n",pDosHeader->e_lfanew);

    pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader->e_lfanew + pFileBuffer);
	printf("*********************NT头*********************\n");
	printf("NT: %x\n",pNtHeader->Signature);
	pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNtHeader)+4);
	printf("*********************文件头*********************\n");
	printf("PE: %x\n",pPEHeader->Machine);
	printf("节的数量: %x\n",pPEHeader->NumberOfSections);
	printf("SizeOfOptionalHeader: %x\n",pPEHeader->SizeOfOptionalHeader);
    pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
    printf("*********************OptionalHeader*********************\n");
	printf("OPTIONAL_PE: %x\n",pOptionalHeader->SizeOfImage);
    pExport = (PIMAGE_EXPORT_DIRECTORY)(pOptionalHeader->DataDirectory[0].VirtualAddress + pFileBuffer);

    //导出表的成员
    DWORD ExAddressOfNames = (DWORD)(pExport->AddressOfNames);
    printf("ExAddressOfNames: %x\n",ExAddressOfNames);
    PDWORD pExAddressOfNames = (PDWORD)(pFileBuffer + ExAddressOfNames);
    DWORD ExNumberOfNames = pExport->NumberOfNames;
    printf("ExNumberOfNames: %x\n",ExNumberOfNames);

    //函数名称表
    int k;
    char* s_name;
    for(k = 0;k<ExNumberOfNames;k++)
    {
        //遍历查找函数名称
        PDWORD NameAddress = (PDWORD)(*pExAddressOfNames);
        char* s_name = (char*)((DWORD)pFileBuffer + (DWORD)NameAddress);
        printf("Function: %s\n",s_name);
        //比较名称相同为0
        WORD num = strcmp(FunctionName,s_name);
        if(num == 0)
        {
            break;
        }
        pExAddressOfNames++;//按照单位进行后移
    }
    //printf("%d",k);
    WORD Location_Fum = k;//存储k作为与名称表相同的序号表的索引值

    //函数序号表
    DWORD ExAddressOfNameOrdinals = pExport->AddressOfNameOrdinals;
    ExNumberOfNames = pExport->NumberOfNames;
    PWORD pExAddressOfNameOrdinals = (PWORD)(pFileBuffer + ExAddressOfNameOrdinals);
    WORD NUM_FUN = pExAddressOfNameOrdinals[Location_Fum];
    printf("NUM_FUN: %d \n",NUM_FUN);
    //函数地址表的信息
    DWORD ExAddressOfFunctions = pExport->AddressOfFunctions;
    DWORD ExNumberOfFunctions = pExport->NumberOfFunctions;

    PDWORD pExAddressOfFunctions = NULL;
    pExAddressOfFunctions  = (PDWORD)(ExAddressOfFunctions + (DWORD)pFileBuffer);
    //确定函数的地址
    DWORD Fun_Addr = pExAddressOfFunctions[NUM_FUN];
    return Fun_Addr;

}
int main()
{
    LPSTR FunctionName = "Plus";
    DWORD Fun_Addr = GetFunctionAddrByName(FunctionName);
    printf("Address: %x\n",Fun_Addr);
    return 0;
}

根据函数导出序号获取函数的地址

1.查找导出表的Base值,用导出序号 - Base值作为函数的序号值(函数地址表的索引值)。
2.根据索引找到函数地址。
在这里插入图片描述

代码演示

DWORD GetFunctionAddrByOrdinals(WORD FunctionOrdinals)//函数名导出序号
{
    LPVOID pFileBuffer = NULL;
    PIMAGE_DOS_HEADER pDosHeader = NULL;
    PIMAGE_NT_HEADERS pNtHeader = NULL;
    PIMAGE_FILE_HEADER pPEHeader = NULL;
    PIMAGE_OPTIONAL_HEADER pOptionalHeader = NULL;
    PIMAGE_EXPORT_DIRECTORY pExport;
    LPSTR FilePath = "D://test//Dym.dll";
    //将文件读入内存
    pFileBuffer = ReadPEFile(FilePath);
    pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
    pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader->e_lfanew + pFileBuffer);
	pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNtHeader)+4);
    pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader+IMAGE_SIZEOF_FILE_HEADER);
    //定位导出表
    pExport = (PIMAGE_EXPORT_DIRECTORY)(pOptionalHeader->DataDirectory[0].VirtualAddress + pFileBuffer);
    //定位函数的位置
    WORD Function_Index = FunctionOrdinals - pExport->Base;
    //输出函数地址表信息
    DWORD ExAddressOfFunctions = pExport->AddressOfFunctions;
    DWORD ExNumberOfFunctions = pExport->NumberOfFunctions;

    PDWORD pExAddressOfFunctions = NULL;
    pExAddressOfFunctions  = (PDWORD)(ExAddressOfFunctions + (DWORD)pFileBuffer);
    //确定函数的地址
    DWORD Fun_Addr = pExAddressOfFunctions[Function_Index];
    return Fun_Addr;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值