今天我很高兴向您介绍我的第一个WinDbg扩展lld,目前它只包含一个命令:!inject DLL,它允许您将DLL注入正在调试的进程。sdbgext扩展中有一个类似的命令,但它只适用于32位进程。用法非常简单——只要记住以正确的位加载扩展(32位进程的32位版本)。示例会话可能如下所示:
0:000> .load lld 0:000> !injectdll c:\temp\Test.exe ModLoad: 00000001`3f820000 00000001`3f924000 c:\temp\Test.exe ModLoad: 000007fe`fd960000 000007fe`fd98e000 C:\Windows\system32\IMM32.DLL ModLoad: 000007fe`ff410000 000007fe`ff519000 C:\Windows\system32\MSCTF.dll (bac.5a0): Break instruction exception - code 80000003 (first chance) ntdll!LdrpDoDebuggerBreak+0x30: 00000000`778c7800 cc int 3
进制文件可以在源代码存储库的“发布”选项卡下找到。
工作原理
这个扩展背后的逻辑非常简单,依赖于线程劫持机制。我通过增加除当前线程以外的所有进程线程的挂起计数来启动注入进程。然后,我在目标进程中分配一块内存,用于存储DLL名称和负载。我将当前线程上下文保存到一个局部变量,并更改负载所需的寄存器的值。最后,我恢复当前线程,该线程执行有效负载并返回到调试器中,在调试器中,我可以减少所有其他线程的挂起计数并恢复被劫持的线程上下文。
32位的有效负载只有4字节长,64位的有效负载只有3字节长:
; x64 0x00000000 call rax 0x00000002 int3 ; x86 0x00000000 push ecx 0x00000001 call eax 0x00000003 int3
我将eax/rax注册表设置为LoadLibraryA的地址,将ecx/rcx注册表设置为DLL名称的地址。如您所见,在调用之后,线程执行断点指令(int3),从而返回到调试器。