通過導出表找導出函數


例如:一個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();
}
 
 
結果:
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM