1.环境
.Windows 10 20H2
2.工具
.virsual studio 2019、IDA Pro、WinDbg、VMware
3.目的
.在内核中实现对64位进程进行朴素地DLL注入
4.实验原理
.首先要说一下三环下实现远程线程DLL注入的工艺流程:
.0x1:通过OpenProcess
获取目标进程句柄;
.0x2:通过VirtualAllocEx
在目标进程内申请一块可读可写内存;
.0x3:通过WriteProcessMemory
将loadlibraryA
的参数,也就是待注入DLL的路径,写入刚申请的内存;
.0x4:通过CreateRemoteThread
对目标进程创建远程线程,线程指向LoadLibraryA
函数,参数便是刚写入申请内存的DLL路径;
.0x5:等待线程注入DLL完毕,释放申请的虚拟内存,关闭句柄.
.0x6:代码例子
#include <iostream>
#include<Windows.h>
using namespace std;
BOOL inject(DWORD dwProcessId, const WCHAR * szFilePath) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
LPVOID lpAddress = VirtualAllocEx(hProcess, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
SIZE_T dwRet = 0;
BOOL bRet = WriteProcessMemory(hProcess, lpAddress, szFilePath, ((wcslen(szFilePath) + 1) * 2), &dwRet);
if (!bRet) {
return FALSE;
}
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryW, lpAddress, NULL, NULL);
WaitForSingleObject(hThread, -1);
VirtualFreeEx(hProcess, lpAddress, ((wcslen(szFilePath) + 1) * 2), MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
int main()
{
getchar();
inject(16444, L"C:\\Users\\demo\\Desktop\\Dll1.dll");
system("pause");
return 0;
}
.在零环下,我们知道内核下是不能直接调用这些函数进行注入的,但是思路是不变的,只是调用的函数变了,有些3环下对应的内核函数在内核下是不被公开的,所以我思路中采用了很多特征码定位函数地址,接下来我们就说说详细思路:
.0x1:因为我们在内核下不能直接调用LoadLibraryA
函数,但是这个函数又是在KERNEL32.DLL
模块中的,所以第一步我们需要获取模块的函数地址,在内核中我们通过遍历EPROCESS
--->Peb
--->Ldr
-->InLoadOrderModuleList
--->BaseDllName DllBase
,如果BaseDllName
等于KERNEL32.DLL
的话就返回对应的DllBase
,也就是KERNEL32.DLL
模块在内存中的地址,实现代码如下:
ULONG64 GetProcessModule(HANDLE hPid, UNICODE_STRING unModule,PULONG pSize)
{
PEPROCESS pEprocess = NULL;
NTSTATUS st = STATUS_SUCCESS;
PPEB pPeb = ExAllocatePool(NonPagedPool,sizeof(pEprocess));
PPEB_LDR_DATA pPebLdr = NULL;
PLDR_DATA_TABLE_ENTRY pFirLdrForPebLdr = NULL;
PLDR_DATA_TABLE_ENTRY pDataLdr = NULL;
PLDR_DATA_TABLE_ENTRY pDataLdrTmp = NULL;
KAPC_STATE kApc;
ULONG64 uRetAddr = 0;
st = PsLookupProcessByProcessId(hPid, &pEprocess);
if (!NT_SUCCESS(st))
{
ObDereferenceObject(pEprocess);
return;
}
pPeb = PsGetProcessPeb(pEprocess);
KeStackAttachProcess(pEprocess, &kApc);
if (pPeb)
{
if (MmIsAddressValid(&(pPeb->Ldr)))
{
pPebLdr = pPeb->Ldr;
pFirLdrForPebLdr = pPebLdr->InLoadOrderModuleList.Blink;
pDataLdr = pFirLdrForPebLdr->InLoadOrderLinks.Flink;
pDataLdrTmp = pDataLdr;
do
{
DbgPrintEx(77, 0, "[db]%wZ,地址:%llx\n", pDataLdrTmp->BaseDllName, pDataLdrTmp->DllBase);
if (RtlEqualUnicodeString(&(pDataLdrTmp->BaseDllName), &unModule,FALSE))
{
*pSize = pDataLdrTmp->SizeOfImage;
uRetAddr = (ULONG64)pDataLdrTmp->DllBase;
break;
}
pDataLdrTmp = pDataLdrTmp->InLoadOrderLinks.Flink;
} while (pDataLdrTmp!= pDataLdr);
}
}
KeUnstackDetachProcess(&kApc);
return uRetAddr;
}
.0x2:既然我们拿到了KERNEL32.DLL
的模块地址,那么我们接下来就是需要再去拿一下LoadLibraryA
的函数地址,这里我们采用特征码搜索,我们把KERNEL32.DLL
拖入IDA Pro
解析完符号后定位到LoadLibraryA
函数或者附近看看有没有特征性比较强的字节段,有点话就可以直接使用特征码搜索便可以找到LoadLibraryA
的函数地址了,以下是IDA Pro
解析LoadLibraryA
函数字节段的内容:
.text:0000000180020C32 loc_180020C32:
.text:0000000180020C32 85 C0 test eax, eax
.text:0000000180020C34 0F 95 C1 setnz cl
.text:0000000180020C37 33 C0 xor eax, eax
.text:0000000180020C39 41 89 09 mov [r9], ecx
.text:0000000180020C3C C3 retn
.text:0000000180020C3C
.text:0000000180020C3C sub_180020C20 endp
.text:0000000180020C3C
.text:0000000180020C3C ;
.text:0000000180020C3D CC CC CC CC CC CC CC CC CC CC+db 13h dup(0CCh)
.text:0000000180020C50 ; Exported entry 969. LoadLibraryA
.text:0000000180020C50
.text:0000000180020C50 ; =============== S U B R O U T I N E ===================
.text:0000000180020C50
.text:0000000180020C50 ; Attributes: thunk
.text:0000000180020C50
.text:0000000180020C50 ; HMODULE __stdcall LoadLibraryA(LPCSTR lpLibFileName)
.text:0000000180020C50 public LoadLibraryA
.text:0000000180020C50 LoadLibraryA proc near
.text:0000000180020C50
.text:0000000180020C50
.text:0000000180020C50 48 FF 25 19 19 06 00 jmp cs:LoadLibraryA_0
.我们可以看到在0000000180020C32
处有一段固定的字节码,而0000000180020C50
便是我们LoadLibraryA
函数的地址,所以我们只要找到0000000180020C32
处的字节码位置便可轻易地通过偏移+0x1e
得到LoadLibraryA
函数的地址,此处用到了特征码搜索,使用方法我以前的帖子说过,这里就不再赘述,代码如下:
PVOID SearchSpecialCode(PVOID pSearchBeginAddr, ULONG ulSearchLength, PUCHAR pSpecialCode, ULONG ulSpecialCodeLength)
{
PVOID pDestAddr = NULL;
PUCHAR pBeginAddr = (PUCHAR)pSearchBeginAddr;
PUCHAR pEndAddr = pBeginAddr + ulSearchLength;
PUCHAR i = NULL;
ULONG j = 0;
for (i = pBeginAddr; i <= pEndAddr; i++)
{
for (j = 0; j < ulSpecialCodeLength; j++)
{
if (FALSE == MmIsAddressValid((PVOID)(i + j)))
{
break;
}
if (*(PUCHAR)(i + j) != pSpecialCode[j])
{
break;
}
}
if (j >= ulSpecialCodeLength)
{
pDestAddr = (PVOID)i;
break;
}
}
return pDestAddr;
}
.LoadLibraryA
函数地址查找代码如下:
unsigned char loadliboffsetcode[] =
{
0x85, 0xC0, 0x0F, 0x95, 0xC1, 0x33, 0xC0, 0x41, 0x89, 0x09,
0xC3
};
pLoadLibAddr = SearchSpecialCode(pKernel32ModulAddr, uKernel32ModulSize, loadliboffsetcode, 11);
pLoadLibAddr = (PUCHAR)pLoadLibAddr + 0x1e;
.0x3:现在我们已经拿到LoadLibraryA
函数地址了,接下来就是在目标进程内申请一段可读可写的虚拟内存了,内核中是由对应函数可以实现申请虚拟内存的
.申请函数是:
NTSYSAPI
NTSTATUS
NTAPI
ZwAllocateVirtualMemory (
__in HANDLE ProcessHandle,
__inout PVOID *BaseAddress,
__in ULONG_PTR ZeroBits,
__inout PSIZE_T RegionSize,
__in ULONG AllocationType,
__in ULONG Protect
);
.释放的函数:
NTSYSAPI
NTSTATUS
NTAPI
ZwFreeVirtualMemory (
__in HANDLE ProcessHandle,
__inout PVOID *BaseAddress,
__inout PSIZE_T RegionSize,
__in ULONG FreeType
);
.在申请时,第一个参数填入需要申请的目标进程句柄,这里可以先通过PsLookupProcessByProcessId
函数获取进程EPROCESS
结构,再通过ObOpenObjectByPointer
获取进程句柄,第二个参数填入申请得到的内存地址的二级指针,第三个参数NULL,第四个参数填入你要申请内存的大小的指针,第五个参数填入MEM_COMMIT
(提交内存),第六个参数填入你想要申请的内存保护类型,填入PAGE_READWRITE
就行,代码如下:
st = ZwAllocateVirtualMemory(hProcess,
&pVirsualAddr,
0,
®ionSize,
MEM_COMMIT,
PAGE_READWRITE);
.0x4:现在已经申请得到内存,那我们就需要把我们的参数写入刚申请的内存区域就行,使用KeStackAttachProcess
附加上目标进程后使用 RtlCopyMemory
把DLL路径拷贝进去就行了,代码如下:
KeStackAttachProcess(pTargetProcess, &kapcstate);
RtlZeroMemory(pVirsualAddr, regionSize);
RtlCopyMemory(pVirsualAddr, path,strlen(path));
.0x5:这时候我们还差一步,就是创建线程,但是内核中没有CreateRemoteThread
函数,所以我们需要用一个系统函数NtCreateThreadEx
函数,但是这个函数是未公开的,所以我们需要通过特征码去定位这个函数地址,和第二步的方法差不多,这里我直接给出NtCreateThreadEx
函数原型和查找函数地址代码:
typedef NTSTATUS (*ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
PVOID StartRoutine,
PVOID StartContext,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
PPS_ATTRIBUTE_LIST AttributeList
);
unsigned char ZwCreateThreadCode[] =
{
0x40,0x55,0x53,0x56,0x57,0x41,0x54,0x41,0x55,0x41,0x56,0x41,0x57,
0x48,0x81,0xec,0x28,0x03,0x00,0x00
};
.这里有个注意的是这个函数地址是在ntoskrnl.exe
中,所以我们这里需要通过遍历系统模块列表对比去拿到该模块的地址和大小,代码如下:
ULONG_PTR GetKernelModuleBase(PUCHAR moduleName, PULONG pModuleSize) {
RTL_PROCESS_MODULES SysModules = {
0 };
PRTL_PROCESS_MODULES pModules = &SysModules;
ULONG SystemInformationLength = 0;
NTSTATUS status = ZwQuerySystemInformation(SystemModuleInformation, pModules, sizeof(RTL_PROCESS_MODULES), &SystemInformationLength);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
pModules = ExAllocatePool(NonPagedPool, SystemInformationLength + sizeof(RTL_PROCESS_MODULES));
RtlZeroMemory(pModules, SystemInformationLength + sizeof(RTL_PROCESS_MODULES));
status = ZwQuerySystemInformation(SystemModuleInformation, pModules, SystemInformationLength + sizeof(RTL_PROCESS_MODULES), &SystemInformationLength);
if (!NT_SUCCESS(status)) {
ExFreePool(pModules);
return 0;
}
}
if (!strcmp("ntoskrnl.exe", moduleName) || !strcmp("ntkrnlpa.exe", moduleName)) {
*pModuleSize = pModules->Modules[0].ImageSize;
ULONG_PTR ret = pModules->Modules[0].ImageBase;
if (SystemInformationLength) {
ExFreePool(pModules);
}
return ret;
}
for (ULONG i = 0; i < pModules->NumberOfModules; i++) {
if (strstr(pModules->Modules[i].FullPathName, moduleName)) {
*pModuleSize = pModules->Modules[i].ImageSize;
ULONG_PTR ret = pModules->Modules[i].ImageBase;
if (SystemInformationLength) {
ExFreePool(pModules);
}
return ret;
}
}
if (SystemInformationLength) {
ExFreePool(pModules);
}
return 0;
}
pZwCreatThraedFuncAddr = SearchSpecialCode(pNtoskrnlModulAddr, uNtoskrnlModulSize, ZwCreateThreadCode, 17);
if (pZwCreatThraedFuncAddr)
{
DbgPrintEx(77, 0, "[db]pZwCreatThraedFuncAddr:%llx\n", pZwCreatThraedFuncAddr);
}
.随后我们便可以调用这个函数去创建线程,代码如下:
mycreate = pZwCreatThraedFuncAddr;
st = mycreate(&hThread, THREAD_ALL_ACCESS, NULL, hProcess,pLoadLibAddr, pVirsualAddr,0x2, 0, 0, 0, NULL);
DbgPrintEx(77, 0, "[db]创建线程状态st:%d\n", st);
.函数参数的意义如下:
- ThreadHandle:指向一个HANDLE类型的指针,用于接收新创建线程的句柄。
- DesiredAccess:指定请求的访问权限,包括THREAD_ALL_ACCESS、THREAD_DIRECT_IMPERSONATION、THREAD_GET_CONTEXT、THREAD_IMPERSONATE、THREAD_QUERY_INFORMATION、THREAD_QUERY_LIMITED_INFORMATION、THREAD_SET_CONTEXT、THREAD_SET_INFORMATION、THREAD_SET_LIMITED_INFORMATION、THREAD_SET_THREAD_TOKEN等。
- ObjectAttributes:指向一个OBJECT_ATTRIBUTES类型的结构体,用于指定对象的属性,例如对象名、安全描述符等。如果为NULL,则使用默认属性。
- ProcessHandle:指定目标进程的句柄。如果为NULL,则表示创建的线程与当前进程关联。
- ClientId:指向一个CLIENT_ID类型的结构体,用于接收新创建线程的客户端ID。
- StartRoutine:指向新线程的起始函数。
- StartContext:指向一个参数,传递给起始函数。
- CreateFlag:进程创建的标志。
- StackZeroBits:指定堆栈的起始地址的位数。例如,如果指定4,则堆栈的起始地址必须是16的倍数。
- StackSize:指定线程堆栈的大小。
- MaximumStackSize:指定线程堆栈的最大大小。
.其中CreateFlag参数填2的原因是想让其创建后挂起等待我们使用NtResumeThread
去恢复线程执行,其NtResumeThread
也是未公开的,我们也需要特征码去搜索,当然如果你想线程创建后就执行的话CreateFlag填0就行了,不需要使用NtResumeThread
了,NtResumeThread
的函数原型以及函数地址搜索代码如下:
typedef NTSTATUS
(*NtResumeThread)(
__in HANDLE ThreadHandle,
__out_opt PULONG PreviousSuspendCount
);
pNtResumeThreadFuncAddr = SearchSpecialCode(pNtoskrnlModulAddr, uNtoskrnlModulSize, NtResumeThreadcode, 14);
if (pNtResumeThreadFuncAddr)
{
DbgPrintEx(77, 0, "[db]pNtResumeThreadFuncAddr:%llx\n", pNtResumeThreadFuncAddr);
}
0x6:完整代码:
#pragma once
#include <ntifs.h>
#include <string.h>
#include <ntimage.h>
typedef struct _INITIAL_TEB {
struct {
PVOID OldStackBase;
PVOID OldStackLimit;
} OldInitialTeb;
PVOID StackBase;
PVOID StackLimit;
PVOID StackAllocationBase;
} INITIAL_TEB, * PINITIAL_TEB;
typedef struct _RTL_PROCESS_MODULE_INFORMATION {
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
typedef struct _RTL_PROCESS_MODULES {
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;
typedef struct _PEB_LDR_DATA
{
ULONG Length;
UCHAR Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID EntryInProgress;
UCHAR ShutdownInProgress;
PVOID ShutdownThreadId;
}PEB_LDR_DATA, * PPEB_LDR_DATA;
typedef struct _PEB
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
union
{
UCHAR BitField;
struct
{
UCHAR ImageUsesLargePages : 1;
UCHAR IsProtectedProcess : 1;
UCHAR IsImageDynamicallyRelocated : 1;
UCHAR SkipPatchingUser32Forwarders : 1;
UCHAR IsPackagedProcess : 1;
UCHAR IsAppContainer : 1;
UCHAR IsProtectedProcessLight : 1;
UCHAR IsLongPathAwareProcess : 1;
};
};
UCHAR Padding0[4];
PVOID Mutant;
PVOID ImageBaseAddress;
PEB_LDR_DATA* Ldr;
}PEB,*PPEB;
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
}LDR_DATA_TABLE_ENTRY,*PLDR_DATA_TABLE_ENTRY;
typedef struct _PS_ATTRIBUTE {
ULONG_PTR Attribute;
SIZE_T Size;
union {
ULONG_PTR Value;
PVOID ValuePtr;
} u1;
PSIZE_T ReturnLength;
} PS_ATTRIBUTE, * PPS_ATTRIBUTE;
typedef struct _PS_ATTRIBUTE_LIST {
SIZE_T TotalLength;
PS_ATTRIBUTE Attributes[1];
} PS_ATTRIBUTE_LIST, * PPS_ATTRIBUTE_LIST;
PVOID SearchSpecialCode(PVOID pSearchBeginAddr, ULONG ulSearchLength, PUCHAR pSpecialCode, ULONG ulSpecialCodeLength);
NTSYSAPI
NTSTATUS
NTAPI ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength
);
typedef NTSTATUS (*ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
PVOID StartRoutine,
PVOID StartContext,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
PPS_ATTRIBUTE_LIST AttributeList
);
typedef NTSTATUS
(*NtResumeThread)(
__in HANDLE ThreadHandle,
__out_opt PULONG PreviousSuspendCount
);
EXTERN_C
PPEB
PsGetProcessPeb(
__in PEPROCESS Process
);
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation,
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemPathInformation,
SystemProcessInformation,
SystemCallCountInformation,
SystemDeviceInformation,
SystemProcessorPerformanceInformation,
SystemFlagsInformation,
SystemCallTimeInformation,
SystemModuleInformation,
SystemLocksInformation,
SystemStackTraceInformation,
SystemPagedPoolInformation,
SystemNonPagedPoolInformation,
SystemHandleInformation,
SystemObjectInformation,
SystemPageFileInformation,
SystemVdmInstemulInformation,
SystemVdmBopInformation,
SystemFileCacheInformation,
SystemPoolTagInformation,
SystemInterruptInformation,
SystemDpcBehaviorInformation,
SystemFullMemoryInformation,
SystemLoadGdiDriverInformation,
SystemUnloadGdiDriverInformation,
SystemTimeAdjustmentInformation,
SystemSummaryMemoryInformation,
SystemMirrorMemoryInformation,
SystemPerformanceTraceInformation,
SystemObsolete0,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemExtendServiceTableInformation,
SystemPrioritySeperation,
SystemVerifierAddDriverInformation,
SystemVerifierRemoveDriverInformation,
SystemProcessorIdleInformation,
SystemLegacyDriverInformation,
SystemCurrentTimeZoneInformation,
SystemLookasideInformation,
SystemTimeSlipNotification,
SystemSessionCreate,
SystemSessionDetach,
SystemSessionInformation,
SystemRangeStartInformation,
SystemVerifierInformation,
SystemVerifierThunkExtend,
SystemSessionProcessInformation,
SystemLoadGdiDriverInSystemSpace,
SystemNumaProcessorMap,
SystemPrefetcherInformation,
SystemExtendedProcessInformation,
SystemRecommendedSharedDataAlignment,
SystemComPlusPackage,
SystemNumaAvailableMemory,
SystemProcessorPowerInformation,
SystemEmulationBasicInformation,
SystemEmulationProcessorInformation,
SystemExtendedHandleInformation,
SystemLostDelayedWriteInformation,
SystemBigPoolInformation,
SystemSessionPoolTagInformation,
SystemSessionMappedViewInformation,
SystemHotpatchInformation,
SystemObjectSecurityMode,
SystemWatchdogTimerHandler,
SystemWatchdogTimerInformation,
SystemLogicalProcessorInformation,
SystemWow64SharedInformation,
SystemRegisterFirmwareTableInformationHandler,
SystemFirmwareTableInformation,
SystemModuleInformationEx,
SystemVerifierTriageInformation,
SystemSuperfetchInformation,
SystemMemoryListInformation,
SystemFileCacheInformationEx,
MaxSystemInfoClass
} SYSTEM_INFORMATION_CLASS;
BOOLEAN MDLWriteMemory(PVOID pBaseAddress, PVOID pWriteData, SIZE_T writeDataSize);
ULONG_PTR GetKernelModuleBase(PUCHAR moduleName, PULONG pModuleSize);
ULONG64 GetProcessModule(HANDLE hPid,UNICODE_STRING unModule, PULONG pSize);
NTSTATUS InsertDll(HANDLE hPid, char* path);
#include "tool.h"
BOOLEAN MDLWriteMemory(PVOID pBaseAddress, PVOID pWriteData, SIZE_T writeDataSize)
{
PMDL pMdl = NULL;
PVOID pNewAddress = NULL;
pMdl = MmCreateMdl(NULL, pBaseAddress, writeDataSize);
if (NULL == pMdl)
{
return FALSE;
}
MmBuildMdlForNonPagedPool(pMdl);
pNewAddress = MmMapLockedPages(pMdl, KernelMode);
if (NULL == pNewAddress)
{
IoFreeMdl(pMdl);
}
RtlCopyMemory(pNewAddress, pWriteData, writeDataSize);
MmUnmapLockedPages(pNewAddress, pMdl);
IoFreeMdl(pMdl);
return TRUE;
}
ULONG_PTR GetKernelModuleBase(PUCHAR moduleName, PULONG pModuleSize) {
RTL_PROCESS_MODULES SysModules = {
0 };
PRTL_PROCESS_MODULES pModules = &SysModules;
ULONG SystemInformationLength = 0;
NTSTATUS status = ZwQuerySystemInformation(SystemModuleInformation, pModules, sizeof(RTL_PROCESS_MODULES), &SystemInformationLength);
if (status == STATUS_INFO_LENGTH_MISMATCH) {
pModules = ExAllocatePool(NonPagedPool, SystemInformationLength + sizeof(RTL_PROCESS_MODULES));
RtlZeroMemory(pModules, SystemInformationLength + sizeof(RTL_PROCESS_MODULES));
status = ZwQuerySystemInformation(SystemModuleInformation, pModules, SystemInformationLength + sizeof(RTL_PROCESS_MODULES), &SystemInformationLength);
if (!NT_SUCCESS(status)) {
ExFreePool(pModules);
return 0;
}
}
if (!strcmp("ntoskrnl.exe", moduleName) || !strcmp("ntkrnlpa.exe", moduleName)) {
*pModuleSize = pModules->Modules[0].ImageSize;
ULONG_PTR ret = pModules->Modules[0].ImageBase;
if (SystemInformationLength) {
ExFreePool(pModules);
}
return ret;
}
for (ULONG i = 0; i < pModules->NumberOfModules; i++) {
if (strstr(pModules->Modules[i].FullPathName, moduleName)) {
*pModuleSize = pModules->Modules[i].ImageSize;
ULONG_PTR ret = pModules->Modules[i].ImageBase;
if (SystemInformationLength) {
ExFreePool(pModules);
}
return ret;
}
}
if (SystemInformationLength) {
ExFreePool(pModules);
}
return 0;
}
PVOID SearchSpecialCode(PVOID pSearchBeginAddr, ULONG ulSearchLength, PUCHAR pSpecialCode, ULONG ulSpecialCodeLength)
{
PVOID pDestAddr = NULL;
PUCHAR pBeginAddr = (PUCHAR)pSearchBeginAddr;
PUCHAR pEndAddr = pBeginAddr + ulSearchLength;
PUCHAR i = NULL;
ULONG j = 0;
for (i = pBeginAddr; i <= pEndAddr; i++)
{
for (j = 0; j < ulSpecialCodeLength; j++)
{
if (FALSE == MmIsAddressValid((PVOID)(i + j)))
{
break;
}
if (*(PUCHAR)(i + j) != pSpecialCode[j])
{
break;
}
}
if (j >= ulSpecialCodeLength)
{
pDestAddr = (PVOID)i;
break;
}
}
return pDestAddr;
}
ULONG64 GetProcessModule(HANDLE hPid, UNICODE_STRING unModule,PULONG pSize)
{
PEPROCESS pEprocess = NULL;
NTSTATUS st = STATUS_SUCCESS;
PPEB pPeb = ExAllocatePool(NonPagedPool,sizeof(pEprocess));
PPEB_LDR_DATA pPebLdr = NULL;
PLDR_DATA_TABLE_ENTRY pFirLdrForPebLdr = NULL;
PLDR_DATA_TABLE_ENTRY pDataLdr = NULL;
PLDR_DATA_TABLE_ENTRY pDataLdrTmp = NULL;
KAPC_STATE kApc;
ULONG64 uRetAddr = 0;
st = PsLookupProcessByProcessId(hPid, &pEprocess);
if (!NT_SUCCESS(st))
{
ObDereferenceObject(pEprocess);
return;
}
pPeb = PsGetProcessPeb(pEprocess);
KeStackAttachProcess(pEprocess, &kApc);
if (pPeb)
{
if (MmIsAddressValid(&(pPeb->Ldr)))
{
pPebLdr = pPeb->Ldr;
pFirLdrForPebLdr = pPebLdr->InLoadOrderModuleList.Blink;
pDataLdr = pFirLdrForPebLdr->InLoadOrderLinks.Flink;
pDataLdrTmp = pDataLdr;
do
{
DbgPrintEx(77, 0, "[db]%wZ,地址:%llx\n", pDataLdrTmp->BaseDllName, pDataLdrTmp->DllBase);
if (RtlEqualUnicodeString(&(pDataLdrTmp->BaseDllName), &unModule,FALSE))
{
*pSize = pDataLdrTmp->SizeOfImage;
uRetAddr = (ULONG64)pDataLdrTmp->DllBase;
break;
}
pDataLdrTmp = pDataLdrTmp->InLoadOrderLinks.Flink;
} while (pDataLdrTmp!= pDataLdr);
}
}
KeUnstackDetachProcess(&kApc);
return uRetAddr;
}
NTSTATUS InsertDll(HANDLE hPid, char* path)
{
PEPROCESS pTargetProcess = NULL;
NTSTATUS st = STATUS_SUCCESS;
KAPC_STATE kapcstate;
HANDLE hProcess = 0;
HANDLE hThread = 0;
PVOID pNtoskrnlModulAddr = NULL;
ULONG uNtoskrnlModulSize = 0;
PVOID pKernel32ModulAddr = NULL;
ULONG uKernel32ModulSize = 0;
PVOID pZwCreatThraedFuncAddr = NULL;
PVOID pNtResumeThreadFuncAddr = NULL;
PVOID pLoadLibAddr = NULL;
PVOID pVirsualAddr = NULL;
SIZE_T regionSize = 0x1000;
ULONG PreviousSuspendCount = 0;
ZwCreateThreadEx mycreate = NULL;
NtResumeThread myresume = NULL;
UNICODE_STRING unKernel32Name = RTL_CONSTANT_STRING(L"KERNEL32.DLL");
st = PsLookupProcessByProcessId(hPid, &pTargetProcess);
if (!NT_SUCCESS(st))
{
return st;
}
ObOpenObjectByPointer(pTargetProcess,
OBJ_KERNEL_HANDLE,
NULL,
PROCESS_ALL_ACCESS,
*PsProcessType,
KernelMode,
&hProcess);
if (!NT_SUCCESS(st))
{
return st;
}
KeStackAttachProcess(pTargetProcess, &kapcstate);
unsigned char ZwCreateThreadCode[] =
{
0x40,0x55,0x53,0x56,0x57,0x41,0x54,0x41,0x55,0x41,0x56,0x41,0x57,
0x48,0x81,0xec,0x28,0x03,0x00,0x00
};
unsigned char NtResumeThreadcode[] =
{
0x40,0x53,0x48,0x83,0xec,0x50,0x48,
0x8b,0xda,0x4c,0x8b,0xd1,0xc7,0x44
};
unsigned char loadliboffsetcode[] =
{
0x85, 0xC0, 0x0F, 0x95, 0xC1, 0x33, 0xC0, 0x41, 0x89, 0x09,
0xC3
};
pNtoskrnlModulAddr = GetKernelModuleBase("ntoskrnl.exe", &uNtoskrnlModulSize);
if (pNtoskrnlModulAddr)
{
DbgPrintEx(77, 0, "[db]pKernelModulAddr:%llx\n", pNtoskrnlModulAddr);
}
pKernel32ModulAddr = GetProcessModule(hPid, unKernel32Name, &uKernel32ModulSize);
if (pKernel32ModulAddr)
{
DbgPrintEx(77, 0, "[db]pKernel32ModulAddr:%llx\n", pKernel32ModulAddr);
}
pZwCreatThraedFuncAddr = SearchSpecialCode(pNtoskrnlModulAddr, uNtoskrnlModulSize, ZwCreateThreadCode, 17);
if (pZwCreatThraedFuncAddr)
{
DbgPrintEx(77, 0, "[db]pZwCreatThraedFuncAddr:%llx\n", pZwCreatThraedFuncAddr);
}
pNtResumeThreadFuncAddr = SearchSpecialCode(pNtoskrnlModulAddr, uNtoskrnlModulSize, NtResumeThreadcode, 14);
if (pNtResumeThreadFuncAddr)
{
DbgPrintEx(77, 0, "[db]pNtResumeThreadFuncAddr:%llx\n", pNtResumeThreadFuncAddr);
}
pLoadLibAddr = SearchSpecialCode(pKernel32ModulAddr, uKernel32ModulSize, loadliboffsetcode, 11);
pLoadLibAddr = (PUCHAR)pLoadLibAddr + 0x1e;
if (pLoadLibAddr)
{
DbgPrintEx(77, 0, "[db]pLoadLibAddr:%llx\n", pLoadLibAddr);
}
st = ZwAllocateVirtualMemory(hProcess,
&pVirsualAddr,
0,
®ionSize,
MEM_COMMIT,
PAGE_READWRITE);
if (!NT_SUCCESS(st)) {
DbgPrintEx(77, 0, "[db]内存申请失败: st:%d\n", st);
return st;
}
DbgPrintEx(77, 0, "[db]内存申请成功,st:%d\n", st);
DbgPrintEx(77, 0, "[db]申请到的地址为:%llx\n", pVirsualAddr);
RtlZeroMemory(pVirsualAddr, regionSize);
RtlCopyMemory(pVirsualAddr, path,strlen(path));
mycreate = pZwCreatThraedFuncAddr;
myresume = pNtResumeThreadFuncAddr;
st = mycreate(&hThread, THREAD_ALL_ACCESS, NULL, hProcess,pLoadLibAddr, pVirsualAddr,0x2, 0, 0, 0, NULL);
DbgPrintEx(77, 0, "[db]创建线程状态st:%d\n", st);
if (NT_SUCCESS(st))
{
st = myresume(hThread, &PreviousSuspendCount);
}
DbgPrintEx(77, 0, "[db]线程调用状态:st:%d\n", st);
st = ZwWaitForSingleObject(
, FALSE, NULL);
if (!NT_SUCCESS(st))
{
DbgPrintEx(77, 0, "WaitForSingleObject st = %d\n", st);
}
st = ZwFreeVirtualMemory(hProcess,&pVirsualAddr,®ionSize,MEM_RELEASE);
if (!NT_SUCCESS(st)) {
DbgPrintEx(77, 0, "ZwFreeVirtualMemory st = %d\n", st);
}
KeUnstackDetachProcess(&kapcstate);
}
#include "tool.h"
VOID UnloadDriver(PDRIVER_OBJECT pDriver)
{
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath)
{
InsertDll(2464, "C:\\demo.dll");
pDriver->DriverUnload = UnloadDriver;
return STATUS_SUCCESS;
}
.DLL代码:
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
MessageBoxA(NULL, NULL, NULL, NULL);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
return TRUE;
}
}
0x5:注入效果
![image-20230703230948435.png](https://ucc.alicdn.com/pic/developer-ecology/zxe5qplgim75s_a2f354a0fede40409566504a49b49c76.png?x-oss-process=image/resize,w_1400/format,webp)
0x6:总结
.首先,这里注入的是64位的进程,32位的话获取模块列表需要换一下对应的32位结构,其次,这是一个非常朴素的驱动注入DLL,需要让它变强大就去发挥你们自己的思路