diff --git a/KsDumperDriver/Driver.c b/KsDumperDriver/Driver.c new file mode 100644 index 0000000..e598c13 --- /dev/null +++ b/KsDumperDriver/Driver.c @@ -0,0 +1,168 @@ +#include "NTUndocumented.h" +#include "ProcessLister.h" +#include "UserModeBridge.h" +#include + +DRIVER_INITIALIZE DriverEntry; +#pragma alloc_text(INIT, DriverEntry) + +UNICODE_STRING deviceName, symLink; +PDEVICE_OBJECT deviceObject; + +NTSTATUS CopyVirtualMemory(PEPROCESS targetProcess, PVOID sourceAddress, PVOID targetAddress, SIZE_T size) +{ + PSIZE_T readBytes; + return MmCopyVirtualMemory(targetProcess, sourceAddress, PsGetCurrentProcess(), targetAddress, size, UserMode, &readBytes); +} + +NTSTATUS UnsupportedDispatch(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Irp->IoStatus.Status; +} + +NTSTATUS CreateDispatch(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Irp->IoStatus.Status; +} + +NTSTATUS CloseDispatch(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp) +{ + UNREFERENCED_PARAMETER(DeviceObject); + + IoCompleteRequest(Irp, IO_NO_INCREMENT); + return Irp->IoStatus.Status; +} + +//NTSTATUS Unload(IN PDRIVER_OBJECT DriverObject) +//{ +// IoDeleteSymbolicLink(&symLink); +// IoDeleteDevice(DriverObject->DeviceObject); +//} + +NTSTATUS Unload(IN PDRIVER_OBJECT DriverObject) +{ + IoDeleteSymbolicLink(&symLink); + IoDeleteSymbolicLink(&deviceName); + IoDeleteDevice(deviceObject); + return ZwUnloadDriver(&deviceName); +} + +NTSTATUS IoControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) +{ + NTSTATUS status; + ULONG bytesIO = 0; + PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); + ULONG controlCode = stack->Parameters.DeviceIoControl.IoControlCode; + + if (controlCode == IO_COPY_MEMORY) + { + if (stack->Parameters.DeviceIoControl.InputBufferLength == sizeof(KERNEL_COPY_MEMORY_OPERATION)) + { + PKERNEL_COPY_MEMORY_OPERATION request = (PKERNEL_COPY_MEMORY_OPERATION)Irp->AssociatedIrp.SystemBuffer; + PEPROCESS targetProcess; + + if (NT_SUCCESS(PsLookupProcessByProcessId(request->targetProcessId, &targetProcess))) + { + CopyVirtualMemory(targetProcess, request->targetAddress, request->bufferAddress, request->bufferSize); + ObDereferenceObject(targetProcess); + } + + status = STATUS_SUCCESS; + bytesIO = sizeof(KERNEL_COPY_MEMORY_OPERATION); + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + bytesIO = 0; + } + } + else if (controlCode == IO_GET_PROCESS_LIST) + { + if (stack->Parameters.DeviceIoControl.InputBufferLength == sizeof(KERNEL_PROCESS_LIST_OPERATION) && + stack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(KERNEL_PROCESS_LIST_OPERATION)) + { + PKERNEL_PROCESS_LIST_OPERATION request = (PKERNEL_PROCESS_LIST_OPERATION)Irp->AssociatedIrp.SystemBuffer; + + GetProcessList(request->bufferAddress, request->bufferSize, &request->bufferSize, &request->processCount); + + status = STATUS_SUCCESS; + bytesIO = sizeof(KERNEL_PROCESS_LIST_OPERATION); + } + else + { + status = STATUS_INFO_LENGTH_MISMATCH; + bytesIO = 0; + } + } + else if (controlCode == IO_UNLOAD_DRIVER) + { + Unload(NULL); + bytesIO = 0; + status = STATUS_SUCCESS; + } + else + { + status = STATUS_INVALID_PARAMETER; + bytesIO = 0; + } + + Irp->IoStatus.Status = status; + Irp->IoStatus.Information = bytesIO; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return status; +} + +NTSTATUS DriverInitialize(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) +{ + NTSTATUS status; + + + UNREFERENCED_PARAMETER(RegistryPath); + + RtlInitUnicodeString(&deviceName, L"\\Device\\KsDumper"); + RtlInitUnicodeString(&symLink, L"\\DosDevices\\KsDumper"); + + status = IoCreateDevice(DriverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject); + + if (!NT_SUCCESS(status)) + { + return status; + } + status = IoCreateSymbolicLink(&symLink, &deviceName); + + if (!NT_SUCCESS(status)) + { + IoDeleteDevice(deviceObject); + return status; + } + deviceObject->Flags |= DO_BUFFERED_IO; + + for (ULONG t = 0; t <= IRP_MJ_MAXIMUM_FUNCTION; t++) + DriverObject->MajorFunction[t] = &UnsupportedDispatch; + + DriverObject->MajorFunction[IRP_MJ_CREATE] = &CreateDispatch; + DriverObject->MajorFunction[IRP_MJ_CLOSE] = &CloseDispatch; + DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = &IoControl; + DriverObject->DriverUnload = &Unload; + deviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + + return status; +} + + + +NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) +{ + UNREFERENCED_PARAMETER(DriverObject); + UNREFERENCED_PARAMETER(RegistryPath); + + return IoCreateDriver(NULL, &DriverInitialize); +} diff --git a/KsDumperDriver/NTUndocumented.h b/KsDumperDriver/NTUndocumented.h new file mode 100644 index 0000000..1357f49 --- /dev/null +++ b/KsDumperDriver/NTUndocumented.h @@ -0,0 +1,50 @@ +#pragma once +#include + +typedef struct _KAPC_STATE { + LIST_ENTRY ApcListHead[MaximumMode]; + struct _KPROCESS *Process; + BOOLEAN KernelApcInProgress; + BOOLEAN KernelApcPending; + BOOLEAN UserApcPending; +} KAPC_STATE, *PKAPC_STATE, *PRKAPC_STATE; + +typedef enum _SYSTEM_INFORMATION_CLASS +{ + SystemProcessInformation = 5 +} SYSTEM_INFORMATION_CLASS; + +typedef enum _MEMORY_INFORMATION_CLASS +{ + MemoryBasicInformation, + MemoryWorkingSetInformation, + MemoryMappedFilenameInformation, + MemoryRegionInformation, + MemoryWorkingSetExInformation + +} MEMORY_INFORMATION_CLASS; + + +typedef struct _MEMORY_BASIC_INFORMATION { + PVOID BaseAddress; + PVOID AllocationBase; + INT32 AllocationProtect; + SIZE_T RegionSize; + INT32 State; + INT32 Protect; + INT32 Type; +} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION; + +NTKERNELAPI NTSTATUS IoCreateDriver(IN PUNICODE_STRING DriverName, OPTIONAL IN PDRIVER_INITIALIZE InitializationFunction); + +NTKERNELAPI VOID KeStackAttachProcess(__inout struct _KPROCESS * PROCESS, __out PRKAPC_STATE ApcState); +NTKERNELAPI VOID KeUnstackDetachProcess(__in PRKAPC_STATE ApcState); + +NTKERNELAPI NTSTATUS NTAPI MmCopyVirtualMemory(IN PEPROCESS FromProcess, IN PVOID FromAddress, IN PEPROCESS ToProcess, OUT PVOID ToAddress, IN SIZE_T BufferSize, IN KPROCESSOR_MODE PreviousMode, OUT PSIZE_T NumberOfBytesCopied); + +NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation(IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL); +NTSYSAPI NTSTATUS NTAPI ZwQueryVirtualMemory(IN HANDLE ProcessHandle, IN PVOID BaseAddress, IN MEMORY_INFORMATION_CLASS MemoryInformationClass, OUT PVOID MemoryInformation, IN SIZE_T MemoryInformationLength, OUT PSIZE_T ReturnLength OPTIONAL); + +NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(IN HANDLE ProcessId, OUT PEPROCESS *Process); +NTKERNELAPI PVOID PsGetProcessSectionBaseAddress(__in PEPROCESS Process); +NTKERNELAPI PPEB NTAPI PsGetProcessPeb(IN PEPROCESS Process); \ No newline at end of file diff --git a/KsDumperDriver/ProcessLister.c b/KsDumperDriver/ProcessLister.c new file mode 100644 index 0000000..9d24ca7 --- /dev/null +++ b/KsDumperDriver/ProcessLister.c @@ -0,0 +1,156 @@ +#include "NTUndocumented.h" +#include "ProcessLister.h" +#include "Utility.h" + +static PSYSTEM_PROCESS_INFORMATION GetRawProcessList() +{ + ULONG bufferSize = 0; + PVOID bufferPtr = NULL; + + if (ZwQuerySystemInformation(SystemProcessInformation, 0, bufferSize, &bufferSize) == STATUS_INFO_LENGTH_MISMATCH) + { + bufferPtr = ExAllocatePool(NonPagedPool, bufferSize); + + if (bufferPtr != NULL) + { + ZwQuerySystemInformation(SystemProcessInformation, bufferPtr, bufferSize, &bufferSize); + } + } + return (PSYSTEM_PROCESS_INFORMATION)bufferPtr; +} + +static ULONG CalculateProcessListOutputSize(PSYSTEM_PROCESS_INFORMATION rawProcessList) +{ + int size = 0; + + while (rawProcessList->NextEntryOffset) + { + size += sizeof(PROCESS_SUMMARY); + rawProcessList = (PSYSTEM_PROCESS_INFORMATION)(((CHAR*)rawProcessList) + rawProcessList->NextEntryOffset); + } + return size; +} + +static PLDR_DATA_TABLE_ENTRY GetMainModuleDataTableEntry(PPEB64 peb) +{ + if (SanitizeUserPointer(peb, sizeof(PEB64))) + { + if (peb->Ldr) + { + if (SanitizeUserPointer(peb->Ldr, sizeof(PEB_LDR_DATA))) + { + if (!peb->Ldr->Initialized) + { + int initLoadCount = 0; + + while (!peb->Ldr->Initialized && initLoadCount++ < 4) + { + DriverSleep(250); + } + } + + if (peb->Ldr->Initialized) + { + return CONTAINING_RECORD(peb->Ldr->InLoadOrderModuleList.Flink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + } + } + } + } + return NULL; +} + +NTSTATUS GetProcessList(PVOID listedProcessBuffer, INT32 bufferSize, PINT32 requiredBufferSize, PINT32 processCount) +{ + PPROCESS_SUMMARY processSummary = (PPROCESS_SUMMARY)listedProcessBuffer; + PSYSTEM_PROCESS_INFORMATION rawProcessList = GetRawProcessList(); + PVOID listHeadPointer = rawProcessList; + *processCount = 0; + + if (rawProcessList) + { + int expectedBufferSize = CalculateProcessListOutputSize(rawProcessList); + + if (!listedProcessBuffer || bufferSize < expectedBufferSize) + { + *requiredBufferSize = expectedBufferSize; + return STATUS_INFO_LENGTH_MISMATCH; + } + + while (rawProcessList->NextEntryOffset) + { + PEPROCESS targetProcess; + PKAPC_STATE state = NULL; + + if (NT_SUCCESS(PsLookupProcessByProcessId(rawProcessList->UniqueProcessId, &targetProcess))) + { + PVOID mainModuleBase = NULL; + PVOID mainModuleEntryPoint = NULL; + UINT32 mainModuleImageSize = 0; + PWCHAR mainModuleFileName = NULL; + BOOLEAN isWow64 = 0; + + __try + { + KeStackAttachProcess(targetProcess, &state); + + __try + { + mainModuleBase = PsGetProcessSectionBaseAddress(targetProcess); + + if (mainModuleBase) + { + PPEB64 peb = (PPEB64)PsGetProcessPeb(targetProcess); + + if (peb) + { + PLDR_DATA_TABLE_ENTRY mainModuleEntry = GetMainModuleDataTableEntry(peb); + mainModuleEntry = SanitizeUserPointer(mainModuleEntry, sizeof(LDR_DATA_TABLE_ENTRY)); + + if (mainModuleEntry) + { + mainModuleEntryPoint = mainModuleEntry->EntryPoint; + mainModuleImageSize = mainModuleEntry->SizeOfImage; + isWow64 = IS_WOW64_PE(mainModuleBase); + + mainModuleFileName = ExAllocatePool(NonPagedPool, 256 * sizeof(WCHAR)); + RtlZeroMemory(mainModuleFileName, 256 * sizeof(WCHAR)); + RtlCopyMemory(mainModuleFileName, mainModuleEntry->FullDllName.Buffer, 256 * sizeof(WCHAR)); + } + } + } + } + __except (GetExceptionCode()) + { + DbgPrintEx(0, 0, "Peb Interaction Failed.\n"); + } + } + __finally + { + KeUnstackDetachProcess(&state); + } + + if (mainModuleFileName) + { + RtlCopyMemory(processSummary->MainModuleFileName, mainModuleFileName, 256 * sizeof(WCHAR)); + ExFreePool(mainModuleFileName); + + processSummary->ProcessId = rawProcessList->UniqueProcessId; + processSummary->MainModuleBase = mainModuleBase; + processSummary->MainModuleEntryPoint = mainModuleEntryPoint; + processSummary->MainModuleImageSize = mainModuleImageSize; + processSummary->WOW64 = isWow64; + + processSummary++; + (*processCount)++; + } + + ObDereferenceObject(targetProcess); + } + + rawProcessList = (PSYSTEM_PROCESS_INFORMATION)(((CHAR*)rawProcessList) + rawProcessList->NextEntryOffset); + } + + ExFreePool(listHeadPointer); + return STATUS_SUCCESS; + } +} \ No newline at end of file diff --git a/KsDumperDriver/ProcessLister.h b/KsDumperDriver/ProcessLister.h new file mode 100644 index 0000000..c9b10d0 --- /dev/null +++ b/KsDumperDriver/ProcessLister.h @@ -0,0 +1,123 @@ +#pragma once +#include + +#pragma pack(push, 1) +typedef struct _PROCESS_SUMMARY +{ + INT32 ProcessId; + PVOID MainModuleBase; + WCHAR MainModuleFileName[256]; + UINT32 MainModuleImageSize; + PVOID MainModuleEntryPoint; + BOOLEAN WOW64; +} PROCESS_SUMMARY, *PPROCESS_SUMMARY; +#pragma pack(pop) + +typedef struct _SYSTEM_PROCESS_INFORMATION +{ + ULONG NextEntryOffset; + ULONG NumberOfThreads; + LARGE_INTEGER SpareLi1; + LARGE_INTEGER SpareLi2; + LARGE_INTEGER SpareLi3; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ImageName; + KPRIORITY BasePriority; + HANDLE UniqueProcessId; + HANDLE InheritedFromUniqueProcessId; + ULONG HandleCount; + ULONG SessionId; + ULONG_PTR PageDirectoryBase; + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + ULONG PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; + LARGE_INTEGER ReadOperationCount; + LARGE_INTEGER WriteOperationCount; + LARGE_INTEGER OtherOperationCount; + LARGE_INTEGER ReadTransferCount; + LARGE_INTEGER WriteTransferCount; + LARGE_INTEGER OtherTransferCount; +} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; + +typedef struct _LDR_DATA_TABLE_ENTRY +{ + LIST_ENTRY InLoadOrderLinks; + LIST_ENTRY InMemoryOrderLinks; + CHAR Reserved0[0x10]; + PVOID DllBase; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; +} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; + +typedef struct _PEB_LDR_DATA +{ + ULONG Length; + BOOLEAN Initialized; + PVOID SsHandler; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _PEB64 { + CHAR Reserved[0x10]; + PVOID ImageBaseAddress; + PPEB_LDR_DATA Ldr; +} PEB64, *PPEB64; + +typedef struct _IMAGE_DOS_HEADER { + USHORT e_magic; + USHORT e_cblp; + USHORT e_cp; + USHORT e_crlc; + USHORT e_cparhdr; + USHORT e_minalloc; + USHORT e_maxalloc; + USHORT e_ss; + USHORT e_sp; + USHORT e_csum; + USHORT e_ip; + USHORT e_cs; + USHORT e_lfarlc; + USHORT e_ovno; + USHORT e_res[4]; + USHORT e_oemid; + USHORT e_oeminfo; + USHORT e_res2[10]; + LONG e_lfanew; +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + +typedef struct _PE_HEADER { + CHAR Signature[4]; + USHORT Machine; + USHORT NumberOfSections; + UINT32 TimeDateStamp; + UINT32 PointerToSymbolTable; + UINT32 NumberOfSymbols; + USHORT SizeOfOptionalHeader; + USHORT Characteristics; + USHORT Magic; +} PE_HEADER, *PPE_HEADER; + +#define PE_HEADER_MAGIC_OFFSET 0x18 +#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b + +#define IS_WOW64_PE( baseAddress ) (*((USHORT*)((CHAR *)baseAddress + \ + ((PIMAGE_DOS_HEADER)baseAddress)->e_lfanew + PE_HEADER_MAGIC_OFFSET)) \ + == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + +NTSTATUS GetProcessList(PVOID listedProcessBuffer, INT32 bufferSize, PINT32 requiredBufferSize, PINT32 processCount); \ No newline at end of file diff --git a/KsDumperDriver/UserModeBridge.h b/KsDumperDriver/UserModeBridge.h new file mode 100644 index 0000000..956f88e --- /dev/null +++ b/KsDumperDriver/UserModeBridge.h @@ -0,0 +1,23 @@ +#pragma once +#include + +#define IO_GET_PROCESS_LIST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1724, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) + +#define IO_COPY_MEMORY CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1725, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) + +#define IO_UNLOAD_DRIVER CTL_CODE(FILE_DEVICE_UNKNOWN, 0x1726, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) + +typedef struct _KERNEL_PROCESS_LIST_OPERATION +{ + PVOID bufferAddress; + INT32 bufferSize; + INT32 processCount; +} KERNEL_PROCESS_LIST_OPERATION, *PKERNEL_PROCESS_LIST_OPERATION; + +typedef struct _KERNEL_COPY_MEMORY_OPERATION +{ + INT32 targetProcessId; + PVOID targetAddress; + PVOID bufferAddress; + INT32 bufferSize; +} KERNEL_COPY_MEMORY_OPERATION, *PKERNEL_COPY_MEMORY_OPERATION; \ No newline at end of file diff --git a/KsDumperDriver/Utility.c b/KsDumperDriver/Utility.c new file mode 100644 index 0000000..4bc80f3 --- /dev/null +++ b/KsDumperDriver/Utility.c @@ -0,0 +1,35 @@ +#include "NTUndocumented.h" +#include "Utility.h" + +NTSTATUS DriverSleep(int ms) +{ + LARGE_INTEGER li; + li.QuadPart = -10000; + + for (int i = 0; i < ms; i++) + { + KeDelayExecutionThread(KernelMode, FALSE, &li); + return STATUS_SUCCESS; + } + return STATUS_UNSUCCESSFUL; +} + +PVOID SanitizeUserPointer(PVOID pointer, SIZE_T size) +{ + MEMORY_BASIC_INFORMATION memInfo; + + if (NT_SUCCESS(ZwQueryVirtualMemory(ZwCurrentProcess(), pointer, MemoryBasicInformation, &memInfo, sizeof(MEMORY_BASIC_INFORMATION), NULL))) + { + if (!(((uintptr_t)memInfo.BaseAddress + memInfo.RegionSize) < (((uintptr_t)pointer + size)))) + { + if (memInfo.State & MEM_COMMIT || !(memInfo.Protect & (PAGE_GUARD | PAGE_NOACCESS))) + { + if (memInfo.Protect & PAGE_EXECUTE_READWRITE || memInfo.Protect & PAGE_EXECUTE_WRITECOPY || memInfo.Protect & PAGE_READWRITE || memInfo.Protect & PAGE_WRITECOPY) + { + return pointer; + } + } + } + } + return NULL; +} diff --git a/KsDumperDriver/Utility.h b/KsDumperDriver/Utility.h new file mode 100644 index 0000000..baaaeb7 --- /dev/null +++ b/KsDumperDriver/Utility.h @@ -0,0 +1,6 @@ +#pragma once +#include + +NTSTATUS DriverSleep(int ms); + +PVOID SanitizeUserPointer(PVOID pointer, SIZE_T size); \ No newline at end of file