Windows x64内核下注入DLL姿势之一

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
简介: 讲述了Windows x64内核下注入DLL的思路与流程

1.环境

.Windows 10 20H2

2.工具

.virsual studio 2019、IDA Pro、WinDbg、VMware

3.目的

.在内核中实现对64位进程进行朴素地DLL注入

4.实验原理

.首先要说一下三环下实现远程线程DLL注入的工艺流程:

.0x1:通过OpenProcess获取目标进程句柄;

.0x2:通过VirtualAllocEx在目标进程内申请一块可读可写内存;

.0x3:通过WriteProcessMemoryloadlibraryA的参数,也就是待注入DLL的路径,写入刚申请的内存;

.0x4:通过CreateRemoteThread对目标进程创建远程线程,线程指向LoadLibraryA函数,参数便是刚写入申请内存的DLL路径;

.0x5:等待线程注入DLL完毕,释放申请的虚拟内存,关闭句柄.

.0x6:代码例子

#include <iostream>
#include<Windows.h>
using namespace std;
//目标进程ID,动态链接库的路径
BOOL inject(DWORD dwProcessId, const WCHAR * szFilePath) {
   
   
        //进程全部的权限,句柄继承(通常为FALSE),目标进程ID
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);//返回的是进程句柄
        //目标进程句柄,需要开辟的地址(NULL为操作系统分配),需要分配地址的大小,现在操作的类型,提供可读可写的属性
        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;
        }
        //目标进程句柄,安全特性(通常为NULL),栈的尺寸(可以为NULL),该线程函数启动地址(强制转换类型为LPTHREAD_START_ROUTINE,可用LoadLibraryW加载),需要提供的参数(此处为需要写入的地址),标志(可以为NULL),线程ID(可以为NULL)
        HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryW, lpAddress, NULL, NULL);
        //-1代表一直等
        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模块在内存中的地址,实现代码如下:

//hPid传入一个64位进程,且进程中加载了KERNEL32.DLL
//unModule传入模块名,这里我们传KERNEL32.DLL
//pSize传入一个记录模块大小变量的指针
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);

.函数参数的意义如下:

  1. ThreadHandle:指向一个HANDLE类型的指针,用于接收新创建线程的句柄。
  2. 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等。
  3. ObjectAttributes:指向一个OBJECT_ATTRIBUTES类型的结构体,用于指定对象的属性,例如对象名、安全描述符等。如果为NULL,则使用默认属性。
  4. ProcessHandle:指定目标进程的句柄。如果为NULL,则表示创建的线程与当前进程关联。
  5. ClientId:指向一个CLIENT_ID类型的结构体,用于接收新创建线程的客户端ID。
  6. StartRoutine:指向新线程的起始函数。
  7. StartContext:指向一个参数,传递给起始函数。
  8. CreateFlag:进程创建的标志。
  9. StackZeroBits:指定堆栈的起始地址的位数。例如,如果指定4,则堆栈的起始地址必须是16的倍数。
  10. StackSize:指定线程堆栈的大小。
  11. 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:完整代码:

//tool.h
#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;                 // Not filled in
        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;



//0x58 bytes (sizeof)
typedef struct _PEB_LDR_DATA
{
   
   
        ULONG Length;                                                           //0x0
        UCHAR Initialized;                                                      //0x4
        PVOID SsHandle;                                                         //0x8
        LIST_ENTRY InLoadOrderModuleList;                               //0x10
        LIST_ENTRY InMemoryOrderModuleList;                             //0x20
        LIST_ENTRY InInitializationOrderModuleList;                     //0x30
        PVOID EntryInProgress;                                                  //0x40
        UCHAR ShutdownInProgress;                                               //0x48
        PVOID ShutdownThreadId;                                                 //0x50
}PEB_LDR_DATA, * PPEB_LDR_DATA;


//0x7c8 bytes (sizeof)
typedef struct _PEB
{
   
   
        UCHAR InheritedAddressSpace;                                            //0x0
        UCHAR ReadImageFileExecOptions;                                         //0x1
        UCHAR BeingDebugged;                                                    //0x2
        union
        {
   
   
                UCHAR BitField;                                                     //0x3
                struct
                {
   
   
                        UCHAR ImageUsesLargePages : 1;                                    //0x3
                        UCHAR IsProtectedProcess : 1;                                     //0x3
                        UCHAR IsImageDynamicallyRelocated : 1;                            //0x3
                        UCHAR SkipPatchingUser32Forwarders : 1;                           //0x3
                        UCHAR IsPackagedProcess : 1;                                      //0x3
                        UCHAR IsAppContainer : 1;                                         //0x3
                        UCHAR IsProtectedProcessLight : 1;                                //0x3
                        UCHAR IsLongPathAwareProcess : 1;                                 //0x3
                };
        };
        UCHAR Padding0[4];                                                      //0x4
        PVOID Mutant;                                                           //0x8
        PVOID ImageBaseAddress;                                                 //0x10
        PEB_LDR_DATA* Ldr;                                              //0x18                    //0xc                         //0x18
}PEB,*PPEB;




