吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3781|回复: 9
收起左侧

[调试逆向] 手动添加导入表

  [复制链接]
fily 发表于 2021-6-5 21:40

手动添加导入表

00 什么是导入表

  • 当一个可执行文件需要依赖外部(模块)的函数时,就会在PE文件中记录这模块的信息

  • 这个模块信息就是PE文件的数据目录表[1]项,指向的导出表结构如下

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
      union {
          DWORD   Characteristics;            // 0 for terminating null import descriptor
          DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
      } DUMMYUNIONNAME;
      DWORD   TimeDateStamp;                  // 0 if not bound,
                                              // -1 if bound, and real date\time stamp
                                              //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                                              // O.W. date/time stamp of DLL bound to (Old BIND)
    
      DWORD   ForwarderChain;                 // -1 if no forwarders
      DWORD   Name;
      DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
    } IMAGE_IMPORT_DESCRIPTOR;
    typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
  • 该程序可能导入的模块可能不止一个,所以该导入表时结构体数组,最后一个元素是一个全为零。

  • 导入表中存在两个字段OriginalFirstThunk,和FirstThunk本别指向INT(导入名表),IAT(导入地址表),表中记录该当前PE文件依赖外部dll的函数名称或地址。名称或者地址取决于当前程序是否加载到内存(双击运行),如果加载到内存那么IAT指向真是函数地址,如果存放磁盘中,那么IAT和INT都指向函数名称。

    1.png

  • 例如一个exe需要创建窗口,那么肯定需要使用user32.dll模块中的CreateWindow函数

01 编写测试代码

  • 使用RadAsm编写测试代码

    .386
    .model flat,stdcall
    include user32.inc
    includelib user32.lib
    .data
    szText db "hello",0
    .code
    main:
    push 0
    push 0
    push offset szText
    push 0
    call MessageBox  ;IAT函数调用
    ret
    end main
    end
  • 该程序编译该程序后,查看汇编代码

    2.png

02 分析导入表

  • 在数据目录表【1】项中保存导入表。

    3.png

  • 这里RVA = 0x2008,该段位于.rdata段,可以计算出FOA = 0x608

    4.png

    FOA = 0x2008 - 0x2000(区段RVA)+ 0x600(文件FOA)
    FOA = 0x608
  • 通过FOA找到导入表结构体

    5.png

  • 导入表信息,IAT和INT保存都是相同数据 保存0x0002038这个地址指向函数名称

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
      union {
          .Characteristics  =  0x2030
          .OriginalFirstThunk =  0x2030  //保存INT的RVA =>FOA 0x630 (保存0x0002038)
      } DUMMYUNIONNAME;
      .TimeDateStamp            = 0   
      .ForwarderChain           = 0                 
      .Name                     = 0x2046  // 保存dll名称的RVA => FOA 0x646("user32.dll")
      .FirstThunk           = 0x2000  // 保存IAT的RVA =>FOA 0x600 (保存0x0002038)     
    } IMAGE_IMPORT_DESCRIPTOR;
    // 函数名称
    typedef struct _IMAGE_IMPORT_BY_NAME {
      WORD    Hint;
      CHAR   Name[1];
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

6.png

  • 最后当可执行程序被执行时,加载器找到PE文件中的导入表结构体数组,找到该模块的名称Name,使用LoadLibrary加载这个dll,然后通过IAT表找到这个函数需要导入的函数名称,调用GetProcAddress函数加载这个函数地址,将地址写回IAT数组中。之后程序中我们会发现 代码中会有 call  IAT表【N】函数或者间接 jmp IAT[N]的函数。

  • 到此我们知道了PE文件如何使用外部函数了。

03 构建导入表

  • 基于上述分析,我们也可以构建一个导入表,插入一个dll到这个程序中,同时调用这个dll导出的函数

  • 首先编写一个dll,由于插入到这个测试程序中,这里方便用vs编写

    // test.dll
    #include <windows.h>
    
    extern "C" __declspec(dllexport) void fun1()
    {
    MessageBoxA(0,"我是注入的dll",0,0);
    }

7.png

  • 接下来需要构建导入表,由于编译器编译后导入表与IAT,INT离的太近,无法在后面继续添加导入表,需要另外安排一个地址来修改导入表,或者将原有的导入表进行覆盖,这里我使用第1种,找一个新的地址安排导入表。

  • 选择0x680(FOA) 作为新的导入表,需要将转换后的(0x2060)RVA填充到数据目录表【1】项的virtaladdress,至于size最后填充

    RVA = FOA(0x680) - 区段的FOA(0x600) + 区段的RVA(0x2000)
    RVA = 0x2080

    8.png

  • 定制新的导入表

    // -第一个导入表
    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
      union {
          .Characteristics  =  0x20E0
          .OriginalFirstThunk =  0x20E0  //保存INT的RVA =>FOA 0x6E0 (0x00002150)
      } DUMMYUNIONNAME;
      .TimeDateStamp            = 0   
      .ForwarderChain           = 0                 
      .Name                     = 0x2130  // 保存dll名称的RVA => FOA  0x730 ("user32.dll")
      .FirstThunk           = 0x20F0  // 保存IAT的RVA =>FOA 0x6F0  (0x00002150)
    } IMAGE_IMPORT_DESCRIPTOR;
    // -第二个导入表
    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
      union {
          .Characteristics  =  0x2100
          .OriginalFirstThunk =  0x2100  //保存INT的RVA =>FOA 0x700 (0x00002160)
      } DUMMYUNIONNAME;
      .TimeDateStamp            = 0   
      .ForwarderChain           = 0                 
      .Name                     = 0x2140  // 保存dll名称的RVA => FOA 0x740 ("test.dll")
      .FirstThunk           = 0x2110  // 保存IAT的RVA =>FOA 0x710  (0x00002160)    
    } IMAGE_IMPORT_DESCRIPTOR;

    9.png

  • 每个IAT,和INT的值都是相同,指向函数名称

    10.png

  • 最后计算结构体大小,和导入表偏移,填写到数据目录表[1]项中。

    • 其中Size = 所有数据总和  = 0x760 - 0x680 = 0xE0
    • virtualAddress = 0x2080

    11.png

  • 到此所有结构填充完毕。

04 验证导入表

  • 最后验证是否加载dll

    12.png

  • 可以发现程序无法运行,IAT调用有问题!

    13.png

  • 需要将IAT调用指向我们修改后新的IAT地址,也就是RVA =0x20F0 -(VA)0x004020F0

    14.png

  • 最后成功执行

    15.png

免费评分

参与人数 2威望 +1 吾爱币 +20 热心值 +2 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
sam喵喵 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

加奈绘 发表于 2021-6-6 06:18
感谢分享,学到了很多
stanba 发表于 2021-6-6 09:58
wildfire_810 发表于 2021-6-6 17:03
xdnice 发表于 2021-6-7 08:03
谢谢楼主的分享,学习中。
kicebeauty 发表于 2021-6-7 09:15
哇。。。这难道。。。是系统级编程?完全看不懂,瑟瑟发抖
头像被屏蔽
yyspawn 发表于 2021-6-21 06:15
提示: 作者被禁止或删除 内容自动屏蔽
junfeng0119 发表于 2023-7-31 17:26
楼主分析得很好,思路清晰。
hjsen 发表于 2023-8-4 21:45
学习中,感谢分享!
hjsen 发表于 2023-8-5 20:27
研究中,感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-20 03:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表