Windows驱动开发学习笔记(七)—— 多核同步&内核重载

前言

一、学习自滴水编程达人中级班课程,官网:https://bcdaren.com
二、海东老师牛逼!

基础知识

并发与同步

并发:指多个线程在同时执行

  • 单核:各个线程分时执行,不是真正意义上的同时
  • 多核:在某一时刻,会同时有多个线程在执行

同步:保证在并发执行的环境中各个线程可以有序的执行

示例

DWORD dwVal = 0;	//全局变量

某线程中的代码

dwVal++;	//只有一行,安全吗

对应的汇编代码

mov eax, [0x12345678]
add eax, 1				/*若当一个线程执行完这行代码时,发生了线程切换
						 *另一个线程在它的时间片中执行了这三行代码
						 *此时,0x12345678中存储的是4
						 *当再次发生线程切换,回到原线程,执行第三行代码后
						 *0x12345678钟存储的理应是5
						 *然而由于在第一次发生线程切换时,eax中存储的是4
						 *因此在回到原线程,执行第三行代码后,0x12345678中存储的仍然是4
						 */
mov [0x12345678], eax

解决方案:LOCK指令


add eax, 1
改为
LOCK add eax, 1

参考:kernel32.InterlockedIncrement

分析 InterlockedIncrement

在这里插入图片描述

原子操作相关API

InterlockedIncrement		InterlockedExchangeAdd
InterlockedDecrement		InterlockedFlushSList		
InterlockedExchange			InterlockedPopEntrySList
InterlockedCompareExchange	InterlockedPushEntrySList
...

思考:如何实现多行代码的原子操作?

关键代码A	//N行代码要求原子操作
关键代码B	//单独加LOCK可以吗?
关键代码C
...

内核文件

描述:在同一个操作系统中,单核模式多核模式的内核文件(ntoskrnl.exe)会有一点小区别

多核同步

临界区

描述:一次只允许一个线程进入直到离开

示例一:错误的临界区

//实现临界区的方式就是加锁
//锁:全局变量,进去加一,出去减一
DWORD dwFlag = 0;

if( dwFlag == 0)	//进入临界区
{						↑↓
	dwFlag = 1;		//进入临界区
	...
	dwFalg = 0;		//离开临界区
}

思考:以上代码哪里存在问题?
答案:当第一个线程进入if中,但还未执行dwFlag=1时,若发生线程切换,第二个线程仍然能够进入if

示例二:正确的临界区

全局变量

Flag = 0;

进入临界区

Lab:
	mov eax,1
	//多核情况下必须加lock
	lock xadd [Flag],eax
	cmp eax,0
	jz endLab
	dec [Flag]
	//线程等待Sleep..
	jmp Lab
endLab:
	ret

离开临界区

lock dec [Flag]

自旋锁

描述

  1. 自旋锁只对多核有意义
  2. 自旋锁与临界区、事件、互斥体一样,都是一种同步机制, 都可以让当前线程处于等待状态,区别在于自旋锁不用切换线程

参考:KeAcquireSpinLockAtDpcLevel

分析 KeAcquireSpinLockAtDpcLevel

在这里插入图片描述

思考

  1. 如何HOOK高并发的内核函数?
    描述:在hook的时候如果使用memcpy等函数写入长跳转指令(五个字节),那么在函数内部实现中是逐个字节对内存进行修改的,若在修改的过程中有线程运行到这行指令,必然会引发错误,甚至蓝屏
    答案

    1. 先在附近未使用的内存空间构造一个长跳转(五个字节),再在要hook的地方构造一个短跳转(一次性写入两个字节)指向长跳转位置
    2. 使用 cmpxchg8b 指令最多可一次性写入八个字节
  2. 若HOOK时需要覆盖多行指令(例如5个push指令),如何保证此时没有线程正在执行这几行指令而引发错误
    答案

    1. 依然是短跳转
    2. 尽量避免在这些地方进行hook
  3. 使用临界区或者自旋锁是否可以实现多核HOOK?
    答案:不能,因为使用锁的前提是其它线程也在使用这把锁,否则锁是没有意义的

内核重载

描述

  1. 内核中的很多函数被层层HOOK,重载一份内核可以绕过这些HOOK
  2. 内核重载与映射PE文件的方法无异,本质上没有区别
  3. 内核重载就是重新加载一份内核文件(ntkrnlpa.exe

步骤

  1. 申请内存,按内存对齐展开
  2. 根据重定位表修复全局变量
  3. 修复IAT表(修复导入表的说法不准确)
  4. 山寨系统服务表
  5. 狸猫换太子(Hook KiFastCallEntry)

练习

描述:通过代码实现内核重载
答案:略(待补充)

发布了45 篇原创文章 · 获赞 2 · 访问量 1817

猜你喜欢

转载自blog.csdn.net/qq_41988448/article/details/103585673