2.2 Windows驱动开发:内核自旋锁结构

news/2024/10/17 14:18:20/

提到自旋锁那就必须要说链表,在上一篇《内核中的链表与结构体》文章中简单实用链表结构来存储进程信息列表,相信读者应该已经理解了内核链表的基本使用,本篇文章将讲解自旋锁的简单应用,自旋锁是为了解决内核链表读写时存在线程同步问题,解决多线程同步问题必须要用锁,通常使用自旋锁,自旋锁是内核中提供的一种高IRQL锁,用同步以及独占的方式访问某个资源。

在了解自旋锁之前需简单介绍一下内核中如何分配内存,一般而言分配内存有两个函数来实现ExAllocatePool可实现分配不带有任何标签的内存空间,而ExAllocatePoolWithTag则可分配带标签的,两者在使用上没有任何区别与之对应的就是释放ExFreePool用于释放非标签内存,而ExFreePoolWithTag则用于通过传入的标签释放对应的内存。

此处的分页属性常用的有三种,NonPagedPool用于分配非分页内存,PagedPool是分页内存,NonPagedPoolExecute是带有执行权限的非分页内存。

  • NonPagedPool: 用于分配非分页内存,该内存不会被交换到磁盘上,并且可以直接被内核访问。适用于需要快速访问的内存,例如驱动程序的代码、中断处理程序、系统调用等。

  • PagedPool: 用于分配分页内存,该内存可能会被交换到磁盘上,需要通过分页机制进行访问。适用于占用空间较大、访问频率较低的内存,例如缓存、数据结构等。

  • NonPagedPoolExecute: 是带有执行权限的非分页内存,适用于需要执行代码的情况,例如一些特殊的驱动程序。

需要注意的是,使用NonPagedPoolExecute分配内存存在一定的安全风险,因为恶意软件可能会利用该内存进行攻击。因此,建议仅在必要时使用该分页属性。

#include <ntifs.h>typedef struct _MyStruct
{ULONG x;ULONG y;
}MyStruct, *pMyStruct;VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动已卸载 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");// 用于分配与释放非标签内存PVOID buffer = ExAllocatePool(NonPagedPool, 1024);DbgPrint("[*] 分配内存地址 = %p \n", buffer);ExFreePool(buffer);// 用于分配带有标签的内存pMyStruct ptr_buffer = (pMyStruct)ExAllocatePoolWithTag(NonPagedPoolExecute, sizeof(pMyStruct),"lyshark");ptr_buffer->x = 100;ptr_buffer->y = 200;DbgPrint("[*] 分配内存 x = %d y = %d \n", ptr_buffer->x, ptr_buffer->y);ExFreePoolWithTag(ptr_buffer, "lyshark");UNICODE_STRING dst = { 0 };UNICODE_STRING src = RTL_CONSTANT_STRING(L"hello lyshark");dst.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, src.Length);if (dst.Buffer == NULL){DbgPrint("[-] 分配空间错误 \n");}dst.Length = dst.MaximumLength = src.Length;RtlCopyUnicodeString(&dst, &src);DbgPrint("[*] 输出拷贝 = %wZ \n", dst);Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

代码输出效果如下图所示;

接着步入正题,以简单的链表为案例,链表主要分为单向链表与双向链表,单向链表的链表节点中只有一个链表指针,其指向后一个链表元素,而双向链表节点中有两个链表节点指针,其中Blink指向前一个链表节点Flink指向后一个节点,以双向链表为例。

