1. 首页 > 手游攻略

深入分析 深入分析的英文

作者:admin 更新时间:2025-04-18
摘要:网友SubTee认为,DLL注入是多余的(如下图所示);我倾向于同意TA的观点,然而DLL注入并不仅仅是加载DLL那么简单。你确实可以利用签名认证的微软二进制文,深入分析 深入分析的英文

 

今天给各位同享深入解析 DLL 注入技术的多样应用的姿势,其中也会对进行解释,如果能碰巧化解你现在面临的难题,别忘了关注本站,现在开始吧!

键盘侠@SubTee认为,DLL注入是多余的(如下图所示);我倾给于同意TA的见解,然而DLL注入并不仅仅是加载DLL那么简单。

你确实可以利用签名认证的微软二进制文件来加载DLL,但你无法附加到壹个特定的进程来干预其内存内容。何故大部分渗透测试师实际上不了解DLL注入是啥子,或者它是怎样职业的?由于Metasploit平台替他们包办的太多了;他们一直盲目地运用它。我认为,进修这种“奇妙的”内存操作技术的最好地点,实际上是游戏黑客论坛。如果你正在进行攻击方测试,那么你就必须干这些“脏”活儿,同时研究这些技术;除非你乐意仅仅运用别人随意编写的工具。

大部分时刻,大家运用很复杂的技术开始一次攻击方测试;如果大家未被发现,则开始降低复杂度。基本上这就是大家开始给磁盘投放二进制文件和应用DLL注入技术的时刻点。

简介

DLL注入技术,一般来讲是给壹个正在运行的进程插入/注入代码的经过。大家注入的代码以动态链接库(DLL)的形式存在。DLL文件在运行时将按需加载(类似于UNIX体系中的共享库)。本工程中,我将仅运用DLL文件,然而实际上,大家可以以其他的多种形式“注入“代码(正如恶意软件中所常见的,任意PE文件,shellcode代码/程序集等)。

