读取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;

 

 

时间: 2024-09-20 20:40:28

读取PE文件的导出表的相关文章

读取PE文件的导入表

在上一篇文章里,我使用一个 TreeList 控件,展示了 PE 文件的内容.在那里可充分了解PE的文件头的信息,但是对 section(备注:常见译文为节,段,块)的一些信息我们还没有涉及.比如全局变量等数据,代码,资源,导入表等信息都位于相应的 section 中,有些 section 通常具有特定的名字,例如资源通常位于 .rsrc,代码通常位于 .text,导入表通常位于 .idata 段,等等.文本讲述的是把一个PE文件的导入表打印出来.我注意到 MS 提供了一个比较有用的函数,Ima

读取PE文件的资源表

在上一篇文章里,已经讲解了加载PE文件的导入表.本篇简要介绍PE文件的资源表的结构和定位方式. 所谓资源表(resource table),就是通常在IDE的资源视图中所看到的那个Tree视图,因此资源表在PE文件中同样是这样的一种类似资源管理器一样的树状逻辑结构. 对树,我们不能想类似导入表那样当作线性表中的数组去比较简单直观的加载,而是要用递归函数去重建,这是因为树的定义就是用递归做的定义,所以对树的操作天生的就和递归函数分不开.看起来不可预判的复杂结构,递归函数的代码却非常简洁.   资源

二进制-c++如何读取dll文件?

问题描述 c++如何读取dll文件? c++如何读取dll文件? 我想读取dll文件二进制数据到指针?请问如何做? 解决方案 C++如何写一个简单的DLL文件 解决方案二: 那就用fopen rb二进制方式打开读取,fread读取内容 解决方案三: 有个工具叫dependency walker,你可以看看是不是你要的效果.http://www.dependencywalker.com/ 解决方案四: 直接读取即可,根据PE文件结构 解决方案五: 不是有一个load函数嘛

为PE文件添加新节显示启动信息

病毒并不神秘,也不复杂.相当多的大侠已经在这方面作出了杰出的贡献,例如 29A 组织,我对他们的崇拜之情啊,真是--咳咳,先别扔鸡蛋.其实我想说的是:技术是一柄双刃剑,我们应该把它运用在对社会有益的事情上.所以请勿利用本文的代码进行违法违纪的活动,否则本人保留追究的权利. 本文的技术其实早已是老掉牙的东西了,so如果你已经懂得了编写病毒的方法,请跳过本文:如果你对病毒抱有好奇心,但是还没知道怎么编写,那么本文应该适合你.  :) 言归正传.在 Windows 环境下,所有的可执行文件都是 PE

免杀的艺术:PE文件后门的植入(二)

本文讲的是免杀的艺术:PE文件后门的植入(二), 在上一篇<免杀的艺术 1>中,我们详细列举了各种实现免杀的技术. 今天,我们将继续为大家进行免杀的话题--PE文件后门的植入, 不过还是和上一篇一样,我们建议读者需要了解x86程序集,调试器和PE文件的基本知识. 现在几乎所有的安全研究人员,安全测试人员和恶意软件分析人员每天都要处理各种各样与后门攻击相关的工作,原因很简单,将后门放置到在系统中插入后门或在程序中保留随意访问的权限是当下最流行的一种攻击方式. 鉴于此,本文会主要为大家介绍往32位

android华为手机读取本地文件夹图片获取path路径null,求指导

问题描述 android华为手机读取本地文件夹图片获取path路径null,求指导 android华为手机读取本地文件夹图片获取path路径null,求指导 解决方案 public String getImagePathFromURI(Uri uri) { Cursor cursor = getActivity().getContentResolver().query(uri null null null null); String path = null; if (cursor != null

java 读取dbf文件-java读取dbf文件问题怎样解决使用javadbf.jar

问题描述 java读取dbf文件问题怎样解决使用javadbf.jar 使用javadbf.jar读取dbf文件只能读取到字段名和第一行记录,然后遇到com.linuxense.javadbf.DBFException: Failed to parse Number: multiple points错误怎么解决

npoi-跪求NPOI.dll读取word文件(.net)

问题描述 跪求NPOI.dll读取word文件(.net) 从官网下载NPOI2.0版本没有XWPFDocument方法. 解决方案 进微软官网查找软件下载更新,重启系统配置 解决方案二: NPOI2.0 只可以操作WORD2007和2010 不支持2003格式.

利用opengl读取txt文件,显示点云

问题描述 利用opengl读取txt文件,显示点云 我是将获取到的三维点云数据保存成txt形式的,里面包含x,y,z,r,g,b信息,可以用opengl直接获取这些信息,画出点云吗? 解决方案 是不是考虑用OSG来做这个 解决方案二: 可以,我就是用OPenG做L的,楼上说得OSG也可以,还有VTK我也试过不过速度慢 解决方案三: 具体见 OSG 王锐<最长的一帧>