通过导出表找导出函数

例如:一个HelloDll.dll
其导出表信息如下:
该dll有4个函数;
用.def的方式导出;
其中有个匿名函数;
 
1.分析
导入dll中的函数有两种方式:
    1】通过序号
    2】通过函数名
例如:显式链接dll时使用的库函数“GetProcAddress”实现了用函数的查找;
myPlus = (lpPlus)GetProcAddress(hModule,   "_Plus@8");
其中的参数hModule实际上是pe文件拉伸后的起始位置ImageBase;
 
2.通过函数名导入
思路:
    遍历名字表,获取函数名,与目标函数名比对,如果有相同的函数名,获得该函数名在名字表中的索引;
        注意:名字表中储存的是函数名的内存镜像中相对ImageBase的偏移地址,需要转换成文件镜像偏移地址;
    用获得的索引在序号表中找到该函数对应的序号;
    以序号为索引在函数地址表中找到函数的地址;
        注意:函数地址表中储存的是函数内存镜像的偏移地址,需要转换为文件镜像的偏移地址;
    用函数指针接收函数的地址;
    使用函数;
 
函数:
GetFunctionAddrByName(FileBuffer指针,函数名指针)
 
代码:
#include "stdafx.h"
#include "PeTool.h"
#include "string.h"
 
#define SRC "C:\\Users\\Administrator\\Desktop\\DllHello.dll"
 
typedef int (__stdcall *lpPlus)(int,int);                    
typedef int (__stdcall *lpSub)(int,int);                    
typedef int (__stdcall *lpMul)(int,int);                    
typedef int (__stdcall *lpDiv)(int,int);
 
 
//通过函数名找dll导出函数
LPVOID GetFunctionAddrByName(LPVOID pFileBuffer, LPSTR funName){
    //定义头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_DATA_DIRECTORY dataDir = NULL;        //数据目录指针
    PIMAGE_EXPORT_DIRECTORY exportDir = NULL;    //导出表指针
    
    //初始化头指针
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)dosHeader + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    dataDir = opHeader->DataDirectory;
    exportDir = (PIMAGE_EXPORT_DIRECTORY) ((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer,dataDir->VirtualAddress));
    if(!exportDir){
        printf("该文件没有导出表\n");
        free(pFileBuffer);
        return NULL;
    }
    
    LPVOID pFun = NULL;
    //1.循环从名字表中找与目标函数名相同的;如有有返回该名字在表中的索引
    int ordIndex = -1;
    for(int i=0;i<exportDir->NumberOfNames;i++){
        DWORD nameOffset = *((LPDWORD)((DWORD)pFileBuffer + (DWORD)((LPDWORD)(exportDir->AddressOfNames)+i)));
        LPSTR nameAddr =(LPSTR) ((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer,nameOffset));
        if(!strcmp(nameAddr, funName)){
            ordIndex = i;
            break;
        }
    }
    if(ordIndex < 0){
        printf("没有该名字的函数\n");
        return NULL;
    }
    //2.用获得的索引从序号表中找函数的序号
    WORD ord = *(LPWORD)((DWORD)pFileBuffer + (DWORD)((exportDir->AddressOfNameOrdinals)+ordIndex));
    //3.以序号表中查出来的序号为索引从函数地址表中找函数地址
    DWORD addr = (DWORD)pFileBuffer + (DWORD)((LPDWORD)(exportDir->AddressOfFunctions) +ord);    
    DWORD offset = *((LPDWORD)addr);
    //5.因为地址表中保存的是内存镜像地址,需要转换为文件镜像地址
    offset = RvaToFileOffset(pFileBuffer,offset);
    pFun = (LPVOID)((DWORD)pFileBuffer + offset);
 
    return pFun;
}
 
//调用dll中的导出函数
void getDllFun(){
 
    //读取文件到缓冲区
    LPVOID pFileBuffer = NULL;
    DWORD fileSize = ReadPEFile(SRC, &pFileBuffer);
    if(!fileSize){
        printf("读取文件失败\n");
        return;
    }
    
    //获取函数指针
    lpPlus myPlus =(lpPlus) GetFunctionAddrByName(pFileBuffer, "Plus");
    printf("1+2=%d\n",     myPlus(1,2));
 
    lpMul myMul = (lpMul) GetFunctionAddrByName(pFileBuffer, "Plus");
    printf("2X3=%d\n", myMul(2,3));
    
    //释放内存
    free(pFileBuffer);
 
}
 