//0x120 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY
{
   
   
        LIST_ENTRY InLoadOrderLinks;                                    //0x0
        LIST_ENTRY InMemoryOrderLinks;                                  //0x10
        LIST_ENTRY InInitializationOrderLinks;                          //0x20
        PVOID DllBase;                                                          //0x30
        PVOID EntryPoint;                                                       //0x38
        ULONG SizeOfImage;                                                      //0x40
        UNICODE_STRING FullDllName;                                     //0x48
        UNICODE_STRING BaseDllName;                                     //0x58                       //0x11c
}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,             // obsolete...delete
        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  // MaxSystemInfoClass should always be the last enum
} 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);
//tool.c
#include "tool.h"
BOOLEAN MDLWriteMemory(PVOID pBaseAddress, PVOID pWriteData, SIZE_T writeDataSize)
{
   
   
        PMDL pMdl = NULL;
        PVOID pNewAddress = NULL;
        // 创建 MDL
        pMdl = MmCreateMdl(NULL, pBaseAddress, writeDataSize);
        if (NULL == pMdl)
        {
   
   
                return FALSE;
        }
        // 更新 MDL 对物理内存的描述
        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);
        //NtCreateThread特征码
        unsigned char ZwCreateThreadCode[] =
        {
   
   
        0x40,0x55,0x53,0x56,0x57,0x41,0x54,0x41,0x55,0x41,0x56,0x41,0x57,
        0x48,0x81,0xec,0x28,0x03,0x00,0x00
        };
        //ResumeThreadcode特征码
        unsigned char NtResumeThreadcode[] =
        {
   
   
          0x40,0x53,0x48,0x83,0xec,0x50,0x48,
          0x8b,0xda,0x4c,0x8b,0xd1,0xc7,0x44
        };
        //LoadLibrary特征码偏移-0x1e
        unsigned char loadliboffsetcode[] =
        {
   
   
          0x85, 0xC0, 0x0F, 0x95, 0xC1, 0x33, 0xC0, 0x41, 0x89, 0x09,
          0xC3
        };
        //内核Ntoskrnl地址
        pNtoskrnlModulAddr = GetKernelModuleBase("ntoskrnl.exe", &uNtoskrnlModulSize);
        if (pNtoskrnlModulAddr)
        {
   
   
                DbgPrintEx(77, 0, "[db]pKernelModulAddr:%llx\n", pNtoskrnlModulAddr);
        }
        //kernel32.dll地址,为了拿LoadLibrary函数地址
        pKernel32ModulAddr = GetProcessModule(hPid, unKernel32Name, &uKernel32ModulSize);
        if (pKernel32ModulAddr)
        {
   
   
                DbgPrintEx(77, 0, "[db]pKernel32ModulAddr:%llx\n", pKernel32ModulAddr);
        }
        //NtCreateThreadEx函数地址
        pZwCreatThraedFuncAddr = SearchSpecialCode(pNtoskrnlModulAddr, uNtoskrnlModulSize, ZwCreateThreadCode, 17);
        if (pZwCreatThraedFuncAddr)
        {
   
   
                DbgPrintEx(77, 0, "[db]pZwCreatThraedFuncAddr:%llx\n", pZwCreatThraedFuncAddr);
        }
        //NtResumeThread函数地址
        pNtResumeThreadFuncAddr = SearchSpecialCode(pNtoskrnlModulAddr, uNtoskrnlModulSize, NtResumeThreadcode, 14);
        if (pNtResumeThreadFuncAddr)
        {
   
   
                DbgPrintEx(77, 0, "[db]pNtResumeThreadFuncAddr:%llx\n", pNtResumeThreadFuncAddr);
        }
        //LoadLibrary函数地址
        pLoadLibAddr = SearchSpecialCode(pKernel32ModulAddr, uKernel32ModulSize, loadliboffsetcode, 11);
        pLoadLibAddr = (PUCHAR)pLoadLibAddr + 0x1e;
        if (pLoadLibAddr)
        {
   
   
                DbgPrintEx(77, 0, "[db]pLoadLibAddr:%llx\n", pLoadLibAddr);
        }
        // 使用ZwAllocateVirtualMemory函数在内核地址空间中分配虚拟内存
        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));
        //NtCreateThreadEx函数
        mycreate = pZwCreatThraedFuncAddr;
        //NtResumeThread函数
        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);
        }
        // 使用ZwFreeVirtualMemory函数释放虚拟内存
        st = ZwFreeVirtualMemory(hProcess,&pVirsualAddr,®ionSize,MEM_RELEASE);
        if (!NT_SUCCESS(st)) {
   
   
                DbgPrintEx(77, 0, "ZwFreeVirtualMemory st = %d\n", st);
        }
        KeUnstackDetachProcess(&kapcstate);
}
//main.c
#include "tool.h"

VOID UnloadDriver(PDRIVER_OBJECT pDriver)
{
   
   
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath)
{
   
   
    //把C:\\demo.dll注入到进程id为2454的进程中
        InsertDll(2464, "C:\\demo.dll");
        pDriver->DriverUnload = UnloadDriver;
        return STATUS_SUCCESS;
}

