当前位置 博文首页 > KOOKNUT的博客:PE文件学习--导出表

    KOOKNUT的博客:PE文件学习--导出表

    作者:[db:作者] 时间:2021-07-03 09:22

    IMAGE_OPTIONAL_HEADER32.DataDirectory数组中的第一成员是导出表,今天来看看导出表的相关知识点。

    #define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
    

    导出表,顾名思义,就是本文件向外部导出的一张表,而这张表中存放一些什么东西呢?里面存放的是一些导出函数,方便代码的重用,一般来说,导出表的概念会和动态链接库联系在一起。相当于动态库告诉外部加载者,我的哪些函数是可以被你调用的。
    我们先来看看导出表中数据存放是如何存放的:

    typedef struct _IMAGE_EXPORT_DIRECTORY
     {
        DWORD   Characteristics;//标志位  未用
        DWORD   TimeDateStamp;	//时间戳
        WORD    MajorVersion;	//0
        WORD    MinorVersion;	//0
        DWORD   Name;			//导出表所在文件的文件名
        DWORD   Base;			//导出函数的起始序号
        DWORD   NumberOfFunctions;	//所有导出函数的个数
        DWORD   NumberOfNames;		//以函数名称导出的函数个数
        DWORD   AddressOfFunctions;     //导出函数地址表RVA
        DWORD   AddressOfNames;         //函数名称地址表RVA
        DWORD   AddressOfNameOrdinals;  //函数序号地址表RVA
    } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
    

    接下来说一些对于32位的PE文件导出表进行遍历时候,所用到的一些重要API函数实现:

    //定位第一个IMAGE_SECTION_HEADER
    #define IMAGE_FIRST_SECTION( NtHeader )                \
      ((PIMAGE_SECTION_HEADER) ((ULONG_PTR)(NtHeader) +    \
       FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) +  \
       ((NtHeader))->FileHeader.SizeOfOptionalHeader))
    
    PVOID
    NTAPI
    RtlImageRvaToVa(
        PIMAGE_NT_HEADERS NtHeader,
        PVOID BaseAddress,
        ULONG Rva,
        PIMAGE_SECTION_HEADER *SectionHeader)
    {
        PIMAGE_SECTION_HEADER Section = NULL;
    
        if (SectionHeader)
            Section = *SectionHeader;
    
        if ((Section == NULL) ||
            (Rva < SWAPD(Section->VirtualAddress)) ||
            (Rva >= SWAPD(Section->VirtualAddress) + SWAPD(Section->SizeOfRawData)))
        {
        //将当前虚拟地址所在的节区定位出来
            Section = RtlImageRvaToSection(NtHeader, BaseAddress, Rva);
            if (Section == NULL)
                return NULL;
    
            if (SectionHeader)
                *SectionHeader = Section;//用于返出值
        }
    /*此处就是进行内存地址和文件地址的转换,其思想为:
    当前所要定位到当前打开文件的基地址(ImageBase)+在节区的相对偏移(RVA-Section->VirtualAddress)+节区在文件中的偏移地址,,得到当前查询数据在文件中的地址
    */
        return (PVOID)((ULONG_PTR)BaseAddress + Rva +
                       (ULONG_PTR)SWAPD(Section->PointerToRawData) -
                       (ULONG_PTR)SWAPD(Section->VirtualAddress));
    }
    ///RtlImageRvaToSection的实现,实现RVA的所在节区定位
    PIMAGE_SECTION_HEADER
    NTAPI
    RtlImageRvaToSection(
        PIMAGE_NT_HEADERS NtHeader,
        PVOID BaseAddress,
        ULONG Rva)
    {
        PIMAGE_SECTION_HEADER Section;
        ULONG Va;
        ULONG Count;
    //节区总数
        Count = SWAPW(NtHeader->FileHeader.NumberOfSections);
        //第一个IMAGE_SECTION_HEADER的定位
        Section = IMAGE_FIRST_SECTION(NtHeader);
    
        while (Count--)
        {
            Va = SWAPD(Section->VirtualAddress);
            if ((Va <= Rva) && (Rva < Va + SWAPD(Section->SizeOfRawData)))
                return Section;
            Section++;
        }
    
        return NULL;
    }
    

    “假令风歇时下来,犹能簸却沧溟水。”–李白
    参考书籍:
    《Windows PE权威指南》

    cs