zoukankan      html  css  js  c++  java
  • 通过导出表找导出函数

    例如:一个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("该文件没有导出表 ");
            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("没有该名字的函数 ");
            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("读取文件失败 ");
            return;
        }
        
        //获取函数指针
        lpPlus myPlus =(lpPlus) GetFunctionAddrByName(pFileBuffer, "Plus");
        printf("1+2=%d ",     myPlus(1,2));
     
        lpMul myMul = (lpMul) GetFunctionAddrByName(pFileBuffer, "Plus");
        printf("2X3=%d ", 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("该文件没有导出表 ");
            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("读取文件失败 ");
            return;
        }
        
        //获取函数指针
        lpSub mySub =(lpSub) GetFunctionAddrByOrdinals(pFileBuffer, 15);
        printf("2-1=%d ",     mySub(2,1));
        
        //释放内存
        free(pFileBuffer);
    }
     
    int main(int argc, char* argv[])
    {
        getDllFun();
        getchar();
    }
     
     
    结果:
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    51CTO资料索引 很不错
    extern和extern“c"作用详解 以及C和C++混合编程 在文章:3.深层揭密extern "C" 部分可以看到 .
    用VC++实现图像检索技术(转)
    OpenSceneGraph FAQ
    NeHe OpenGL教程 02 渲染第一个多边形
    C++经验谈(摘抄)
    利用条件编译实现工程定制版本的自动输出
    没有文件扩展".js"的脚本引擎 解决办法
    OpenGL FAQ
    NeHe OpenGL教程 01 创建OpenGL窗口
  • 原文地址:https://www.cnblogs.com/ShiningArmor/p/11775906.html
Copyright © 2011-2022 走看看