frkngksl/ExportHider
GitHub: frkngksl/ExportHider
ExportHider 在运行时动态重建导出表以隐藏 DLL 导出函数,仅对合法调用保持透明。
Stars: 19 | Forks: 2
# ExportHider
ExportHider 生成一个 C++ DLL 模板,其中包含一个代码存根,允许你在文件系统的 DLL 导出目录中隐藏导出函数。放置函数定义并编译文件后,像 CFF Explorer 这样的 PE 文件查看器将无法看到隐藏的导出函数。然而,由于模板中的代码存根会在运行时重建导出目录,合法的 GetProcAddress 调用仍能成功执行。此方法仅适用于动态 DLL 加载或自定义 DLL 加载器的情况。
# 它是如何工作的?
通常,当你想在 DLL 文件(用 C 或 C++)中定义一个导出函数时,只需要在函数名之前添加 **`__declspec(dllexport)`** 关键字,或者创建一个 .def 文件。编译后,编译器会创建一个称为“导出目录”的特定表,用于存储与导出函数相关的信息。导出目录的结构如下所示:
当某个进程想要使用 DLL 文件中的函数时,Windows 加载器会简单地解析此结构,并使用 `AddressOfFunctions`、`AddressOfNames`、`AddressOfNameOrdinals` 数组导入请求的函数。
导入函数的特定流程在 [ferreirasc 的博客文章](https://ferreirasc.github.io/PE-Export-Address-Table/) 中有详细说明,但简而言之,对于按名称导入的函数,加载器会遍历 `AddressOfNames` 数组(该数组的值只是 RVA),并搜索给定的名称。一旦加载器在“i”位置找到匹配项,它会引用 `AddressOfNameOrdinals` 数组的第 i 个索引,以获取与该函数关联的序号。拥有序号后,加载器会引用 `AddressOfFunctions` 中序号位置的值,最终获取与导入函数关联的 RVA。
这里的关键点是,当调用 LoadLibrary 时,Windows 加载器执行的所有这些搜索和访问操作都是在 DLL 被映射到进程地址空间之后进行的。在 DLL 映射期间,整个 DLL 文件(包括其 PE 头)会被写入内存,加载器解析内存中的头以到达导出目录。这意味着如果 DLL 自身能够在附加到进程后覆盖其内存中的 PE 头,从而修改导出目录地址(简单来说,就是 `DataDirectory` 的第 0 个索引),那么加载器就会在一个任意的导出目录中查找要导入的函数,而不是编译器添加的那个。
# 命令行参数
```
__ _ _ _
/__\_ ___ __ ___ _ __| |_ /\ /(_) __| | ___ _ __
/_\ \ \/ / '_ \ / _ \| '__| __|/ /_/ / |/ _` |/ _ \ '__|
//__ > <| |_) | (_) | | | |_/ __ /| | (_| | __/ |
\__/ /_/\_\ .__/ \___/|_| \__\/ /_/ |_|\__,_|\___|_|
|_|
by @R0h1rr1m
Usage of C:\Users\Public\DLLDemo\ExportHider.exe:
-h | --help Show the help message.
-i | --input
Input path for the list of function names to be hidden. (Mandatory)
-o | --output
标签:AddressOfFunctions, AddressOfNameOrdinals, AddressOfNames, C++ DLL, DLL 注入, DOM解析, Export Directory, GetProcAddress, LoadLibrary, PE 文件结构, Windows 加载器, XML 请求, 云资产清单, 代码混淆, 函数隐藏, 动态加载, 动态链接库, 反调试, 安全开发, 导出表隐藏, 端点可见性, 自定义加载器, 软件保护, 运行时导出, 逆向工程, 防护绕过