首页
社区
课程
招聘
[讨论]内核锁里复杂操作会蓝屏?什么鬼
发表于: 2016-5-30 22:40 10010

[讨论]内核锁里复杂操作会蓝屏?什么鬼

2016-5-30 22:40
10010
KeAcquireInStackQueuedSpinLock
在这里执行了一些进程查找 远程写入内存等操作 直接蓝屏
KeReleaseInStackQueuedSpinLock

放到外面来做 怎么都没事 什么鬼?

        //DbgBreakPoint();

        KeAttachProcess((PRKPROCESS)Process);

        status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &pBuffer, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

        if (NT_SUCCESS(status))
        {
                memcpy(pBuffer, g_MyBuffer, size); //崩溃在这里
//这里加锁后pBuffer 显示
kd> db 03180000 pBuffer == 03180000
00000000`03180000  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

放到外面来做 怎么都没事 什么鬼?

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (13)
雪    币: 155
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
基础知识没学好,自然见到鬼。
都不知道加锁实际做了什么就上来加这勇气也是棒棒哒
Spin lock会提升irql,对于没有换入的分页内存,高irql无法调度内存换入,自然无法访问呢。
2016-6-1 07:20
0
雪    币: 1604
活跃值: (640)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
3
第一,由于自旋锁与队列自旋锁系列函数可能会把当前的 IRQL 从 PASSIVE_LEVEL 提升到 DISPATCH_LEVEL 。另一方面,你用 ZwAllocateVirtualMemory() 分配的缓冲区(pBuffer)可能随时被换出内存,因此 memcpy() 访问的时候可能已经被换出了,这会引发一个缺页异常,而相应的
page falut handler() 无法在 DISPATCH_LEVEL 下完成,因此 pBuffer 指向的内存无法被换入,造成崩溃。
解决办法是,pBuffer 应该用 ExAllocatePool/ExAllocatePoolWithTag() 来分配,可以为它的第一个参数传入 NonPagePool 表示在内核空间的非换页池中分配。这样它就确保不会被换出内存。
第二,很多 Dbg*() 系列例程要求在 PASSIVE_LEVEL 下执行。获得自旋锁后,你可以用 KeGetCurrentIrql() 查看当前 IRQL ,如果再不是 PASSIVE_LEVEL 了,就应该避免执行后续的
操作(包括调用 Dbg*());或者用 KeLowerIrql() 降低到 PASSIVE_LEVEL ,可以在一个 if 语句块中完成检测;
第三,也可能是你没有初始化自旋锁造成崩溃的,正确的使用方法如下:
使用自旋锁:
// 包含 wdm.h ,其中有 KSPIN_LOCK 等数据结构的定义

KSPIN_LOCK  get_spin_lock;
KIRQL  old_irql;      //用于保存并恢复 IRQL
KeInitializeSpinLock(&get_spin_lock);
KeAcquireSpinLock(&get_spin_lock,  &old_irql);
{....在这里完成你的操作}
KeReleaseSpinLock(&get_spin_lock, old_irql);

使用排队的自旋锁:

KSPIN_LOCK  get_spin_lock;
KLOCK_QUEUE_HANDLE   lock_queue_handle;
KeInitializeSpinLock(&get_spin_lock);
KeAcquireInStackQueueSpinLock(&get_spin_lock,  &lock_queue_handle);
{.....完成内存复制操作,记得处理 IRQL 不为 PASSIVE_LEVEL 时的情况}
KeReleaseInStackQueueSpinLock(&lock_queue_handle);

最后,如果上述方案都失效,还可以使用下列的页面锁定函数之一,根据你的实际情况来选择:
MmProbeAndLockPages()
MmLockPagableCodeSection()
MmLockPagableDataSection()
MmLockPagableSectionByHandle()
页面锁定使用这种机制将你的 pBuffer 保留在物理内存中(不会被换出),直到使用 MmUnlockPages() 显式解锁。
2016-6-1 11:38
0
雪    币: 68
活跃值: (345)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
确实对内核研究的少
ExAllocatePool/ExAllocatePoolWithTag() 可以分配用户空间内存吗?
我是想给用户空间分配一块内存 然后在自旋锁里写入
现在的情况是锁内KeGetCurrentIrql() == 2
锁外KeGetCurrentIrql() = 0 也就是我在锁外操作没有问题
对于有点强迫症 我希望在锁内分配用户内存并写入
2016-6-1 12:24
0
雪    币: 44
活跃值: (186)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
呵呵插入进程申请内存???Buffer都没有检查是不是NULL,还有g_MyBuffer是否正确指向你所希望的数据地址
2016-6-1 14:20
0
雪    币: 12504
活跃值: (3093)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
谢谢提示
2016-6-1 14:58
0
雪    币: 1604
活跃值: (640)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
7
ExAllocetePool*() 例程都是在内核空间中分配的。你这整个程序作为一个驱动,都是加载到内核
空间中的,调用 ExAllocetePool*() 时,当然就是在内核内存池中分配了。

如果你的意图是实现用户-内核模式通信,那么可以写一个该驱动的用户模式组件,入口函数为 main()/WinMain(),在其中调用传统的 C 运行库函数比如 malloc() 分配用户堆内存,然后调用用户模式 API:DeviceIoControl() ,并且传入前面由 malloc() 返回的堆内存指针(作为用户空间的输出缓冲区)。
另一方面,在驱动的入口点 DriverEntry() 或者分发例程中,你在不可换页池中分配一块内核内存,加锁,获取指定进程的 EPROCESS 结构中的信息,复制到前面分配的内核缓冲区,解锁,再把 IRP->AssociatedIrp.SystemBuffer 指向这块内核缓冲区,最后调用 IoCompleteRequest(),
它会把内核缓冲区中存储的 EPROCESS 结构信息通过 IRP 返回到稍早时候 malloc() 分配的用户空间输出缓冲区。
如此一来你就可以访问内核对象,并且在应用程序中打印对象内容,我猜这就是你的目的。

根据 wdm.h 头文件中这一段宏定义:

#define PASSIVE_LEVEL 0             // Passive release level
#define LOW_LEVEL 0                 // Lowest interrupt level
#define APC_LEVEL 1                 // APC interrupt level
#define DISPATCH_LEVEL 2            // Dispatcher level
#define CMCI_LEVEL 5                // CMCI handler level

由此可知加锁操作会把 IRQL 提升到 DISPATCH_LEVEL ,导致缺页异常处理函数无法执行换入操作,因此你要确保访问的内核内存映射到的物理页面不会被换出到磁盘上。
其实在这个场景中,不一定要通过自旋锁来同步 EPROCESS 内核对象,因为它相对较少被多线程访问,也就是说,你向控制台输出的 EPROCESS 结构信息不会很快就过时。
真正需要加锁来同步的是由 LIST_ENTRY 组成的双向链表结构,内核代码频繁在其中插入或者移除节点,一个内核线程在向链表中插入节点时,为了保证同一时间不会有其它线程也对该链表执行操作,它就会使用锁机制来同步;如果使用前面的“排队的自旋锁”,还可以提升操作链表的效率——因为第一个线程释放锁后,队列中的第二个线程就可以立即获得锁来操作链表。
2016-6-1 15:29
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
解决了吗?关闭pagefile.sys行不行?
2016-6-1 15:31
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
如果关闭pagefile.sys分页文件后,DISPATCH_LEVEL是不是就可以访问了?
2016-6-1 15:35
0
雪    币: 1604
活跃值: (640)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
10
这个我没有实验过,估计可以,因为这样 Windows 就不会使用分页文件,要读写的对象都驻留在物理内存中,没必要执行 page fault handler() ,具体你们可以试试
2016-6-1 15:46
0
雪    币: 68
活跃值: (345)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
谢谢大家讨论
我是想 在自旋锁里 给用户空间分配一块内存 然后里写入
现在的情况是
锁内KeGetCurrentIrql() == 2
status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &pBuffer, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
这里status是成功的 pBuffer也是成功返回的内存地址 但是访问不了

锁外KeGetCurrentIrql() = 0 也就是我在锁外操作是没有任何问题的

我的程序是个裸驱动  给用户空间分配一块内存 当然是分配写到其他进程里

根据大家提示 降低到 PASSIVE_LEVEL或许是个办法 不知道稳定性如何

能有其他解决方案吗
KeGetCurrentIrql() == 2时
往目标进程分配写入内存
注意是分配的要是用户层内存
2016-6-1 18:10
0
雪    币: 68
活跃值: (345)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
结贴了吧 我就在锁外操作了
好奇心作怪
说白了 就是 一个驱动的某个代码在没解锁的情况下被你拿到了控制权
你KeAttachProcess上去 往RING3写入 shellcode 然后发现不行
2016-6-1 18:20
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
用MDL把分页内存锁住行不行?
2016-6-1 19:51
0
雪    币: 1604
活跃值: (640)
能力值: ( LV13,RANK:460 )
在线值:
发帖
回帖
粉丝
14
确实,都忘记还可以用MDL了,如果采用MDL的方案应该如下来写:

PMDL  map_to_pBuffer;
.......
status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &pBuffer, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
.......
if (NT_SUCCESS(status))
 {
 .....   
map_to_pBuffer = IoAllocateMdl(pBuffer,  size,  FALSE,  FALSE,  NULL);
MmProbeAndLockPages(map_to_pBuffer,  KernelMode,  IoWriteAccess);
RtlCopyMemory(pBuffer, g_MyBuffer, size);              //比 memcpy 有档次 ^_^
//RtlCopyMemory(map_to_pBuffer, g_MyBuffer, size);            //效果应该与上面的调用相等
......
}


map_to_pBuffer 指向的 MDL 映射到内核缓冲区 pBuffer ;其页面都锁定在物理内存中。
这样,后续所有对 map_to_pBuffer 的操作实际上都是在操作 pBuffer 引用的内核缓冲区。
可以调试上面这段代码能否正常工作。
2016-6-2 00:00
0
游客
登录 | 注册 方可回帖
返回