#include <ntifs.h>
#include <ntstrsafe.h>/*
// 链表节点指针
typedef struct _LIST_ENTRY
{struct _LIST_ENTRY *Flink;   // 当前节点的后一个节点struct _LIST_ENTRY *Blink;   // 当前节点的前一个结点
}LIST_ENTRY, *PLIST_ENTRY;
*/typedef struct _MyStruct
{ULONG x;ULONG y;LIST_ENTRY lpListEntry;
}MyStruct,*pMyStruct;VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动卸载成功 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");// 初始化头节点LIST_ENTRY ListHeader = { 0 };InitializeListHead(&ListHeader);// 定义链表元素MyStruct testA = { 0 };MyStruct testB = { 0 };MyStruct testC = { 0 };testA.x = 100;testA.y = 200;testB.x = 1000;testB.y = 2000;testC.x = 10000;testC.y = 20000;// 分别插入节点到头部和尾部InsertHeadList(&ListHeader, &testA.lpListEntry);InsertTailList(&ListHeader, &testB.lpListEntry);InsertTailList(&ListHeader, &testC.lpListEntry);// 节点不为空 则 移除一个节点if (IsListEmpty(&ListHeader) == FALSE){RemoveEntryList(&testA.lpListEntry);}// 输出链表数据PLIST_ENTRY pListEntry = NULL;pListEntry = ListHeader.Flink;while (pListEntry != &ListHeader){// 计算出成员距离结构体顶部内存距离pMyStruct ptr = CONTAINING_RECORD(pListEntry, MyStruct, lpListEntry);DbgPrint("节点元素X = %d 节点元素Y = %d \n", ptr->x, ptr->y);// 得到下一个元素地址pListEntry = pListEntry->Flink;}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

链表输出效果如下图所示;

如上所述,在内核开发中,多线程访问同一数据结构时会存在线程同步问题,为了解决这种问题,可以使用锁机制进行同步。自旋锁是一种常用的锁机制,它是一种高IRQL锁,用于同步和独占地访问某个资源。

  • 自旋锁的基本思想是:当一个线程尝试获取锁时,如果锁已经被占用,则该线程不断循环(即自旋),直到锁被释放。自旋锁适用于锁的持有时间较短,且竞争者较少的情况下,可以避免进程上下文的切换和调度开销。

Windows内核提供了多种类型的自旋锁,例如KSPIN_LOCK、KIRQL等。其中,KSPIN_LOCK是最常用的自旋锁类型,可以通过KeInitializeSpinLock函数初始化一个自旋锁,并通过KeAcquireSpinLockKeReleaseSpinLock函数进行加锁和解锁操作。

需要注意的是,使用自旋锁要注意死锁和优先级反转等问题,因此在实际应用中需要谨慎使用。

#include <ntifs.h>
#include <ntstrsafe.h>/*
// 链表节点指针
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;   // 当前节点的后一个节点
struct _LIST_ENTRY *Blink;   // 当前节点的前一个结点
}LIST_ENTRY, *PLIST_ENTRY;
*/typedef struct _MyStruct
{ULONG x;ULONG y;LIST_ENTRY lpListEntry;
}MyStruct, *pMyStruct;// 定义全局链表和全局锁
LIST_ENTRY my_list_header;
KSPIN_LOCK my_list_lock;// 初始化
void Init()
{InitializeListHead(&my_list_header);KeInitializeSpinLock(&my_list_lock);
}// 函数内使用锁
void function_ins()
{KIRQL Irql;// 加锁KeAcquireSpinLock(&my_list_lock, &Irql);DbgPrint("锁内部执行 \n");// 释放锁KeReleaseSpinLock(&my_list_lock, Irql);
}VOID UnDriver(PDRIVER_OBJECT driver)
{DbgPrint("驱动卸载成功 \n");
}NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{DbgPrint("hello lyshark \n");// 初始化链表Init();// 分配链表空间pMyStruct testA = (pMyStruct)ExAllocatePool(NonPagedPoolExecute, sizeof(pMyStruct));pMyStruct testB = (pMyStruct)ExAllocatePool(NonPagedPoolExecute, sizeof(pMyStruct));// 赋值testA->x = 100;testA->y = 200;testB->x = 1000;testB->y = 2000;// 向全局链表中插入数据if (NULL != testA && NULL != testB){ExInterlockedInsertHeadList(&my_list_header, (PLIST_ENTRY)&testA->lpListEntry, &my_list_lock);ExInterlockedInsertTailList(&my_list_header, (PLIST_ENTRY)&testB->lpListEntry, &my_list_lock);}function_ins();// 移除节点A并放入到remove_entry中PLIST_ENTRY remove_entry = ExInterlockedRemoveHeadList(&testA->lpListEntry, &my_list_lock);// 输出链表数据while (remove_entry != &my_list_header){// 计算出成员距离结构体顶部内存距离pMyStruct ptr = CONTAINING_RECORD(remove_entry, MyStruct, lpListEntry);DbgPrint("节点元素X = %d 节点元素Y = %d \n", ptr->x, ptr->y);// 得到下一个元素地址remove_entry = remove_entry->Flink;}Driver->DriverUnload = UnDriver;return STATUS_SUCCESS;
}

