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

news2024/12/27 8:19:39

提到自旋锁那就必须要说链表,在上一篇《内核中的链表与结构体》文章中简单实用链表结构来存储进程信息列表,相信读者应该已经理解了内核链表的基本使用,本篇文章将讲解自旋锁的简单应用,自旋锁是为了解决内核链表读写时存在线程同步问题,解决多线程同步问题必须要用锁,通常使用自旋锁,自旋锁是内核中提供的一种高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.coloradmin.cn/o/1202933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

若依侧边栏添加计数标记效果

2023.11.13今天我学习了如何对若依的侧边栏添加技术标记的效果&#xff0c;如图&#xff1a; 我们需要用到两个页面&#xff1a; 先说子组件实现计数标记效果 1.item.vue <script> export default {name: MenuItem,functional: true,props: {icon: {type: String,defau…

使用Nginx和uwsgi在自己的服务器上部署python的flask项目

Nginx 是一个高性能的 HTTP 和反向代理服务。其特点是占有内存少&#xff0c;并发能力强&#xff0c;事实上nginx的并发能力在同类型的网页服务器中表现较好。 Nginx 专为性能优化而开发&#xff0c;性能是其最重要的考量指标&#xff0c;实现上非常注重效率&#xff0c;能经受…

竞赛选题 深度学习的口罩佩戴检测 - opencv 卷积神经网络 机器视觉 深度学习

文章目录 0 简介1 课题背景&#x1f6a9; 2 口罩佩戴算法实现2.1 YOLO 模型概览2.2 YOLOv32.3 YOLO 口罩佩戴检测实现数据集 2.4 实现代码2.5 检测效果 3 口罩佩戴检测算法评价指标3.1 准确率&#xff08;Accuracy&#xff09;3.2 精确率(Precision)和召回率(Recall)3.3 平均精…

基于K7的PXIPXIe数据处理板(Kintex-7 FMC载板)

基于PXI&PXIe总线架构的高性能数据预处理FMC 载板&#xff0c;板卡具有 1 个 FMC&#xff08;HPC&#xff09;接口&#xff0c;1 个 X8 PCIe 和1个PCI主机接口&#xff1b;板卡采用 Xilinx 的高性能 Kintex-7 系列 FPGA 作为实时处理器&#xff0c;实现 FMC 接口数据的采集…

事务JdbcTemplate

Spring框架对JDBC进行封装&#xff0c;使用JdbcTemplate方便对数据库操作。 1.搭建模块 2.引入依赖 <dependencies><!-- spring jdba Spring持久化层支持jar包--><dependency><groupId>org.springframework</groupId><artifactId>…

2023数据结构期中测验-2023秋-计算机+未来网络专业

这里写目录标题 选择题函数题6-1 求链式表的表长6-2 逆序数据建立链表6-3 删除单链表偶数节点6-4 求二叉树高度6-5 先序输出叶结点 选择题 2-1 下述程序段的时间复杂度为&#xff08; &#xff09; for&#xff08;i0; i<n-1; i&#xff09;for&#xff08;j0; j<n-1-i…

抖音小程序定制开发: 创新互动体验的代码之旅

随着抖音平台的蓬勃发展&#xff0c;抖音小程序定制开发成为数字创新的前沿领域。在这篇文章中&#xff0c;我们将深入探讨如何通过技术代码&#xff0c;定制开发抖音小程序&#xff0c;为用户带来独特而引人入胜的互动体验。 第一步&#xff1a;环境准备 首先&#xff0c;确…

RT-Thread在嵌入式图像处理系统中的应用

一、介绍RT-Thread RT-Thread&#xff08;Real-Time Thread&#xff09;是一个开源的实时操作系统&#xff0c;专为嵌入式系统设计。它具有轻量级、可裁剪、易移植等特点&#xff0c;适合于资源有限的嵌入式设备。RT-Thread提供了丰富的组件和功能&#xff0c;包括线程管理、任…

视频号小店怎么做?运营四步骤,快来学习!

