3.3 Windows驅(qū)動(dòng)開發(fā):內(nèi)核MDL讀寫進(jìn)程內(nèi)存

mdl內(nèi)存讀寫是通過創(chuàng)建mdl結(jié)構(gòu)體實(shí)現(xiàn)跨進(jìn)程內(nèi)存讀寫的一種方法。在windows操作系統(tǒng)中,每個(gè)進(jìn)程擁有獨(dú)立的虛擬地址空間,不同進(jìn)程的內(nèi)存空間是隔離的。因此,要在一個(gè)進(jìn)程中讀取或?qū)懭肓硪粋€(gè)進(jìn)程的內(nèi)存數(shù)據(jù),首先需要將目標(biāo)進(jìn)程的物理內(nèi)存映射到當(dāng)前進(jìn)程的虛擬地址空間中,然后才能進(jìn)行內(nèi)存讀寫操作。

MDL結(jié)構(gòu)體是windows內(nèi)核中專門用于描述物理內(nèi)存的數(shù)據(jù)結(jié)構(gòu),包含了物理地址、長度、內(nèi)存映射的虛擬地址等信息。通過創(chuàng)建MDL結(jié)構(gòu)體并調(diào)用系統(tǒng)函數(shù)將其映射到當(dāng)前進(jìn)程的虛擬地址空間中,可以實(shí)現(xiàn)跨進(jìn)程內(nèi)存讀寫操作。

與CR3切換方式相比,MDL內(nèi)存讀寫更穩(wěn)定、安全,且不受寄存器影響。同時(shí),MDL內(nèi)存讀寫方式還能充分利用Windows操作系統(tǒng)的內(nèi)存管理機(jī)制,實(shí)現(xiàn)更高效的內(nèi)存讀寫操作。因此,MDL內(nèi)存讀寫是Windows操作系統(tǒng)中最常用和推薦的跨進(jìn)程內(nèi)存讀寫方式。

3.1.1 MDL讀取內(nèi)存步驟

  1. 調(diào)用PsLookupProcessByProcessId獲取進(jìn)程Process結(jié)構(gòu):該函數(shù)根據(jù)進(jìn)程ID查找對應(yīng)的進(jìn)程對象,通過data->pid獲取進(jìn)程ID,然后調(diào)用PsLookupProcessByProcessId獲取PEPROCESS結(jié)構(gòu)。如果獲取失敗,則返回FALSE。
  2. 調(diào)用KeStackAttachProcess附加到目標(biāo)進(jìn)程:在內(nèi)核模式下,讀取其他進(jìn)程的內(nèi)存需要先附加到對應(yīng)進(jìn)程的上下文中。調(diào)用KeStackAttachProcess函數(shù)將當(dāng)前線程切換到目標(biāo)進(jìn)程的上下文,同時(shí)保存當(dāng)前進(jìn)程的上下文狀態(tài)。
  3. 調(diào)用ProbeForRead檢查內(nèi)存是否可讀寫:在內(nèi)核模式下,訪問其他進(jìn)程的內(nèi)存需要保證訪問合法,因此調(diào)用ProbeForRead函數(shù)檢查讀取的內(nèi)存空間是否可讀寫。如果不可讀寫,則會(huì)觸發(fā)異常,通過異常處理機(jī)制處理這種情況。
  4. 將內(nèi)存空間中的數(shù)據(jù)拷貝到自己的緩沖區(qū):完成內(nèi)存空間檢查后,使用RtlCopyMemory函數(shù)將目標(biāo)進(jìn)程的內(nèi)存數(shù)據(jù)拷貝到自己的緩沖區(qū)中。由于內(nèi)存空間可能很大,可能需要多次拷貝操作。
  5. 調(diào)用KeUnstackDetachProcess解除綁定:讀取完內(nèi)存數(shù)據(jù)后,需要將當(dāng)前線程從目標(biāo)進(jìn)程的上下文中解除綁定,返回到原來的上下文中。調(diào)用KeUnstackDetachProcess函數(shù)完成解綁操作,同時(shí)恢復(fù)之前保存的當(dāng)前進(jìn)程的上下文狀態(tài)。
  6. 調(diào)用ObDereferenceObject減少對象引用數(shù):由于第一步中調(diào)用了PsLookupProcessByProcessId函數(shù)獲取了對應(yīng)進(jìn)程的PEPROCESS結(jié)構(gòu),因此需要調(diào)用ObDereferenceObject函數(shù)將其引用計(jì)數(shù)減1,以便釋放對該對象的引用。

