在上一篇博文《驅動開發:核心通過PEB得到程序引數》
中我們通過使用KeStackAttachProcess
附加程序的方式得到了該程序的PEB結構資訊,本篇文章同樣需要使用程序附加功能,但這次我們將實現一個更加有趣的功能,在某些情況下應用層與核心層需要共用一片記憶體區域通過這片區域可打通核心與應用層的隔離,此類功能的實現依附於MDL記憶體對映機制實現。
應用層(R3)資料對映到核心層(R0)
先來實現將R3記憶體資料拷貝到R0中,功能實現所呼叫的API如下:
MDL
(類似初始化)UserMode
代表使用者層,IoReadAccess
以讀取的方式鎖定MDL
中得到對映記憶體地址DstAddr
應用層中的資料拷貝到pMappedSrc
中pSrcMdl
MDL
記憶體拷貝SafeCopyMemory_R3_to_R0
函數封裝程式碼如下:
#include <ntifs.h>
#include <windef.h>
// 分配記憶體
void* RtlAllocateMemory(BOOLEAN InZeroMemory, SIZE_T InSize)
{
void* Result = ExAllocatePoolWithTag(NonPagedPool, InSize, 'lysh');
if (InZeroMemory && (Result != NULL))
RtlZeroMemory(Result, InSize);
return Result;
}
// 釋放記憶體
void RtlFreeMemory(void* InPointer)
{
ExFreePool(InPointer);
}
/*
將應用層中的記憶體複製到核心變數中
SrcAddr r3地址要複製
DstAddr R0申請的地址
Size 拷貝長度
*/
NTSTATUS SafeCopyMemory_R3_to_R0(ULONG_PTR SrcAddr, ULONG_PTR DstAddr, ULONG Size)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG nRemainSize = PAGE_SIZE - (SrcAddr & 0xFFF);
ULONG nCopyedSize = 0;
if (!SrcAddr || !DstAddr || !Size)
{
return status;
}
while (nCopyedSize < Size)
{
PMDL pSrcMdl = NULL;
PVOID pMappedSrc = NULL;
if (Size - nCopyedSize < nRemainSize)
{
nRemainSize = Size - nCopyedSize;
}
// 建立MDL
pSrcMdl = IoAllocateMdl((PVOID)(SrcAddr & 0xFFFFFFFFFFFFF000), PAGE_SIZE, FALSE, FALSE, NULL);
if (pSrcMdl)
{
__try
{
// 鎖定記憶體頁面(UserMode代表應用層)
MmProbeAndLockPages(pSrcMdl, UserMode, IoReadAccess);
// 從MDL中得到對映記憶體地址
pMappedSrc = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
}
if (pMappedSrc)
{
__try
{
// 將MDL中的對映拷貝到pMappedSrc記憶體中
RtlCopyMemory((PVOID)DstAddr, (PVOID)((ULONG_PTR)pMappedSrc + (SrcAddr & 0xFFF)), nRemainSize);
}
__except (1)
{
// 拷貝記憶體異常
}
// 釋放鎖
MmUnlockPages(pSrcMdl);
}
if (pSrcMdl)
{
// 釋放MDL
IoFreeMdl(pSrcMdl);
}
if (nCopyedSize)
{
nRemainSize = PAGE_SIZE;
}
nCopyedSize += nRemainSize;
SrcAddr += nRemainSize;
DstAddr += nRemainSize;
}
status = STATUS_SUCCESS;
return status;
}
呼叫該函數實現拷貝,如下程式碼中首先PsLookupProcessByProcessId
得到程序EProcess
結構,並KeStackAttachProcess
附加程序,宣告pTempBuffer
指標用於儲存RtlAllocateMemory
開闢的記憶體空間,nSize
則代表讀取應用層程序資料長度,ModuleBase
則是讀入程序基址,呼叫SafeCopyMemory_R3_to_R0
即可將應用層資料拷貝到核心空間,並最終BYTE* data
轉為BYTE位元組的方式輸出。
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
}
// lyshark.com
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
NTSTATUS status = STATUS_UNSUCCESSFUL;
PEPROCESS eproc = NULL;
KAPC_STATE kpc = { 0 };
__try
{
// HANDLE 程序PID
status = PsLookupProcessByProcessId((HANDLE)4556, &eproc);
if (NT_SUCCESS(status))
{
// 附加程序
KeStackAttachProcess(eproc, &kpc);
// -------------------------------------------------------------------
// 開始對映
// -------------------------------------------------------------------
// 將使用者空間記憶體對映到核心空間
PVOID pTempBuffer = NULL;
ULONG nSize = 0x1024;
ULONG_PTR ModuleBase = 0x0000000140001000;
// 分配記憶體
pTempBuffer = RtlAllocateMemory(TRUE, nSize);
if (pTempBuffer)
{
// 拷貝資料到R0
status = SafeCopyMemory_R3_to_R0(ModuleBase, (ULONG_PTR)pTempBuffer, nSize);
if (NT_SUCCESS(status))
{
DbgPrint("[*] 拷貝應用層資料到核心裡 \n");
}
// 轉成BYTE方便讀取
BYTE* data = pTempBuffer;
for (size_t i = 0; i < 10; i++)
{
DbgPrint("%02X \n", data[i]);
}
}
// 釋放空間
RtlFreeMemory(pTempBuffer);
// 脫離程序
KeUnstackDetachProcess(&kpc);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
程式碼執行後即可將程序中0x0000000140001000
處的資料讀入核心空間並輸出:
核心層(R0)資料對映到應用層(R3)
與上方功能實現相反SafeCopyMemory_R0_to_R3
函數則用於將一個核心層中的緩衝區寫出到應用層中,寫出過程:
SrcAddr
目標地址DstAddr
均建立pSrcMdl
,pDstMdl
兩個MDL的pDstMdl
的地址記憶體拷貝SafeCopyMemory_R0_to_R3
函數封裝程式碼如下:
// 分配記憶體
void* RtlAllocateMemory(BOOLEAN InZeroMemory, SIZE_T InSize)
{
void* Result = ExAllocatePoolWithTag(NonPagedPool, InSize, 'lysh');
if (InZeroMemory && (Result != NULL))
RtlZeroMemory(Result, InSize);
return Result;
}
// 釋放記憶體
void RtlFreeMemory(void* InPointer)
{
ExFreePool(InPointer);
}
/*
將記憶體中的資料複製到R3中
SrcAddr R0要複製的地址
DstAddr 返回R3的地址
Size 拷貝長度
*/
NTSTATUS SafeCopyMemory_R0_to_R3(PVOID SrcAddr, PVOID DstAddr, ULONG Size)
{
PMDL pSrcMdl = NULL, pDstMdl = NULL;
PUCHAR pSrcAddress = NULL, pDstAddress = NULL;
NTSTATUS st = STATUS_UNSUCCESSFUL;
// 分配MDL 源地址
pSrcMdl = IoAllocateMdl(SrcAddr, Size, FALSE, FALSE, NULL);
if (!pSrcMdl)
{
return st;
}
// 該 MDL 指定非分頁虛擬記憶體緩衝區,並對其進行更新以描述基礎物理頁。
MmBuildMdlForNonPagedPool(pSrcMdl);
// 獲取源地址MDL地址
pSrcAddress = MmGetSystemAddressForMdlSafe(pSrcMdl, NormalPagePriority);
if (!pSrcAddress)
{
IoFreeMdl(pSrcMdl);
return st;
}
// 分配MDL 目標地址
pDstMdl = IoAllocateMdl(DstAddr, Size, FALSE, FALSE, NULL);
if (!pDstMdl)
{
IoFreeMdl(pSrcMdl);
return st;
}
__try
{
// 以寫入的方式鎖定目標MDL
MmProbeAndLockPages(pDstMdl, UserMode, IoWriteAccess);
// 獲取目標地址MDL地址
pDstAddress = MmGetSystemAddressForMdlSafe(pDstMdl, NormalPagePriority);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
}
if (pDstAddress)
{
__try
{
// 將源地址拷貝到目標地址
RtlCopyMemory(pDstAddress, pSrcAddress, Size);
}
__except (1)
{
// 拷貝記憶體異常
}
MmUnlockPages(pDstMdl);
st = STATUS_SUCCESS;
}
IoFreeMdl(pDstMdl);
IoFreeMdl(pSrcMdl);
return st;
}
呼叫該函數實現拷貝,此處除去附加程序以外,在拷貝之前呼叫了ZwAllocateVirtualMemory
將記憶體屬性設定為PAGE_EXECUTE_READWRITE
可讀可寫可執行狀態,然後在向該記憶體中寫出pTempBuffer
變數中的內容,此變數中的資料是0x90
填充的區域。
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint(("Uninstall Driver Is OK \n"));
}
// lyshark.com
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint("hello lyshark.com \n");
NTSTATUS status = STATUS_UNSUCCESSFUL;
PEPROCESS eproc = NULL;
KAPC_STATE kpc = { 0 };
__try
{
// HANDLE 程序PID
status = PsLookupProcessByProcessId((HANDLE)4556, &eproc);
if (NT_SUCCESS(status))
{
// 附加程序
KeStackAttachProcess(eproc, &kpc);
// -------------------------------------------------------------------
// 開始對映
// -------------------------------------------------------------------
// 將使用者空間記憶體對映到核心空間
PVOID pTempBuffer = NULL;
ULONG nSize = 0x1024;
PVOID ModuleBase = 0x0000000140001000;
// 分配記憶體
pTempBuffer = RtlAllocateMemory(TRUE, nSize);
if (pTempBuffer)
{
memset(pTempBuffer, 0x90, nSize);
// 設定記憶體屬性 PAGE_EXECUTE_READWRITE
ZwAllocateVirtualMemory(NtCurrentProcess(), &ModuleBase, 0, &nSize, MEM_RESERVE, PAGE_EXECUTE_READWRITE);
ZwAllocateVirtualMemory(NtCurrentProcess(), &ModuleBase, 0, &nSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// 將資料拷貝到R3中
status = SafeCopyMemory_R0_to_R3(pTempBuffer, &ModuleBase, nSize);
if (NT_SUCCESS(status))
{
DbgPrint("[*] 拷貝核心資料到應用層 \n");
}
}
// 釋放空間
RtlFreeMemory(pTempBuffer);
// 脫離程序
KeUnstackDetachProcess(&kpc);
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
Driver->DriverUnload = UnDriver;
return STATUS_SUCCESS;
}
拷貝成功後,應用層程序內將會被填充為Nop指令。