大家好&#xff0c;我是电商糖果 2023年因为视频号小店的爆火&#xff0c;想尝试开店的朋友也不少。 但是因为自己是新手小白&#xff0c;对做电商方面了解的也并不多&#xff0c;再加上它是一个才出来一年多的电商平台。对它的很多规则和玩法并不清楚。 所以&#xff0c;这…

【中间件篇-Redis缓存数据库07】Redis缓存使用问题及互联网运用

Redis缓存使用问题 数据一致性 只要使用到缓存&#xff0c;无论是本地内存做缓存还是使用 redis 做缓存&#xff0c;那么就会存在数据同步的问题。 我以 Tomcat 向 MySQL 中写入和删改数据为例&#xff0c;来给你解释一下&#xff0c;数据的增删改操作具体是如何进行的。 我…

CCF ChinaSoft 2023 论坛巡礼 | 自动驾驶仿真测试论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

Python采集某网站数据实现简单可视化效果, webpack Js逆向解密

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 环境使用 : Python 3.8 Pycharm nodejs 模块使用: requests --> pip install requests execjs --> pip install PyExecJS csv 采集网站的数据如何去实现: 一. 数据来源分析 <√> 明确需求: 明确采集的网…

【原创】java+swing+mysql办公用品管理系统设计与实现

摘要&#xff1a; 办公用品管理系统是一个设计和实现办公用品库存和使用管理的信息系统。此系统可以提高办公用品的利用率&#xff0c;减少浪费&#xff0c;使办公用品管理更加高效、规范、便捷。本文主要介绍使用javaswingmysql技术去开发实现一个办公用品管理系统。 功能分…

约束条件的安全测试_报错注入

约束条件的安全测试_报错注入 基于约束的SQL攻击 报错注入

ros2_control的简单应用

文章目录 简介插件实现函数介绍代码 调用原理局限性 简介 在利用moveit_setup_assistant配置我们自己机械手后&#xff0c;当运行demo.launch.py时&#xff0c;会实例化一个moveit对象以及一个基于ros2_control的、虚拟的控制对象&#xff0c;从而可以实现一个完整的控制闭环。…

简单实现,在nodejs中简单使用kafka

什么是 Kafka Kafka 是由 Linkedin 公司开发的&#xff0c;它是一个分布式的&#xff0c;支持多分区、多副本&#xff0c;基于 Zookeeper 的分布式消息流平台&#xff0c;它同时也是一款开源的基于发布订阅模式的消息引擎系统。 Kafka 的基本术语 消息&#xff1a;Kafka 中的…

【源码复现】图神经网络之PPNP/APPNH

目录 1、论文简介2、论文核心介绍2.1、现有方法局限2.2、PageRank&Personalized PageRank2.3、PPNP&APPNP 3、源码复现3.1、模型总体框架3.2、PPNP3.3、APPNP3.4、MLP(两层) 1、论文简介 论文题目——《PREDICT THEN PROPAGATE: GRAPH NEURAL NETWORKS MEET PERSONALI…

期中之后老师的福音

老师在期中考试后总是会有一大堆事情要做&#xff0c;批改试卷、统计分数、通知学生成绩等等。今天我就要给大家介绍一个能够减轻老师工作负担、提高工作效率的方法——查询系统 简单来说&#xff0c;成绩查询系统就是能够让学生方便的查询成绩&#xff0c;让老师快捷发布成绩的…

iText v1.8.1(OCR截图文字识别工具)

iText for mac是一款OCR&#xff08;光学字符识别&#xff09;工具&#xff0c;可以从图片中识别文字&#xff0c;适用于从扫描版的PDF等任意图片中提取文字。 使用iText&#xff0c;您可以方便快捷地从图片中摘抄和批注文字&#xff0c;满足您的各种需求。其自带截图功能&…

vscode 快速打印console.log

第一步 输入这些 {// Print Selected Variabl 为自定义快捷键中需要使用的name&#xff0c;可以自行修改"Print Selected Variable": {"body": ["\nconsole.log("," %c $CLIPBOARD: ,"," background-color: #3756d4; padding:…