zoukankan      html  css  js  c++  java
  • 读取PE文件的导出表

        再顺便多写一篇PE文件的文章吧,这一篇文章介绍PE文件的导出表。导出表相对来说应该算最简单的,它的关键结构只有一个。本质上导出表的dir只是指示以下信息,dll的名称地址(ANSI字符串),有多少个导出函数,对导出函数有三个数组,分别是序号数组,函数名称(ANSI字符串)地址数组,函数入口地址(RVA)数组。导入表导出表可以说是直接和动态链接技术相关的 DataDirectory 了。

        首先还是介绍导出表的唯一一个数据结构(仅列出比较关键的成员):

        (1)IMAGE_EXPORT_DIRECTORY:

        (1.1)DWORD   Name; 这个DLL名称(ANSI)字符串的地址(RVA)。

        (1.2)DWORD   Base; 序号数组的计数起始值。(序号数组中的数值加上Base为最终的导出函数序号)

        (1.3)DWORD   NumberOfFunctions; 导出函数个数。

        (1.4)DWORD   NumberOfNames; 函数名个数。这个值通常应该和 NumberOfFunctions 相同(即所有导出函数都应该有一个名称)。

        (1.5)DWORD   AddressOfFunctions; 函数地址RVA的数组。

        (1.6)DWORD   AddressOfNames; 存储函数名称(ANSI)字符串的地址(RVA)的数组的地址(RVA)。

        (1.7)DWORD   AddressOfNameOrdinals; 存储函数序号的数组的地址(RVA)。

        按照惯例,下面是导出表的结构示意图:

        

        下面是读取导入表的代码,由于我的项目属性中使用的是unicode字符串,所以我在使用那些ANSI字符串之前,需要做一次到unicode的转换:

    code_load_exportTable
    //加载导出表
    void CPERcViewDlg::LoadExportTable(LPBYTE lpBaseAddress, PIMAGE_NT_HEADERS pNtHeaders, DWORD rva)
    {
        
    int i;
        TCHAR wcsBuffer[
    256], nodeText[256];

        HTREEITEM hItem_Export 
    = NULL;

        PIMAGE_EXPORT_DIRECTORY pExportTable 
    = (PIMAGE_EXPORT_DIRECTORY)ImageRvaToVa(
            pNtHeaders, 
            lpBaseAddress, 
            rva,
            NULL
            );

        _stprintf(nodeText, _T(
    "ExportTable (FileAddress: %08X)"), (DWORD)pExportTable - (DWORD)lpBaseAddress);
        hItem_Export 
    = m_tree.InsertItem(nodeText, TVI_ROOT, TVI_LAST);

        
    //dll名称(char*)
        LPCSTR szDllName = (LPCSTR)ImageRvaToVa(
            pNtHeaders, lpBaseAddress,
            pExportTable
    ->Name,
            NULL);

        
    //把ANSI的名称转换到unicode
        ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szDllName, -1, nodeText, sizeof(nodeText)/sizeof(TCHAR));

        
    //append dll name node
        HTREEITEM hDllName = m_tree.InsertItem(nodeText, hItem_Export, TVI_LAST);

        
    //现在加载每个节点
        DWORD ImageBase = pNtHeaders->OptionalHeader.ImageBase;

        
    //以下全部是RVA。换算到数组入口处的VA:
        PDWORD pFunctions = (PDWORD)ImageRvaToVa(
            pNtHeaders, lpBaseAddress,
            pExportTable
    ->AddressOfFunctions,
            NULL);

        PWORD pOrdinals 
    = (PWORD)ImageRvaToVa(
            pNtHeaders, lpBaseAddress,
            pExportTable
    ->AddressOfNameOrdinals,
            NULL);

        BOOL hasNames 
    = (pExportTable->AddressOfNames != 0);

        PDWORD pNames 
    = NULL;
        
        
    if(hasNames)
        {
            pNames 
    = (PDWORD)ImageRvaToVa(
                pNtHeaders, lpBaseAddress,
                pExportTable
    ->AddressOfNames,
                NULL);
        }

        
    for(i=0; i<pExportTable->NumberOfFunctions; i++)
        {
            
    //函数名称
            if(hasNames)
            {
                LPCSTR szFuncName 
    = (LPCSTR)ImageRvaToVa(
                    pNtHeaders, lpBaseAddress,
                    pNames[i],
                    NULL);

                
    //转换成unicode
                ::MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szFuncName, -1, wcsBuffer, sizeof(wcsBuffer)/sizeof(TCHAR));
            }

            _stprintf(nodeText, _T(
    "Ordinal: %ld %s, VA: %08X"),
                pExportTable
    ->Base + pOrdinals[i],
                hasNames
    ? wcsBuffer : _T("(null)"),
                ImageBase 
    + pFunctions[i]
                );
            
            m_tree.InsertItem(nodeText, hDllName, TVI_LAST);
        }
    }

        最后导出函数加载到 TreeCtrl 的效果如下:

        

        【补充内容】关于pragma:

        (1)指定内容在 obj 文件中所在的段,具体用法参考MSDN(不太常用),以下分别是代码(函数),初始化数据,未初始化数据,常量:

        #pragma code_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
        #pragma data_seg( [ [ { push | pop }, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
         #pragma bss_seg( [ [ { push | pop }, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
        #pragma const_seg( [ [ { push | pop}, ] [ identifier, ] ] [ "segment-name" [, "segment-class" ] )
     

        (2)在某个obj文件中指定一个在链接时需要搜索的库(比较常用):

        #pragma comment( lib, "emapi" )

        【参考资料】

        (1)看雪论坛精华8;

        (2)winnt.h;

  • 相关阅读:
    转载 jenkins执行selenium 测试 浏览器不显示解决方法
    jmeter用beanshell调用自己写的jar进行MD5加密
    深入理解Spring MVC 思想
    springmvc和json整合配置方法
    getFragmentManager和getChildFragmentManager
    android
    HTTP 请求头与请求体
    Android中ImnageView 中如何让图片拉伸显示?
    ExecutorService 的理解与使用
    SpringMVC源码分析系列
  • 原文地址:https://www.cnblogs.com/hoodlum1980/p/1824133.html
Copyright © 2011-2022 走看看