概述:Windows 内核内存操作以及内存属性的说明
内存属性
在了解自旋锁之前需要先了解下内核的内存分配相关内容,相关的 API 可以参考 内存相关 一文。 这里补充下 ExAllocatePool 的参数1,也就是申请的页属性参数 POOL_TYPE:
微软官方说明:_POOL_TYPE (wdm.h) - Windows drivers | Microsoft Learn
这里主要就常用的三个进行简单说明:
-
NonPagedPool 非分页池,这是不可分页的系统内存。 可以从任何 IRQL 访问非分页池,但它是一种稀缺资源,驱动程序应仅在必要时进行分配。该内存不会被交换到磁盘上,并且可以直接被内核访问,适用于需要快速访问的内存,例如驱动程序的代码、中断处理程序、系统调用等。
-
PagePool 分页池,它是可分页的系统内存。 只能在 IRQL < DISPATCH_LEVEL分配和访问分页池。
-
NonPagedPoolExecute
带有执行权限的非分页内存。如果是使用 NonPagedPoolExecute 申请的内存则需要留意,避免被利用。 从 Windows 8 开始,NonPagedPoolExecute 是 NonPagedPool 值的备用名称。 此值指示分配的内存是非分页且可执行的,即在此内存中启用指令执行。 若要从早期版本的 Windows 移植驱动程序,通常应将驱动程序源代码中 NonPagedPool 名称的所有或大多数实例替换为 NonPagedPoolNx。 避免将 NonPagedPool 名称的实例替换为 NonPagedPoolExecute ,除非显式需要可执行内存。 有关详细信息,请参阅 No-Execute (NX) Nonpaged Pool。
内存操作
ExAllocatePool 内存申请
RtlZeroMemory 内存初始化
RtlCopyMemory 内存拷贝
ExFreePool 内存释放
Demo1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
#include <ntifs.h>
typedef struct _MyStruct { ULONG x; ULONG y; }MyStruct, *PMyStruct;
NTSTATUS UnloadDriver(PDRIVER_OBJECT pDriver) { DbgPrint("Bye, Driver\n");
return STATUS_SUCCESS; }
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) { DbgPrint("Hello World, Driver\n"); DbgPrint("PDRIVER_OBJECT-> [0x%08x]\n", pDriver); DbgPrint("PUNICODE_STRING->[%ws]\n", pReg->Buffer);
PVOID buffer = ExAllocatePool(NonPagedPool, 1024);
DbgPrint("[*] 分配内存地址=%p \n", buffer);
if (buffer) { ExFreePool(buffer); }
PMyStruct pStu = (PMyStruct)ExAllocatePoolWithTag(NonPagedPoolExecute, sizeof(PMyStruct), "KernelTest");
if (pStu) { pStu->x = 100; pStu->y = 200;
DbgPrint("[*] 分配内存 x = %d, y= %d", pStu->x, pStu->y);
ExFreePoolWithTag(pStu, "KernelTest"); }
UNICODE_STRING dst = { 0 }; UNICODE_STRING src = RTL_CONSTANT_STRING(L"Hello KernelTest");
dst.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, src.Length); if (dst.Buffer == NULL) { DbgPrint("[-] 分配空间错误"); }
dst.Length = dst.MaximumLength = src.Length; RtlCopyUnicodeString(&dst, &src);
DbgPrint("[*] 输出拷贝 = %wZ", dst);
pDriver->DriverUnload = UnloadDriver; return STATUS_SUCCESS; }
|
输出
Hello World, Driver
PDRIVER_OBJECT-> [0xb0402e30]
PUNICODE_STRING->[\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\KmdfMemory]
[*] 分配内存地址=FFFFAA02B11358F0
[*] 分配内存 x = 100, y= 200
[*] 输出拷贝 = Hello KernelTest
双向链表
双向链表的结构体如下所示:
1 2 3 4 5
| typedef struct _LIST_ENTRY { struct _LIST_ENTRY* Flink; struct _LIST_ENTRY* Blink; }LIST_ENTRY, * PLIST_ENTRY;
|
需要注意的是:FLink 指向后一个节点,Blink 指向前一个结点。
多线程访问
需要注意的是,多线程访问同一数据结构时会存在线程同步问题,常见的使用方式就是使用锁机制进程同步。自旋锁是一种常用的锁机制,它是一种高 IRQL 锁,用于同步和独占地访问某个资源。
**自旋锁的基本思想:**当一个线程尝试获取锁时,如果锁已经被占用,则该线程不断循环(即自旋),直到锁被释放。自旋锁适用于锁的持有时间较短,且竞争者较少的情况下,可以避免进程上下文的切换和调度开销。在下一章将详细记录和说明。
Demo2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
#include <ntifs.h>
typedef struct _MyStruct { ULONG x; ULONG y; LIST_ENTRY lpListEntry; }MyStruct, * PMyStruct;
NTSTATUS UnloadDriver(PDRIVER_OBJECT pDriver) { DbgPrint("Bye, Driver\n");
return STATUS_SUCCESS; }
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg) { DbgPrint("Hello World, Driver\n"); DbgPrint("PDRIVER_OBJECT-> [0x%016x]\n", pDriver); DbgPrint("PUNICODE_STRING->[%ws]\n", pReg->Buffer);
LIST_ENTRY ListHeader = { 0 }; InitializeListHead(&ListHeader);
MyStruct testA = { 0 }; MyStruct testB = { 0 }; MyStruct testC = { 0 };
testA.x = 10; testA.y = 20;
testB.x = 100; testB.y = 200;
testC.x = 1000; testC.y = 2000;
InsertHeadList(&ListHeader, &testA.lpListEntry); InsertTailList(&ListHeader, &testB.lpListEntry); InsertTailList(&ListHeader, &testC.lpListEntry);
PLIST_ENTRY pListEntry = NULL; pListEntry = ListHeader.Flink;
int i = 0;
while (pListEntry != &ListHeader) { PMyStruct ptr = CONTAINING_RECORD(pListEntry, MyStruct, lpListEntry); DbgPrint("节点元素x = %d, 节点元素y = %d\n", ptr->x, ptr->y);
if (i++ > 10) { break; } pListEntry = pListEntry->Flink; }
pDriver->DriverUnload = UnloadDriver;
return STATUS_SUCCESS; }
|
驱动输出:
Hello World, Driver
PDRIVER_OBJECT-> [0x00000000a78b4950]
PUNICODE_STRING->[\REGISTRY\MACHINE\SYSTEM\ControlSet001\Services\KmdfListEntry]
节点元素x = 10, 节点元素y = 20
节点元素x = 100, 节点元素y = 200
节点元素x = 1000, 节点元素y = 2000