设为首页 加入收藏

TOP

2.9 PE结构:重建导入表结构(一)
2023-09-09 10:25:32 】 浏览:126
Tags:2.9 结构

脱壳修复是指在进行加壳保护后的二进制程序脱壳操作后,由于加壳操作的不同,有些程序的导入表可能会受到影响,导致脱壳后程序无法正常运行。因此,需要进行修复操作,将脱壳前的导入表覆盖到脱壳后的程序中,以使程序恢复正常运行。一般情况下,导入表被分为IAT(Import Address Table,导入地址表)和INT(Import Name Table,导入名称表)两个部分,其中IAT存储着导入函数的地址,而INT存储着导入函数的名称。在脱壳修复中,一般是通过将脱壳前和脱壳后的输入表进行对比,找出IAT和INT表中不一致的地方,然后将脱壳前的输入表覆盖到脱壳后的程序中,以完成修复操作。

数据目录表的第二个成员指向导入表,该指针在PE开头位置向下偏移0x80h处,此处PE开始位置为0xF0h也就是说导入表偏移地址应该在0xf0+0x80h=170h如下图中,导入表相对偏移为0x21d4h

这个地址的读取同样可以使用PeView工具得到,通过输入DataDirectory读者可看到如下图所示的输出信息,其中第二行则是导入表的地址。

这里的0x21d4是一个RVA地址,需要将其转换为磁盘文件FOA偏移才能定位到导入表在文件中的位置,使用RvaToFoa命令可快速完成计算,转换后的文件偏移为0x11d4

此处我们也可以通过使用虚拟偏移地址减去实际偏移地址来得到这个参数,由于0x21d4位于.rdata节,此时的rdata虚拟偏移是0x2000而实际偏移则是0x1000通过使用2000h-1000h=1000h,接着再通过0x21d4h-0x1000h=11D4h同样可以得到相对FOA文件偏移。

我们通过使用WinHex工具跳转到11d4位置处,读者此时能看到如下图所示的地址信息。

如上图就是导入表中的IID数组,每个IID结构包含一个装入DLL的描述信息,现在有三个导入DLL文件,则第四个是一个全部填充为0的结构,标志着IID数组的结束,每一个结构有五个四字节构成,该结构体定义如下所示;

typedef struct _IMAGE_IMPORT_DESCRIPTOR
{
    union
    {
        DWORD   Characteristics;
        DWORD   OriginalFirstThunk;
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;
    DWORD   ForwarderChain;
    DWORD   Name;
    DWORD   FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

我们以第一个调用动态链接库为例,其地址与结构的说明如下所示:

  • 0000 22C0 => OrignalFirstThunk => 指向输入名称表INT的RVA
  • 0000 0000 => TimeDateStamp => 指向一个32位时间戳,默认此处为0
  • 0000 0000 => ForwardChain => 转向API索引,默认为0
  • 0000 244A => Name => 指向DLL名字的指针
  • 0000 209C => FirstThunk => 指向输入地址表IAT的RVA

每个IID结构的第四个字段指向的是DLL名称的地址,以第一个动态链接库为例,其RVA是0000 244A 将其减去1000h得到文件偏移144A,跳转过去看看,调用的是USER32.dll库。

上方提到的两个字段OrignalFirstThunkFirstThunk都可以指向导入结构,在实际装入中,当程序中的OrignalFirstThunk值为0时,则就要看FirstThunk里面的数据,FirstThunk常被叫做IAT它是在程序初始化时被动态填充的,而OrignalFirstThunk常被叫做INT,它是不可改变的,之所以会保留两份是因为,有些时候会存在反查的需求,保留两份是为了更方便的实现。

在上述流程中,我们找到了User32.dllOrignalFirstThunk,其地址为22C0,使用该值减去1000h 得到 12c0h,在偏移为12c0h处保存的就是一个IMAGE_THUNK_DATA32数组,他存储的内容就是指向 IMAGE_IMPORT_BY_NAME 结构的地址,最后一个元素以一串0000 0000作为结束标志,先来看一下IMAGE_THUNK_DATA32的定义规范。

typedef struct _IMAGE_THUNK_DATA32
{
    union
    {
        DWORD ForwarderString;
        DWORD Function;
        DWORD Ordinal;
        DWORD AddressOfData;
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

直接使用WinHex定位到12c0h地址处,此处就是OrignalFirstThunk中保存的INT的内容,如下图,除去最后一个结束符00000000以外,一共有19个四字节,则说明User32.dll中导入了19API函数。

再来看一下FirstThunk也就是IAT中的内容,由于User32FirstThunk字段默认值是209C,使用该值减去1000h即可得到109ch,此处就是IAT的内容,使用WinHex定位过去,可以发现两者内容时完全一致的。

接着我们以第一个导入RVA地址0000243Eh,用该值减去1000h得到143Eh,定位过去正好是EndDialog的字符串,同样的方式,第二个导入RVA地址0000242ch,用该值减去1000h得到142ch 定位过去正好是PostQuitMessage的字符串,如下图绿色部分所示。

如上图中我们已第二个函数PostQuitMessage为例,前两个字节0271h表示的是Hint值,后面的蓝色部分则是PostQuitMessage字符串,最后的0标志结束标志。

当程序被运行前,它的FirstThunk值与OrignalFirstThunk字段都指向同一片INT中,此处我们使用LyDebugger工具对程序进行内存转存,执行命令LyDebugger DumpMemory --path Win32Project.exe生成dump.exe文件,该文件则是内存中的镜像数据。

当程序运行后,OrignalFirstThunk字段不会发生变化,但是FirstThunk值的指向已经改变,系统在装入内存时会自动将FirstThunk指向的偏移转化为一个个真正的函数地址,并回写到原始空间中,定位到dump.exe文件FirstThunk 输入表RVA地址处209Ch查看,如下图;

接着定位到OrignalFirstThunk处,也就是22c0h,观察可发现,绿色的INT并没有变化,但是黄色的IAT则相应的发生了变化

我们以IAT中第一个0x75f8ab90为例,使用x64dbg跟进一下,则可知是载入内存后EngDialog的内存地址。

当系统装入内存后,其实只会用到IAT中的地址解析

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇2.4 PE结构:节表详细解析 下一篇C++系列十:日常学习-多线程

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目