在開始學習內核記憶體讀寫篇之前,我們先來實作一個簡單的記憶體分配銷毀堆的功能,在內核空間內用戶依然可以動態的申請與銷毀一段可控的堆空間,一般而言內核中提供了ZwAllocateVirtualMemory這個函式用于專門分配虛擬空間,而與之相對應的則是ZwFreeVirtualMemory此函式則用于銷毀堆記憶體,當我們需要分配內核空間時往往需要切換到對端行程堆疊上再進行操作,接下來LyShark將從API開始介紹如何運用這兩個函式實作記憶體分配與使用,并以此來作為驅動讀寫篇的入門知識,
首先以記憶體分配為例ZwAllocateVirtualMemory()函式,該系列函式在ntifs.h頭檔案內,且如果需要使用則最好提前在程式頭部進行宣告,該函式的微軟官方定義如下所示;
NTSYSAPI NTSTATUS ZwAllocateVirtualMemory(
[in] HANDLE ProcessHandle, // 行程句柄
[in, out] PVOID *BaseAddress, // 指向將接收已分配頁面區域基址的變數的指標
[in] ULONG_PTR ZeroBits, // 節視圖基址中必須為零的高順序地址位數
[in, out] PSIZE_T RegionSize, // 指向將接收已分配頁面區域的實際大小
[in] ULONG AllocationType, // 包含指定要執行的分配型別的標志的位掩碼
[in] ULONG Protect // 包含頁面保護標志的位掩碼
);
引數ProcessHandle用于傳入一個行程句柄此處我們可以通過NtCurrentProcess()獲取到當前自身行程的句柄,
引數BaseAddress則用于接收分配堆地址的首地址,此處指向將接收已分配頁面區域基址的變數的指標,
引數RegionSize則用于指定需要分配的記憶體空間大小,此引數的初始值指定區域的大小(以位元組為單位)并向上舍入到下一個主機頁大小邊界,
引數AllocationType用于指定分配記憶體的屬性,通常我們會用到的只有兩種MEM_COMMIT指定為提交型別,MEM_PHYSICAL則用于分配物理記憶體,此標志僅用于地址視窗擴展AWE記憶體, 如果設定了MEM_PHYSICAL則還必須設定MEM_RESERVE不能設定其他標志,并且必須將保護設定為PAGE_READWRITE,
引數Protect用于設定當前分批堆的保護屬性,通常當我們需要分配一段可執行指令的記憶體空間時會使用PAGE_EXECUTE_READWRITE,如果無執行需求則推薦使用PAGE_READWRITE屬性,
在對特定行程分配堆時第一步就是要切入到該行程的行程堆疊中,此時可通過KeStackAttachProcess()切換到行程堆疊,于此對應的是KeUnstackDetachProcess()脫離行程堆疊,這兩個函式的具體定義如下;
// 附加到行程堆疊
void KeStackAttachProcess(
PRKPROCESS PROCESS, // 傳入EProcess結構
[out] PRKAPC_STATE ApcState // 指向KAPC_STATE結構的不透明指標
);
// 接觸附加
void KeUnstackDetachProcess(
[in] PRKAPC_STATE ApcState // 指向KAPC_STATE結構的不透明指標
);
此處如果需要附加行程堆疊則必須提供該行程的PRKPROCESS也就是EProcess結構,此結構可通過PsLookupProcessByProcessId()獲取到,該函式接收一個行程PID并將此PID轉為EProcess結構,函式定義如下;
NTSTATUS PsLookupProcessByProcessId(
[in] HANDLE ProcessId, // 行程PID
[out] PEPROCESS *Process // 輸出EP結構
);
基本的函式介紹完了,那么這段代碼應該不難理解了,如下代碼中需要注意一點,引數OUT PVOID Buffer用于輸出堆地址而不是輸入地址,
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <windef.h>
// 定義宣告
NTSTATUS ZwAllocateVirtualMemory(
__in HANDLE ProcessHandle,
__inout PVOID *BaseAddress,
__in ULONG_PTR ZeroBits,
__inout PSIZE_T RegionSize,
__in ULONG AllocationType,
__in ULONG Protect
);
// 分配記憶體空間
NTSTATUS AllocMemory(IN ULONG ProcessPid, IN SIZE_T Length, OUT PVOID Buffer)
{
NTSTATUS Status = STATUS_SUCCESS;
PEPROCESS pEProcess = NULL;
KAPC_STATE ApcState = { 0 };
PVOID BaseAddress = NULL;
// 通過行程PID得到行程EProcess
Status = PsLookupProcessByProcessId((HANDLE)ProcessPid, &pEProcess);
if (!NT_SUCCESS(Status) && !MmIsAddressValid(pEProcess))
{
return STATUS_UNSUCCESSFUL;
}
// 驗證記憶體可讀
if (!MmIsAddressValid(pEProcess))
{
return STATUS_UNSUCCESSFUL;
}
__try
{
// 附加到行程堆疊
KeStackAttachProcess(pEProcess, &ApcState);
// 分配記憶體
Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &BaseAddress, 0, &Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
RtlZeroMemory(BaseAddress, Length);
// 回傳記憶體地址
*(PVOID*)Buffer = BaseAddress;
// 脫離行程堆疊
KeUnstackDetachProcess(&ApcState);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KeUnstackDetachProcess(&ApcState);
Status = STATUS_UNSUCCESSFUL;
}
ObDereferenceObject(pEProcess);
return Status;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint(("hello lyshark \n"));
DWORD process_id = 4160;
DWORD create_size = 1024;
DWORD64 ref_address = 0;
NTSTATUS Status = AllocMemory(process_id, create_size, &ref_address);
DbgPrint("對端行程: %d \n", process_id);
DbgPrint("分配長度: %d \n", create_size);
DbgPrint("分配的內核堆基址: %p \n", ref_address);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
運行如上代碼片段,則會在行程PID=4160中開辟一段堆空間,輸出效果如下;

與創建堆相對ZwFreeVirtualMemory()則用于銷毀一個堆,其微軟定義如下所示;
NTSYSAPI NTSTATUS ZwFreeVirtualMemory(
[in] HANDLE ProcessHandle, // 行程句柄
[in, out] PVOID *BaseAddress, // 堆基址
[in, out] PSIZE_T RegionSize, // 銷毀長度
[in] ULONG FreeType // 釋放型別
);
相對于創建來說,銷毀堆則必須傳入堆空間BaseAddress基址,以及堆空間的RegionSize長度,需要格外注意FreeType引數,這是一個位掩碼,其中包含描述ZwFreeVirtualMemory將為頁面指定區域執行的任意操作型別,如果傳入MEM_DECOMMIT則將取消提交頁面的指定區域,頁面進入保留狀態,而如果設定為MEM_RELEASE則堆地址將被立即釋放,
銷毀堆空間FreeMemory()的完整代碼如下所示,銷毀是我們使用MEM_RELEASE引數即立即銷毀,
// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]
#include <ntifs.h>
#include <windef.h>
// 申請堆
NTSTATUS ZwAllocateVirtualMemory(
__in HANDLE ProcessHandle,
__inout PVOID *BaseAddress,
__in ULONG_PTR ZeroBits,
__inout PSIZE_T RegionSize,
__in ULONG AllocationType,
__in ULONG Protect
);
// 銷毀堆
NTSYSAPI NTSTATUS ZwFreeVirtualMemory(
__in HANDLE ProcessHandle,
__inout PVOID *BaseAddress,
__inout PSIZE_T RegionSize,
__in ULONG FreeType
);
// 分配記憶體空間
NTSTATUS AllocMemory(IN ULONG ProcessPid, IN SIZE_T Length, OUT PVOID Buffer)
{
NTSTATUS Status = STATUS_SUCCESS;
PEPROCESS pEProcess = NULL;
KAPC_STATE ApcState = { 0 };
PVOID BaseAddress = NULL;
// 通過行程PID得到行程EProcess
Status = PsLookupProcessByProcessId((HANDLE)ProcessPid, &pEProcess);
if (!NT_SUCCESS(Status) && !MmIsAddressValid(pEProcess))
{
return STATUS_UNSUCCESSFUL;
}
// 驗證記憶體可讀
if (!MmIsAddressValid(pEProcess))
{
return STATUS_UNSUCCESSFUL;
}
__try
{
// 附加到行程堆疊
KeStackAttachProcess(pEProcess, &ApcState);
// 分配記憶體
Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &BaseAddress, 0, &Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
RtlZeroMemory(BaseAddress, Length);
// 回傳記憶體地址
*(PVOID*)Buffer = BaseAddress;
// 脫離行程堆疊
KeUnstackDetachProcess(&ApcState);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KeUnstackDetachProcess(&ApcState);
Status = STATUS_UNSUCCESSFUL;
}
ObDereferenceObject(pEProcess);
return Status;
}
// 注銷記憶體空間
NTSTATUS FreeMemory(IN ULONG ProcessPid, IN SIZE_T Length, IN PVOID BaseAddress)
{
NTSTATUS Status = STATUS_SUCCESS;
PEPROCESS pEProcess = NULL;
KAPC_STATE ApcState = { 0 };
Status = PsLookupProcessByProcessId((HANDLE)ProcessPid, &pEProcess);
if (!NT_SUCCESS(Status) && !MmIsAddressValid(pEProcess))
{
return STATUS_UNSUCCESSFUL;
}
if (!MmIsAddressValid(pEProcess))
{
return STATUS_UNSUCCESSFUL;
}
__try
{
// 附加到行程堆疊
KeStackAttachProcess(pEProcess, &ApcState);
// 釋放記憶體
Status = ZwFreeVirtualMemory(NtCurrentProcess(), &BaseAddress, &Length, MEM_RELEASE);
// 脫離行程堆疊
KeUnstackDetachProcess(&ApcState);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
KeUnstackDetachProcess(&ApcState);
Status = STATUS_UNSUCCESSFUL;
}
ObDereferenceObject(pEProcess);
return Status;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint(("hello lyshark \n"));
DWORD process_id = 4160;
DWORD create_size = 1024;
DWORD64 ref_address = 0;
NTSTATUS Status = AllocMemory(process_id, create_size, &ref_address);
DbgPrint("對端行程: %d \n", process_id);
DbgPrint("分配長度: %d \n", create_size);
DbgPrint("分配的內核堆基址: %p \n", ref_address);
Status = FreeMemory(process_id, create_size, ref_address);
DbgPrint("銷毀堆地址: %p \n", ref_address);
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
編譯并運行如上這段代碼,代碼會首先呼叫AllocMemory()函式實作分配堆,然后呼叫FreeMemory()函式銷毀堆,并輸出銷毀地址,如下圖所示;

文章出處:https://www.cnblogs.com/LyShark/p/17153846.html
著作權宣告:本博客文章,除去特殊宣告 [轉載標注/參考文獻] 部分, [均為原創] 作品,禁止任何形式的轉載!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/552457.html
標籤:C++
下一篇:返回列表
