windows内存体系结构 内存查询,读,写(附录源码)
“进程内存管理器”这个程序实现的最基本功能也就是对内存的读写,之前的两篇文章也就是做的一个铺垫,介绍了内核模式切换和IoDeviceControl函数进行的应用程序与驱动程序通信的问题。接下来就进入正题了,对于内存查询,读写问题。
先来总结一下windows内存体系结构,这部分的学习主要是参照《windows核心编程》第13,14章的内容,以及网上前辈们的一些总结讨论。
先看看虚拟地址空间的分区(即虚拟地址空间布局):
(1)空指针赋值分区
①为帮助程序员捕获对空指针的赋值,当线程试图读取或写入这一分区的内存地址,就会引发访问违规
②没有任何办法可以让我们分配到位于这一地址区间的虚拟内存。
(2)用户模式分区
①进程地址空间的驻地。对于应用程序来说,大部分数据都保存在这一分区。
②32位下,默认为2GB大小。打开/3GB开关时,可扩大到3GB空间,但同时内核空间缩小为1GB)
【x86 Windows下获得更大的用户模式分区】——修改Windows启动配置数据(Boot Configuration Data,BCD)
①运行BCDEdit.exe
②bcdedit /set IncreaseUserVa 3072,就可以为进程保留3GB用户模式地址空间,IncreaseUserVa可接受的最小值为2048,即默认的2GB。取消的话:bcdedit /deletevalue IncreaseUserVa。
③为了让应用程序可以访问2GB以上的地址空间(特别地,早期的应用程序是不允许这样做的)。在链接时,可以打开/LARGEADDRESSAWARE链接开关。
【在64位Windows下得到2GB用户模式分区】将32位应用程序移植到64位环境下
①因大量使用32位指针开发程序,仅重新编译程序会导致指针截断错误和不正确的内存访问。但可以让应用程序在地址空间沙箱(Address space sandbox)中运行,这也是默认的情况,系统能够保证高33位都为0的64地址截断为32位,这样进程可用的地址空间就被限制在最底部的2GB中。
②当运行64位应用程序时,默认下系统会保留用户模式地址空间中在2GB以下(即最底部的2GB),这就是所谓的地址空间沙箱。这空间对于大多数的应用程序来说是足够的。
③为了让64位应用程序能够访问整个用户地址空间,必须指定/LARGEADDRESSAWARE链接器开关来链接应用程序。
(3)内核模式分区
操作系统代码的驻地。与线程调度、内存管理 、文件系统支持、网络支持以及设备驱动程序相关的代码都载入到这个分区中。该分区中的所有代码和数据都为所有进程共有,但这些代码和数据都是被保护起来的,如果试图在这分区的某个内存地址读取或写入数据时,会引发访问违规。
13.1.2 Windows内存安排(时间上的安排)
(1)每个应用程序都有自己的4GB寻址空间。该空间可存放操作系统、系统DLL和用户DLL代码,它们之中有各种函数供应用程序调用。再除去其他的一些空间,余下的是应用程序的代码、数据和可以分配的地址空间。
(2)不同应用程序的线性地址空间是隔离的。虽然它们在物理内存中同时存在,但在某个程序所属的时间片中,其他应用程序的代码和数据没有被映射到可寻址的线性地址中,所以是不可访问的。从编程的角度看,程序可供使用的4GB的寻址空间,而且这个空间是“私有的”。
(3)DLL程序没有自己的“私有”的空间。它们总是被映射到其他应用程序的地址空间中,当做其他应用程序的一部分运行。原因很简单,如果它不和其他程序同属一个地址空间,应用程序就不能调用它。
(4)操作系统和系统DLL的代码需要供每个应用程序调用,所以在所有的时间片中都必须被映射;
(5)用户程序只在自己所属的时间片内被映射。用户DLL则有选择地被映射。如程序B和C都调用了xxx.dll,那么物理内存中xxx.dll(注意在内存中已经存在了!)的代码在图中的时间片2和n中被映射,其他时间片就不需要被映射。(当然物理内存中只需要一份xxx.dll的代码)。
13.2 地址空间中的区域
(1)预定地址空间中的一块区域(预订:VirtualAlloc、释放:VirtualFree)
①起始地址:分配粒度(一般是64K)的整数倍。(注意:分配粒度与CPU平台有关,同时系统自己预订的区域的起始地址不一定非得是64KB的整数倍,如系统为进程环境块(PEB)和线程环境块(TEB)预定的区域地址就可能不是64KB的整数倍,但区域的大小仍是系统页面大小的整数倍。应用程序自己预订的区域,)
②预定空间的区域的大小:系统页面大小的整数倍(x86和x64的页面大小为4KB,I64系统使用的页面大小为8KB,关于x86,x64,IA64,X86架构的CPU 从早期的 8位,16位,32位,一直发展现在的64位;X64就是基于 x86 架构的64位CPU ,AMD 64位CPU 就是用这种的全称 X86-64;而IA64 是intel面向高端的新架构 IPF的64位 CPU 这种CPU 是不能兼容原先的 X86 下使用的程序,也就是说现在的windows程序都无法在这种CPU下面运行)
(2)将预订区域提交物理存储器
①提交时,可以只提交区域的一部分。如预订64KB空间大小,但可以只提交第2、第4两个页面(同样是调用VirtualAlloc函数,但传入的是MEM_COMMIT类型的参数)。
②撤消提交:VirtualFree,并传入MEM_DECOMMIT
13.3 物理存储器和页交换文件
(1)虚拟内存的实现:当应用程序调用VirtualAlloc函数将预订的空间区域提交物理存储器(物理内存或页交换文件)时,该空间实际上仍然不是从物理内存而是页交换文件中分配得到的!!!!!以后当访问该空间时,会因数据并不存在于物理内存而发生访问“页面错误”,从而引发操作系统利用异常处理机制将虚拟地址空间真正映射到对应的物理内存中,如下图所示。
(2)内存映射文件:把硬盘上的文件映像(如一个.exe或DLL文件)作为虚拟内存的一部分(注意是文件映像,而不是页交换文件)。当用户要执行一个可执行文件时,系统会打开应用程序对应的.exe文件并计算出应用程序的代码和数据的大小。然后预订一块地址空间,并注明与该区域相关的存储场所是.exe文件本身,而不是页交换文件。这样做可以将.exe的实际内容用作程序预订的地址空间区域,不仅载入程序速度快,而且可避免将为每个程序文件的代码和数据复制到页交换文件而造成页交换文件过于庞大和臃肿。
13.4 页面保护属性
保护属性
描述
PAGE_NOACCESS
不可访问。试图读取、写入或执行页面中的数据(代码)时将引发访问违规。
PAGE_READONLY
只读。试图写入页面或执行页面中的代码将引发访问违规
PAGE_READWRITE
读写属性。试图执行页面中的代码将引发访问违规 。
PAGE_EXECUTE
可执行属性。试图读取或写入页面将引发访问违规。
PAGE_EXECUTE_READ
可读、可执行。读图写入页面将引发访问违规。
PAGE_EXECUTE_READWRITE
可读可写可执行。对页面的任何操作都不会引发访问违规
PAGE_WRITECOPY
①写时复制。试图执行页面中的代码将引发访问违规。
②试图写入页面将使系统为进程单独创建一份该页面私有副本(以页交换文件为后备存储器)
PAGE_EXECUTE_WRITECOPY
对页面执行任何操作都不会引发访问违规。试图写入页面将使系统为进程单独创建一份该页面私有副本(以页交换文件为后备存储器)
★注意:如果Windows启用了数据执行保护(Data Execution Protection,DEP),当CPU试图执行某个页面中的代码,而该页面又没有PAGE_EXECUTE_*保护属性,那么CPU会抛出访问违规异常。(DEP开启方法:我的电脑→右键“属性”→高级系统设置→性能→设置→数据执行保护,选中“仅为基本Windows程序和服务启用DEP”)
13.4.1 写时复制
(1)写时复制属性的作用:节省内存和页交换文件的使用
Windows提供一种机制,允许两个或两个以上的进程共享一块存储器。如10个记事本进程正在运行,所有的进程会共享应用程序的代码页和数据页。当只读或执行时,这种共享存储页的方式极大地提高了性能。但当某个实例写入一个存储页时,就要求给共享的存储页指定写时复制属性,这样在映射地址空间时,系统会计算有多少可写页面,然后从页交换文件中分配空间来容纳这些可写页面,在程序真正写入的时候,就存储在页交换文件中。
(2)写入共享页面时,系统介入的操作
①系统在内存中找到一个空闲页面。注意,该空闲页的后备页面来自页交换文件。它是系统最初将模块映射到进程的地址空间时分配的。由于是第1次映射时就分配了所需的页交换文件空间。所以这步不可能失败。
②系统将要修改的页面内容复制到第1步找到的空闲页面,然后给这些空闲页面指定PAGE_READWRITE或PAGE_EXECUTE_READWRITE属性。(注意系统不会修改原始页面的保护属性和数据)
③然后系统更新进程的页面表,这样,原来的虚拟地址现在就对应到内存中一个新的页面了。以后进程就可以访问它自己的副本了。
(3)在预订地址空间或提交物理存储器时,不能使用PAGE_WRITECOPY或PAGE_EXECUTE_WRITECOPY保护属性,否则VirtualAlloc会失败,GetLastError将返回ERROR_INVALID_PARAMETER。
13.4.2 一些特殊的访问保护属性标志
保护属性
描述
PAGE_NOCACHE
禁止对己提交的页面进行缓存。该标志的目的是为了让需要操控内存缓冲区的驱动程序开发人员使用。一般不建议用将这标志用于除此以外的其他用途。
PAGE_WRITECOMBINE
允许把单个设备的多次写操作组合在一起,以提高性能。也是给驱动程序开发人员用的。
PAGE_GUARD
使应用程序能够在页面中的任何一个字节被写入时得到通知。
(1)基地址:
①从0x0000 0000开始,到0x7FFE 0000+ FFFF结束。
②几乎所有的非空闲区域的基地址都是64KB的整数倍(这是由系统地址空间的分配粒度决定的)。如果不是64KB的整数倍,这意味着该区域是由操作系统以进程名义分配的。
(2)区域类型
类型
描述
Free(空闲)
区域的虚拟地址没有任何后备存储器。该地址空间尚未预订,应用程序可以从基地址开始预订,也可以从空闲区域内的任何地方开始预订区域
Private(私有)
区域的虚拟地址以系统的页交换文件为后备存储器
Image(映像)
一开始以映像文件(如exe或DLL)为后备存储器,但以后不一定以映像文件为后备存储器(如程序写入映像文件中一个全局变量,那么写时复制会改用页交换文件来作为后备存储器)(映射文件可理解为exe或dll文件)
Mapped(己映射)
一开始以内存映射文件为后备存储器,此后不一定以内存映像文件为后备存储器。(如内存映射文件可能会使用写时复制保护属性。任何写操作会使对应的页面改用页交换文件来作为后备存储器)
★注意:对于每个区域整体而言,该区域的类型是推测出来的(除空闲外),详细见13.5.2节《区域内部》的内容。
(3)区域预订的字节数
①始终是CPU页面大小的整数倍(对于x86为4字节,即4096的倍数)
②为了节省磁盘空间,链接器会尽可能对对PE文件进行压缩,所以磁盘上的文件大小与映射到内存所需要的字节数是有差异的。
(4)预订区域内部的块的数量(block)
①块是一些连续的页面,这些页面具有相同的保护属性,并以相同类型的物理存储器为后备存储器。对闲置页面来说,由于不可能将存储器拨给他们,该值始终为0。
②每个区域最大能容纳的块的数量为:区域大小/页面大小,即当每个页面都是一个不同的块时,这里块的数量最多。
(5)区域的保护属性:
①E=execute,R=read,W=Write,C=copy on write。如果区域没有显示任保护属性,表示该区域没有任何访问保护。闲置区域没有与之相关联的保护属性。
②PAGE_GAUARD和PAGE_ONCACHE标志对地址空间没有意义,这些标志只有当用于物理存储时才有意义。
③如果同时给区域和物理存储器指定了保护属性,那么以后者为准。(见区域内部一节的分析)
13.6 数据对齐的重要性
(1)数据对齐:将数据的地址 % 数据大小 = 0时的数据是对齐的。
(2)x86CPU对错位数据的处理
①EFLAGS寄存器的AC标志位(AlignmentCheck)为0时,CPU自动执行必要的操作来访问错位数据)
②AC标志位为1时,如果试图访问错位数据,CPU会触发INT 17H中断。(对于x86版本的Windows从来不变为AC标志位(即永远为0),因此x86处理器上运行应用程序,绝对不会发生数据错位的异常,但IA-64CPU处理器不能自己处理数据错误的错误,因此当访问错位数据时,会抛出一个EXECPTION_DATATYPE_MISALIGNMENT异常,我们通用SetErrorMode函数并传为SEM_NOALIGNMENTFAULTEXCEPT标志,让系统自动修正数据错位的错误。(注意传入这个标志会影响进程中所有的线程,而且这个错误模式会被进程的子进程继承)
进入程序中的相关代码。直接进入到驱动程序接收到查询内存命令的case语句:
case CTL_QUERY_PROCESS_MEMORY: { if (!MmIsAddressValid(OutputData)) { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; break; } if (InputLength != sizeof(ULONG) || InputData == NULL) { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; break; } __try { ProbeForWrite(OutputData, OutputLength, 1); //检测内存是否可写,这个函数需要在用户模式下使用,详见MSDN: //If Irp->RequestorMode = KernelMode, the Irp->AssociatedIrp.SystemBuffer and Irp->UserBuffer fields do not contain user-mode addresses, //and a call to ProbeForWrite to probe a buffer pointed to by either field will raise an exception. Status = RtlQueryVirtualMemory(*(PULONG)InputData, OutputData, OutputLength); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = Status; } __except (EXCEPTION_EXECUTE_HANDLER) { Irp->IoStatus.Information = 0; Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } break; }
这段代码里首先用MmIsAddressValid函数,判断用作传出数据准备的缓冲区地址是否有效,之后对InputLength,InputData做一下判断,再用ProbeForWrite函数判断内存地址处是否可写,最后查询的操作在这个RtlQueryVirtualMemory函数中:
RtlQueryVirtualMemory(ULONG ProcessID, PVOID BufferData, SIZE_T BufferLength) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PEPROCESS EProcess = NULL; SIZE_T Count = 0; if (ProcessID) { Status = PsLookupProcessByProcessId((HANDLE)ProcessID, &EProcess); //通过进程ID获取进程EPROCESS if (!NT_SUCCESS(Status)) { return Status; } } ObfDereferenceObject(EProcess); //减少一个内核对象的引用计数的,否则蓝屏! if (IsRealProcess(EProcess)) //通过进程对象特征码判断EPROCESS是否有效 { Count = (BufferLength - sizeof(RTL_PROCESS_MEMORY)) / sizeof(RTL_PROCESS_MEMORY_INFORMATION); Status = ExQueryVirtualMemory(EProcess, (PRTL_PROCESS_MEMORY)BufferData, Count); if (NT_SUCCESS(Status)) { if (Count >= ((PRTL_PROCESS_MEMORY)BufferData)->NumberOfMemorys) { Status = STATUS_SUCCESS; } else { Status = STATUS_BUFFER_TOO_SMALL; } } } return Status; }
在RtlQueryVirtualMemory函数中,先计算出了进程内存信息的条数Count,再使用PsLookupProcessByProcessId函数,通过应用程序传入的进程ID得到进程EPROCESS,将EPROCESS传入自己编写的IsRealProcess函数来判断是否是一个真正的进程,最后调用了ExQueryVirtualMemory函数来查询内存。
关于Count的计算,涉及到的是自定义的进程内存信息结构体:
typedef struct _RTL_PROCESS_MEMORY_INFORMATION { PVOID BaseAddress; SIZE_T RegionSize; ULONG Protect; ULONG State; ULONG Type; }RTL_PROCESS_MEMORY_INFORMATION, *PRTL_PROCESS_MEMORY_INFORMATION; typedef struct _PROCESS_MEMORY_INFORMATION_ { ULONG NumberOfMemorys; RTL_PROCESS_MEMORY_INFORMATION Memorys[1]; }RTL_PROCESS_MEMORY, *PRTL_PROCESS_MEMORY;
通过EPROCESS判断是否为一个真正的进程:
BOOLEAN IsRealProcess(PEPROCESS EProcess) { ULONG_PTR ObjectType; ULONG_PTR ObjectTypeAddress; ULONG_PTR ProcessType = ((ULONG_PTR)*PsProcessType); //系统导出的全局变量 if (ProcessType && EProcess && MmIsAddressValid((PVOID)(EProcess))) { ObjectType = KeGetObjectType((PVOID)EProcess); //通过EProcess 获得进程对象特征码 if (ObjectType && ProcessType == ObjectType) { return TRUE; } } return FALSE; }
下面进入被调用的ExQueryVirtualMemory函数:
NTSTATUS ExQueryVirtualMemory(PEPROCESS EProcess, PRTL_PROCESS_MEMORY ProcessMemory, SIZE_T Count) { NTSTATUS Status = STATUS_UNSUCCESSFUL; HANDLE ProcessHandle = NULL; MEMORY_BASIC_INFORMATION mbi; SIZE_T ReturnLength = 0; //通过EProcess得到进程Handle Status = ObOpenObjectByPointer(EProcess, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, GENERIC_ALL, *PsProcessType, KernelMode, &ProcessHandle); if (NT_SUCCESS(Status)) { PVOID VirtualAddress = 0; PETHREAD EThread = PsGetCurrentThread(); CHAR PreviousMode = ChangePreviousMode(EThread); //EThread KThread 0x1f6 内核模式 while (VirtualAddress < RIN3_END) { Status = __NtQueryVirtualMemory(ProcessHandle, //1.进程句柄 (PVOID)VirtualAddress, //2.被枚举空间的虚拟地址 MemoryBasicInformation, //3.枚举的方式 &mbi, //4.事先准备好用于接收内存信息的缓冲区 sizeof(MEMORY_BASIC_INFORMATION),//缓冲区大小 &ReturnLength); //6.本次调用实际使用缓冲区长度 if (NT_SUCCESS(Status)) { if (Count > ProcessMemory->NumberOfMemorys) { ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].BaseAddress = mbi.BaseAddress; ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].RegionSize = mbi.RegionSize; ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].Protect = mbi.Protect; ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].State = mbi.State; ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].Type = mbi.Type; } ProcessMemory->NumberOfMemorys++; (PUINT8)VirtualAddress += mbi.RegionSize; } else { (PUINT8)VirtualAddress += PAGE_SIZE; } } NtClose(ProcessHandle); RecoverPreviousMode(EThread, PreviousMode); } DbgPrint("%d\r\n", ProcessMemory->NumberOfMemorys); return STATUS_SUCCESS; }
对比函数形参可以看到这里的ProcessMemory实际上就是应用程序中IoDeviceControl函数传给驱动程序的lpOutBuffer,用来接收驱动程序返回给应用程序数据的缓冲区,也就是接收进程内存信息的缓冲区,先回顾一下IoDeviceControl函数原型:
WINBASEAPI BOOL WINAPI DeviceIoControl( _In_ HANDLE hDevice, _In_ DWORD dwIoControlCode, _In_reads_bytes_opt_(nInBufferSize) LPVOID lpInBuffer, _In_ DWORD nInBufferSize, _Out_writes_bytes_to_opt_(nOutBufferSize, *lpBytesReturned) LPVOID lpOutBuffer, _In_ DWORD nOutBufferSize, _Out_opt_ LPDWORD lpBytesReturned, _Inout_opt_ LPOVERLAPPED lpOverlapped );
切换到内核模式的原因不再赘述了,参见之前写的这篇博客:
http://www.cnblogs.com/lsh123/p/6884988.html
应用程序驱动程序通信也不多说了,参见之前写的这篇博客:
http://www.cnblogs.com/lsh123/p/6890797.html
这个函数体的核心就是调用了内核层的的NtQueryVirtualMemory函数,这个函数并能直接调用,这里的做法是通过SSDT表索引找到内核中NtQueryVirtualMemory函数的地址,通过typedef定义出NtQueryVirtualMemory函数的参数形式来进行调用:
typedef NTSTATUS (*pfnNtQueryVirtualMemory)(HANDLE ProcessHandle,
PVOID BaseAddress, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID MemoryInformation, SIZE_T MemoryInformationLength, PSIZE_T ReturnLength);
NtQueryVirtualMemory大法好吖!
这里是一个暴力查询的方式NtQueryVirtualMemory查询不正确的话,就地址放过去一页,+4096个字节,继续调用NtQueryVirtualMemory函数,成功的话,就将对应的内存属性填充到应用程序准备好的缓冲区中返回给应用程序。直至Count = ProcessMemory->NumberOfMemorys为止。到此查询功能可谓是功成身退了~
看一下运行效果:
附录源码:
ProcessMemoryManagerDlg.h
// ProcessMemoryManagerDlg.h : 头文件 // #pragma once #include "afxcmn.h" #include <WinIoCtl.h> #include <afxtempl.h> #include <vector> using namespace std; #define CTL_QUERY_PROCESS_MEMORY \ CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_NEITHER,FILE_ANY_ACCESS) typedef struct _RTL_PROCESS_MEMORY_INFORMATION { PVOID BaseAddress; SIZE_T RegionSize; ULONG Protect; ULONG State; ULONG Type; }RTL_PROCESS_MEMORY_INFORMATION, *PRTL_PROCESS_MEMORY_INFORMATION; typedef struct _PROCESS_MEMORY_INFORMATION_ { ULONG NumberOfMemorys; RTL_PROCESS_MEMORY_INFORMATION Memorys[1]; }RTL_PROCESS_MEMORY, *PRTL_PROCESS_MEMORY; enum { QUERY_PROCESS_MEMORY = 0 }; // CProcessMemoryManagerDlg 对话框 class CProcessMemoryManagerDlg : public CDialogEx { // 构造 public: CProcessMemoryManagerDlg(CWnd* pParent = NULL); // 标准构造函数 ~CProcessMemoryManagerDlg() { m_Vector.clear(); if (m_DeviceHandle != NULL) { CloseHandle(m_DeviceHandle); m_DeviceHandle = NULL; } } // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_PROCESSMEMORYMANAGER_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() private: vector<RTL_PROCESS_MEMORY_INFORMATION> m_Vector; public: HANDLE m_DeviceHandle; afx_msg void OnEnChangeEditProcessId(); UINT m_CEdit_Process_ID; CListCtrl m_CListCtrl_Process_Memory; afx_msg void OnBnClickedOk(); BOOL CProcessMemoryManagerDlg::RtlQueryVirtualMemory(); BOOL OpenDeviceObject(LPCTSTR DeviceFullPathData) { m_DeviceHandle = CreateFile(DeviceFullPathData, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (m_DeviceHandle == INVALID_HANDLE_VALUE) { return FALSE; } return TRUE; } BOOL SendIoControlCode(ULONG32 IoControlCode); VOID CProcessMemoryManagerDlg::AddItemToControlList(RTL_PROCESS_MEMORY_INFORMATION MemoryBasicInfo); VOID CProcessMemoryManagerDlg::InitSolidMenu(); afx_msg void OnMemoryReadOperation(); afx_msg void OnMemoryWriteOperation(); afx_msg void OnMemoryModifyOperation(); };
ProcessMemoryManagerDlg.cpp
// ProcessMemoryManagerDlg.cpp : 实现文件 // #include "stdafx.h" #include "ProcessMemoryManager.h" #include "ProcessMemoryManagerDlg.h" #include "afxdialogex.h" #include "ReadOperationDlg.h" #include "WriteOperationDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx { public: CAboutDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ABOUTBOX }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CProcessMemoryManagerDlg 对话框 CProcessMemoryManagerDlg::CProcessMemoryManagerDlg(CWnd* pParent /*=NULL*/) : CDialogEx(IDD_PROCESSMEMORYMANAGER_DIALOG, pParent) , m_CEdit_Process_ID(0) { m_DeviceHandle = NULL; m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CProcessMemoryManagerDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_PROCESS_ID, m_CEdit_Process_ID); DDX_Control(pDX, IDC_LIST1, m_CListCtrl_Process_Memory); } BEGIN_MESSAGE_MAP(CProcessMemoryManagerDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_EN_CHANGE(IDC_EDIT_PROCESS_ID, &CProcessMemoryManagerDlg::OnEnChangeEditProcessId) ON_BN_CLICKED(IDOK, &CProcessMemoryManagerDlg::OnBnClickedOk) ON_COMMAND(ID_MEMORY_READ_OPERATION, &CProcessMemoryManagerDlg::OnMemoryReadOperation) ON_COMMAND(ID_MEMORY_WRITE_OPERATION, &CProcessMemoryManagerDlg::OnMemoryWriteOperation) ON_COMMAND(ID_MEMORY_MODIFY_OPERATION, &CProcessMemoryManagerDlg::OnMemoryModifyOperation) END_MESSAGE_MAP() // CProcessMemoryManagerDlg 消息处理程序 BOOL CProcessMemoryManagerDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 if (m_DeviceHandle == NULL) { OpenDeviceObject(L"\\\\.\\KProcessMemoryLinkName"); } m_CListCtrl_Process_Memory.SetExtendedStyle(LVS_EX_FULLROWSELECT); m_CListCtrl_Process_Memory.InsertColumn(0, L"内存基地址", LVCFMT_LEFT, 170); m_CListCtrl_Process_Memory.InsertColumn(1, L"范围", LVCFMT_LEFT, 170); m_CListCtrl_Process_Memory.InsertColumn(2, L"保护", LVCFMT_LEFT, 100); m_CListCtrl_Process_Memory.InsertColumn(3, L"状态", LVCFMT_LEFT, 80); m_CListCtrl_Process_Memory.InsertColumn(4, L"类型", LVCFMT_LEFT, 100); InitSolidMenu(); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } VOID CProcessMemoryManagerDlg::InitSolidMenu() { HMENU Menu; Menu = LoadMenu(NULL, MAKEINTRESOURCE(IDR_MENU_MAIN)); //载入菜单资源 ::SetMenu(this->GetSafeHwnd(), Menu); //为窗口设置菜单 ::DrawMenuBar(this->GetSafeHwnd()); //显示菜单 } void CProcessMemoryManagerDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CProcessMemoryManagerDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CProcessMemoryManagerDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CProcessMemoryManagerDlg::OnEnChangeEditProcessId() { // TODO: 如果该控件是 RICHEDIT 控件,它将不 // 发送此通知,除非重写 CDialogEx::OnInitDialog() // 函数并调用 CRichEditCtrl().SetEventMask(), // 同时将 ENM_CHANGE 标志“或”运算到掩码中。 // TODO: 在此添加控件通知处理程序代码 } void CProcessMemoryManagerDlg::OnBnClickedOk() { // TODO: 在此添加控件通知处理程序代码 UpdateData(TRUE);//FALSE是将变量映射到控件,TRUE则是从控件到变量 if (m_CEdit_Process_ID == 0) { MessageBox(L"ID错误"); return; } SendIoControlCode(QUERY_PROCESS_MEMORY); } BOOL CProcessMemoryManagerDlg::SendIoControlCode(ULONG32 IoControlCode) { switch (IoControlCode) { case QUERY_PROCESS_MEMORY: { RtlQueryVirtualMemory(); if (m_Vector.empty()) { return FALSE; } for (vector <RTL_PROCESS_MEMORY_INFORMATION>::iterator Travel = m_Vector.begin(); Travel != m_Vector.end(); Travel++) { AddItemToControlList(*Travel); } break; } default: break; } return TRUE; } BOOL CProcessMemoryManagerDlg::RtlQueryVirtualMemory() { BOOL IsOk = FALSE; DWORD ReturnLength = 0; BOOL IsReturn = FALSE; m_Vector.clear(); m_CListCtrl_Process_Memory.DeleteAllItems(); ULONG32 Count = 0x10; PRTL_PROCESS_MEMORY ProcessMemory = NULL; do { ULONG32 BufferLength = 0; if (ProcessMemory) { free(ProcessMemory); ProcessMemory = NULL; } BufferLength = sizeof(RTL_PROCESS_MEMORY) + Count * sizeof(RTL_PROCESS_MEMORY_INFORMATION); ProcessMemory = (PRTL_PROCESS_MEMORY)malloc(BufferLength); if (!ProcessMemory) { break; } memset(ProcessMemory, 0, BufferLength); IsReturn = DeviceIoControl(m_DeviceHandle, CTL_QUERY_PROCESS_MEMORY, &m_CEdit_Process_ID, sizeof(ULONG), ProcessMemory, BufferLength, &ReturnLength, NULL); Count = ProcessMemory->NumberOfMemorys + 10; } while (IsReturn == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER); if (IsReturn && ProcessMemory) { for (ULONG i = 0; i < ProcessMemory->NumberOfMemorys; i++) { m_Vector.push_back(ProcessMemory->Memorys[i]); } IsOk = TRUE; } if (ProcessMemory) { free(ProcessMemory); ProcessMemory = NULL; } return IsOk; } VOID CProcessMemoryManagerDlg::AddItemToControlList(RTL_PROCESS_MEMORY_INFORMATION MemoryBasicInfo) { CString BaseAddress, RegionSize, Protect, State, Type; BaseAddress.Format(L"0x%04p", MemoryBasicInfo.BaseAddress); RegionSize.Format(L"0x%08p", MemoryBasicInfo.RegionSize); Protect.Format(L"%d", MemoryBasicInfo.Protect); State.Format(L"%p", MemoryBasicInfo.State); Type.Format(L"%p", MemoryBasicInfo.Type); switch (MemoryBasicInfo.Type) { case 0x1000000: { Type = L"Image"; break; } case 0x40000: { Type = L"Map"; break; } case 0x20000: { Type = L"Private"; break; } default: { Type = L""; break; } } switch (MemoryBasicInfo.State) { case 0x10000: { State = L"Free"; break; } case 0x2000: { State = L"Reserve"; break; } case 0x1000: { State = L"Commit"; break; } default: { State = L""; break; } } switch (MemoryBasicInfo.Protect) { case 1: { Protect = L"No Access"; break; } case 2: { Protect = L"Read"; break; } case 4: { Protect = L"ReadWrite"; break; } case 260: { Protect = L"ReadWrite GUARD"; break; } case 32: { Protect = L"ReadExecute"; break; } case 8: { Protect = L"WriteCopy"; break; } default: { Protect = L""; break; } } int n = m_CListCtrl_Process_Memory.InsertItem(m_CListCtrl_Process_Memory.GetItemCount(), BaseAddress); m_CListCtrl_Process_Memory.SetItemText(n, 1, RegionSize); m_CListCtrl_Process_Memory.SetItemText(n, 2, Protect); m_CListCtrl_Process_Memory.SetItemText(n, 3, State); m_CListCtrl_Process_Memory.SetItemText(n, 4, Type); } void CProcessMemoryManagerDlg::OnMemoryReadOperation() { // TODO: 在此添加命令处理程序代码 UpdateData(TRUE); if (m_CEdit_Process_ID == 0) { return; } CReadOperationDlg Dlg(m_CEdit_Process_ID, this); Dlg.DoModal(); } void CProcessMemoryManagerDlg::OnMemoryWriteOperation() { // TODO: 在此添加命令处理程序代码 UpdateData(TRUE); if (m_CEdit_Process_ID == 0) { return; } CWriteOperationDlg Dlg(m_CEdit_Process_ID, this); Dlg.DoModal(); } void CProcessMemoryManagerDlg::OnMemoryModifyOperation() { // TODO: 在此添加命令处理程序代码 }
kProcessMemory.h
#include <ntifs.h> #include <ntimage.h> #define DEVICE_NAME L"\\Device\\KProcessMemoryDeviceName" #define LINK_NAME L"\\DosDevices\\KProcessMemoryLinkName" #define CTL_QUERY_PROCESS_MEMORY \ CTL_CODE(FILE_DEVICE_UNKNOWN,0x830,METHOD_NEITHER,FILE_ANY_ACCESS) #define CTL_READ_PROCESS_MEMORY \ CTL_CODE(FILE_DEVICE_UNKNOWN,0x831,METHOD_NEITHER,FILE_ANY_ACCESS) #define CTL_WRITE_PROCESS_MEMORY \ CTL_CODE(FILE_DEVICE_UNKNOWN,0x832,METHOD_NEITHER,FILE_ANY_ACCESS) #define CTL_MODIFY_PROCESS_MEMORY \ CTL_CODE(FILE_DEVICE_UNKNOWN,0x833,METHOD_NEITHER,FILE_ANY_ACCESS) /* #define CTL_CODE( DeviceType, Function, Method, Access ) ( \ ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ )*/ /* Function功能号 FunctionDefines an action within the device category. Function codes 0-2047 are reserved for Microsoft; codes 2048-4095 are reserved for OEMs and IHVs. A function code can be no larger then 4095. */ /* Ring0与Ring3的四种通信方式, #define METHOD_BUFFERED 0 #define METHOD_IN_DIRECT 1 #define METHOD_OUT_DIRECT 2 #define METHOD_NEITHER 3 */ /* 对应Ring3层DeviceIoControl函数中的dwIoControlCode参数,第二个 WINBASEAPI BOOL WINAPI DeviceIoControl( _In_ HANDLE hDevice, _In_ DWORD dwIoControlCode, _In_reads_bytes_opt_(nInBufferSize) LPVOID lpInBuffer, _In_ DWORD nInBufferSize, _Out_writes_bytes_to_opt_(nOutBufferSize, *lpBytesReturned) LPVOID lpOutBuffer, _In_ DWORD nOutBufferSize, _Out_opt_ LPDWORD lpBytesReturned, _Inout_opt_ LPOVERLAPPED lpOverlapped ); */ //Ring3 Ring0 typedef struct _RTL_PROCESS_MEMORY_INFORMATION { PVOID BaseAddress; SIZE_T RegionSize; ULONG Protect; ULONG State; ULONG Type; }RTL_PROCESS_MEMORY_INFORMATION, *PRTL_PROCESS_MEMORY_INFORMATION; typedef struct _PROCESS_MEMORY_INFORMATION_ { ULONG NumberOfMemorys; RTL_PROCESS_MEMORY_INFORMATION Memorys[1]; }RTL_PROCESS_MEMORY, *PRTL_PROCESS_MEMORY; typedef struct _SYSTEM_SERVICE_DESCRIPTOR_TABLE_ { PVOID Unknow0; PVOID Unknow1; PVOID Unknow2; PVOID Unknow3; }SYSTEM_SERVICE_DESCRIPTOR_TABLE, *PSYSTEM_SERVICE_DESCRIPTOR_TABLE; BOOLEAN GetSSDTFunctionAddressByFunctionName(char* FunctionName, PVOID* FunctionAddress); BOOLEAN GetSSDTAddress(ULONG64* SSDTAddress); BOOLEAN GetSSDTFunctionIndexFromNtdllExportTableByFunctionName(CHAR* FunctionName, ULONG32* SSDTFunctionIndex); BOOLEAN MappingPEFileInRing0Space(WCHAR* FileFullPathData, PVOID* MappingModuleBase, PSIZE_T MappingModuleSize); BOOLEAN IsRealProcess(PEPROCESS EProcess); NTSTATUS RtlQueryVirtualMemory(ULONG ProcessID, PVOID BufferData, SIZE_T BufferLength); NTSTATUS ExQueryVirtualMemory(PEPROCESS EProcess, PRTL_PROCESS_MEMORY ProcessMemory, SIZE_T Count); VOID DriverUnload(PDRIVER_OBJECT DriverObject); NTSTATUS PassThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp); NTSTATUS DeviceControlDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp); ULONG_PTR KeGetObjectType(PVOID ObjectBody); PVOID GetFunctionAddressByName(WCHAR *FunctionName); typedef ULONG_PTR (*pfnObGetObjectType)(PVOID ObjectBody); CHAR ChangePreviousMode(PETHREAD EThread); VOID RecoverPreviousMode(PETHREAD EThread, CHAR PreviousMode); typedef NTSTATUS (*pfnNtQueryVirtualMemory)(HANDLE ProcessHandle, PVOID BaseAddress, MEMORY_INFORMATION_CLASS MemoryInformationClass, PVOID MemoryInformation, SIZE_T MemoryInformationLength, PSIZE_T ReturnLength); extern PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader(PVOID BaseAddress); extern POBJECT_TYPE* PsProcessType; typedef struct _READ_OPERATION_ { PVOID BaseAddress; SIZE_T RegionSize; ULONG ProcessID; }READ_OPERATION, *PREAD_OPERATION; NTSTATUS RtlReadVirtualMemory(PVOID InputData, PVOID OutputData, SIZE_T OutputLength); NTSTATUS ExReadVirtualMemory(PREAD_OPERATION InputData, PVOID OutputData, SIZE_T OutputLength); typedef NTSTATUS (*pfnNtProtectVirtualMemory)( IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, IN OUT PSIZE_T RegionSize, IN ULONG NewProtect, OUT PULONG OldProtect); #pragma pack(1) typedef struct _WRITE_OPERATION_ { PVOID BaseAddress; SIZE_T RegionSize; ULONG ProcessID; char* BufferData; }WRITE_OPERATION, *PWRITE_OPERATION; NTSTATUS RtlWriteVirtualMemory(PVOID InputData); NTSTATUS ExWriteVirtualMemory(PWRITE_OPERATION InputData);
kProcessMemory.c
#include "KProcessMemory.h" //bp KProcessMemory!DriverEntry #define PREVIOUSMODE_KTHREAD 0x1f6 #define RIN3_END 0x80000000000 #define SEC_IMAGE 0x001000000 #define MAX_LENGTH 20 pfnNtQueryVirtualMemory __NtQueryVirtualMemory = NULL; pfnNtProtectVirtualMemory __NtProtectVirtualMemory = NULL; NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath) { NTSTATUS Status = STATUS_SUCCESS; PDEVICE_OBJECT DeviceObject = NULL; UNICODE_STRING DeviceName; UNICODE_STRING LinkName; int i = 0; DriverObject->DriverUnload = DriverUnload; RtlInitUnicodeString(&DeviceName, DEVICE_NAME); Status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) { return Status; } RtlInitUnicodeString(&LinkName, LINK_NAME); Status = IoCreateSymbolicLink(&LinkName, &DeviceName); 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] = DeviceControlDispatch; if (GetSSDTFunctionAddressByFunctionName("NtQueryVirtualMemory", (PVOID*)&__NtQueryVirtualMemory) == FALSE) { return Status; } return Status; } BOOLEAN GetSSDTFunctionAddressByFunctionName(char* FunctionName, PVOID* FunctionAddress) { ULONG64 SSDTAddress = 0; PVOID ServiceTableBase = NULL; ULONG32 SSDTFunctionIndex = 0; ULONG32 Offset = 0; ULONG32 v1 = 0; if (GetSSDTAddress(&SSDTAddress) == FALSE) { return FALSE; } ServiceTableBase = ((PSYSTEM_SERVICE_DESCRIPTOR_TABLE)SSDTAddress)->Unknow0; if (GetSSDTFunctionIndexFromNtdllExportTableByFunctionName(FunctionName, &SSDTFunctionIndex) == FALSE) { return FALSE; } Offset = ((PULONG32)ServiceTableBase)[SSDTFunctionIndex]; v1 = Offset >> 4; *FunctionAddress = (PVOID)((PUINT8)ServiceTableBase + v1); } BOOLEAN GetSSDTAddress(ULONG64* SSDTAddress) { //kd> rdmsr c0000082 PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082); PUCHAR EndSearchAddress = StartSearchAddress + PAGE_SIZE; PUCHAR i = NULL; UCHAR v1 = 0, v2 = 0, v3 = 0; INT64 Offset = 0; //002320c7 ULONG64 VariableAddress = 0; *SSDTAddress = NULL; for (i = StartSearchAddress; i < EndSearchAddress; i++) { if (MmIsAddressValid(i) && MmIsAddressValid(i + 1) && MmIsAddressValid(i + 2)) { v1 = *i; v2 = *(i + 1); v3 = *(i + 2); if (v1 == 0x4c && v2 == 0x8d && v3 == 0x15) { memcpy(&Offset, i + 3, 4); *SSDTAddress = Offset + (ULONG64)i + 7; break; } } } if (*SSDTAddress == NULL) { return FALSE; } return TRUE; } BOOLEAN GetSSDTFunctionIndexFromNtdllExportTableByFunctionName(CHAR* FunctionName, ULONG32* SSDTFunctionIndex) { ULONG i; BOOLEAN IsOk = FALSE; WCHAR FileFullPathData[] = L"\\SystemRoot\\System32\\ntdll.dll"; //C:\Windows\ SIZE_T MappingModuleSize = 0; PVOID MappingModuleBase = NULL; PIMAGE_EXPORT_DIRECTORY ImageExportDirectory = NULL; PIMAGE_NT_HEADERS ImageNtHeaders = NULL; UINT32* AddressOfFunctions = NULL; UINT32* AddressOfNames = NULL; UINT16* AddressOfNameOrdinals = NULL; CHAR* v1 = NULL; ULONG32 FunctionOrdinal = 0; PVOID FunctionAddress = 0; ULONG32 Offset_SSDTFunctionIndex = 4; //将Ntdll.dll 当前的空间中 *SSDTFunctionIndex = -1; IsOk = MappingPEFileInRing0Space(FileFullPathData, &MappingModuleBase, &MappingModuleSize); if (IsOk == FALSE) { return FALSE; } else { __try { ImageNtHeaders = RtlImageNtHeader(MappingModuleBase); //extern if (ImageNtHeaders && ImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { ImageExportDirectory = (IMAGE_EXPORT_DIRECTORY*)((UINT8*)MappingModuleBase + ImageNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); AddressOfFunctions = (UINT32*)((UINT8*)MappingModuleBase + ImageExportDirectory->AddressOfFunctions); AddressOfNames = (UINT32*)((UINT8*)MappingModuleBase + ImageExportDirectory->AddressOfNames); AddressOfNameOrdinals = (UINT16*)((UINT8*)MappingModuleBase + ImageExportDirectory->AddressOfNameOrdinals); for (i = 0; i < ImageExportDirectory->NumberOfNames; i++) { v1 = (char*)((ULONG64)MappingModuleBase + AddressOfNames[i]); //获得函数名称 if (_stricmp(FunctionName, v1) == 0) { FunctionOrdinal = AddressOfNameOrdinals[i]; FunctionAddress = (PVOID)((UINT8*)MappingModuleBase + AddressOfFunctions[FunctionOrdinal]); *SSDTFunctionIndex = *(ULONG32*)((UINT8*)FunctionAddress + Offset_SSDTFunctionIndex); break; } } } } __except (EXCEPTION_EXECUTE_HANDLER) { ; } } ZwUnmapViewOfSection(NtCurrentProcess(), MappingModuleBase); //解除映射 if (*SSDTFunctionIndex == -1) { return FALSE; } return TRUE; } BOOLEAN MappingPEFileInRing0Space(WCHAR* FileFullPathData, PVOID* MappingModuleBase, PSIZE_T MappingModuleSize) { NTSTATUS Status; UNICODE_STRING v1; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; HANDLE FileHandle = NULL; HANDLE SectionHandle = NULL; if (!FileFullPathData &&MmIsAddressValid(FileFullPathData)) { return FALSE; } if (!MappingModuleBase&&MmIsAddressValid(MappingModuleBase)) { return FALSE; } RtlInitUnicodeString(&v1, FileFullPathData); InitializeObjectAttributes(&ObjectAttributes, &v1, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL ); //获得文件句柄 Status = IoCreateFile(&FileHandle, GENERIC_READ | SYNCHRONIZE, &ObjectAttributes, //文件绝对路径 &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, CreateFileTypeNone, NULL, IO_NO_PARAMETER_CHECKING ); if (!NT_SUCCESS(Status)) { return FALSE; } ObjectAttributes.ObjectName = NULL; Status = ZwCreateSection(&SectionHandle, SECTION_QUERY | SECTION_MAP_READ, &ObjectAttributes, NULL, PAGE_WRITECOPY, SEC_IMAGE, //内存对齐 0x1000 FileHandle ); ZwClose(FileHandle); if (!NT_SUCCESS(Status)) { return FALSE; } Status = ZwMapViewOfSection(SectionHandle, NtCurrentProcess(), //映射到当前进程的内存空间中 MappingModuleBase, 0, 0, 0, MappingModuleSize, ViewUnmap, 0, PAGE_WRITECOPY ); ZwClose(SectionHandle); if (!NT_SUCCESS(Status)) { return FALSE; } return TRUE; } NTSTATUS DeviceControlDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) { NTSTATUS Status = STATUS_SUCCESS; ULONG IoControlCode = 0; ULONG InputLength = 0; SIZE_T OutputLength = 0; PVOID InputData = NULL; PVOID OutputData = NULL; PIO_STACK_LOCATION IoStackLocation = IoGetCurrentIrpStackLocation(Irp); IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode; //BufferIO //InputData = OutputData = Irp->AssociatedIrp.SystemBuffer; /* 直接方式DO_DIRECT_IO / 非直接方式(缓冲方式)DO_BUFFERD_IO 1) 在buffered(AssociatedIrp.SystemBuffer)方式中,I/O管理器先创建一个与用户模式数据缓冲区大小相等的系统缓冲区。而你的驱动程序将使用这个系统缓冲区工作。 I/O管理器负责在系统缓冲区和用户模式缓冲区之间复制数据。 2) 在direct(MdlAddress)方式中,I/O管理器锁定了包含用户模式缓冲区的物理内存页,并创建一个称为MDL(内存描述符表)的辅助数据结构来描述锁定页。 因此你的驱动程序将使用MDL工作。 3) 在neither(UserBuffer)方式中,I/O管理器仅简单地把用户模式的虚拟地址传递给你。 而使用用户模式地址的驱动程序应十分小心。 */ //Neither方式提高了通信效率,但是不够安全,在读写之前应使用ProbeForRead和ProbeForWrite函数探测地址是可读和可写 //详见eDiary笔记中“Driver——DeviceIoControl函数与IoControlCode” InputData = IoStackLocation->Parameters.DeviceIoControl.Type3InputBuffer;//得到Ring3的输入缓冲区地址 OutputData = Irp->UserBuffer; //得到Ring3的输出缓冲区地址 InputLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength; OutputLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength; switch (IoControlCode) //IO控制码 { case CTL_QUERY_PROCESS_MEMORY: { if (!MmIsAddressValid(OutputData)) { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; break; } if (InputLength != sizeof(ULONG) || InputData == NULL) { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; break; } __try { ProbeForWrite(OutputData, OutputLength, 1); //检测内存是否可写,这个函数需要在用户模式下使用,详见MSDN: //If Irp->RequestorMode = KernelMode, the Irp->AssociatedIrp.SystemBuffer and Irp->UserBuffer fields do not contain user-mode addresses, //and a call to ProbeForWrite to probe a buffer pointed to by either field will raise an exception. Status = RtlQueryVirtualMemory(*(PULONG)InputData, OutputData, OutputLength); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = Status; } __except (EXCEPTION_EXECUTE_HANDLER) { Irp->IoStatus.Information = 0; Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } break; } case CTL_READ_PROCESS_MEMORY: { if (!MmIsAddressValid(OutputData) || OutputLength>MAX_LENGTH) { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; break; } if (InputLength != sizeof(READ_OPERATION) || InputData == NULL) { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; break; } __try { ProbeForWrite(OutputData, OutputLength, 1); Status = RtlReadVirtualMemory(InputData, OutputData, OutputLength); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = Status; } __except (EXCEPTION_EXECUTE_HANDLER) { Irp->IoStatus.Information = 0; Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } break; } case CTL_WRITE_PROCESS_MEMORY: { if (InputLength < sizeof(WRITE_OPERATION) || InputData == NULL) { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; break; } __try { Status = RtlWriteVirtualMemory(InputData); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = Status; } __except (EXCEPTION_EXECUTE_HANDLER) { Irp->IoStatus.Information = 0; Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } break; } default: { Irp->IoStatus.Information = 0; Status = Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; break; } } IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给IO管理器 return Status; } NTSTATUS PassThroughDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); //将Irp返回给IO管理器 return STATUS_SUCCESS; } VOID DriverUnload(PDRIVER_OBJECT DriverObject) { PDEVICE_OBJECT DeviceObject = NULL; PDEVICE_OBJECT v1 = NULL; UNICODE_STRING LinkName; RtlInitUnicodeString(&LinkName, LINK_NAME); IoDeleteSymbolicLink(&LinkName); DeviceObject = DriverObject->DeviceObject; v1 = DeviceObject; while (DeviceObject != NULL) { v1 = DeviceObject->NextDevice; IoDeleteDevice(DeviceObject); DeviceObject = v1; } } NTSTATUS RtlQueryVirtualMemory(ULONG ProcessID, PVOID BufferData, SIZE_T BufferLength) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PEPROCESS EProcess = NULL; SIZE_T Count = 0; if (ProcessID) { Status = PsLookupProcessByProcessId((HANDLE)ProcessID, &EProcess); //通过进程ID获取进程EPROCESS if (!NT_SUCCESS(Status)) { return Status; } } ObfDereferenceObject(EProcess); //减少一个内核对象的引用计数的,否则蓝屏! if (IsRealProcess(EProcess)) //通过进程对象特征码判断EPROCESS是否有效 { Count = (BufferLength - sizeof(RTL_PROCESS_MEMORY)) / sizeof(RTL_PROCESS_MEMORY_INFORMATION); Status = ExQueryVirtualMemory(EProcess, (PRTL_PROCESS_MEMORY)BufferData, Count); if (NT_SUCCESS(Status)) { if (Count >= ((PRTL_PROCESS_MEMORY)BufferData)->NumberOfMemorys) { Status = STATUS_SUCCESS; } else { Status = STATUS_BUFFER_TOO_SMALL; } } } return Status; } NTSTATUS ExQueryVirtualMemory(PEPROCESS EProcess, PRTL_PROCESS_MEMORY ProcessMemory, SIZE_T Count) { NTSTATUS Status = STATUS_UNSUCCESSFUL; HANDLE ProcessHandle = NULL; MEMORY_BASIC_INFORMATION mbi; SIZE_T ReturnLength = 0; //通过EProcess得到进程Handle Status = ObOpenObjectByPointer(EProcess, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, GENERIC_ALL, *PsProcessType, KernelMode, &ProcessHandle); if (NT_SUCCESS(Status)) { PVOID VirtualAddress = 0; PETHREAD EThread = PsGetCurrentThread(); CHAR PreviousMode = ChangePreviousMode(EThread); //EThread KThread 0x1f6 内核模式 while (VirtualAddress < RIN3_END) { Status = __NtQueryVirtualMemory(ProcessHandle, //1.进程句柄 (PVOID)VirtualAddress, //2.被枚举空间的虚拟地址 MemoryBasicInformation, //3.枚举的方式 &mbi, //4.事先准备好用于接收内存信息的缓冲区 sizeof(MEMORY_BASIC_INFORMATION),//缓冲区大小 &ReturnLength); //6.本次调用实际使用缓冲区长度 if (NT_SUCCESS(Status)) { if (Count > ProcessMemory->NumberOfMemorys) { ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].BaseAddress = mbi.BaseAddress; ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].RegionSize = mbi.RegionSize; ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].Protect = mbi.Protect; ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].State = mbi.State; ProcessMemory->Memorys[ProcessMemory->NumberOfMemorys].Type = mbi.Type; } ProcessMemory->NumberOfMemorys++; (PUINT8)VirtualAddress += mbi.RegionSize; } else { (PUINT8)VirtualAddress += PAGE_SIZE; } } NtClose(ProcessHandle); RecoverPreviousMode(EThread, PreviousMode); } DbgPrint("%d\r\n", ProcessMemory->NumberOfMemorys); return STATUS_SUCCESS; } BOOLEAN IsRealProcess(PEPROCESS EProcess) { ULONG_PTR ObjectType; ULONG_PTR ObjectTypeAddress; ULONG_PTR ProcessType = ((ULONG_PTR)*PsProcessType); //系统导出的全局变量 if (ProcessType && EProcess && MmIsAddressValid((PVOID)(EProcess))) { ObjectType = KeGetObjectType((PVOID)EProcess); //通过EProcess 获得进程对象特征码 if (ObjectType && ProcessType == ObjectType) { return TRUE; } } return FALSE; } ULONG_PTR KeGetObjectType(PVOID ObjectBody) { ULONG_PTR ObjectType = NULL; pfnObGetObjectType ObGetObjectType = NULL; if (!MmIsAddressValid || !ObjectBody || !MmIsAddressValid(ObjectBody)) { return NULL; } ObGetObjectType = (pfnObGetObjectType)GetFunctionAddressByName(L"ObGetObjectType"); if (ObGetObjectType) { ObjectType = ObGetObjectType(ObjectBody); } return ObjectType; } PVOID GetFunctionAddressByName(WCHAR *FunctionName) { UNICODE_STRING v1; PVOID FunctionAddress = NULL; if (FunctionName && wcslen(FunctionName) > 0) { RtlInitUnicodeString(&v1, FunctionName); FunctionAddress = MmGetSystemRoutineAddress(&v1); //在系统第一个模块ntoskrnl.exe 导出表中搜索 } return FunctionAddress; } CHAR ChangePreviousMode(PETHREAD EThread) { CHAR PreviousMode = *(PCHAR)((PUINT8)EThread + PREVIOUSMODE_KTHREAD); *(PCHAR)((ULONG_PTR)EThread + PREVIOUSMODE_KTHREAD) = KernelMode; return PreviousMode; } VOID RecoverPreviousMode(PETHREAD EThread, CHAR PreviousMode) { *(PCHAR)((PUINT8)EThread + PREVIOUSMODE_KTHREAD) = PreviousMode; } //读内存 NTSTATUS RtlReadVirtualMemory(PVOID InputData, PVOID OutputData, SIZE_T OutputLength) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PREAD_OPERATION v1 = (PREAD_OPERATION)InputData; if (v1->BaseAddress < RIN3_END && v1->BaseAddress > 0 && v1->ProcessID&&v1->RegionSize <= MAX_LENGTH) { Status = ExReadVirtualMemory(v1, OutputData, OutputLength); } return Status; } NTSTATUS ExReadVirtualMemory(PREAD_OPERATION InputData, PVOID OutputData, SIZE_T OutputLength) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PEPROCESS EProcess; PVOID BufferData = NULL; BOOLEAN IsAttach = FALSE; KAPC_STATE ApcState; if (InputData->ProcessID) { Status = PsLookupProcessByProcessId((HANDLE)InputData->ProcessID, &EProcess); if (NT_SUCCESS(Status) && EProcess != NULL && IsRealProcess(EProcess)) { ObfDereferenceObject(EProcess); BufferData = ExAllocatePool(PagedPool, OutputLength); if (BufferData == NULL) { return STATUS_UNSUCCESSFUL; } memset(BufferData, 0, OutputLength); __try { ULONG32 ProcessID = InputData->ProcessID; PVOID BaseAddress = InputData->BaseAddress; SIZE_T RegionSize = InputData->RegionSize; KeStackAttachProcess(EProcess, &ApcState); IsAttach = TRUE; ProbeForRead(BaseAddress, RegionSize, 1); memcpy(BufferData, (PVOID)BaseAddress, RegionSize); if (IsAttach) { KeUnstackDetachProcess(&ApcState); IsAttach = FALSE; } memcpy(OutputData, BufferData, OutputLength); if (BufferData != NULL) { ExFreePool(BufferData); } Status = STATUS_SUCCESS; } __except (1) { if (IsAttach == TRUE) { KeUnstackDetachProcess(&ApcState); } if (BufferData != NULL) { ExFreePool(BufferData); } Status = STATUS_UNSUCCESSFUL; } } } return Status; } //写内存 NTSTATUS RtlWriteVirtualMemory(PVOID InputData) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PWRITE_OPERATION v1 = (PWRITE_OPERATION)InputData; if (v1->BaseAddress < RIN3_END && v1->BaseAddress > 0 && v1->ProcessID) { Status = ExWriteVirtualMemory(v1); } return Status; } NTSTATUS ExWriteVirtualMemory(PWRITE_OPERATION InputData) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PEPROCESS EProcess; PVOID BufferData = NULL; BOOLEAN IsAttach = FALSE; KAPC_STATE ApcState; PETHREAD EThread; CHAR PreviousMode; HANDLE ProcessHandle = NULL; ULONG OldProtect = 0; if (InputData->ProcessID) { Status = PsLookupProcessByProcessId((HANDLE)InputData->ProcessID, &EProcess); if (NT_SUCCESS(Status) && EProcess &&IsRealProcess(EProcess)) { ObfDereferenceObject(EProcess); BufferData = ExAllocatePool(PagedPool, InputData->RegionSize); if (BufferData == NULL) { return STATUS_UNSUCCESSFUL; } memcpy(BufferData, InputData->BufferData, InputData->RegionSize); __try { PVOID BaseAddress = InputData->BaseAddress; SIZE_T RegionSize = InputData->RegionSize; PVOID v5 = BaseAddress; SIZE_T v7 = RegionSize; KeStackAttachProcess(EProcess, &ApcState); IsAttach = TRUE; __try { memcpy((PVOID)BaseAddress, (PVOID)BufferData, RegionSize); //修改目标进程数据 ReadOnly if (IsAttach) { KeUnstackDetachProcess(&ApcState); IsAttach = FALSE; } if (BufferData != NULL) { ExFreePool(BufferData); } return STATUS_SUCCESS; } __except (1) { if (IsAttach) { KeUnstackDetachProcess(&ApcState); IsAttach = FALSE; } EThread = PsGetCurrentThread(); PreviousMode = ChangePreviousMode(EThread); Status = ObOpenObjectByPointer(EProcess, OBJ_KERNEL_HANDLE, NULL, GENERIC_ALL, *PsProcessType, KernelMode, &ProcessHandle); if (!NT_SUCCESS(Status)) { RecoverPreviousMode(EThread, PreviousMode); if (BufferData != NULL) { ExFreePool(BufferData); } return STATUS_UNSUCCESSFUL; } Status = __NtProtectVirtualMemory(ProcessHandle, &v5, &v7, PAGE_READWRITE, &OldProtect); if (!NT_SUCCESS(Status)) { ZwClose(ProcessHandle); RecoverPreviousMode(EThread, PreviousMode); if (BufferData != NULL) { ExFreePool(BufferData); } return STATUS_UNSUCCESSFUL; } RecoverPreviousMode(EThread, PreviousMode); __try { KeStackAttachProcess(EProcess, &ApcState); IsAttach = TRUE; memcpy((PVOID)BaseAddress, (PVOID)BufferData, RegionSize); //修改目标进程数据 ReadOnly if (IsAttach) { KeUnstackDetachProcess(&ApcState); IsAttach = FALSE; } EThread = PsGetCurrentThread(); PreviousMode = ChangePreviousMode(EThread); Status = __NtProtectVirtualMemory(ProcessHandle, &v5, &v7, OldProtect, NULL); RecoverPreviousMode(EThread, PreviousMode); ZwClose(ProcessHandle); if (BufferData != NULL) { ExFreePool(BufferData); } if (!NT_SUCCESS(Status)) { return STATUS_UNSUCCESSFUL; } return STATUS_SUCCESS; } __except (1) { if (IsAttach) { KeUnstackDetachProcess(&ApcState); IsAttach = FALSE; } if (BufferData != NULL) { ExFreePool(BufferData); } return STATUS_UNSUCCESSFUL; } } } __except (1) { if (IsAttach) { KeUnstackDetachProcess(&ApcState); IsAttach = FALSE; } if (BufferData != NULL) { ExFreePool(BufferData); } return STATUS_UNSUCCESSFUL; } } } return STATUS_UNSUCCESSFUL; }