驅(qū)動開發(fā):內(nèi)核讀寫內(nèi)存多級偏移
讓我們繼續(xù)在《內(nèi)核讀寫內(nèi)存浮點數(shù)》的基礎(chǔ)之上做一個簡單的延申,如何實現(xiàn)多級偏移讀寫,其實很簡單,讀寫函數(shù)無需改變,只是在讀寫之前提前做好計算工作,以此來得到一個內(nèi)存偏移值,并通過調(diào)用內(nèi)存寫入原函數(shù)實現(xiàn)寫出數(shù)據(jù)的目的。
以讀取偏移內(nèi)存為例,如下代碼同樣來源于本人的LyMemory讀寫驅(qū)動項目,其中核心函數(shù)為WIN10_ReadDeviationIntMemory()該函數(shù)的主要作用是通過用戶傳入的基地址與偏移值,動態(tài)計算出當前的動態(tài)地址。
函數(shù)首先將基地址指向要讀取的變量,并將其轉(zhuǎn)換為LPCVOID類型的指針。然后將指向變量值的緩沖區(qū)轉(zhuǎn)換為LPVOID類型的指針。接下來,函數(shù)使用PsLookupProcessByProcessId函數(shù)查找目標進程并返回其PEPROCESS結(jié)構(gòu)體。隨后,函數(shù)從偏移地址數(shù)組的最后一個元素開始迭代,每次循環(huán)都從目標進程中讀取4字節(jié)整數(shù)型數(shù)據(jù),并將其存儲在Value變量中。然后,函數(shù)將基地址指向Value和偏移地址的和,以便在下一次循環(huán)中讀取更深層次的變量。最后,函數(shù)將基地址指向最終變量的地址,讀取變量的值,并返回。
如下案例所示,用戶傳入進程基址以及offset偏移值時,只需要動態(tài)計算出該偏移地址,并與基址相加即可得到動態(tài)地址。
#include <ntifs.h>
#include <ntintsafe.h>
#include <windef.h>
// 普通Ke內(nèi)存讀取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
PEPROCESS SourceProcess = Process;
PEPROCESS TargetProcess = PsGetCurrentProcess();
SIZE_T Result;
if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
return STATUS_SUCCESS;
else
return STATUS_ACCESS_DENIED;
}
// 讀取整數(shù)內(nèi)存多級偏移
/*
Pid: 目標進程的進程ID。
Base: 變量的基地址。
offset: 相對基地址的多級偏移地址,用于定位變量。
len: 偏移地址的數(shù)量。
*/
INT64 WIN10_ReadDeviationIntMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len)
{
INT64 Value = 0;
LPCVOID pbase = (LPCVOID)Base;
LPVOID rbuffer = (LPVOID)&Value;
PEPROCESS Process;
PsLookupProcessByProcessId((HANDLE)Pid, &Process);
for (int x = len - 1; x >= 0; x--)
{
__try
{
KeReadProcessMemory(Process, pbase, rbuffer, 4);
pbase = (LPCVOID)(Value + offset[x]);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
}
__try
{
DbgPrint("讀取基址:%x \n", pbase);
KeReadProcessMemory(Process, pbase, rbuffer, 4);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
return Value;
}
// 驅(qū)動卸載例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("Uninstall Driver \n");
}
// 驅(qū)動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark \n");
DWORD PID = 4884;
LONG PBase = 0x6566e0;
LONG Size = 4;
DWORD Offset[32] = { 0 };
Offset[0] = 0x18;
Offset[1] = 0x0;
Offset[2] = 0x14;
Offset[3] = 0x0c;
// 讀取內(nèi)存數(shù)據(jù)
INT64 read = WIN10_ReadDeviationIntMemory(PID, PBase, Offset, Size);
DbgPrint("PID: %d 基址: %p 偏移長度: %d \n", PID, PBase, Size);
DbgPrint("[+] 1級偏移: %x \n", Offset[0]);
DbgPrint("[+] 2級偏移: %x \n", Offset[1]);
DbgPrint("[+] 3級偏移: %x \n", Offset[2]);
DbgPrint("[+] 4級偏移: %x \n", Offset[3]);
DbgPrint("[ReadMemory] 讀取偏移數(shù)據(jù): %d \n", read);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
編譯并運行如上這段代碼,則可獲取到PID=4884的PBase的動態(tài)地址中的數(shù)據(jù),如下圖所示;
至于如何將數(shù)據(jù)寫出四級偏移的基址上面,則只需要取出pbase里面的基址,并通過原函數(shù)WIN10_WriteProcessMemory直接寫出數(shù)據(jù)即可,此出的原函數(shù)在《內(nèi)核MDL讀寫進程內(nèi)存》中已經(jīng)做了詳細介紹,實現(xiàn)寫出代碼如下所示;
#include <ntifs.h>
#include <ntintsafe.h>
#include <windef.h>
// 普通Ke內(nèi)存讀取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
PEPROCESS SourceProcess = Process;
PEPROCESS TargetProcess = PsGetCurrentProcess();
SIZE_T Result;
if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
return STATUS_SUCCESS;
else
return STATUS_ACCESS_DENIED;
}
// Win10 內(nèi)存寫入函數(shù)
BOOLEAN WIN10_WriteProcessMemory(HANDLE Pid, PVOID Address, SIZE_T BYTE_size, PVOID VirtualAddress)
{
PVOID buff1;
VOID *buff2;
int MemoryNumerical = 0;
KAPC_STATE KAPC = { 0 };
PEPROCESS Process;
PsLookupProcessByProcessId((HANDLE)Pid, &Process);
__try
{
//分配內(nèi)存
buff1 = ExAllocatePoolWithTag((POOL_TYPE)0, BYTE_size, 1997);
buff2 = buff1;
*(int*)buff1 = 1;
if (MmIsAddressValid((PVOID)VirtualAddress))
{
// 復(fù)制內(nèi)存
memcpy(buff2, VirtualAddress, BYTE_size);
}
else
{
return FALSE;
}
// 附加到要讀寫的進程
KeStackAttachProcess((PRKPROCESS)Process, &KAPC);
if (MmIsAddressValid((PVOID)Address))
{
// 判斷地址是否可寫
ProbeForWrite(Address, BYTE_size, 1);
// 復(fù)制內(nèi)存
memcpy(Address, buff2, BYTE_size);
}
else
{
return FALSE;
}
// 剝離附加的進程
KeUnstackDetachProcess(&KAPC);
ExFreePoolWithTag(buff2, 1997);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return FALSE;
}
return FALSE;
}
// 寫入整數(shù)內(nèi)存多級偏移
INT64 WIN10_WriteDeviationIntMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len, INT64 SetValue)
{
INT64 Value = 0;
LPCVOID pbase = (LPCVOID)Base;
LPVOID rbuffer = (LPVOID)&Value;
PEPROCESS Process;
PsLookupProcessByProcessId((HANDLE)Pid, &Process);
for (int x = len - 1; x >= 0; x--)
{
__try
{
KeReadProcessMemory(Process, pbase, rbuffer, 4);
pbase = (LPCVOID)(Value + offset[x]);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
}
__try
{
KeReadProcessMemory(Process, pbase, rbuffer, 4);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
// 使用原函數(shù)寫入
BOOLEAN ref = WIN10_WriteProcessMemory(Pid, (void *)pbase, 4, &SetValue);
if (ref == TRUE)
{
DbgPrint("[內(nèi)核寫成功] # 寫入地址: %x \n", pbase);
return 1;
}
return 0;
}
// 驅(qū)動卸載例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("Uninstall Driver \n");
}
// 驅(qū)動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark \n");
DWORD PID = 4884;
LONG PBase = 0x6566e0;
LONG Size = 4;
INT64 SetValue = 100;
DWORD Offset[32] = { 0 };
Offset[0] = 0x18;
Offset[1] = 0x0;
Offset[2] = 0x14;
Offset[3] = 0x0c;
// 寫出內(nèi)存數(shù)據(jù)
INT64 write = WIN10_WriteDeviationIntMemory(PID, PBase, Offset, Size, SetValue);
DbgPrint("PID: %d 基址: %p 偏移長度: %d \n", PID, PBase, Size);
DbgPrint("[+] 1級偏移: %x \n", Offset[0]);
DbgPrint("[+] 2級偏移: %x \n", Offset[1]);
DbgPrint("[+] 3級偏移: %x \n", Offset[2]);
DbgPrint("[+] 4級偏移: %x \n", Offset[3]);
DbgPrint("[WriteMemory] 寫出偏移數(shù)據(jù): %d \n", SetValue);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行如上代碼將在0x6566e0所在的基址上,將數(shù)據(jù)替換為100,實現(xiàn)效果圖如下所示;
那么如何實現(xiàn)讀寫內(nèi)存浮點數(shù),字節(jié)集等多級偏移呢?
其實我們可以封裝一個WIN10_ReadDeviationMemory函數(shù),讓其只計算得出偏移地址,而所需要寫出的類型則根據(jù)自己的實際需求配合不同的寫入函數(shù)完成,也就是將兩者分離開,如下則是一段實現(xiàn)計算偏移的代碼片段,該代碼同樣來自于本人的LyMemory驅(qū)動讀寫項目;
#include <ntifs.h>
#include <ntintsafe.h>
#include <windef.h>
// 普通Ke內(nèi)存讀取
NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID SourceAddress, PVOID TargetAddress, SIZE_T Size)
{
PEPROCESS SourceProcess = Process;
PEPROCESS TargetProcess = PsGetCurrentProcess();
SIZE_T Result;
if (NT_SUCCESS(MmCopyVirtualMemory(SourceProcess, SourceAddress, TargetProcess, TargetAddress, Size, KernelMode, &Result)))
return STATUS_SUCCESS;
else
return STATUS_ACCESS_DENIED;
}
// 讀取多級偏移內(nèi)存動態(tài)地址
DWORD64 WIN10_ReadDeviationMemory(HANDLE Pid, LONG Base, DWORD offset[32], DWORD len)
{
INT64 Value = 0;
LPCVOID pbase = (LPCVOID)Base;
LPVOID rbuffer = (LPVOID)&Value;
PEPROCESS Process;
PsLookupProcessByProcessId((HANDLE)Pid, &Process);
for (int x = len - 1; x >= 0; x--)
{
__try
{
KeReadProcessMemory(Process, pbase, rbuffer, 4);
pbase = (LPCVOID)(Value + offset[x]);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
}
return pbase;
}
// 驅(qū)動卸載例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("Uninstall Driver \n");
}
// 驅(qū)動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark \n");
DWORD PID = 4884;
LONG PBase = 0x6566e0;
LONG Size = 4;
DWORD Offset[32] = { 0 };
Offset[0] = 0x18;
Offset[1] = 0x0;
Offset[2] = 0x14;
Offset[3] = 0x0c;
// 寫出內(nèi)存數(shù)據(jù)
DWORD64 offsets = WIN10_ReadDeviationMemory(PID, PBase, Offset, Size);
DbgPrint("PID: %d 基址: %p 偏移長度: %d \n", PID, PBase, Size);
DbgPrint("[+] 1級偏移: %x \n", Offset[0]);
DbgPrint("[+] 2級偏移: %x \n", Offset[1]);
DbgPrint("[+] 3級偏移: %x \n", Offset[2]);
DbgPrint("[+] 4級偏移: %x \n", Offset[3]);
DbgPrint("[CheckMemory] 計算偏移地址: %x \n", offsets);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}