DecoyMini 技术交流社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3732|回复: 0

[工具] 在 Hint 或名称表中嵌入 shellcode 的 PoC 工程 —— HintInject

[复制链接]

188

主题

35

回帖

30

荣誉

Rank: 9Rank: 9Rank: 9

UID
2
积分
354
精华
1
沃币
2 枚
注册时间
2021-6-24

论坛管理

发表于 2022-5-11 11:08:54 | 显示全部楼层 |阅读模式

HintInject 是我在使用 PE 文件中导入表结构时开发的 shellcode 嵌入器和加载器。原理是获取一个原始的 shellcode 文件并将 shellcode 分块放入 Hint/Name 表条目中,这些条目可通过加载程序可执行文件上的假导入 DLL 条目的导入查找表访问。然后加载器合并这些块以执行 shellcode。我不知道这是否是一种新颖的技术,但我想分享它的乐趣。

DLL 加载过程


在解释 HintInject 的工作原理之前,我想通过 Stack Overflow 中的一篇文章来简要描述下 DLL 加载过程。

要获取有关所需 DLL 的函数/导入信息,首先应查看导入表。导入表是一个 entry 表,每个导入的 DLL 都有一个 entry。 这些 entry 包含指向导入的 DLL 名称的指针、指向导入查找表的指针、指向导入地址表的指针以及用于不同信息的其他字段。

简单来说,Import Lookup Table 指向导入的信息,Import Address Table 指向导入的地址。 但是,当可执行文件在磁盘上时,或者就在 DLL 加载过程之前,导入地址表与导入查找表的相同。 导入地址表的内容在 DLL 加载过程中被导入地址覆盖。

可以看下图,看看这三个表之间的关系



更详细一点,导入查找表不直接保存导入的名称。对于按名称导入的函数,它包含 Hint/Name 表条目的 RVA。这些条目将函数名称为以 Null 结尾的 ASCII 字符串存储。因此,Hint/Name 表条目的结构定义如下:

  1. typedef struct _IMAGE_IMPORT_BY_NAME {
  2.     WORD    Hint;
  3.     CHAR   Name[1];
  4. } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
复制代码

这里的 Hint 字段实际上是 DLL 的导出名称指针表的索引。它用于加速查找该导入的位置。因此,可以用下图总结 Import Lookup Table 和 Hint/Name 表之间的关系。



总而言之,为了找到一个 DLL 及其导入,应该遵循以下步骤:

  • 从 Optional Header 的 DataDirectory 数组中转到 Import Directory Table
  • 通过检查其 Name 字段来遍历导入目录表条目以查找所需的 DLL
  • 找到正确的条目后,使用 OriginalFirstThunk 字段转到其导入查找表
  • 遍历导入查找表中的所有条目,这些条目是指向 Hint/Name 表条目的指针
  • 检查 Hint/Name 表条目的名称字段中的导入名称

HintInject 是如何工作的


最近在研究 x86matthew 共享的 ImportDLLInjection 技术时,我看到了如何在内存中加载的 DLL 的导入目录中添加一个虚假条目。之后,我想开发一个小项目,将虚假条目直接添加到磁盘上二进制文件的导入表中,既是为了好玩又是为了更新我的知识。在回顾开发此项目的 DLL 加载过程时,此过程中使用的 Hint/Name 表条目中的 Hint 字段引起了我的注意。

根据 MSDN 文档得知,Windows Loader 使用该字段直接从其所在的 DLL 的导出名称表中查找该导入的地址,该导入是按名称导入的。但是,在同一个文档中, 据说如果使用此字段无法找到该函数,则将通过 DLL 的导出名称表中的二进制搜索来搜索该函数。基于此,我认为为该字段设置错误的值不会破坏 DLL 加载过程。

正如我上面提到的,Hint/Name 表中有一个对应于要导入的每个函数的条目。换句话说,我们有 2 个字节用于每次导入。通过组合多个导入,可以获得足够的 Hint 字段来存储恶意 shellcode。因此,可以使用加载程序将 shellcode 嵌入到虚假导入 DLL 条目的 Hint/Name 条目中。该加载程序可以访问这些条目以合并要在运行时执行的 shellcode。

HintInject 可用于创建这样的加载程序,该加载程序将 shellcode 保存在其 Hint/Name 表中。 它首先创建一个名为 .rrdata 的新节,并将当前导入目录复制到该节中。之后它会附加一个新的假条目,其导入将用于保存输入的 shellcode。该部分的剩余字节用于存储新伪条目的导入查找表、导入地址表、DLL 名称和 Hint/Name 表。作为最后一步,HintInject 使用导入的 Hint 字段来放置输入的 shellcode 块。新段的大概内存布局如下:



加载器二进制文件的示例导入:



使用


  • 使用 Release 选项构建项目
  • 使用原始格式的 shellcode 和输出路径执行 HintInject.exe 以创建 shellcode 加载器:HintInject.exe <Shellcode File> <Output Name>
  • 运行 loader 直接执行 shellcode,也可以给一个 PID 注入 shellcode:Loader.exe <PID>
  • 如果需要,可以通过修改 HintInjectLoader/Main.cpp 文件中的 InjectShellcodefunction 来更改 shellcode 注入方法

使用 Visual Studio 2019 在 Windows 10 19044 上测试:



限制


  • 关于执行 shellcode 的加载器上的虚假 DLL 条目,DLL 和导入的函数名都必须实际存在。否则,它会在启动加载程序可执行文件时给出 Missing DLL 错误。因此,我在项目中使用了一些 System32 下的合法 dll
  • 如果你的 shellcode 较大并且无法为你的 shellcode 创建加载器,可以将其他 dll 添加到 DllNamesForFakeImports.h 文件中的 dllNames 数组中
  • 对于每个创建的加载器,导入的函数都是随机选择的。但是,如果想更改用于创建虚假条目的 dll 的顺序,也应该编辑 DllNamesForFakeImports.h 文件中的 dllNames 数组。默认情况下,HintInject 会根据数组顺序从 dlss 中选择函数。也就是说,该工具首先从 user32.dll 中选择导出的函数,如果 shellcode 大小大于 2 * user32.dll 导出的数量,它也会开始使用 advapi32.dll 等的导出

  1. static LPCSTR dllNames[] = {"user32.dll","advapi32.dll","gdi32.dll","wininet.dll","comctl32.dll","shell32.dll","wsock32.dll","oleaut32.dll","ws2_32.dll","urlmon.dll"};
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|小黑屋|DecoyMini 技术交流社区 (吉沃科技) ( 京ICP备2021005070号 )

GMT+8, 2024-12-22 11:08 , Processed in 0.063939 second(s), 27 queries .

Powered by Discuz! X3.4

Copyright © 2001-2023, Tencent Cloud.

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