.DLL代码:

// dllmain.cpp : 定义 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

0x6:总结

.首先,这里注入的是64位的进程,32位的话获取模块列表需要换一下对应的32位结构,其次,这是一个非常朴素的驱动注入DLL,需要让它变强大就去发挥你们自己的思路

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
相关文章
|
3月前
|
存储 Java C语言
Windows 下 JNI 调用动态链接库 dll
Windows 下 JNI 调用动态链接库 dll
122 0
|
6天前
|
XML C# 数据格式
绝密档案曝光!Windows平台如何深挖一个dll背后的神秘依赖,揭露隐藏的秘密!
【8月更文挑战第14天】在Windows系统中,动态链接库(DLL)对程序运行至关重要。了解DLL的依赖关系有助于软件的调试与优化。本文以具体案例演示如何查看DLL依赖。首先确保环境已安装Windows及具备基本开发知识。
15 0
|
2月前
|
Windows
【Windows内核驱动函数(1)】IoCreateSymbolicLink()-----创建符号链接函数
【Windows内核驱动函数(1)】IoCreateSymbolicLink()-----创建符号链接函数
|
3月前
|
存储 Java C++
Windows 下 JNA 调用动态链接库 dll
Windows 下 JNA 调用动态链接库 dll
95 0
|
3月前
|
监控 安全 API
5.9 Windows驱动开发:内核InlineHook挂钩技术
在上一章`《内核LDE64引擎计算汇编长度》`中,`LyShark`教大家如何通过`LDE64`引擎实现计算反汇编指令长度,本章将在此基础之上实现内联函数挂钩,内核中的`InlineHook`函数挂钩其实与应用层一致,都是使用`劫持执行流`并跳转到我们自己的函数上来做处理,唯一的不同的是内核`Hook`只针对`内核API`函数,但由于其身处在`最底层`所以一旦被挂钩其整个应用层都将会受到影响,这就直接决定了在内核层挂钩的效果是应用层无法比拟的,对于安全从业者来说学会使用内核挂钩也是很重要。
70 1
5.9 Windows驱动开发:内核InlineHook挂钩技术
|
8月前
|
监控 安全 API
7.6 Windows驱动开发:内核监控FileObject文件回调
本篇文章与上一篇文章`《内核注册并监控对象回调》`所使用的方式是一样的都是使用`ObRegisterCallbacks`注册回调事件,只不过上一篇博文中`LyShark`将回调结构体`OB_OPERATION_REGISTRATION`中的`ObjectType`填充为了`PsProcessType`和`PsThreadType`格式从而实现监控进程与线程,本章我们需要将该结构填充为`IoFileObjectType`以此来实现对文件的监控,文件过滤驱动不仅仅可以用来监控文件的打开,还可以用它实现对文件的保护,一旦驱动加载则文件是不可被删除和改动的。
45 1
7.6 Windows驱动开发:内核监控FileObject文件回调
|
3月前
|
监控 Windows
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
在笔者上一篇文章`《内核监视LoadImage映像回调》`中`LyShark`简单介绍了如何通过`PsSetLoadImageNotifyRoutine`函数注册回调来`监视驱动`模块的加载,注意我这里用的是`监视`而不是`监控`之所以是监视而不是监控那是因为`PsSetLoadImageNotifyRoutine`无法实现参数控制,而如果我们想要控制特定驱动的加载则需要自己做一些事情来实现,如下`LyShark`将解密如何实现屏蔽特定驱动的加载。
57 0
7.4 Windows驱动开发:内核运用LoadImage屏蔽驱动
|
3月前
|
监控 安全 API
7.3 Windows驱动开发:内核监视LoadImage映像回调
在笔者上一篇文章`《内核注册并监控对象回调》`介绍了如何运用`ObRegisterCallbacks`注册`进程与线程`回调,并通过该回调实现了`拦截`指定进行运行的效果,本章`LyShark`将带大家继续探索一个新的回调注册函数,`PsSetLoadImageNotifyRoutine`常用于注册`LoadImage`映像监视,当有模块被系统加载时则可以第一时间获取到加载模块信息,需要注意的是该回调函数内无法进行拦截,如需要拦截则需写入返回指令这部分内容将在下一章进行讲解,本章将主要实现对模块的监视功能。
77 0
7.3 Windows驱动开发:内核监视LoadImage映像回调
|
3月前
|
监控 安全 API
7.2 Windows驱动开发:内核注册并监控对象回调
在笔者上一篇文章`《内核枚举进程与线程ObCall回调》`简单介绍了如何枚举系统中已经存在的`进程与线程`回调,本章`LyShark`将通过对象回调实现对进程线程的`句柄`监控,在内核中提供了`ObRegisterCallbacks`回调,使用这个内核`回调`函数,可注册一个`对象`回调,不过目前该函数`只能`监控进程与线程句柄操作,通过监控进程或线程句柄,可实现保护指定进程线程不被终止的目的。
67 0
7.2 Windows驱动开发:内核注册并监控对象回调
|
Windows
WINDOWS下获得DLL所在目录的代码
WINDOWS下获得DLL所在目录的代码
130 0