驱动开发:内核中实现Dump进程转储

多数ARK反内核工具中都存在驱动级别的内存转存功能,该功能可以将应用层中运行进程的内存镜像转存到特定目录下,内存转存功能在应对加壳程序的分析尤为重要,当进程在内存中解码后,我们可以很容易的将内存镜像导出 , 从而更好的对样本进行分析,当然某些加密壳可能无效但绝大多数情况下是可以被转存的 。

驱动开发:内核中实现Dump进程转储

文章插图
在上一篇文章《驱动开发:内核R3与R0内存映射拷贝》介绍了一种方式SafeCopyMemory_R3_to_R0可以将应用层进程的内存空间映射到内核中 , 要实现内存转储功能我们还是需要使用这个映射函数 , 只是需要在此函数上增加一些功能而已 。
在实现转存之前,需要得到两个东西 , 进程内模块基地址以及模块长度这两个参数是必不可少的,至于内核中如何得到指定进程的模块数据,在很早之前的文章《驱动开发:内核中枚举进线程与模块》中有详细的参考方法,这里就在此基础之上实现一个简单的进程模块遍历功能 。
如下代码中使用的就是枚举进程PEB结构得到更多参数的具体实现,如果不懂得可以研读《驱动开发:内核通过PEB得到进程参数》这篇文章此处不再赘述 。
#include <ntddk.h>#include <windef.h>// 声明结构体typedef struct _KAPC_STATE{ LIST_ENTRY ApcListHead[2]; PKPROCESS Process; UCHAR KernelApcInProgress; UCHAR KernelApcPending; UCHAR UserApcPending;} KAPC_STATE, *PKAPC_STATE;typedef struct _LDR_DATA_TABLE_ENTRY{ LIST_ENTRY64 InLoadOrderLinks; LIST_ENTRY64 InMemoryOrderLinks; LIST_ENTRY64 InInitializationOrderLinks; PVOIDDllBase; PVOIDEntryPoint; ULONGSizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRINGBaseDllName; ULONGFlags; USHORTLoadCount; USHORTTlsIndex; PVOIDSectionPointer; ULONGCheckSum; PVOIDLoadedImports; PVOIDEntryPointActivationContext; PVOIDPatchInformation; LIST_ENTRY64 ForwarderLinks; LIST_ENTRY64 ServiceTagLinks; LIST_ENTRY64 StaticLinks; PVOIDContextInformation; ULONG64OriginalBase; LARGE_INTEGER LoadTime;} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;// 偏移地址ULONG64 LdrInPebOffset = 0x018;//peb.ldrULONG64 ModListInPebOffset = 0x010; //peb.ldr.InLoadOrderModuleList// 声明APINTKERNELAPI UCHAR* PsGetProcessImageFileName(IN PEPROCESS Process);NTKERNELAPI PPEB PsGetProcessPeb(PEPROCESS Process);NTKERNELAPI HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);// 根据进程ID返回进程EPROCESS,失败返回NULLPEPROCESS LookupProcess(HANDLE Pid){ PEPROCESS eprocess = NULL; if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess)))return eprocess; elsereturn NULL;}// 枚举指定进程的模块// By: LyShark.comVOID EnumModule(PEPROCESS Process){ SIZE_T Peb = 0; SIZE_T Ldr = 0; PLIST_ENTRY ModListHead = 0; PLIST_ENTRY Module = 0; ANSI_STRING AnsiString; KAPC_STATE ks; // EPROCESS地址无效则退出 if (!MmIsAddressValid(Process))return; // 获取PEB地址 Peb = (SIZE_T)PsGetProcessPeb(Process); // PEB地址无效则退出 if (!Peb)return; // 依附进程 KeStackAttachProcess(Process, &ks); __try {// 获得LDR地址Ldr = Peb + (SIZE_T)LdrInPebOffset;// 测试是否可读,不可读则抛出异常退出ProbeForRead((CONST PVOID)Ldr, 8, 8);// 获得链表头ModListHead = (PLIST_ENTRY)(*(PULONG64)Ldr + ModListInPebOffset);// 再次测试可读性ProbeForRead((CONST PVOID)ModListHead, 8, 8);// 获得第一个模块的信息Module = ModListHead->Flink;while (ModListHead != Module){//打印信息:基址、大小、DLL路径DbgPrint("模块基址 = %p | 大小 = %ld | 模块名 = %wZ | 完整路径= %wZ \n",(PVOID)(((PLDR_DATA_TABLE_ENTRY)Module)->DllBase),(ULONG)(((PLDR_DATA_TABLE_ENTRY)Module)->SizeOfImage),&(((PLDR_DATA_TABLE_ENTRY)Module)->BaseDllName),&(((PLDR_DATA_TABLE_ENTRY)Module)->FullDllName));Module = Module->Flink;// 测试下一个模块信息的可读性ProbeForRead((CONST PVOID)Module, 80, 8);} } __except (EXCEPTION_EXECUTE_HANDLER){ ; } // 取消依附进程 KeUnstackDetachProcess(&ks);}VOID DriverUnload(IN PDRIVER_OBJECT DriverObject){}NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath){ DbgPrint("hello lyshark.com \n"); ULONG i = 0; PEPROCESS eproc = NULL; for (i = 4; i<100000000; i = i + 4) {eproc = LookupProcess((HANDLE)i);if (eproc != NULL){ObDereferenceObject(eproc);if (strstr(PsGetProcessImageFileName(eproc), "lyshark.exe") != NULL){EnumModule(eproc);}} } DriverObject->DriverUnload = DriverUnload; return STATUS_SUCCESS;}如上我们指定获取应用层lyshark.exe进程的模块信息 , 并可得到以下输出效果:
驱动开发:内核中实现Dump进程转储

推荐阅读