流水账笔记:PE文件格式(导出表)

来源:互联网 发布:监听2121端口 编辑:程序博客网 时间:2024/05/16 09:02

数据目录下记录了导出表的地址,其结构体如下:

这里写图片描述

Characteristics

属性,不重要

TimeDataStamp

导出表的生成时间,可以修改,系统不关心

MajorVersion & MinorVersion

版本号,不重要

Name

导出 dll 的名称

AddressOfFunctions

表示加载到内存后相对该模块的偏移地址(RVA)


在分析下面字段时,我们用 DEPENDS.exe 借助理解,将 dll 用 DEPENDS 打开,我们看到如下信息:

这里写图片描述

  • Ordinal字段的值通过 AddressOfNameOrdinals 这张表里中的各个值和 Base 值相加得出
  • Function 字段代表的是 AddresssOfNames 表里的内容
  • Entry Point 字段代表的是 AddressOfFunctions 的内容
  • Hint 字段是通过 AddresssOfNames 的排列顺序得出

将 dll 分为不同的导出方式,来观察这张表的变化。

情况一

这里写图片描述

此时,用 DEPENDS 打开,发现 fun1 和 fun2 之间插了许多填充的数据

这里写图片描述

这里写图片描述

再观察 dll 中的导出表字段,发现 Base 字段为 1。各张表信息如下表所示:

AddressOfFunctions 内容 第 1 项 0x00001000 第 2 项 0 … … 第 999 项 0 第 1000 项 0x00001020 第 1001 项 0x00001040

————————————————-——-——————)))图表割线)

AddresssOfNames 内容 第 1 项 字符串指针(fun1) 第 2 项 字符串指针(fun2) 第 3 项 字符串指针(fun3)

————————————————-——-——————)))图表割线)

AddressOfNameOrdinals 内容 第 1 项 0 第 2 项 0x03e7 (999) 第 3 项 0x03e8 (1000)

情况二

这里写图片描述

此时,用 DEPENDS 打开:

这里写图片描述

再观察 dll 中的导出表字段,发现 Base 字段的值为 999。各张表信息如下表所示:

AddressOfFunctions 内容 第 1 项 0x00001000 第 2项 0x00001020 第 3项 0x00001040

————————————————-——-——————)))图表割线)

AddresssOfNames 内容 第 1 项 字符串指针(fun1) 第 2 项 字符串指针(fun2) 第 3 项 字符串指针(fun3)

————————————————-——-——————)))图表割线)

AddressOfNameOrdinals 内容 第 1 项 0x0000 第 2 项 0x0001 第 3 项 0x0002

情况三

如果我们将一项函数不导出名称

这里写图片描述

此时,用 DEPENDS 打开:

这里写图片描述

再观察 dll 中的导出表字段,发现 Base 字段的值为 999。各张表信息如下表所示:

AddressOfFunctions 内容 第 1 项 0x00001000 第 2 项 0x00001020 第 3 项 0x00001040

————————————————-——-——————)))图表割线)

AddresssOfNames 内容 第 1 项 字符串指针(fun1) 第 2 项 字符串指针(fun2)

————————————————-——-——————)))图表割线)

AddressOfNameOrdinals 内容 第 1 项 0x0000 第 2 项 0x0001

情况四

如果导出的序号按如图排列

这里写图片描述

此时,用 DEPENDS 打开:

这里写图片描述

再观察 dll 中的导出表字段,发现 Base 字段的值为 1。各张表信息如下表所示:

AddressOfFunctions 内容 第 1 项 0x00001020 第 2 项 0x00001000 第 3 项 0x00001040

————————————————-——-——————)))图表割线)

AddresssOfNames 内容 第 1 项 字符串指针(fun1) 第 2 项 字符串指针(fun2) 第 3 项 字符串指针(fun3)

————————————————-——-——————)))图表割线)

AddressOfNameOrdinals 内容 第 1 项 0x0001 第 2 项 0x0000 第 3 项 0x0002

