Windows中的内存地址分为几种? Windows11中内存数据分为物理内存与虚拟内存
在Windows 11(以及所有现代Windows版本)中,内存按照以下描述的方式组织:
物理内存 - 这是实际的硬件RAM,由内存条提供的实际存储空间。
虚拟内存
- 这是一个抽象层,被划分为两个主要区域:
低地址空间 (用户空间):分配给应用程序使用的虚拟地址。在64位Windows系统中,用户空间通常是从0x0000000000000000到0x00007FFFFFFFFFFF,即低48位地址空间。每个进程都有自己独立的用户空间虚拟地址映射。
高地址空间 (内核空间):保留给操作系统内核和驱动程序使用的虚拟地址。在64位Windows系统中,内核空间通常从0xFFFF800000000000开始,即高位地址空间。所有进程共享相同的内核空间映射。
读取物理内存地址数据 1. 使用Windbg读取物理内存地址
windbg断点
输入!db命令查询地址
带感叹号的命令读取物理内存地址,不带感叹号读取虚拟内存地址
在WinDbg中读取物理内存的常用命令:
!db 物理地址 - 以字节格式显示物理内存
!dw 物理地址 - 以字(word)格式(2字节)显示物理内存
!dd 物理地址 - 以双字(dword)格式(4字节)显示物理内存
!dq 物理地址 - 以四字(qword)格式(8字节)显示物理内存
读取物理内存内容与长度:
2. 使用MM_COPY_ADDRESS结构读取物理内存
entry.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <ntddk.h> #include <wdf.h> #ifdef DBG #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__) #else #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__) #endif VOID DriverUnload (PDRIVER_OBJECT pDriver) { pDriver; DebugMessage(("驱动卸载成功\r\n" )); } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { DbgSetDebugFilterState(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, TRUE); DriverObject->DriverUnload = DriverUnload; DebugMessage("entry\r\n" ); NTSTATUS status; PVOID buffer = NULL ; SIZE_T size = 0x1000 ; SIZE_T bytesTransferred = 0 ; MM_COPY_ADDRESS sourceAddress; buffer = ExAllocatePoolWithTag(NonPagedPool, size, 'MeRd' ); if (buffer == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } sourceAddress.PhysicalAddress.QuadPart = 0x100000 ; status = MmCopyMemory(buffer, sourceAddress, size, MM_COPY_MEMORY_PHYSICAL, &bytesTransferred); if (NT_SUCCESS(status)) { DebugMessage("读取物理内存成功,传输了 %d 字节\n" , (ULONG)bytesTransferred); if (bytesTransferred == 0 ) { DebugMessage("警告:没有传输任何字节!\n" ); } DebugMessage("内存内容: " ); for (ULONG i = 0 ; i < min(16 , bytesTransferred); i++) { DebugMessage( "%02X " , ((PUCHAR)buffer)[i]); } DebugMessage("\n" ); } else { DebugMessage("读取物理内存失败,状态码: 0x%x\n" , status); } if (buffer != NULL ) { ExFreePoolWithTag(buffer, 'MeRd' ); } return status; }
2. 驱动程序中使用MmMapIoSpace将物理内存映射到虚拟内存中读取
entry.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include "ReadAddrP.h" NTSTATUS UnloadDriver (PDRIVER_OBJECT pDriverObject) { DebugMessage("unload pDriverObject addr is %p\n" , pDriverObject); return STATUS_SUCCESS; } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { pDriverObject->DriverUnload = UnloadDriver; DebugMessage("Into DriverEntry %p\n" , pDriverObject); DebugMessage("pDriverObject address is %p\n" , pDriverObject); DebugMessage("pRegistryPath is %wZ\n" , pRegistryPath); PHYSICAL_ADDRESS physicalAddr; UCHAR buffer[16 ] = { 0 }; physicalAddr.QuadPart = 0x1000 ; NTSTATUS status = ReadPhysicalMemory(physicalAddr, buffer, sizeof (buffer)); if (NT_SUCCESS(status)) { DebugMessage("Successfully read physical memory\n" ); } else { DebugMessage("Failed to read physical memory, status: 0x%X\n" , status); } return STATUS_SUCCESS; }
ReadAddrP.h
1 2 3 4 5 6 7 8 #pragma once #include <ntifs.h> #pragma warning (disable : 4100) #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__); NTSTATUS ReadPhysicalMemory (PHYSICAL_ADDRESS physicalAddr, PVOID buffer, SIZE_T size) ;
ReadAddrP.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include "Function.h" #include <ntifs.h> NTSTATUS ReadPhysicalMemory (PHYSICAL_ADDRESS physicalAddr, PVOID buffer, SIZE_T size) { NTSTATUS status = STATUS_SUCCESS; PVOID mappedAddress = NULL ; mappedAddress = MmMapIoSpace(physicalAddr, size, MmNonCached); if (mappedAddress != NULL ) { __try { RtlCopyMemory(buffer, mappedAddress, size); DebugMessage("Physical memory at 0x%llX:" , physicalAddr.QuadPart); PUCHAR byteBuffer = (PUCHAR)buffer; for (SIZE_T i = 0 ; i < size; i++) { if (i % 8 == 0 ) DebugMessage("\n" ); DebugMessage("%02X " , byteBuffer[i]); } DebugMessage("\n" ); } __except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); DebugMessage("Exception occurred while accessing memory: 0x%X\n" , status); } MmUnmapIoSpace(mappedAddress, size); } else { status = STATUS_UNSUCCESSFUL; DebugMessage("Failed to map physical address 0x%llX\n" , physicalAddr.QuadPart); } return status; }
3.物理内存映射到虚拟内存中读取,使用MDL的方式读取物理内存 MDL(内存描述符列表)读取物理内存的原理基于Windows内核提供的内存管理机制。我来详细解释一下代码中展示的过程:
物理地址映射到系统空间 : 首先使用MmMapIoSpace函数将物理地址映射到系统虚拟地址空间。这是访问物理内存的第一步,因为内核代码通常在虚拟地址空间中运行。
创建MDL : 使用IoAllocateMdl创建一个MDL结构,它描述了刚刚映射的内存区域。MDL本质上是一种数据结构,用于描述内存页的物理位置。
构建MDL : 调用MmBuildMdlForNonPagedPool来初始化MDL,这个函数填充MDL结构体中的物理页面信息。因为目标内存在非分页池中,所以不会有分页问题。
获取系统地址 : 通过MmGetSystemAddressForMdlSafe获取可以安全访问的系统地址。这个函数确保内存可以被当前执行上下文访问,并处理了可能的锁定问题。
数据复制 : 使用RtlCopyMemory将数据从映射的地址复制到用户提供的缓冲区。
资源清理 : 完成操作后,释放MDL资源并解除物理内存映射。
MDL的主要优势在于:
内存锁定 : MDL可以锁定物理内存页,防止它们被分页出去
描述不连续内存 : 可以描述物理上不连续但逻辑上连续的内存区域
安全访问 : 提供了一种安全的方式来访问并操作物理内存
DMA操作支持 : 适用于需要物理地址的DMA操作
这种方法比直接访问物理内存更安全,因为它利用了系统提供的内存管理机制,避免了可能的页面错误和访问冲突。
代码实现
entry.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 #include <ntddk.h> #include <stdio.h> #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__); #define TARGET_PHYSICAL_ADDRESS 0x100000 #define BYTES_TO_READ 256 VOID PrintMemoryHex (PVOID Buffer, SIZE_T Size) ; NTSTATUS ReadPhysicalMemoryWithMdl ( _In_ PHYSICAL_ADDRESS PhysicalAddress, _Out_ PVOID Buffer, _In_ SIZE_T Length, _Out_ PSIZE_T BytesRead ) ;VOID DriverUnload (PDRIVER_OBJECT DriverObject) { DbgPrint("物理内存读取驱动已卸载\n" ); } NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { NTSTATUS status; SIZE_T bytesRead = 0 ; PVOID buffer = NULL ; PHYSICAL_ADDRESS physicalAddress; UNREFERENCED_PARAMETER(RegistryPath); DriverObject->DriverUnload = DriverUnload; DbgPrint("物理内存读取驱动已加载\n" ); buffer = ExAllocatePoolWithTag(NonPagedPool, BYTES_TO_READ, 'rMeM' ); if (buffer == NULL ) { DbgPrint("无法分配内存缓冲区\n" ); return STATUS_INSUFFICIENT_RESOURCES; } physicalAddress.QuadPart = TARGET_PHYSICAL_ADDRESS; status = ReadPhysicalMemoryWithMdl(physicalAddress, buffer, BYTES_TO_READ, &bytesRead); if (!NT_SUCCESS(status)) { DbgPrint("读取物理内存失败: 0x%08X\n" , status); ExFreePoolWithTag(buffer, 'rMeM' ); return status; } DbgPrint("成功读取 %llu 字节的物理内存\n" , (ULONGLONG)bytesRead); PrintMemoryHex(buffer, bytesRead); ExFreePoolWithTag(buffer, 'rMeM' ); return STATUS_SUCCESS; } NTSTATUS ReadPhysicalMemoryWithMdl ( _In_ PHYSICAL_ADDRESS PhysicalAddress, _Out_ PVOID Buffer, _In_ SIZE_T Length, _Out_ PSIZE_T BytesRead ) { NTSTATUS status = STATUS_SUCCESS; PVOID mappedAddress = NULL ; PMDL mdl = NULL ; *BytesRead = 0 ; mappedAddress = MmMapIoSpace(PhysicalAddress, Length, MmNonCached); if (mappedAddress == NULL ) { return STATUS_INSUFFICIENT_RESOURCES; } __try { mdl = IoAllocateMdl(mappedAddress, (ULONG)Length, FALSE, FALSE, NULL ); if (mdl == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; __leave; } MmBuildMdlForNonPagedPool(mdl); PVOID mappedSystemAddress = MmGetSystemAddressForMdlSafe(mdl, NormalPagePriority | MdlMappingNoExecute); if (mappedSystemAddress == NULL ) { status = STATUS_INSUFFICIENT_RESOURCES; __leave; } RtlCopyMemory(Buffer, mappedSystemAddress, Length); *BytesRead = Length; } __except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); KdPrint(("异常发生在MDL内存访问过程中: 0x%x\n" , status)); } if (mdl != NULL ) { IoFreeMdl(mdl); } if (mappedAddress != NULL ) { MmUnmapIoSpace(mappedAddress, Length); } return status; } VOID PrintMemoryHex (PVOID Buffer, SIZE_T Size) { PUCHAR byteBuffer = (PUCHAR)Buffer; char lineBuf[100 ]; for (SIZE_T i = 0 ; i < Size; i += 16 ) { int linePos = sprintf_s(lineBuf, sizeof (lineBuf), "%04X: " , (UINT32)i); for (SIZE_T j = 0 ; j < 16 && (i + j) < Size; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%02X " , byteBuffer[i + j]); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } if (i + 16 > Size) { for (SIZE_T j = Size - i; j < 16 ; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " |" ); for (SIZE_T j = 0 ; j < 16 && (i + j) < Size; j++) { UCHAR ch = byteBuffer[i + j]; if (ch >= 32 && ch <= 126 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%c" , ch); } else { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "." ); } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "|" ); DebugMessage("%s\n" , lineBuf); } }
读取系统虚拟内存地址数据 1. 使用Windbg读取系统虚拟内存地址数据
Windbg中断点
输入db命令读取虚拟内存数据
2. 使用驱动程序读取系统虚拟内存地址数据,可以使用指针或者内存复制函数直接读取地址
entry.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <ntifs.h> #include <stdio.h> #pragma warning (disable : 4100) #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__); NTSTATUS ReadSystemVirtualMemory (PVOID virtualAddr, PVOID buffer, SIZE_T size) ; void PrintBuffer (PVOID buffer, SIZE_T size) ;NTSTATUS UnloadDriver (PDRIVER_OBJECT pDriverObject) { DebugMessage("unload pDriverObject addr is %p\n" , pDriverObject); return STATUS_SUCCESS; } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { UCHAR buffer[64 ] = { 0 }; pDriverObject->DriverUnload = UnloadDriver; DebugMessage("Into DriverEntry %p\n" , pDriverObject); DebugMessage("pDriverObject address is %p\n" , pDriverObject); DebugMessage("pRegistryPath is %wZ\n" , pRegistryPath); PVOID systemVirtualAddr = (PVOID)0xFFFFB68807741D30 ; NTSTATUS status = ReadSystemVirtualMemory(systemVirtualAddr, buffer, sizeof (buffer)); if (NT_SUCCESS(status)) { DebugMessage("Successfully read system virtual memory\n" ); } else { DebugMessage("Failed to read system virtual memory, status: 0x%X\n" , status); } return STATUS_SUCCESS; } NTSTATUS ReadSystemVirtualMemory (PVOID virtualAddr, PVOID buffer, SIZE_T size) { NTSTATUS status = STATUS_SUCCESS; __try { RtlCopyMemory(buffer, virtualAddr, size); DebugMessage("System virtual memory at 0x%p:" , virtualAddr); PrintBuffer(buffer, size); } __except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); DebugMessage("Exception occurred while accessing memory: 0x%X\n" , status); } return status; } void PrintBuffer (PVOID buffer, SIZE_T size) { PUCHAR byteBuffer = (PUCHAR)buffer; char lineBuf[100 ]; int linePos = 0 ; for (SIZE_T i = 0 ; i < size; i++) { if (i % 16 == 0 ) { if (i > 0 ) { DebugMessage("%s" , lineBuf); } linePos = 0 ; linePos += sprintf_s(lineBuf, sizeof (lineBuf), "%04X: " , (UINT32)i); } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%02X " , byteBuffer[i]); } if (linePos > 0 ) { DebugMessage("%s" , lineBuf); } }
使用现成的函数读取应用进程中的虚拟内存地址数据 1. 使用MmCopyVirtualMemory读取虚拟内存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 #include <ntifs.h> #include <stdio.h> #ifdef DBG #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__) #else #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__) #endif void ReadVirtualMemory () ;void PrintBuffer (PVOID buffer, SIZE_T size) ;NTSTATUS DriverUnload (PDRIVER_OBJECT pDriver) { pDriver; DebugMessage("驱动卸载成功\r\n" ); return STATUS_SUCCESS; } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { DbgSetDebugFilterState(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, TRUE); DriverObject->DriverUnload = DriverUnload; DebugMessage("entry\r\n" ); ReadVirtualMemory(); return STATUS_SUCCESS; } __declspec(dllimport) NTSTATUS NTAPI MmCopyVirtualMemory (PEPROCESS SourceProcess, PVOID SourceAddress, PEPROCESS TargetProcess, PVOID TargetAddress, SIZE_T BufferSize, KPROCESSOR_MODE PreviousMode, PSIZE_T ReturnSize) ; void ReadVirtualMemory () { NTSTATUS status; PVOID buffer = NULL ; SIZE_T size = 0x1000 ; PEPROCESS targetProcess = NULL ; HANDLE targetPid = (HANDLE)0xC4C ; SIZE_T bytesRead = 0 ; buffer = ExAllocatePoolWithTag(NonPagedPool, size, 'rvmB' ); if (buffer == NULL ) { DebugMessage("分配缓冲区失败\r\n" ); return ; } status = PsLookupProcessByProcessId(targetPid, &targetProcess); if (!NT_SUCCESS(status)) { DebugMessage("获取目标进程失败\r\n" ); ExFreePool(buffer); return ; } status = MmCopyVirtualMemory( targetProcess, (PVOID)0x7FF7571ADA80 , PsGetCurrentProcess(), buffer, size, KernelMode, &bytesRead ); if (NT_SUCCESS(status)) { DebugMessage("成功读取 %llu 字节\r\n" , bytesRead); } else { DebugMessage("读取虚拟内存失败\r\n" ); } ObDereferenceObject(targetProcess); PrintBuffer(buffer, size); ExFreePool(buffer); } void PrintBuffer (PVOID buffer, SIZE_T size) { PUCHAR byteBuffer = (PUCHAR)buffer; char lineBuf[100 ]; for (SIZE_T i = 0 ; i < size; i += 16 ) { int linePos = sprintf_s(lineBuf, sizeof (lineBuf), "%016llX: " , (ULONGLONG)((ULONG_PTR)buffer + i)); for (SIZE_T j = 0 ; j < 16 && (i + j) < size; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%02X " , byteBuffer[i + j]); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } if (i + 16 > size) { for (SIZE_T j = size - i; j < 16 ; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " |" ); for (SIZE_T j = 0 ; j < 16 && (i + j) < size; j++) { UCHAR ch = byteBuffer[i + j]; if (ch >= 32 && ch <= 126 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%c" , ch); } else { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "." ); } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "|" ); DebugMessage("%s\n" , lineBuf); } }
附加进程后读取应用进程中的虚拟内存地址数据 1. 如何读取进程中的虚拟内存地址? 在驱动程序中读取进程内存主要通过以下步骤:
获取目标进程的EPROCESS结构
使用KeStackAttachProcess将当前线程附加到目标进程的地址空间
读取内存数据
使用KeUnstackDetachProcess分离线程
2.测试在驱动中读取进程虚拟内存地址数据
打开notepad
使用ce附加notepad->点击查看内存->例如跳转内存(0x7FF682CC0000)->查看数值
附加别的进程会发现读取不到内存。
应用程序中需要先打开进程句柄或者附加进程才能读取内存。
在驱动程序中需要附加进程才能读取内存。
要想在驱动中读取内存,需要先使用api函数KeStackAttachProcess附加到notepad的进程中,而后才能读取到内存。
KeStackAttachProcess函数的第一个参数为eprocess,获取eprocess需要使用函数PsLookupProcessByProcessld
Eprocess用PsLookupProcessByProcessld引用一次后需要引用计数减一后释放
在驱动入口函数中添加代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 PEPROCESS Eprocess = 0 ; PsLookupProcessByProcessId((HANDLE)0x8EC , &Eprocess); if (!Eprocess) return STATUS_UNSUCCESSFUL; KAPC_STATE kapc; KeStackAttachProcess(Eprocess, &kapc); ULONG64 Data = *(ULONG64*)0x7FF682CC0000 ; KdPrint(("qi;进入 DriverEntry入口点Data=%d\n" ,Data)); long long *read = 0x7FF682CC0000 ;KdPrint(("qi;*read=%d,read=%llx\n" , *read,read)); long long addr = 0x7FF682CC0000 ;KdPrint(("qi;addr=%llx\n" , addr)); KeUnstackDetachProcess(&kapc); ObfDereferenceObject(Eprocess);
3.完整驱动代码实现在驱动中读取进程虚拟内存地址数据 entry.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 #include <ntifs.h> #include <stdio.h> #pragma warning (disable : 4100) #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__); NTSTATUS FindProcessByPid (HANDLE pid, PEPROCESS* Process) ; NTSTATUS ReadProcessMemory (PEPROCESS Process, PVOID userAddr, PVOID buffer, SIZE_T size) ; void PrintBuffer (PVOID buffer, SIZE_T size) ;NTSTATUS UnloadDriver (PDRIVER_OBJECT pDriverObject) { DebugMessage("unload pDriverObject addr is %p\n" , pDriverObject); return STATUS_SUCCESS; } NTSTATUS FindProcessByPid (HANDLE pid, PEPROCESS* Process) { NTSTATUS status; status = PsLookupProcessByProcessId(pid, Process); if (NT_SUCCESS(status)) { PUCHAR procNamePtr = (PUCHAR)(*Process) + 0x5A8 ; char imageName[16 ] = { 0 }; RtlCopyMemory(imageName, procNamePtr, 15 ); DebugMessage("Found process PID: 0x%X (%d), name: %s\n" , (ULONG)(ULONG_PTR)pid, (ULONG)(ULONG_PTR)pid, imageName); return STATUS_SUCCESS; } else { DebugMessage("Failed to find process with PID: 0x%X (%d), status: 0x%X\n" , (ULONG)(ULONG_PTR)pid, (ULONG)(ULONG_PTR)pid, status); return status; } } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { UCHAR buffer[64 ] = { 0 }; NTSTATUS status; PEPROCESS targetProcess = NULL ; pDriverObject->DriverUnload = UnloadDriver; DebugMessage("Into DriverEntry %p\n" , pDriverObject); DebugMessage("pDriverObject address is %p\n" , pDriverObject); DebugMessage("pRegistryPath is %wZ\n" , pRegistryPath); HANDLE pid = (HANDLE)0x8EC ; status = FindProcessByPid(pid, &targetProcess); if (NT_SUCCESS(status)) { PVOID userVirtualAddr = (PVOID)0x7FF682CC0000 ; RtlZeroMemory(buffer, sizeof (buffer)); status = ReadProcessMemory(targetProcess, userVirtualAddr, buffer, sizeof (buffer)); if (NT_SUCCESS(status)) { DebugMessage("Successfully read process memory from address 0x%p\n" , userVirtualAddr); PrintBuffer(buffer, sizeof (buffer)); } else { DebugMessage("Failed to read process memory, status: 0x%X\n" , status); } ObDereferenceObject(targetProcess); } return STATUS_SUCCESS; } NTSTATUS ReadProcessMemory (PEPROCESS Process, PVOID userAddr, PVOID buffer, SIZE_T size) { NTSTATUS status = STATUS_SUCCESS; KAPC_STATE apcState; KeStackAttachProcess(Process, &apcState); __try { if (MmIsAddressValid(userAddr)) { ProbeForRead(userAddr, size, 1 ); RtlCopyMemory(buffer, userAddr, size); DebugMessage("Process virtual memory at 0x%p read successfully\n" , userAddr); } else { status = STATUS_INVALID_ADDRESS; DebugMessage("Invalid address: 0x%p\n" , userAddr); } } __except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); DebugMessage("Exception occurred while accessing process memory: 0x%X\n" , status); } KeUnstackDetachProcess(&apcState); return status; } void PrintBuffer (PVOID buffer, SIZE_T size) { PUCHAR byteBuffer = (PUCHAR)buffer; char lineBuf[100 ]; for (SIZE_T i = 0 ; i < size; i += 16 ) { int linePos = sprintf_s(lineBuf, sizeof (lineBuf), "%016llX: " , (ULONGLONG)((ULONG_PTR)buffer + i)); for (SIZE_T j = 0 ; j < 16 && (i + j) < size; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%02X " , byteBuffer[i + j]); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } if (i + 16 > size) { for (SIZE_T j = size - i; j < 16 ; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " |" ); for (SIZE_T j = 0 ; j < 16 && (i + j) < size; j++) { UCHAR ch = byteBuffer[i + j]; if (ch >= 32 && ch <= 126 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%c" , ch); } else { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "." ); } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "|" ); DebugMessage("%s\n" , lineBuf); } }
4. WinDbg用户模式根据Pid切换到目标进程后读取进程内存? 用户模式调试器(cdb>或windbg>)可以直接使用PID
Windbg读取特定进程的内存分为几个步骤,而不是单个命令。以读取Notepad.exe (PID 0x8EC) 中地址0x7FF682CC0000的内存内容为例,正确的步骤是:
首先,切换到目标进程上下文:
这个命令会切换到PID为0x8EC的进程,并刷新(/r)符号和内存信息。Windbg会显示一条消息,表明已经切换到该进程的上下文。
然后,读取指定地址的内存内容:
这里使用db命令以字节格式显示内存。也可以使用其他显示格式:
db - 以字节(byte)格式显示
dw - 以字(word/2字节)格式显示
dd - 以双字(dword/4字节)格式显示
dq - 以四字(qword/8字节)格式显示
u - 反汇编显示(如果是代码)
如果想显示更多内存内容,可以指定长度:
这将显示从该地址开始的256个字节(0x100)。
当完成操作后,如果需要返回到之前的上下文,可以使用:
或者简单地重新连接到调试会话。
这种分步骤的方法比单命令更灵活,可以在切换到进程上下文后执行多种操作,而不仅仅是读取内存。
5. 怎么在Windbg中查找进程pid? 对于WinDbg而言,进程名本身确实不是直接用于命令执行的关键参数。WinDbg主要依赖进程ID (PID)来定位和操作特定进程,而不是通过进程名。
在WinDbg中:
进程ID (PID) 是唯一标识进程的方式,如.process /p /r 0x8EC
进程名可以用来帮助识别进程,但不是直接用于命令语法的
虽然WinDbg不直接使用进程名来执行命令,但它确实提供了一些方法来查找进程名对应的PID:
可以使用!process 0 0命令列出所有进程,然后手动查找Notepad.exe对应的PID
或者使用筛选命令:!process 0 0 notepad.exe来找到特定名称的进程
这与许多其他调试器和系统工具的设计理念一致——进程ID是系统资源的唯一标识符,而进程名只是一个人类可读的标签,可能存在重复(比如多个notepad.exe实例)。
所以在WinDbg工作流程中,通常需要先确定目标进程的PID,然后才能对该进程执行操作。
6.Windbg内核调试会话中使用EPROCESS切换进程后读取内存数据 在内核调试会话中尝试直接切换到用户模式进程上下文是行不通的会出现错误”Process 00000000`000008ec has invalid page directories”表明在内核调试器中无法直接使用PID切换到该进程。
因为在内核调试会话(kd>提示符)中,需要使用进程地址(EPROCESS)而不是PID来切换进程上下文。
Windbg内核调试会话中读取内存数据的步骤:
使用!process 0 0输出可以看出Notepad.exe进程的EPROCESS地址是ffffb6880beee080`。
使用EPROCESS地址切换进程:
1 .process /p /r 0xffffb6880beee080
然后读取内存:
当完成操作后,如果需要返回到之前的上下文,可以使用:
或者简单地重新连接到调试会话。
另一种方法是,可以使用!process命令找到进程后,右键点击输出中的进程地址,然后选择”Switch To Process”菜单项来切换。
物理内存与虚拟内存有映射关系,那么读取到对应的虚拟内存与物理内存数据是否相同? 结论:读取到对应的虚拟内存与物理内存数据完全相同
1.读取系统虚拟内存对应的物理地址,将此物理地址映射到系统虚拟内存中读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 #include <ntifs.h> #include <stdio.h> #pragma warning (disable : 4100) #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__); VOID QueryKernelAddressMapping (PVOID VirtualAddress) ; VOID QueryAndPrintAllocatedMemory (SIZE_T Size) ; VOID ReadAndPrintMemory (PVOID VirtualAddress, PHYSICAL_ADDRESS PhysicalAddress, SIZE_T Size) ; NTSTATUS UnloadDriver (PDRIVER_OBJECT pDriverObject) { DebugMessage("驱动程序卸载, 地址: %p\n" , pDriverObject); return STATUS_SUCCESS; } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { pDriverObject->DriverUnload = UnloadDriver; QueryKernelAddressMapping(pDriverObject); QueryAndPrintAllocatedMemory(64 ); return STATUS_SUCCESS; } VOID QueryKernelAddressMapping (PVOID VirtualAddress) { PHYSICAL_ADDRESS physicalAddress; if (!MmIsAddressValid(VirtualAddress)) { DebugMessage("错误: 地址 0x%p 无效\n" , VirtualAddress); return ; } physicalAddress = MmGetPhysicalAddress(VirtualAddress); DebugMessage("内核虚拟地址: 0x%p 映射到物理地址: 0x%016llX\n" , VirtualAddress, physicalAddress.QuadPart); ReadAndPrintMemory(VirtualAddress, physicalAddress, 64 ); } VOID QueryAndPrintAllocatedMemory (SIZE_T Size) { PVOID allocatedMemory; PHYSICAL_ADDRESS physicalAddress; allocatedMemory = ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, 'VMAP' ); if (allocatedMemory == NULL ) { DebugMessage("错误: 内存分配失败\n" ); return ; } for (SIZE_T i = 0 ; i < Size; i++) { ((PUCHAR)allocatedMemory)[i] = (UCHAR)(i & 0xFF ); } physicalAddress = MmGetPhysicalAddress(allocatedMemory); DebugMessage("分配的内存: 虚拟地址 0x%p 映射到物理地址 0x%016llX\n" , allocatedMemory, physicalAddress.QuadPart); ReadAndPrintMemory(allocatedMemory, physicalAddress, Size); ExFreePool(allocatedMemory); } VOID ReadAndPrintMemory (PVOID VirtualAddress, PHYSICAL_ADDRESS PhysicalAddress, SIZE_T Size) { PVOID mappedPhysAddress; PUCHAR virtualBuffer; PUCHAR physicalBuffer; virtualBuffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, 'VBUF' ); if (virtualBuffer == NULL ) { DebugMessage("错误: 无法分配虚拟缓冲区\n" ); return ; } RtlCopyMemory(virtualBuffer, VirtualAddress, Size); DebugMessage("虚拟地址 0x%p 的内存内容:\n" , VirtualAddress); PrintMemoryBuffer(VirtualAddress, virtualBuffer, Size); mappedPhysAddress = MmMapIoSpace(PhysicalAddress, Size, MmNonCached); if (mappedPhysAddress != NULL ) { physicalBuffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, 'PBUF' ); if (physicalBuffer != NULL ) { RtlCopyMemory(physicalBuffer, mappedPhysAddress, Size); DebugMessage("物理地址 0x%016llX 的内存内容:\n" , PhysicalAddress.QuadPart); PrintMemoryBuffer((PVOID)PhysicalAddress.QuadPart, physicalBuffer, Size); ExFreePool(physicalBuffer); } MmUnmapIoSpace(mappedPhysAddress, Size); } else { DebugMessage("警告: 无法映射物理地址 0x%016llX\n" , PhysicalAddress.QuadPart); } ExFreePool(virtualBuffer); } VOID PrintMemoryBuffer (PVOID VirtualAddress, PVOID Buffer, SIZE_T Size) { PUCHAR byteBuffer = (PUCHAR)Buffer; char lineBuf[100 ]; ULONG_PTR baseAddr = (ULONG_PTR)VirtualAddress; for (SIZE_T i = 0 ; i < Size; i += 16 ) { int linePos = sprintf_s(lineBuf, sizeof (lineBuf), "%016llX: " , (ULONGLONG)(baseAddr + i)); for (SIZE_T j = 0 ; j < 16 && (i + j) < Size; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%02X " , byteBuffer[i + j]); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } if (Size - i < 16 ) { SIZE_T spaces = 16 - (Size - i); for (SIZE_T j = 0 ; j < spaces; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); if (j == 7 - (Size - i) || (Size - i <= 8 && j == spaces - 1 )) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " |" ); for (SIZE_T j = 0 ; j < 16 && (i + j) < Size; j++) { UCHAR ch = byteBuffer[i + j]; if (ch >= 32 && ch <= 126 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%c" , ch); } else { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "." ); } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "|" ); DebugMessage("%s\n" , lineBuf); } }
2.读取进程虚拟内存对应的物理地址,将此物理地址映射到系统虚拟内存中读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 #include <ntifs.h> #include <stdio.h> #pragma warning (disable : 4100) #define DebugMessage(x, ...) DbgPrintEx(0, 0, x, __VA_ARGS__); NTSTATUS QueryProcessVirtualAddressMapping (HANDLE ProcessId, PVOID VirtualAddress) ; NTSTATUS ReadProcessMemoryByPhysicalAddress (PHYSICAL_ADDRESS PhysicalAddress, PVOID Buffer, SIZE_T Size) ; VOID PrintMemoryContents (PVOID Buffer, SIZE_T Size) ; NTSTATUS UnloadDriver (PDRIVER_OBJECT pDriverObject) { DebugMessage("驱动程序已卸载\n" ); return STATUS_SUCCESS; } NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath) { NTSTATUS status; pDriverObject->DriverUnload = UnloadDriver; HANDLE targetPid = (HANDLE)0x8EC ; PVOID targetAddress = (PVOID)0x7FF682CC0000 ; status = QueryProcessVirtualAddressMapping(targetPid, targetAddress); return STATUS_SUCCESS; } NTSTATUS QueryProcessVirtualAddressMapping (HANDLE ProcessId, PVOID VirtualAddress) { NTSTATUS status; PEPROCESS process = NULL ; KAPC_STATE apcState; UCHAR buffer[64 ] = { 0 }; PHYSICAL_ADDRESS physicalAddress = { 0 }; DebugMessage("开始查询进程(PID: 0x%X)虚拟地址 0x%p 的映射\n" , (ULONG)(ULONG_PTR)ProcessId, VirtualAddress); status = PsLookupProcessByProcessId(ProcessId, &process); if (!NT_SUCCESS(status)) { DebugMessage("无法找到进程(PID: 0x%X), 错误码: 0x%X\n" , (ULONG)(ULONG_PTR)ProcessId, status); return status; } PUCHAR procNamePtr = (PUCHAR)process + 0x5A8 ; char imageName[16 ] = { 0 }; RtlCopyMemory(imageName, procNamePtr, 15 ); DebugMessage("已找到进程: %s (PID: 0x%X)\n" , imageName, (ULONG)(ULONG_PTR)ProcessId); KeStackAttachProcess(process, &apcState); __try { if (MmIsAddressValid(VirtualAddress)) { DebugMessage("虚拟地址 0x%p 有效\n" , VirtualAddress); physicalAddress = MmGetPhysicalAddress(VirtualAddress); DebugMessage("对应的物理地址: 0x%016llX\n" , physicalAddress.QuadPart); RtlZeroMemory(buffer, sizeof (buffer)); __try { ProbeForRead(VirtualAddress, sizeof (buffer), 1 ); RtlCopyMemory(buffer, VirtualAddress, sizeof (buffer)); DebugMessage("通过虚拟地址读取的内存内容:\n" ); PrintMemoryContents(buffer, sizeof (buffer)); } __except (EXCEPTION_EXECUTE_HANDLER) { DebugMessage("通过虚拟地址读取内存时发生异常: 0x%X\n" , GetExceptionCode()); } } else { DebugMessage("虚拟地址 0x%p 无效\n" , VirtualAddress); } } __except (EXCEPTION_EXECUTE_HANDLER) { DebugMessage("查询地址映射时发生异常: 0x%X\n" , GetExceptionCode()); } KeUnstackDetachProcess(&apcState); if (physicalAddress.QuadPart != 0 ) { RtlZeroMemory(buffer, sizeof (buffer)); status = ReadProcessMemoryByPhysicalAddress(physicalAddress, buffer, sizeof (buffer)); if (NT_SUCCESS(status)) { DebugMessage("通过物理地址 0x%016llX 读取的内存内容:\n" , physicalAddress.QuadPart); PrintMemoryContents(buffer, sizeof (buffer)); } } ObDereferenceObject(process); return STATUS_SUCCESS; } NTSTATUS ReadProcessMemoryByPhysicalAddress (PHYSICAL_ADDRESS PhysicalAddress, PVOID Buffer, SIZE_T Size) { NTSTATUS status = STATUS_SUCCESS; PVOID mappedAddress = NULL ; mappedAddress = MmMapIoSpace(PhysicalAddress, Size, MmNonCached); if (mappedAddress == NULL ) { DebugMessage("无法映射物理地址 0x%016llX\n" , PhysicalAddress.QuadPart); return STATUS_UNSUCCESSFUL; } __try { RtlCopyMemory(Buffer, mappedAddress, Size); } __except (EXCEPTION_EXECUTE_HANDLER) { status = GetExceptionCode(); DebugMessage("读取映射内存时发生异常: 0x%X\n" , status); } MmUnmapIoSpace(mappedAddress, Size); return status; } VOID PrintMemoryContents (PVOID Buffer, SIZE_T Size) { PUCHAR byteBuffer = (PUCHAR)Buffer; char lineBuf[100 ]; for (SIZE_T i = 0 ; i < Size; i += 16 ) { int linePos = sprintf_s(lineBuf, sizeof (lineBuf), "%04X: " , (UINT32)i); for (SIZE_T j = 0 ; j < 16 && (i + j) < Size; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%02X " , byteBuffer[i + j]); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } if (i + 16 > Size) { for (SIZE_T j = Size - i; j < 16 ; j++) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); if (j == 7 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " " ); } } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, " |" ); for (SIZE_T j = 0 ; j < 16 && (i + j) < Size; j++) { UCHAR ch = byteBuffer[i + j]; if (ch >= 32 && ch <= 126 ) { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "%c" , ch); } else { linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "." ); } } linePos += sprintf_s(lineBuf + linePos, sizeof (lineBuf) - linePos, "|" ); DebugMessage("%s\n" , lineBuf); } }
3.WinDbg中查看物理地址与虚拟地址之间的映射关系 WinDbg还提供!pte命令,可以查看物理地址与虚拟地址之间的映射关系
1. 物理地址查询虚拟地址 要查看物理地址0x100000映射到哪些虚拟地址,可以使用以下命令:
这个命令会显示该物理地址当前映射到的系统虚拟地址(如果有映射的话)。
2. 系统虚拟地址查询物理地址 要查看系统虚拟地址0xFFFFB68807741D30对应的物理地址:
1 !vtop 0 0xFFFFB68807741D30
其中第一个参数0是CR3寄存器值,使用0表示使用当前进程的CR3。这个命令会显示虚拟地址的物理映射信息,包括PDE、PTE等页表结构。
3. 使用DT命令查看页表结构 如果需要详细了解页表结构:
4. 进程虚拟地址查询物理地址 对于特定进程的虚拟地址(0x7FF682CC0000)映射:
使用!process 0 0输出可以看出Notepad.exe进程的EPROCESS地址是ffffb6880beee080`。
使用EPROCESS地址切换进程:
1 .process /p /r 0xffffb6880beee080
然后读取内存映射:
当完成操作后,如果需要返回到之前的上下文,可以使用:
这些命令可以帮助理解Windows系统中虚拟地址和物理地址之间的映射关系。在调试驱动程序或分析内存问题时非常有用。需要记住,物理地址到虚拟地址的映射是动态的,可能会随着系统运行而变化。