KMDllInjector 是一个基于内核模式的 DLL 注入器

admin 2025年4月28日23:33:00评论0 views字数 3611阅读12分2秒阅读模式
KMDllInjector 是一个基于内核模式的 DLL 注入器

关于

KMDllInjector 是一个基于内核模式的 DLL 注入器。该驱动程序可以配置为DllInjectorClient.exe使用PsSetLoadImageNotifyRoutine或PsSetCreateProcessNotifyRoutineEx注册内核回调。一旦触发回调(加载映像或创建进程),它就会将 DLL 注入目标用户模式进程。

它是如何工作的?

为了在调用进程入口点之前注入 dll,驱动程序可以使用两种技术:

PsSetCreateProcessNotifyRoutineEx + Shellcode注入

此技术用于PsSetCreateProcessNotifyRoutineEx注册一个回调函数,每当新进程创建时都会触发该回调函数。由于ntdll.dll该函数是从内核模式加载的,因此我们将在回调函数中hookNtdll!LdrLoadDll一段detour shellcode。

问题在于,在进程创建的这个阶段,PEB->Ldr结构尚未初始化。

KMDllInjector 是一个基于内核模式的 DLL 注入器

那么我们如何找到的基地址ntdll.dll?

我想到解决方案是,由于ntdll.dll已映射到进程虚拟内存空间,我可以使用它ntoskernel!ZwQueryVirtualMemory来枚举图像映射类型的内存区域,检查内存区域基址是否包含有效的 PE 头,然后解析 PE 头以确定它是否是 DLL。

while ( TRUE ) {  status = ZwQueryVirtualMemory( ZwCurrentProcess( ), baseAddress, MemoryBasicInformation, &memInfo, sizeof( memInfo ), &returnLength );if ( !NT_SUCCESS( status ) )break;// Check if this region is a mapped imageif ( memInfo.Type == MEM_IMAGE ) {if ( IsDllModule( memInfo.BaseAddress ) ) {      DBG_PRINT( "Ntdll: %p", memInfo.BaseAddress );      *NtdllBase = memInfo.BaseAddress;return STATUS_SUCCESS;    }  }// Move to the next region  baseAddress = ( PVOID ) ( ( ULONG_PTR ) memInfo.BaseAddress + memInfo.RegionSize );}

ntdll.dll在我们找到目标进程的基地址之后,我们Ntdll!LdrLoadDll使用 detour shellcode 进行钩住,shellcode 将执行以下操作:

  • 恢复 LdrLoadDll 的原始序言(删除钩子),

  • 使用传递的参数调用 LdrLoadDll。

  • 然后将我们自定义的 DLL 加载到进程中。

我没有用汇编语言编写 Shellcode,而是使用了Rhydon1337 的一个技巧:windows-kernel-dll-injector,将一个函数用作 Shellcode。由于该函数是位置无关的代码,我禁用了堆栈 Cookie、优化和控制流保护 (CFG)。我还#pragma code_seg(".text$")确保函数的顺序与 cpp 文件中的顺序一致。

#pragma optimize("", off)#pragma code_seg(".text$A")__declspec( safebuffers ) // disable stack cookies// CFG can be disabled from Properties > C/C++ > Code Generation > Control Flow Guad > NoNTSTATUS HookLdrLoadDll( PWCHAR pwPathToFile, ULONG ulFlags, PUNICODE_STRING puModuleFileName, PHANDLE phModuleHandle ){  PHOOK_CONTEXT pContext = ( PHOOK_CONTEXT ) 0xBAADF00DBAADBEEF;}#pragma code_seg(".text$B")DWORD HookLdrLoadDllEnd( ){return2;}#pragma optimize("", on)

shellcode 需要一个上下文,其中包含 ldrloaddll 序言的保存副本(使用它来恢复Ntdll!LdrLoadDll)和一些 ntdll 导出(NtProtectVirtualMemory,LdrLoadDLl,RtlInitUnicodeString)

初始化上下文后,我们扫描模式0xBAADF00DBAADBEEF并将其替换为上下文的地址。

// Search for '0xBAADF00DBAADBEEF' pattern and replace it with the address to the contextpbFunctionStart = ( PBYTE ) ( ( DWORD_PTR ) pBuffer + sizeof( HOOK_CONTEXT ) );for ( DWORD dwIndex = 0; dwIndex < sTotalSize; dwIndex++ ){if ( *( DWORD64* ) ( pbFunctionStart + dwIndex ) == 0xBAADF00DBAADBEEF )  {    *( DWORD64* ) ( pbFunctionStart + dwIndex ) = ( DWORD64 ) pBuffer;    bIsFound = TRUE;break;  }}

显示了目标进程中分配的内存情况:

KMDllInjector 是一个基于内核模式的 DLL 注入器

演示 01

PsImageLoadNotify + APC注入

PsImageLoadNotify用于注册内核回调函数,该回调函数在图像加载时触发。由于我们只想将 DLL 注入到新创建的用户模式进程中,因此我们将使用以下 if 语句进行过滤:

if (// Exclude system images.      !ImageInfo->SystemModeImage &&// Exclude images loaded remotely.      ProcessId == PsGetCurrentProcessId( ) &&// Exclude image name that not end with kernel32.dll.      (the first dll that is get loaded from user-mode on process creation is kernel32.dll)      Utils::EndsWithUnicodeString( FullImageName, &uKernel32, TRUE ) &&// Exclude images that not get loaded via `LdrLoadDll`.// (This is checked by verifying if Teb->ArbitraryUserPointer == L"...kernel32.dll".)      Utils::IsLoadedByLdrLoadDll( &uKernel32 )){// At this point, we're in a good position to inject the DLL// right after kernel32.dll has been loaded. }

LdrInitializeThunk是进程在用户模式下执行的第一个函数,此时进程仍处于创建阶段。该函数执行的最后一件事是Ntdll!NtTestAlert释放 APC 队列。想要了解更多关于该函数的信息,可以阅读这篇博客@outflank:早期级联注入简介:https://www.outflank.nl/blog/2024/10/15/introducing-early-cascade-injection-from-windows-process-creation-to-stealthy-injection

这为我们注入 DLL 提供了一个绝佳的机会,如果我们在调用之前将 APC 排队Ntdll!NtTestAlert,我们的代码就会作为进程正常流程的一部分执行。我们可以使用 和 从内核模式注入/排队 KeInitializeApcAPC KeInsertQueueApc。

KeInitializeApc( apc, Thread, OriginalApcEnvironment, KernelAPC, NULL, APCCallbackCodeCave, UserMode, Arguments );KeInsertQueueApc( apc, NULLNULL0 );

演示 02

项目地址:

https://github.com/0xPrimo/KMDllInjector

原文始发于微信公众号(Ots安全):KMDllInjector 是一个基于内核模式的 DLL 注入器

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月28日23:33:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   KMDllInjector 是一个基于内核模式的 DLL 注入器http://cn-sec.com/archives/4011715.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息