加锁后执行效果如下图所示;


http://www.ppmy.cn/news/1222814.html

相关文章

2023-11-17 VsCode使用makefile进行多文件编译

点击 <C 语言编程核心突破> 快速C语言入门 VsCode使用makefile进行多文件编译 前言一、一个简单的多文件示例二、makefile基本语法三、VsCode使用makefile总结 前言 要解决问题: C或C可以多文件编译, 意味着需要进行代码组织, 为了方便多文件编译, gnu开发了make工具, …

为什么原生IP可以降低Google play账号关联风险?企业号解决8.3/10.3账号关联问题?

在Google paly应用上架的过程中&#xff0c;相信大多数开发者都遇到过开发者账号因为关联问题&#xff0c;导致应用包被拒审和封号的情况。 而众所周知&#xff0c;开发者账号注册或登录的IP地址及设备是造成账号关联的重要因素之一。酷鸟云最新上线的原生IP能有效降低账号因I…

封阳台怎么避坑

雅静说主要是看细节,你别到时候住进来漏雨      还不隔音不隔热呢,      1,提醒大家一个很重要的事,就是一定要在安窗户前      跟商家签个正规合同,内容不只是要求质量,还要加上安全隐患标明      因为封窗属于高危职业,要写好安全隐患和我们业主无关,请一定做…

Spring Cloud GateWay简介

什么是网关 网关是一种充当转换重任的计算机系统或设备&#xff0c;使用在不同的通信协议、数据格式或语言&#xff0c;甚至网关是一种充当转换重任的计算机系统或设备&#xff0c;使用在不同的通信协议、数据格式或语言&#xff0c;甚至体系结构完全不同的两种系统之间进行数…

geoserver面的填充样式错误记录

qgis生成如上的sld格式文件后发布到geoserver填充色没有问题&#xff0c;但是填充的斜线显示不出来&#xff0c;需要进行替换&#xff0c;例如 <se:WellKnownName>horline</se:WellKnownName> 替换成 <se:WellKnownName>shape://backslash</se:WellKnown…

双11背后的中国云厂商:新“标准化”,和调整后的新韧性

降价并不代表一味的压缩自身利润空间&#xff0c;云厂商已经开始向具有更高利润空间的PaaS、SaaS产品腾挪&#xff0c;核心产品在总包占比越来越高。 作者|斗斗 编辑|皮爷 出品|产业家 今年云厂商&#xff0c;全面拥抱双11。 作为中国最大的云计算服务提供商&#xff0…

扬帆未来,成就架构之路:十本书籍助力你的架构师梦想 | 文末送书

相信大家都对未来的职业发展有着憧憬和规划&#xff0c;要做架构师、要做技术总监、要做CTO。对于如何实现自己的职业规划也都信心满满&#xff0c;努力工作、好好学习、不断提升自己。 规划职业发展 当涉及未来职业发展时&#xff0c;我们都怀揣着远大的目标和野心。对许多人…

Hacker 资讯|11 月中下旬区块链黑客松活动汇总

「TinTin Hacker 快讯」是 TinTinLand 建立的一个资讯专栏&#xff0c;汇集近期线上线下的黑客松及 Grant&#xff0c;旨在帮助开发者和区块链爱好者获取最新的黑客松资讯&#xff0c;鼓励他们了解并根据自身情况参与不同的黑客松&#xff0c;更好地建设 Web3 生态。 2023 Wint…