我们可知,AddresssOfNames 中字符串的排列顺序是按照.def 文件中字符串排列顺序排列的;而 AddressOfNameOrdinals 中的序号排列顺序是按照.def 文件中序号的顺序。

系统在调用 GetProcAddress,流程如图:(先画一半…好累)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(流程图请忽视…)

Created with Raphaël 2.1.0Start是否序号导入根据 Base 计算循环遍历 AddressOfFunctions http://www.baidu.comEndhttp://www.baidu.com遍历 AddressOfFunctions 遍历 AddressOfNameOrdinal遍历 AddressOfFunctionsyesno

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(流程图请忽视…)

模拟 GetProcAddress 代码

#include "stdafx.h"#include <windows.h>typedef void (*PFN_fun)();int MyGetProc(HMODULE hModule, LPCSTR lpProcName);int _tmain(int argc, _TCHAR* argv[]){    HMODULE hModule = LoadLibraryA("mydll.dll");    if (hModule == NULL)    {        MessageBox(NULL, _T("Dll加载失败!"), NULL, MB_OK);        return -1;    }    int nItem = 3;    //PFN_fun pfn = (PFN_fun)GetProcAddress(hModule, "Fun1");    PFN_fun pfn = (PFN_fun)MyGetProc(hModule, "fun2");    if (pfn == NULL)    {        return -1;    }    pfn();    return 0;}int MyGetProc(HMODULE hModule, LPCSTR lpProcName){    //得到 Dos 结构体    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;    //定位到 OptionalHeader 的地址    DWORD dwOpAddr = pDosHeader->e_lfanew + (DWORD)hModule + sizeof(IMAGE_FILE_HEADER) + sizeof(DWORD);    PIMAGE_OPTIONAL_HEADER32 pOpHeader = (PIMAGE_OPTIONAL_HEADER32)dwOpAddr;    //得到导出表结构体    DWORD dwExportAddr = pOpHeader->DataDirectory->VirtualAddress + (DWORD)hModule;    PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)dwExportAddr;    //如果高四位为 0,则为序号导入    if (((DWORD)(lpProcName) & 0xffff0000) == 0)    {        /* 序号导入 */        int nNumOfFun = pExportDir->NumberOfFunctions;        int nGetFunAddr = pExportDir->AddressOfFunctions + (DWORD)hModule;        //1. 由 序号 - Base 得出在 AddressOfFunctions 表的第几项        int nItem = (DWORD)(lpProcName) - pExportDir->Base;        if (nItem >= nNumOfFun)        {            return NULL;        }        //2. 遍历 AddressOfFunctions 表,定位到某一项        while (-- nNumOfFun)        {            nGetFunAddr += sizeof(DWORD);        }        return *((int *)nGetFunAddr) + (DWORD)hModule;    }    else    {        /* 字符串导入 */        int nItem = pExportDir->NumberOfNames;        char* pNameTableAddr = (char *)(pExportDir->AddressOfNames + (DWORD)hModule);        int nOrdinalCount = 0;        SHORT nOrdinal = 0;        //1. 遍历 AddressOfNames 表        while (nItem --)        {            char *pString = (char *)(*(DWORD *)(pNameTableAddr) + (DWORD)hModule);            if (strcmp(pString, lpProcName) == 0)            {                //2. 遍历 AddressOfNameOrdinal 表                char *pOrdinalTable = (char *)(pExportDir->AddressOfNameOrdinals) + (DWORD)hModule;                while (nOrdinalCount --)                {                    pOrdinalTable += sizeof(SHORT);                }                nOrdinal = (SHORT&)(*pOrdinalTable);                //3. 遍历 AddressOfFunctions 表                int nGetFunAddr = pExportDir->AddressOfFunctions + (DWORD)hModule;                while (nOrdinal --)                {                    nGetFunAddr += sizeof(DWORD);                }                return *((int *)nGetFunAddr) + (DWORD)hModule;            }            pNameTableAddr += sizeof(LPCSTR);            nOrdinalCount++;        }        return NULL;    }    return NULL;}
原创粉丝点击