“進程內存管理器”這個程序實現的最基本功能也就是對內存的讀寫,之前的兩篇文章也就是做的一個鋪墊,介紹了內核模式切換和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; }