通過上述步驟,我們可以封裝MDLReadMemory()內(nèi)存讀函數(shù),代碼如下,該函數(shù)用于在Windows內(nèi)核模式下讀取指定進(jìn)程的內(nèi)存數(shù)據(jù):

#include <ntifs.h> #include <windef.h> <p>typedef struct { DWORD pid;                // 要讀寫的進(jìn)程ID DWORD64 address;          // 要讀寫的地址 DWORD size;               // 讀寫長度 BYTE* data;               // 要讀寫的數(shù)據(jù) } ReadMemoryStruct;</p><p>// MDL讀內(nèi)存 BOOL MDLReadMemory(ReadMemoryStruct<em> data) { BOOL bRet = TRUE; PEPROCESS process = NULL; PsLookupProcessByProcessId(data->pid, &process); if (process == NULL) { return FALSE; } BYTE</em> GetData; <strong>try { GetData = ExAllocatePool(PagedPool, data->size); } </strong>except (1) { return FALSE; } KAPC_STATE stack = { 0 }; KeStackAttachProcess(process, &stack); <strong>try { ProbeForRead(data->address, data->size, 1); RtlCopyMemory(GetData, data->address, data->size); } </strong>except (1) { bRet = FALSE; } ObDereferenceObject(process); KeUnstackDetachProcess(&stack); RtlCopyMemory(data->data, GetData, data->size); ExFreePool(GetData); return bRet; }</p><p>VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint(("Uninstall Driver Is OK n")); }</p><p>NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint(("hello lyshark n")); ReadMemoryStruct ptr; ptr.pid = 6672; ptr.address = 0x402c00; ptr.size = 100; // 分配空間接收數(shù)據(jù) ptr.data = ExAllocatePool(PagedPool, ptr.size); // 讀內(nèi)存 MDLReadMemory(&ptr); // 輸出數(shù)據(jù) for (size_t i = 0; i < ptr.size; i++) { DbgPrint(("%02x ", ptr.data[i])); } DbgPrint(("n")); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }

讀取內(nèi)存地址0x402c00效果如下所示:

3.3 Windows驅(qū)動(dòng)開發(fā):內(nèi)核MDL讀寫進(jìn)程內(nèi)存

