遍历导出表(上课代码)

// 01 遍历导出表.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"

//************************************
// Method:    IsPeFile
// FullName:  IsPeFile
// Access:    public 
// Returns:   bool   成功失败
// Qualifier:
// Parameter: TCHAR * szPath  路径
//************************************
bool  IsPeFile(TCHAR* szPath)
{
    BOOL bSuccess = TRUE;
    //1 将PE文件读取到内存
    HANDLE hFile = CreateFile(
        szPath,
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL
        , NULL
        );
    DWORD dwSize = GetFileSize(hFile, NULL);
    DWORD dwRubbish = 0;
    unsigned char * pBuf = new unsigned char[dwSize];
    ReadFile(hFile, pBuf, dwSize, &dwRubbish, NULL);
    //2 判断是否是PE文件
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBuf;
    if (pDos->e_magic != IMAGE_DOS_SIGNATURE)
    {
        bSuccess = FALSE;
        goto Error;

    }
    PIMAGE_NT_HEADERS  pNt = (PIMAGE_NT_HEADERS)(pBuf + pDos->e_lfanew);
    if (pNt->Signature != IMAGE_NT_SIGNATURE)
    {
        bSuccess = FALSE;
        goto Error;
    }

Error:

    if (pBuf != NULL)
    {
        delete[]pBuf;
    }
    if (hFile != INVALID_HANDLE_VALUE)
    {
        CloseHandle(hFile);
    }
    return bSuccess;
}

//************************************
// Method:    RvaToOffect
// FullName:  RvaToOffect
// Access:    public 
// Returns:   DWORD
// Qualifier: 将RVA转换为Offect
// Parameter: DWORD rva    要转换的RVA
// Parameter: unsigned char * pFile   存储pe文件内容的缓冲区
//************************************
DWORD RvaToOffect(DWORD rva, unsigned char* pFile)
{
    //1 找到NT头
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFile;
    PIMAGE_NT_HEADERS  pNt = (PIMAGE_NT_HEADERS)(pFile + pDos->e_lfanew);
    //2 找到数据目录表
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
    //3 判断要转换的位置是不是PE头部
    if (rva < pSection->VirtualAddress)
    {
        return rva;
    }
    //4 在数据目录表中遍历,进行计算
    for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)
    {
        if (
            (rva >= pSection->VirtualAddress) &&
            (rva <= pSection->VirtualAddress + pSection->Misc.VirtualSize)
            )
        {
            return rva - pSection->VirtualAddress + pSection->PointerToRawData;
        }
        pSection++;
    }
    return -1;
}

int _tmain(int argc, _TCHAR* argv[])
{
    //1 将PE文件读取到内存
    HANDLE hFile = CreateFile(
        L"D:\user32.dll",
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        NULL, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL
        , NULL
        );
    DWORD dwSize = GetFileSize(hFile, NULL);
    DWORD dwRubbish = 0;
    unsigned char * pBuf = new unsigned char[dwSize];
    ReadFile(hFile, pBuf, dwSize, &dwRubbish, NULL);
    //2 找到dos头
    PIMAGE_DOS_HEADER  pDos = PIMAGE_DOS_HEADER(pBuf);
    //3 找到nt头
    PIMAGE_NT_HEADERS pNt = PIMAGE_NT_HEADERS(pBuf + pDos->e_lfanew);
    //4 找到扩展头
    PIMAGE_OPTIONAL_HEADER pOption = &(pNt->OptionalHeader);
    //5 找到数据目录表
    PIMAGE_DATA_DIRECTORY  pDataDirectory = pOption->DataDirectory;
    //6 找到导出表的数据目录
    PIMAGE_DATA_DIRECTORY pExportDirectory = (pDataDirectory +0);

    //7 解析导出表的数据目录
    //7.1 得到导出表的文件偏移
    DWORD dwExOffect  = RvaToOffect(pExportDirectory->VirtualAddress, pBuf);
    //7.2 得到导出表结构体
    PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(pBuf + dwExOffect);
    //7.3打印dll的名字,注意:并不能直接打印,它提供的只是一个名字的RVA偏移
    char* pName = (char*)(RvaToOffect(pExport->Name, pBuf) + pBuf);
    printf("%s
", pName);
    //8 为解析导出表做准备
    //8.1 函数的个数
    DWORD dwNumOfFun =  pExport->NumberOfFunctions;
    //8.2 名称的个数
    DWORD dwNumOfName =  pExport->NumberOfNames;
    //8.3 函数地址表的位置
    PDWORD pOffectOfFun = (PDWORD) 
        (RvaToOffect(pExport->AddressOfFunctions, pBuf) + pBuf);
    //8.4序号表的位置
    PWORD  pOrder = (PWORD)
        (RvaToOffect(pExport->AddressOfNameOrdinals, pBuf) + pBuf);
    //8.5 名称表的位置
    PDWORD pOffectOfName = (PDWORD)
    (RvaToOffect(pExport->AddressOfNames,pBuf) + pBuf);
    //8.6 序号基数
    WORD wBase = pExport->Base;
    //9 开始解析导出表
    for (int i = 0; i < dwNumOfFun;i++){
        //9.1 假如这是一个无效地址
        if (pOffectOfFun[i] == 0)
            continue;
        //9.2 不是无效地址,就去序号表中找到这个序号
        int j = 0;
        for (; j < dwNumOfName; j++){
            if (pOrder[j] == i){
                //9.2.1找到了这个序号,说明这个函数有名字,属于名称导出
                char* pNameOfFun = (char*)(RvaToOffect(pOffectOfName[j], pBuf) + pBuf);
                printf(" 函数序号为:%hx 函数地址为:%X  函数名为:%s
",
                    wBase+i,
                    pOffectOfFun[i], pNameOfFun);
                break;
            }
        }
        if (j == dwNumOfName){
            //9.2.2假如没有找到这个序号,说明这个函数没有名字,只有序号,
            //属于序号导出,这个序号叫做虚序号
            printf(" 函数序号为:%hx 函数地址为:%X 函数名为:NULL
",
                wBase+i,
                pOffectOfFun[i]);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Alyoyojie/p/5329481.html