int main(int argc, char* argv[])
{
    getDllFun();
    getchar();
}
 
    
结果:
 
3.用序号导入函数
思路:
    序号 - Base = 函数地址在地址表中的索引;
    用索引在地址表中找到函数地址,注意内存镜像地址转文件镜像地址;
    用函数指针接收函数地址;
    使用函数;
    有的函数以匿名导出,不能通过函数名找到,可以用这种方式;
 
函数:
GetFunctionAddrByOrdinals(FileBuffer指针,函数名导出序号)
 
实现:
#include "stdafx.h"
#include "PeTool.h"
#include "string.h"
 
#define SRC "C:\\Users\\Administrator\\Desktop\\DllHello.dll"
 
typedef int (__stdcall *lpPlus)(int,int);                    
typedef int (__stdcall *lpSub)(int,int);                    
typedef int (__stdcall *lpMul)(int,int);                    
typedef int (__stdcall *lpDiv)(int,int);
 
//通过函数序号找dll导出函数
LPVOID GetFunctionAddrByOrdinals(LPVOID pFileBuffer, DWORD ord){
    //定义头结构指针
    PIMAGE_DOS_HEADER dosHeader = NULL;        //dos头指针
    PIMAGE_FILE_HEADER peHeader = NULL;        //pe头指针
    PIMAGE_OPTIONAL_HEADER32 opHeader = NULL;    //可选pe头指针
    PIMAGE_DATA_DIRECTORY dataDir = NULL;        //数据目录指针
    PIMAGE_EXPORT_DIRECTORY exportDir = NULL;    //导出表指针
    
    //初始化头指针
    dosHeader = (PIMAGE_DOS_HEADER) pFileBuffer;
    peHeader = (PIMAGE_FILE_HEADER) ((DWORD)dosHeader + dosHeader->e_lfanew + 4);
    opHeader = (PIMAGE_OPTIONAL_HEADER32) ((DWORD)peHeader + IMAGE_SIZEOF_FILE_HEADER);
    dataDir = opHeader->DataDirectory;
    exportDir = (PIMAGE_EXPORT_DIRECTORY) ((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer,dataDir->VirtualAddress));
    if(!exportDir){
        printf("该文件没有导出表\n");
        free(pFileBuffer);
        return NULL;
    }
    
    LPVOID pFun = NULL;
    //1.函数地址索引 = 序号 - Base
    DWORD index = ord - exportDir->Base;
    //2.利用索引从函数地址表中找函数地址
    DWORD addr = (DWORD)pFileBuffer + (DWORD)((LPDWORD)(exportDir->AddressOfFunctions) + index);    
    DWORD offset = *((LPDWORD)addr);
    //5.因为地址表中保存的是内存镜像地址,需要转换为文件镜像地址
    offset = RvaToFileOffset(pFileBuffer,offset);
    pFun = (LPVOID)((DWORD)pFileBuffer + offset);
 
    return pFun;
}
 
//调用dll中的导出函数
void getDllFun(){
 
    //读取文件到缓冲区
    LPVOID pFileBuffer = NULL;
    DWORD fileSize = ReadPEFile(SRC, &pFileBuffer);
    if(!fileSize){
        printf("读取文件失败\n");
        return;
    }
    
    //获取函数指针
    lpSub mySub =(lpSub) GetFunctionAddrByOrdinals(pFileBuffer, 15);
    printf("2-1=%d\n",     mySub(2,1));
    
    //释放内存
    free(pFileBuffer);
}
 
int main(int argc, char* argv[])
{
    getDllFun();
    getchar();
}
 
 
结果:
 
 
 
 
 
 
 
 
 
posted @ 2019-11-01 11:15  L丶银甲闪闪  阅读(1042)  评论(0编辑  收藏  举报