如前所述,在前幾章內容中筆者簡單介紹了記憶體讀寫
的基本實作方式,這其中包括了CR3切換
讀寫,MDL映射
讀寫,記憶體拷貝
讀寫,本章將在如前所述的讀寫函式進一步封裝,并以此來實作驅動讀寫記憶體浮點數的目的,記憶體浮點數
的讀寫依賴于讀寫記憶體位元組
的實作,因為浮點數本質上也可以看作是一個位元組集,對于單精度浮點數
來說這個位元組集串列是4位元組,而對于雙精度浮點數
,此串列長度則為8位元組,
如下代碼片段摘取自本人的LyMemory
驅動讀寫專案,函式ReadProcessMemoryByte
用于讀取記憶體特定位元組型別的資料,函式WriteProcessMemoryByte
則用于寫入位元組型別資料,完整代碼如下所示;
這段代碼中依然采用了《驅動開發:內核MDL讀寫行程記憶體》
中所示的讀寫方法,通過MDL附加到行程并RtlCopyMemory
拷貝資料,至于如何讀寫位元組集只需要回圈讀寫即可實作;
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <windef.h>
// 讀取記憶體位元組
BYTE ReadProcessMemoryByte(HANDLE Pid, ULONG64 Address, DWORD Size)
{
KAPC_STATE state = { 0 };
BYTE OpCode;
PEPROCESS Process;
PsLookupProcessByProcessId((HANDLE)Pid, &Process);
// 系結行程物件,進入行程地址空間
KeStackAttachProcess(Process, &state);
__try
{
// ProbeForRead 檢查記憶體地址是否有效, RtlCopyMemory 讀取記憶體
ProbeForRead((HANDLE)Address, Size, 1);
RtlCopyMemory(&OpCode, (BYTE *)Address, Size);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// 呼叫KeUnstackDetachProcess解除與行程的系結,退出行程地址空間
KeUnstackDetachProcess(&state);
// 讓內核物件參考數減1
ObDereferenceObject(Process);
// DbgPrint("讀取行程 %d 的地址 %x 出錯", ptr->Pid, ptr->Address);
return FALSE;
}
// 解除系結
KeUnstackDetachProcess(&state);
// 讓內核物件參考數減1
ObDereferenceObject(Process);
DbgPrint("[內核讀位元組] # 讀取地址: 0x%x 讀取資料: %x \n", Address, OpCode);
return OpCode;
}
// 寫入記憶體位元組
BOOLEAN WriteProcessMemoryByte(HANDLE Pid, ULONG64 Address, DWORD Size, BYTE *OpCode)
{
KAPC_STATE state = { 0 };
PEPROCESS Process;
PsLookupProcessByProcessId((HANDLE)Pid, &Process);
// 系結行程,進入行程的地址空間
KeStackAttachProcess(Process, &state);
// 創建MDL地址描述符
PMDL mdl = IoAllocateMdl((HANDLE)Address, Size, 0, 0, NULL);
if (mdl == NULL)
{
return FALSE;
}
//使MDL與驅動進行系結
MmBuildMdlForNonPagedPool(mdl);
BYTE* ChangeData = https://www.cnblogs.com/LyShark/p/NULL;
__try
{
// 將MDL映射到我們驅動里的一個變數,對該變數讀寫就是對MDL對應的物理記憶體讀寫
ChangeData = (BYTE *)MmMapLockedPages(mdl, KernelMode);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
// DbgPrint("映射記憶體失敗");
IoFreeMdl(mdl);
// 解除映射
KeUnstackDetachProcess(&state);
// 讓內核物件參考數減1
ObDereferenceObject(Process);
return FALSE;
}
// 寫入資料到指定位置
RtlCopyMemory(ChangeData, OpCode, Size);
DbgPrint("[內核寫位元組] # 寫入地址: 0x%x 寫入資料: %x \n", Address, OpCode);
// 讓內核物件參考數減1
ObDereferenceObject(Process);
MmUnmapLockedPages(ChangeData, mdl);
KeUnstackDetachProcess(&state);
return TRUE;
}
實作讀取記憶體位元組集并將讀入的資料放入到LySharkReadByte
位元組串列中,這段代碼如下所示,通過呼叫ReadProcessMemoryByte
都記憶體位元組并每次0x401000 + i
在基址上面增加變數i以此來實作位元組集讀取;
// 驅動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark \n");
// 讀記憶體位元組集
BYTE LySharkReadByte[8] = { 0 };
for (size_t i = 0; i < 8; i++)
{
LySharkReadByte[i] = ReadProcessMemoryByte(4884, 0x401000 + i, 1);
}
// 輸出讀取的記憶體位元組
for (size_t i = 0; i < 8; i++)
{
DbgPrint("[+] 列印資料: %x \n", LySharkReadByte[i]);
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行如上代碼片段,你會看到如下圖所示的讀取效果;
那么如何實作寫記憶體位元組集呢?其實寫入記憶體位元組集與讀取基本類似,通過填充LySharkWriteByte
位元組集串列,并呼叫WriteProcessMemoryByte
函式依次回圈位元組集串列即可實作寫出位元組集的目的;
// 驅動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark \n");
// 記憶體寫位元組集
BYTE LySharkWriteByte[8] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
for (size_t i = 0; i < 8; i++)
{
BOOLEAN ref = WriteProcessMemoryByte(4884, 0x401000 + i, 1, LySharkWriteByte[i]);
DbgPrint("[*] 寫出狀態: %d \n", ref);
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行如上代碼片段,即可將LySharkWriteByte[8]
中的位元組集寫出到記憶體0x401000 + i
的位置處,輸出效果圖如下所示;
接下來不如本章的重點內容,首先如何實作讀記憶體單精度與雙精度
浮點數的目的,實作原理是通過讀取BYTE型別的前4或者8位元組的資料,并通過*((FLOAT*)buffpyr)
將其轉換為浮點數,通過此方法即可實作位元組集到浮點數的轉換,而決定是單精度還是雙精度則只是一個位元組集長度問題,這段讀寫代碼實作原理如下所示;
// 讀記憶體單精度浮點數
FLOAT ReadProcessFloat(DWORD Pid, ULONG64 Address)
{
BYTE buff[4] = { 0 };
BYTE* buffpyr = buff;
for (DWORD x = 0; x < 4; x++)
{
BYTE item = ReadProcessMemoryByte(Pid, Address + x, 1);
buff[x] = item;
}
return *((FLOAT*)buffpyr);
}
// 讀記憶體雙精度浮點數
DOUBLE ReadProcessMemoryDouble(DWORD Pid, ULONG64 Address)
{
BYTE buff[8] = { 0 };
BYTE* buffpyr = buff;
for (DWORD x = 0; x < 8; x++)
{
BYTE item = ReadProcessMemoryByte(Pid, Address + x, 1);
buff[x] = item;
}
return *((DOUBLE*)buffpyr);
}
// 驅動卸載例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("Uninstall Driver \n");
}
// 驅動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark \n");
// 讀取單精度
FLOAT fl = ReadProcessFloat(4884, 0x401000);
DbgPrint("[讀取單精度] = %d \n", fl);
// 讀取雙精度浮點數
DOUBLE fl = ReadProcessMemoryDouble(4884, 0x401000);
DbgPrint("[讀取雙精度] = %d \n", fl);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
如上代碼就是實作浮點數
讀寫的關鍵所在,這段代碼中的浮點數
傳值如果在內核中會提示無法決議的外部符號 _fltused
此處只用于演示核心原理,如果想要實作不報錯,該代碼中的傳值操作應在應用層進行,而傳入引數也應改為位元組型別即可,
同理,對于寫記憶體浮點數而言依舊如此,只是在接收到用戶層傳遞引數后應對其dtoc
雙精度浮點數轉為CHAR或者ftoc
單精度浮點數轉為CHAR型別,再寫出即可;
// 將DOUBLE適配為合適的Char型別
VOID dtoc(double dvalue, unsigned char* arr)
{
unsigned char* pf;
unsigned char* px;
unsigned char i;
// unsigned char型指標取得浮點數的首地址
pf = (unsigned char*)&dvalue;
// 字符陣列arr準備存盤浮點數的四個位元組,px指標指向位元組陣列arr
px = arr;
for (i = 0; i < 8; i++)
{
// 使用unsigned char型指標從低地址一個位元組一個位元組取出
*(px + i) = *(pf + i);
}
}
// 將Float適配為合適的Char型別
VOID ftoc(float fvalue, unsigned char* arr)
{
unsigned char* pf;
unsigned char* px;
unsigned char i;
// unsigned char型指標取得浮點數的首地址
pf = (unsigned char*)&fvalue;
// 字符陣列arr準備存盤浮點數的四個位元組,px指標指向位元組陣列arr
px = arr;
for (i = 0; i < 4; i++)
{
// 使用unsigned char型指標從低地址一個位元組一個位元組取出
*(px + i) = *(pf + i);
}
}
// 寫記憶體單精度浮點數
BOOL WriteProcessMemoryFloat(DWORD Pid, ULONG64 Address, FLOAT write)
{
BYTE buff[4] = { 0 };
ftoc(write, buff);
for (DWORD x = 0; x < 4; x++)
{
BYTE item = WriteProcessMemoryByte(Pid, Address + x, buff[x], 1);
buff[x] = item;
}
return TRUE;
}
// 寫記憶體雙精度浮點數
BOOL WriteProcessMemoryDouble(DWORD Pid, ULONG64 Address, DOUBLE write)
{
BYTE buff[8] = { 0 };
dtoc(write, buff);
for (DWORD x = 0; x < 8; x++)
{
BYTE item = WriteProcessMemoryByte(Pid, Address + x, buff[x], 1);
buff[x] = item;
}
return TRUE;
}
// 驅動卸載例程
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint("Uninstall Driver \n");
}
// 驅動入口地址
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("Hello LyShark \n");
// 寫單精度
FLOAT LySharkFloat1 = 12.5;
INT fl = WriteProcessMemoryFloat(4884, 0x401000, LySharkFloat1);
DbgPrint("[寫單精度] = %d \n", fl);
// 讀取雙精度浮點數
DOUBLE LySharkFloat2 = 12.5;
INT d1 = WriteProcessMemoryDouble(4884, 0x401000, LySharkFloat2);
DbgPrint("[寫雙精度] = %d \n", d1);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/553798.html
標籤:C
上一篇:c++11: all_of 、 any_of 和 none_of
下一篇:返回列表