概述
在内核编程有时为了能够读取目标三环进程的内存,主要有以下几种不错的方式
1.附加后通过直接memcpy复制目标内存进行读取,但是这种方式非常不稳定,不推荐
2.使用MmCopyVirtualMemory进行读取,因为这是微软写好的一个内核函数,所以异常分发等都完善可以直接使用
3.切换CR3读取,当切换了CR3时,访问的地址都将以CR3为目录项基址,所以可以直接访问到目标进程的内存
4.使用MDL方式,附加进程然后将物理页上的内容映射一份到了内核,进而通过memcpy对映射后的地址进行拷贝即可
代码演示
三环测试代码
#include "stdio.h"
#include <windows.h>
#define DEVICE_LINK_NAME L"\\\\.\\BufferedIODevcieLinkName"
#define CTL_SYS \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
int main()
{
HANDLE DeviceHandle = CreateFile(DEVICE_LINK_NAME,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (DeviceHandle == INVALID_HANDLE_VALUE)
{
return 0;
}
char BufferData = NULL;
DWORD ReturnLength = 0;
char pid[] = "2828";
BOOL IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,
pid,
strlen(pid) + 1,
(LPVOID)BufferData,
0,
&ReturnLength,
NULL);
if (IsOk == FALSE)
{
int LastError = GetLastError();
if (LastError == ERROR_NO_SYSTEM_RESOURCES)
{
char BufferData[MAX_PATH] = { 0 };
IsOk = DeviceIoControl(DeviceHandle, CTL_SYS,
pid,
strlen(pid) + 1,
(LPVOID)BufferData,
MAX_PATH,
&ReturnLength,
NULL);
if (IsOk == TRUE)
{
printf("%s\r\n", BufferData);
}
}
}
if (DeviceHandle != NULL)
{
CloseHandle(DeviceHandle);
DeviceHandle = NULL;
}
printf("Input AnyKey To Exit\r\n");
getchar();
return 0;
}
内核function.c文件
#include"GetModule.h"
#include<ntifs.h>
#include<ntstrsafe.h>
NTSTATUS ConvertCharToUnicodeString(char* charString, UNICODE_STRING* unicodeString)
{
ANSI_STRING ansiString = { 0 };
RtlInitAnsiString(&ansiString, charString);
return RtlAnsiStringToUnicodeString(unicodeString, &ansiString, TRUE);
}
/*
功能:获取目标进程的模块的DllBase
参数一:目标进程的PID
参数二:需要寻找的模块名称
返回:寻找到的模块DllBase
*/
HANDLE GetObjectModule(HANDLE pid, CHAR* ModuleName)
{
PEPROCESS Process = 0;
PLDR_DATA_TABLE_ENTRY32 pBase32, pNext32;
PLDR_DATA_TABLE_ENTRY64 pBase64, pNext64;
UNICODE_STRING CompareModuleName = { 0 };
BOOLEAN FindIs = FALSE;
HANDLE return_handle = 0;
UNICODE_STRING uniFunctionName;
ConvertCharToUnicodeString(ModuleName, &CompareModuleName);
PFNPsGetProcessPeb PsGetProcessPeb = NULL;
NTSTATUS is = PsLookupProcessByProcessId(pid, &Process);
if (!NT_SUCCESS(is))
{
return 0;
}
KAPC_STATE stack = { 0 };
KeStackAttachProcess(Process, &stack);
PPEB32 peb32 = PsGetProcessWow64Process(Process);
if (peb32 == NULL) //如果为64位进程
{
RtlInitUnicodeString(&uniFunctionName, L"PsGetProcessPeb");
PsGetProcessPeb = (PFNPsGetProcessPeb)MmGetSystemRoutineAddress(&uniFunctionName);
PPEB64 peb64 = PsGetProcessPeb(Process);
PPEB_LDR_DATA64 peb64ldr = (PPEB_LDR_DATA64)peb64->Ldr;
pBase64 = (PLDR_DATA_TABLE_ENTRY64)(peb64ldr->InLoadOrderModuleList.Flink);
pNext64 = pBase64;
do
{
UNICODE_STRING UnicodeString = { 0 };
RtlUnicodeStringInit(&UnicodeString, pNext64->BaseDllName.Buffer);
if (!RtlCompareUnicodeString(&UnicodeString, &CompareModuleName, TRUE))
{
FindIs = TRUE;
return_handle = pNext64->DllBase;
break;
}
else
{
pNext64 = pNext64->InLoadOrderLinks.Flink;
}
} while (pNext64 != pBase64);
KeUnstackDetachProcess(&stack);
ObDereferenceObject(Process);
RtlFreeUnicodeString(&CompareModuleName);
}
else //如果为32位进程
{
PPEB_LDR_DATA32 peb32ldr = (PPEB_LDR_DATA32)peb32->Ldr;
pBase32 = (PLDR_DATA_TABLE_ENTRY32)(peb32ldr->InLoadOrderModuleList.Flink);
pNext32 = pBase32;
do
{
UNICODE_STRING UnicodeString = { 0 };
RtlUnicodeStringInit(&UnicodeString, (PWCH)pNext32->BaseDllName.Buffer);
if (!RtlCompareUnicodeString(&UnicodeString, &CompareModuleName, TRUE))
{
FindIs = TRUE;
return_handle = pNext32->DllBase;
break;
}
else
{
pNext32 = pNext32->InLoadOrderLinks.Flink;
}
} while (pNext32 != pBase32);
KeUnstackDetachProcess(&stack);
ObDereferenceObject(Process);
RtlFreeUnicodeString(&CompareModuleName);
}
if (FindIs == TRUE)
{
return return_handle;
}
return 0;
}
内核driver.c文件
#include "BufferIO.h"
#include "GetModule.h"
#include <Intrin.h>
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT DeviceObject = NULL;
UNICODE_STRING DeviceObjectName;
UNICODE_STRING DeviceLinkName;
ULONG i;
// 栈
// 堆
// 全局(global Static Const)
DriverObject->DriverUnload = DriverUnload;
//创建设备对象名称
RtlInitUnicodeString(&DeviceObjectName, DEVICE_OBJECT_NAME);
//创建设备对象
Status = IoCreateDevice(DriverObject, NULL,&DeviceObjectName,FILE_DEVICE_UNKNOWN,0, FALSE,&DeviceObject);
if (!NT_SUCCESS(Status))
{
return Status;
}
//创建设备连接名称
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
//将设备连接名称与设备名称关联
Status = IoCreateSymbolicLink(&DeviceLinkName, &DeviceObjectName);
if (!NT_SUCCESS(Status))
{
IoDeleteDevice(DeviceObject);
return Status;
}
//设计符合我们代码的派遣历程
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
{
DriverObject->MajorFunction[i] = PassThroughDispatch; //函数指针
}
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlThroughDispatch;
return Status;
}
//派遣历程
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS; //LastError()
Irp->IoStatus.Information = 0; //ReturnLength
IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器
return STATUS_SUCCESS;
}
PVOID MdlMapMemory(PMDL* mdl, PVOID TargetAddress, SIZE_T size, MODE preMode)
{
PMDL pMdl = IoAllocateMdl(TargetAddress, size, FALSE, FALSE, NULL);
PVOID mapAddr = NULL;
if (!pMdl)
{
return NULL;
}
BOOLEAN isLock = FALSE;
__try
{
MmProbeAndLockPages(pMdl, preMode, IoReadAccess);
isLock = TRUE;
mapAddr = MmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, NULL, FALSE, NormalPagePriority);
}
__except (1)
{
IoFreeMdl(pMdl);
if (isLock)
{
MmUnlockPages(pMdl);
}
return;
}
*mdl = pMdl;
return mapAddr;
}
VOID MdlUnMapMemory(PMDL mdl, PVOID mapBase)
{
__try
{
MmUnmapLockedPages(mapBase, mdl);
MmUnlockPages(mdl);
IoFreeMdl(mdl);
}
__except (1)
{
return;
}
}
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
NTSTATUS Status;
ULONG_PTR Informaiton = 0;
PVOID InputData = NULL;
ULONG InputDataLength = 0;
PVOID OutputData = NULL;
ULONG OutputDataLength = 0;
ULONG IoControlCode = 0;
PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp); //Irp堆栈
IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
InputData = Irp->AssociatedIrp.SystemBuffer;
OutputData = Irp->AssociatedIrp.SystemBuffer;
InputDataLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
OutputDataLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
switch (IoControlCode)
{
case CTL_SYS:
{
if (InputData != NULL && InputDataLength > 0)
{
ANSI_STRING PID ;
//获取我们的PID
RtlInitAnsiString(&PID,InputData);
UNICODE_STRING PID_U = {0};
RtlAnsiStringToUnicodeString(&PID_U,&PID,TRUE);
ULONG number;
RtlUnicodeStringToInteger(&PID_U,10,&number);
DbgPrintEx(77, 0, "PID == %d\n", number);
PEPROCESS Global_Peprocess;
ULONG ref_value = 0;
KAPC_STATE KAPC = { 0 };
//第一种 直接使用MmCopyVirtualMemory读取
PsLookupProcessByProcessId((HANDLE)number, &Global_Peprocess);
HANDLE dllbase = GetObjectModule(number, "NOTEPAD.EXE");
SIZE_T Result;
DbgBreakPoint();
Status = MmCopyVirtualMemory(Global_Peprocess, dllbase,IoGetCurrentProcess(), &ref_value,4, KernelMode,&Result);
DbgPrintEx(77, 0, "ref_value == %x\n", ref_value);
//第二种 MDL读取方式
ref_value = 0;
PMDL mdl = NULL;
KeStackAttachProcess(Global_Peprocess, &KAPC);
PVOID map = MdlMapMemory(&mdl, dllbase, 4, UserMode);
if (map)
{
memcpy(&ref_value, map, 4);
MdlUnMapMemory(mdl, map);
KeUnstackDetachProcess(&KAPC);
}
DbgPrintEx(77, 0, "ref_value == %x\n", ref_value);
//第三种方式 切换CR3读取
ref_value = 0;
ULONG64 newCr3 = *(PULONG64)((PUCHAR)Global_Peprocess + 0x28);
ULONG64 oldCr3 = __readcr3();
KeEnterCriticalRegion(); //关闭APC
_disable(); //关中断
__writecr3(newCr3);
if (MmIsAddressValid(dllbase))
{
memcpy(&ref_value, dllbase, 4);
}
//恢复环境
__writecr3(oldCr3);
_enable();
KeLeaveCriticalRegion();
DbgPrintEx(77, 0, "ref_value == %x\n", ref_value);
}
if (OutputData != NULL && OutputDataLength >= strlen("Ring0->Ring3") + 1)
{
memcpy(OutputData, "Ring0->Ring3", strlen("Ring0->Ring3") + 1);
Status = STATUS_SUCCESS;
Informaiton = strlen("Ring0->Ring3") + 1;
}
else
{
Status = STATUS_INSUFFICIENT_RESOURCES; //内存不够
Informaiton = 0;
}
break;
}
default:
break;
}
Irp->IoStatus.Status = Status; //Ring3 GetLastError();
Irp->IoStatus.Information = Informaiton;
IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给Io管理器
return Status; //Ring3 DeviceIoControl()返回值
}
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING DeviceLinkName;
PDEVICE_OBJECT v1 = NULL;
PDEVICE_OBJECT DeleteDeviceObject = NULL;
RtlInitUnicodeString(&DeviceLinkName, DEVICE_LINK_NAME);
IoDeleteSymbolicLink(&DeviceLinkName);
DeleteDeviceObject = DriverObject->DeviceObject;
while (DeleteDeviceObject != NULL)
{
v1 = DeleteDeviceObject->NextDevice;
IoDeleteDevice(DeleteDeviceObject);
DeleteDeviceObject = v1;
}
}
内核struct.h
#pragma once
#include <ntifs.h>
typedef enum _KAPC_ENVIRONMENT {
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
typedef VOID(*PKNORMAL_ROUTINE) (
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
typedef VOID(*PKKERNEL_ROUTINE) (
IN struct _KAPC* Apc,
IN OUT PKNORMAL_ROUTINE* NormalRoutine,
IN OUT PVOID* NormalContext,
IN OUT PVOID* SystemArgument1,
IN OUT PVOID* SystemArgument2
);
typedef VOID(*PKRUNDOWN_ROUTINE) (
IN struct _KAPC* Apc
);
VOID KeInitializeApc(
__out PRKAPC Apc,
__in PRKTHREAD Thread,
__in KAPC_ENVIRONMENT Environment,
__in PKKERNEL_ROUTINE KernelRoutine,
__in_opt PKRUNDOWN_ROUTINE RundownRoutine,
__in_opt PKNORMAL_ROUTINE NormalRoutine,
__in_opt KPROCESSOR_MODE ApcMode,
__in_opt PVOID NormalContext
);
BOOLEAN KeInsertQueueApc(
__inout PRKAPC Apc,
__in_opt PVOID SystemArgument1,
__in_opt PVOID SystemArgument2,
__in KPRIORITY Increment
);
BOOLEAN
KeAlertThread(
__inout PKTHREAD Thread,
__in KPROCESSOR_MODE AlertMode
);
EXTERN_C NTSTATUS PsWrapApcWow64Thread(PVOID* ApcContext, PVOID* ApcRoutine);
内核GetMoudule.h
#pragma once
#include<ntifs.h>
HANDLE GetObjectModule(HANDLE pid, CHAR* ModuleName);
EXTERN_C PVOID NTAPI PsGetProcessWow64Process(PEPROCESS Process);
typedef PPEB(__stdcall* PFNPsGetProcessPeb)(PEPROCESS pEProcess);
typedef struct _PEB32 {
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
UCHAR Spare;
ULONG Mutant;
ULONG ImageBaseAddress;
ULONG/*PPEB_LDR_DATA32*/ Ldr;
} PEB32, * PPEB32;
typedef struct _PEB64
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages : 1; //0x3
UCHAR IsProtectedProcess : 1; //0x3
UCHAR IsLegacyProcess : 1; //0x3
UCHAR IsImageDynamicallyRelocated : 1; //0x3
UCHAR SkipPatchingUser32Forwarders : 1; //0x3
UCHAR SpareBits : 3; //0x3
};
};
ULONGLONG Mutant; //0x8
ULONGLONG ImageBaseAddress; //0x10
ULONGLONG Ldr; //0x18
}PEB64, * PPEB64;
//0x58 bytes (sizeof)
typedef struct _PEB_LDR_DATA64
{
ULONG Length; //0x0
UCHAR Initialized; //0x4
VOID* SsHandle; //0x8
struct _LIST_ENTRY InLoadOrderModuleList; //0x10
struct _LIST_ENTRY InMemoryOrderModuleList; //0x20
struct _LIST_ENTRY InInitializationOrderModuleList; //0x30
VOID* EntryInProgress; //0x40
UCHAR ShutdownInProgress; //0x48
VOID* ShutdownThreadId; //0x50
}PEB_LDR_DATA64, * PPEB_LDR_DATA64;
typedef struct _PEB_LDR_DATA32
{
ULONG Length; //0x0
UCHAR Initialized; //0x4
ULONG SsHandle; //0x8
LIST_ENTRY32 InLoadOrderModuleList; //0xc
LIST_ENTRY32 InMemoryOrderModuleList; //0x14
LIST_ENTRY32 InInitializationOrderModuleList; //0x1c
ULONG EntryInProgress; //0x24
UCHAR ShutdownInProgress; //0x28
ULONG ShutdownThreadId; //0x2c
}PEB_LDR_DATA32, * PPEB_LDR_DATA32;
//0x78 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY32
{
LIST_ENTRY32 InLoadOrderLinks; //0x0
LIST_ENTRY32 InMemoryOrderLinks; //0x8
LIST_ENTRY32 InInitializationOrderLinks; //0x10
ULONG DllBase; //0x18
ULONG EntryPoint; //0x1c
ULONG SizeOfImage; //0x20
UNICODE_STRING32 FullDllName; //0x24
UNICODE_STRING32 BaseDllName; //0x2c
ULONG Flags; //0x34
USHORT LoadCount; //0x38
USHORT TlsIndex; //0x3a
}LDR_DATA_TABLE_ENTRY32, * PLDR_DATA_TABLE_ENTRY32;
//0xe0 bytes (sizeof)
typedef struct _LDR_DATA_TABLE_ENTRY64
{
struct _LIST_ENTRY InLoadOrderLinks; //0x0
struct _LIST_ENTRY InMemoryOrderLinks; //0x10
struct _LIST_ENTRY InInitializationOrderLinks; //0x20
VOID* DllBase; //0x30
VOID* EntryPoint; //0x38
LONGLONG SizeOfImage; //0x40
struct _UNICODE_STRING FullDllName; //0x48
struct _UNICODE_STRING BaseDllName; //0x58
}LDR_DATA_TABLE_ENTRY64, * PLDR_DATA_TABLE_ENTRY64;
内核BufferIO.h
#pragma once
#include <ntifs.h>
#define CTL_SYS \
CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define DEVICE_OBJECT_NAME L"\\Device\\BufferedIODeviceObjectName"
//设备与设备之间通信
#define DEVICE_LINK_NAME L"\\DosDevices\\BufferedIODevcieLinkName"
//设备与Ring3之间通信
VOID DriverUnload(PDRIVER_OBJECT DriverObject);
NTSTATUS PassThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS ControlThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS NTAPI MmCopyVirtualMemory(PEPROCESS SourceProcess, PVOID SourceAddress, PEPROCESS TargetProcess, PVOID TargetAddress, SIZE_T BufferSize, KPROCESSOR_MODE PreviousMode, PSIZE_T ReturnSize);
代码讲解
程序通信框架借用的网上IRP通信框架,主要读取内存功能在函数ControlThroughDispatch
,里面介绍了三种读取内存的方式,其中第一种直接附加memcpy不太稳定并没有演示,可以参考MDL读写
参考资料
火哥五期学习资料
应用程序与驱动程序通信