同时要记下,你需要合适的权限级别来操控其他进程的内存空间。但我不会在此讨论保护进程(相关网址:https://www.microsoftpressstore.com/articles/article.aspx?p=2233328&seqNum=2)和权限级别(通过Vista体系说明,相关网址:https://msdn.microsoft.com/en-gb/library/windows/desktop/bb648648(v=vs.85).aspx);这属于完全不同的另壹个主题。

再次强调一下,正如我之前所说,DLL注入技术可以被用于合法正当的用途。比如,反病毒软件和端点安全化解方法运用这些技术来将其软件的代码嵌入/拦截体系中“全部”正在运行的进程,这使得它们可以在其运行经过中监控每壹个进程,从而更好地保护大家。同样存在恶意的用途。一种经常被用到的通用技术是注入“lsass”进程来获取口令哈希值。大家之前都这么干过。很明显,恶意代码同样广泛应用了代码注入技术:不管是运行shellcode代码,运行PE文件,还是在另壹个进程的内存空间中加载DLL文件以隐藏自身,等等。

基础姿势

对于每一种技术,大家都将用到微软Windows API,由于它为大家提供了大量的函数来附加和操纵其他进程。从微软Windows操作体系的第壹个版本开始,DLL文件就是其基石。事实上,微软Windows

API中的全部函数都包含于DLL文件之中。其中,最重要的是“Kernel32.dll”(包含管理内存,进程和线程相关的函数),“User32.dll”(大部分是用户接口函数),和“GDI32.dll”(绘制图形和显示文本相关的函数)。

你也许会有疑问,何故会有这些API接口,何故微软为大家提供如此丰盛的函数集来操纵和修改其他进程的内存空间?主要缘故是为了扩展应用程序的功能。比如,壹个企业创建了一款应用程序,而且允许其他企业来扩展或增强这个应用程序;如此,这就有了壹个合法正当的用途。除此之外,DLL文件还用于项目管理,内存保护,资源共享,等等。

下图尝试说明了几乎每一种DLL注入技术的流程。

如上所见,我认为 DLL 注入共四个流程:

1. 附加到目标/超距离进程

2. 在目标/超距离进程内分配内存

4. 控制进程运行DLL文件

全部这些流程是通过调用一系列指定的API函数来完成的。每种技术需要进行特定的配置和选项配置。我认为,每种技术都有其优点和缺点

(1)技术说明

•CreateRemoteThread()

•NtCreateThreadEx()

•QueueUserAPC

•SetWindowsHookEx()

•RtlCreateUserThread()

•利用SetThreadContext()找到的代码区域

•反射DLL

你也许通过其他的名字了解其中某些技术。以上并不是包含每一种DLL注入技术的完整列表;正如我所说的,还有更多技术,如果之后我在某个工程中需要对其接触进修的话我会将它们添加进来。到目前为止,这就是我在某些工程中所用到的技术列表;其中某些可以稳定利用,某些不可以。需要注意的是,不能够稳定利用的那些技术也许是由于我所编写代码的自身难题。

(2)LoadLibrary()

正如MSDN中所述,“LoadLibrary()”函数“被用于给调用进程的地址空间加载指定模块,而该指定模块也许导致其他模块被加载”。函数原型和参数说明如下所示:

HMODULE WINAPI LoadLibrary(

换言之,该函数只需要壹个文件名作为其唯一的参数。即,大家只需要为大家的DLL文件途径分配内存,将执行起点配置为“LoadLibrary()”函数的地址,之后将途径的内存地址传递给函数作为参数。

正如你所了解(或不了解)的,最大的难题是“LoadLibrary()”会给程序注册已加载的DLL模块;这意味着这种方式很容易被检测到,但令人惊奇的是很多端点安全化解方法仍检测不出。不管如何,正如我之前所说,DLL注入也有一些合法正当的用途,因此大家还要注意的是,如果壹个DLL文件已经用“LoadLibrary()”加载过了,则它不会再次执行。你可以试验一下,但我没有对任何一种技术试过。当然,运用反射DLL注入技术不会有这方面的难题,由于DLL模块并未被注册。不同于运用“LoadLibrary()”,反射DLL注入技术将整个DLL文件载入内存,接着通过确定DLL模块的入口点偏移来将其加载;这样可以按照需求更隐蔽的对其进行调用。取证人员仍然能够在内存中找到你的DLL,但会很艰难。Metasploit平台大量运用了这项技术,而且大部分端点化解方法也还乐意始终运用它。如果你想查找这方面的技术资料,或者你在攻防游戏中处于防守方,可以参阅下面内容网址:

https://www.defcon.org/html/defcon-20/dc-20-speakers.html#King

https://github.com/aking1012/dc20

附注一下,如果你正在折腾你的端点安全软件,而它很好地利用了以上全部这些技术,你也许需要运用下面内容攻防反欺骗引擎来尝试(注意,我只是尝试轻松的说法,以便你能领会)。某些反欺骗工具的反Rookit性能要比某些反病毒软件要先进得多。reddit网站上有一本书你肯定读过,叫“黑客游戏”,它的作者Nick Cano对其有特别深入的研究;只需了解一下他的职业,你就会领会我所谈论的内容。

附加到目标/超距离进程

首先,大家需要获取大家想要交互的进程句柄;为此大家调用“OpenProcess()”API函数。函数原型如下:

HANDLE WINAPI OpenProcess(

如果你读过MSDN中相关的文档,那么你应该了解大家需要请求获取一系列特定的访问权限;访问权限的完整列表参阅网址:https://msdn.microsoft.com/en-gb/library/windows/desktop/ms684880(v=vs.85).aspx。

这些权限随微软Windows操作体系版本不同而不同;下面内容调用代码可用于几乎每一种技术之中:

HANDLE hProcess = OpenProcess(

在目标/超距离进程空间分配内存

大家调用“VirtualAllocEx()”函数为DLL途径分配内存。正如MSDN中所述,“VirtualAllocEx()”函数“保留,提交或改变指定进程虚拟地址空间中的一块内存区域的情形;函数通过置零来初始化内存。”函数原型如下:

LPVOID WINAPI VirtualAllocEx(

基本上,大家进行如下的调用操作:

// 计算DLL文件途径名称所需的字节数

或者你可以更伶俐一点地调用“GetFullPathName()”API函数;接着,我在整个工程中都没有调用这个API函数,仅仅是出于个人偏好或者是不够伶俐。

但这样会有一点复杂,由于当DLL模块加载到内存中时大家需要获取其入口点;反射DLL工程的“LoadRemoteLibraryR()”函数部分为大家完成了这项职业。如有需要请参阅源码。

需要注意的是,大家将注入的DLL文件需要运用适当的包含和选项来进行编译,这样它才能和ReflectiveDLLInjection方式相匹配。“InjectAllThings”工程中包含了壹个名为“rdll_32.dll/rdll_64.dll”的DLL文件,可用于练习。

控制进程来运行DLL文件

(1)CreateRemoteThread()

可以说,“CreateRemoteThread()”是最传统和最流行,以及最多文档资料说明的DLL注入技术。

它包括下面内容多少流程:

1.运用OpenProcess()函数打开目标进程

2.通过调用GetProAddress()函数找到LoadLibrary()函数的地址

3.通过调用VirtualAllocEx()函数在目标/超距离进程地址空间中为DLL文件途径开辟内存空间

4.调用WriteProcessMemory()函数在之前所分配的内存空间中写入DLL文件途径

5.调用CreateRemoteThread()函数创建壹个新的线程,新线程以DLL文件途径名称作为参数来调用LoadLibrary()函数

如果你看过MSDN中关于“CreateRemoteThread()”函数的文档,那么你应该了解,大家需要壹个指针,“指给将由线程执行的,类型为‘LPTHREAD_START_ROUTINE’的应用程序定义函数,而且该指针代表超距离进程中线程的起始地址”。

这意味着要运行大家的DLL文件,大家只需要控制进程来做就好(译者注:由下文可知,应该是将“LoadLibrary()”函数作为线程的启动函数,来加载待注入的DLL文件)。很简单。

下面内容代码即之前所列的全部基本流程。

HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessId);

完整代码请参阅源文件“t_CreateRemoteThread.cpp”。

(2)NtCreateThreadEx()

另壹个选择是运用“NtCreateThreadEx()”函数;这一个未公开的“ntdll.dll”中的函数,未来也许会消失或改变。这种技术相比而言实现更加复杂,由于大家需要壹个结构体(具体如下所示)来给函数传递参数,以及另壹个结构体用于从函数接收数据。

struct NtCreateThreadExBuffer {

网址:http://securityxploded.com/ntcreatethreadex.php处的文章详细说明了该函数调用。配置部分和“CreateRemoteThread()”特别类似;然而,相较于直接调用“CreateRemoteThread()”函数,大家运用如下代码来调用“NtCreateThreadEx()”。

PTHREAD_START_ROUTINE ntCreateThreadExAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtCreateThreadEx");

完整代码请参阅源文件“t_NtCreateThreadEx.cpp”。

(3)QueueUserAPC()

除了之前说明的方式还有一种选择,不用在目标/超距离进程中创建壹个新的线程,那就是“QueueUserAPC()”函数。

根据MSDN中的文档说明,该函数“给指定线程的APC队列中添加壹个用户态的异步经过调用(APC)对象”。

函数原型和参数说明如下所示。

DWORD WINAPI QueueUserAPC(

因此,如果不想创建大家自己的线程,大家可以调用“QueueUserAPC()”函数来劫持目标/超距离进程中壹个已存在的线程;即,调用该函数将在指定线程的APC队列中添加壹个异步经过调用。

大家可以运用壹个真正的APC回调函数,而不运用“LoadLibrary()”。事实上参数可以是指给大家想要注入的DLL文件名称的指针,具体代码如下所示。

DWORD dwResult = QueueUserAPC((PAPCFUNC)pfnThreadRtn, hThread, (ULONG_PTR)pszLibFileRemote);

如果你想试用这种技术,那么有一点你也许注意到了,即它和微软Windows操作体系执行APC的方法有关。没有能够查看APC队列的调度器,这意味着只有线程配置成可唤醒玩法才能够检查队列。

因此,大家基本上劫持每壹个单独的线程,具体代码如下所示。

BOOL bResult = Thread32First(hSnapshot, &threadEntry);

这样做的理由主要是期望其中壹个线程会被设为可唤醒玩法。

另外,运用“双脉冲星”(网址:https://countercept.com/our-thinking/doublepulsar-usermode-analysis-generic-reflective-dll-loader/,DOUBLEPULSAR 用户玩法解析:通用反射DLL加载器)工具解析进修这项技术,是个很好的办法。

完整代码请参阅源文件“t_QueueUserAPC.cpp”。

(4)SetWindowsHookEx()

运用这项技术的首要职业是,大家需要领会在微软Windows操作体系中劫持的职业原理。本质上,劫持技术是拦截并干预事件的一种方法。

正如你所猜想的那样,有很多种不同类型的劫持技术。最通用的一种也许是WH_KEYBOARD和WH_MOUSE消息拦截;没错,它们可被用于监控键盘和鼠标的输入。

函数“SetWindowsHookEx()”将壹个应用程序定义的拦截例程配置到壹个拦截链表中。函数原型和参数定义如下所示。

HHOOK WINAPI SetWindowsHookEx(

MSDN中有一段很有趣的备注如下:

“SetWindowsHookEx函数可被用于给另壹个进程注入DLL文件。壹个32位的DLL文件不能注入壹个64位的进程,反之亦然。如果壹个应用程序需要在其他进程中运用劫持技术,那么就标准壹个32(64)位的应用程序调用SetWindowsHookEx函数来将壹个32(64)位的DLL文件注入到壹个32(64)位的进程中。32位和64位DLL文件的名称必须不同。”

记下以上内容。

下面内容代码是实现的简要经过。

GetWindowThreadProcessId(targetWnd, &dwProcessId)

大家需要领会的是,每壹个发生的事件都要遍历拦截链表,该链表包含一系列响应事件的例程。“SetWindowsHookEx()”函数的配置职业基本上就是怎样将大家自己的拦截例程植入拦截链表中。

以上代码用到了待配置的劫持消息类型(WH_KEYBOARD)例程指针,包含例程的DLL模块句柄,以及劫持所关联的线程标识号。

注意,正如大家在维基解密(网址:https://wikileaks.org/ciav7p1/cms/page_6062133.html)中所看到的,就连联邦调查局的人员也有也许用到“SetWindowsHookEx()”函数。

完整代码请参阅源文件“t_SetWindowsHookEx.cpp”。

(5)RtlCreateUserThread()

“RtlCreateUserThread()”一个未公开的API函数。它的配置职业几乎和“CreateRemoteThread()”函数相同,相应的也和“NtCreateThreadEx()”函数相同。

实际上,“RtlCreateUserThread()”调用“NtCreateThreadEx()”,这意味着“RtlCreateUserThread()”是“NtCreateThreadEx()”的壹个小型封装函数;因此,这个函数并没有新的内容。然而,大家也许只是单纯地想运用“RtlCreateUserThread()”而不用“NtCreateThreadEx()”。哪怕之后发生变动,大家的“RtlCreateUserThread()”仍能正常职业。

正如你所了解的,不同于其他工具,mimikatz工具和Metasploit平台都用到了“RtlCreateUserThread()”。如果你对此感兴趣,请参阅网址:https://github.com/gentilkiwi/mimikatz/blob/d5676aa66cb3f01afc373b0a2f8fcc1a2822fd27/modules/kull_m_remotelib.c#L59和网址:https://github.com/rapid7/meterpreter/blob/6d43284689240f4261cae44a47f0fb557c1dde27/source/common/arch/win/remote_thread.c。

因此,如果mimikatz工具和Metasploit平台都运用“RtlCreateUserThread()”函数,那么(是的,他们了解自己的处理对象)听从他们的“提议”,运用“RtlCreateUserThread()”;特别是你规划做一项相比于简单的“injectAllTheThings”工程来说更认真的项目。

完整代码请参阅源文件“t_RtlCreateUserThread.cpp”。

(6)SetThreadContext()

实际上这是一种特别酷的方式:通过在目标/超距离进程中分配一块内存区域,给目标/超距离进程注入一段特别构造的代码,这段代码的用途是加载DLL模块。

下面内容是32位环境下的代码。

0x68, 0xCC, 0xCC, 0xCC, 0xCC, // push 0xDEADBEEF (为返回地址占位)

对于64位环境,实际上我没有找到任何完整的职业代码,因此我简单写了我自己的代码,如下所示。

0x50, // push rax (保存RAX寄存器)

在大家想目标进程注入这段代码之前,下面内容占位符需要修改填充:

  • ·返回地址(代码桩执行完毕之后,线程恢复应回到的地址)

  • ·DLL途径名称

  • ·LoadLibrary()函数地址

  • 而这也是进行劫持,挂起,注入和恢复线程这一系列操作的时机。

    大家需要附加到目标/超距离进程,之后当然是在目标/超距离进程中分配内存。注意,大家需要以读写权限分配内存,以便操作DLL途径名称和用于加载DLL文件的封装代码。具体代码如下所示。

    LPVOID lpDllAddr = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    之后,大家需要获取壹个运行于目标/超距离进程之上的线程上下文(即大家将要注入封装代码的目标线程)。

    大家调用函数“getThreadID()”来找到线程,你可以在文件“auxiliary.cpp”中找到该函数。

    有了线程标识号之后,大家需要配置线程上下文。具体代码如下所示。

    hThread = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME), false, threadID);

    接着,大家需要挂起线程来获取其上下文;壹个线程的上下文是指其寄存器的情形,大家格外关注的是EIP/RIP寄存器(根据需要也可以称其为IP——instruction pointer,指令指针)。

    由于线程已被挂起,因此大家可以改变EIP/RIP寄存器的值,控制线程在不同的途径上(大家的代码区域)继续执行。具体代码如下所示。

    ctx.ContextFlags = CONTEXT_CONTROL;

    因此,大家中断线程,获取上下文,并从上下文中提取EIP/RIP值;保存的旧值用于在注入代码执行完成时恢复线程的执行流程。新的EIP/RIP值配置为大家注入的代码位置。

    接着大家用返回地址,DLL途径名称地址和“LoadLibrary”函数地址填充全部的占位符。

    线程开始执行的时候,大家的DLL文件将被加载;而当注入代码执行完成时,执行流程将返回县城挂起点,并从此恢复线程的正常执行流程。

    如果你想要调试这种技术方式来进修练习,下面内容是操作流程。启动你想要注入的应用程序,在此大家以“notepad.exe”为例。运用“x64dbg”调试工具来运行“injectAllTheThings_64.exe”,如下图所示。

    运用下面内容命令(根据你的实际环境来调整)。

    "C:\Users\rui\Documents\Visual Studio 2024\Projects\injectAllTheThings\bin\injectAllTheThings_64.exe" -t 6 notepad.exe "c:\Users\rui\Documents\Visual Studio 2024\Projects\injectAllTheThings\bin\dllmain_64.dll"

    在调用“WriteProcessMemory()”函数处设下断点,如下图所示。

    继续运行程序,当运行到断点处时,注意寄存器RDX中的内存地址,如图所示。如果你对何故这里需要关注RDX有疑问,请去查阅x64环境下的调用约定;搞清楚再过来继续进修。

    很酷吧?现在在Shellcode代码起始处设下断点。转给“injectAllTheThings”调试进程,并运行程序。大家的断点被成功断下,如下图所示;现在大家可以步过代码,并解析这段代码的功能。

    当大家调用“LoadLibrary()”函数时,大家的DLL文件成功加载,如下图所示。

    太棒了~

    大家的Shellcode代码将返回到之前保存的RIP地址处,“notepad.exe”进程将恢复执行。

    完整代码请参阅源文件“t_suspendInjectResume.cpp”。

    (7)反射DLL注入

    我将StepheFewer(这项技术的先驱)的代码也整合到了这个“injectAllTheThings”工程中,同时还构建了壹个反射DLL文件用于这项技术。注意,大家要注入的DLL文件必须运用适当的包含和选项来进行编译,这样它才能和反射DLL注入技术相匹配。

    Fewer的代码;他的工程中所包含的“LoadRemoteLibrary()”函数为大家完成这项职业。大家运用“GetReflectLoaderOffset()”函数来确定在大家进程内存中的偏移,接着大家将偏移加上目标/超距离进程(即大家写入DLL模块的进程)的内存基址,将该结局作为执行起始点。

    太复杂?好吧,也许有点儿;下面内容是实现上述经过的4个主要流程。

    1.将DLL文件头部写入内存

    2.将每个区块写入内存(通过解析区块表)

    3.检查输入表,并加载任何引用的DLL文件

    4.调用DLLMain函数的入口点

    相比于其他方式,这种技术有很好的隐蔽性,主要被用于Metasploit平台。

    还可以参阅“MemoryModule”项目的作者Joachim Bauch所写的“从内存中加载DLL文件”,以及一篇好文章“不调用LoadLibrary()函数‘手动’加载32位/64位DLL文件”。

    代码

    还有一些模糊复杂的注入方式,因此我未来将对“injectAllTheThings”工程进行更新。其中某些最有趣的技术包括:

    •“双脉冲星”工具所用到的技术

    •键盘侠@zerosum0x0所编写的工具,运用SetThreadContext()和NtContinue()实现的反射DLL注入,详细描述参见网址:https://zerosum0x0.blogspot.co.uk/2024/07/threadcontinue-reflective-injection.html,可用代码详见网址:https://github.com/zerosum0x0/ThreadContinue。

    以上我所描述的全部技术,都在壹个单独的工程中实现了,我将其放在GitHub库中;其中还包括每种技术所需的DLL文件。为了便于领会,下表简单说明了所实现的方式和具体用法。

    需要说明的是,从安全角度出发,应该坚持运用injectAllTheThings_32.exe注入32位进程,或者injectAllTheThings_64.exe来注入64位进程;虽然你也可以运用injectAllTheThings_64.exe来注入32位进程。而实际上我并没有这样实现,但也许之后我会尝试一下,具体请参考下面内容网址:http://blog.rewolf.pl/blog/?p=102。参考网址中的技术基本上就是Metasploit平台上“smart_migrate”工具所用到的,详见网址:https://github.com/rapid7/meterpreter/blob/5e24206d510a48db284d5f399a6951cd1b4c754b/source/common/arch/win/i386/base_inject.c。

    整个工程的代码(包括DLL文件)都在GitHub库中。代码以32位/64位环境编译,包含或不包含调试信息都可以。

    参考