3.1.2 MDL寫入內(nèi)存步驟

  1. 調(diào)用PsLookupProcessByProcessId獲取目標(biāo)進(jìn)程的進(jìn)程結(jié)構(gòu):該函數(shù)根據(jù)傳遞的進(jìn)程ID返回對應(yīng)進(jìn)程的PEPROCESS結(jié)構(gòu)體,包含了進(jìn)程的各種信息。
  2. 使用KeStackAttachProcess附加到目標(biāo)進(jìn)程的上下文環(huán)境:該函數(shù)將當(dāng)前線程的上下文環(huán)境切換到目標(biāo)進(jìn)程的上下文環(huán)境,使得該線程可以訪問和修改目標(biāo)進(jìn)程的內(nèi)存。
  3. 調(diào)用ProbeForRead檢查要寫入的內(nèi)存空間是否可讀寫:這個(gè)步驟是為了確保要寫入的內(nèi)存空間沒有被保護(hù)或被其他進(jìn)程占用,以避免對系統(tǒng)造成不良影響。
  4. 將目標(biāo)進(jìn)程的內(nèi)存空間中的數(shù)據(jù)拷貝到當(dāng)前進(jìn)程的緩沖區(qū):便于進(jìn)行修改操作。
  5. 調(diào)用MmMapLockedPages鎖定當(dāng)前內(nèi)存頁面:該函數(shù)將返回一個(gè)指向系統(tǒng)虛擬地址的指針,該地址是由系統(tǒng)自動(dòng)分配的。在寫入完成后,需要使用MmUnmapLockedPages函數(shù)來釋放鎖定的內(nèi)存頁面。
  6. 使用RtlCopyMemory完成內(nèi)存拷貝操作:將緩沖區(qū)中的數(shù)據(jù)寫入到鎖定的內(nèi)存頁面中。
  7. 調(diào)用IoFreeMdl釋放MDL鎖:MDL鎖用于鎖定MDL描述的內(nèi)存頁面,以便可以對其進(jìn)行操作。
  8. 使用KeUnstackDetachProcess解除當(dāng)前進(jìn)程與目標(biāo)進(jìn)程之間的綁定:使得當(dāng)前線程的上下文環(huán)境恢復(fù)到原始的狀態(tài)。
  9. 調(diào)用ObDereferenceObject將MDL對象的引用計(jì)數(shù)減1:便于在不再需要該對象時(shí)釋放它所占用的系統(tǒng)資源。

從上述分析來看,寫入操作與讀取操作基本類似,只是多了鎖定頁面和解鎖操作。MDL寫內(nèi)存的完整實(shí)現(xiàn)代碼如下所示:

#include <ntifs.h></p><h1>include <windef.h></h1><p>typedef struct { DWORD pid;                // 要讀寫的進(jìn)程ID DWORD64 address;          // 要讀寫的地址 DWORD size;               // 讀寫長度 BYTE* data;               // 要讀寫的數(shù)據(jù) } WriteMemoryStruct;</p><p>// MDL寫內(nèi)存 BOOL MDLWriteMemory(WriteMemoryStruct<em> data) { BOOL bRet = TRUE; PEPROCESS process = NULL; PsLookupProcessByProcessId(data->pid, &process); if (process == NULL) { return FALSE; } BYTE</em> GetData; <strong>try { GetData = ExAllocatePool(PagedPool, data->size); } </strong>except (1) { return FALSE; } for (int i = 0; i < data->size; i++) { GetData[i] = data->data[i]; } KAPC_STATE stack = { 0 }; KeStackAttachProcess(process, &stack); PMDL mdl = IoAllocateMdl(data->address, data->size, 0, 0, NULL); if (mdl == NULL) { return FALSE; } MmBuildMdlForNonPagedPool(mdl); BYTE* ChangeData = NULL; <strong>try { ChangeData = MmMapLockedPages(mdl, KernelMode); RtlCopyMemory(ChangeData, GetData, data->size); } </strong>except (1) { bRet = FALSE; goto END; } END: IoFreeMdl(mdl); ExFreePool(GetData); KeUnstackDetachProcess(&stack); ObDereferenceObject(process); return bRet; }</p><p>VOID UnDriver(PDRIVER_OBJECT driver) { DbgPrint(("Uninstall Driver Is OK n")); }</p><p>NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint(("hello lyshark n")); WriteMemoryStruct ptr; ptr.pid = 6672; ptr.address = 0x402c00; ptr.size = 5; // 需要寫入的數(shù)據(jù) ptr.data = ExAllocatePool(PagedPool, ptr.size); // 循環(huán)設(shè)置 for (size_t i = 0; i < ptr.size; i++) { ptr.data[i] = 0x90; } // 寫內(nèi)存 MDLWriteMemory(&ptr); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; }

寫出效果如下:

3.3 Windows驅(qū)動(dòng)開發(fā):內(nèi)核MDL讀寫進(jìn)程內(nèi)存

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊8 分享