首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >调用 NtUserXXX 引发系统 BSOD 的问题分析

调用 NtUserXXX 引发系统 BSOD 的问题分析

作者头像
稻草小刀
发布于 2022-12-12 08:13:04
发布于 2022-12-12 08:13:04
35600
代码可运行
举报
文章被收录于专栏:小刀志小刀志
运行总次数:0
代码可运行

这篇文章通过一次在 Windows XP 和 Windows 7 操作系统内核中分别调用同一个 NtUserXxx 系统调用产生不同现象的问题,对其做了简单分析。

最近在驱动中需要实现在一些 HOOK 处理函数中调用如 NtUserBuildHwndList 这样的 API 对目标样本进程的窗口状态(是否存在窗口等)进行判定。NtUserBuildHwndList 是用来根据线程 ID 生成与线程信息结构体 tagTHREADINFO 关联的 tagDESKTOP 桌面对象中存在的窗口对象句柄列表的 USER 系统调用,其函数声明如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
NTSTATUS
NtUserBuildHwndList (
    IN  HDESK  hdesk,
    IN  HWND   hwndNext,
    IN  BOOL   fEnumChildren,
    IN  DWORD  idThread,
    IN  UINT   cHwndMax,
    OUT HWND  *phwndFirst,
    OUT PUINT  pcHwndNeeded
    );

实现代码在 Windows 7 下一切正常,但在 Windows XP 中的部分进程上下文中调用时会产生的偶发 BSOD 异常。为了解决该问题,通过内核调试进行分析。

分析

挂上 WinDBG 内核调试模式启动 Windows XP 的虚拟机镜像,加载驱动并执行样本进程。幸运的是很快触发预期的异常。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Access violation - code c0000005 (!!! second chance !!!)
win32k!InternalBuildHwndList+0x1a:
bf835e26 8b402c          mov     eax,dword ptr [eax+2Ch]
kd> dc eax+2Ch l 1
0000002c  ????????                             ????
kd> r eax
eax=00000000
kd> kv
ChildEBP RetAddr  Args to Child
ee609c04 bf835d37 e12dc350 bc6bc8c8 0000000a win32k!InternalBuildHwndList+0x1a (FPO: [Non-Fpo])
ee609c1c bf835fa7 bc6bc8c8 0000000a e2610870 win32k!BuildHwndList+0x4f (FPO: [Non-Fpo])
ee609c60 ede0b2aa 00000000 00000000 00000000 win32k!NtUserBuildHwndList+0xd8 (FPO: [Non-Fpo])
ee609ca8 ede0b3f3 85e45da0 862845a0 c0000001 MyDriver!MyCallOfNtUserBuildHwndList+0x10a (FPO: [Non-Fpo])

根据信息显示,是在 win32k!InternalBuildHwndList 函数中触发了异常。根据栈回溯可知,在我们的驱动模块调用 win32k!NtUserBuildHwndList 例程之后,实际调用 win32k!BuildHwndList 函数,随后进入 win32k!InternalBuildHwndList 例程中。最终在 InternalBuildHwndList 中发生了异常。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
win32k!InternalBuildHwndList:
bf835e10 8bff            mov     edi,edi
bf835e12 55              push    ebp
bf835e13 8bec            mov     ebp,esp
bf835e15 56              push    esi
bf835e16 57              push    edi
bf835e17 8b7d0c          mov     edi,dword ptr [ebp+0Ch]
bf835e1a 85ff            test    edi,edi
bf835e1c 74e8            je      win32k!InternalBuildHwndList+0x94 (bf835e06)
bf835e1e 8b7508          mov     esi,dword ptr [ebp+8]
bf835e21 a118ae9abf      mov     eax,dword ptr [win32k!gptiCurrent (bf9aae18)]
bf835e26 8b402c          mov     eax,dword ptr [eax+2Ch]   <- ACCESS VIOLATION, eax=0x00000000
bf835e29 8b8894010000    mov     ecx,dword ptr [eax+194h]
bf835e2f 8b460c          mov     eax,dword ptr [esi+0Ch]

win32k!BuildHwndList:
bf835d08 8bff            mov     edi,edi
bf835d0a 55              push    ebp
bf835d0b 8bec            mov     ebp,esp
bf835d0d a174949abf      mov     eax,dword ptr [win32k!pbwlCache (bf9a9474)]
bf835d12 85c0            test    eax,eax
bf835d14 74cd            je      win32k!BuildHwndList+0x17 (bf835ce3)
bf835d16 832574949abf00  and     dword ptr [win32k!pbwlCache (bf9a9474)],0
bf835d1d 53              push    ebx
bf835d1e 8b5d0c          mov     ebx,dword ptr [ebp+0Ch]
bf835d21 53              push    ebx
bf835d22 ff7508          push    dword ptr [ebp+8]
bf835d25 8d4810          lea     ecx,[eax+10h]
bf835d28 894804          mov     dword ptr [eax+4],ecx
bf835d2b 8b4d10          mov     ecx,dword ptr [ebp+10h]
bf835d2e 50              push    eax
bf835d2f 89480c          mov     dword ptr [eax+0Ch],ecx
bf835d32 e8d9000000      call    win32k!InternalBuildHwndList (bf835e10)   <- CALL InternalBuildHwndList
bf835d37 8b4804          mov     ecx,dword ptr [eax+4]
bf835d3a 3b4808          cmp     ecx,dword ptr [eax+8]

win32k!NtUserBuildHwndList:
bf835f21 6a14            push    14h
bf835f23 68e8d798bf      push    offset win32k!`string'+0x550 (bf98d7e8)
bf835f28 e8dbacfcff      call    win32k!_SEH_prolog (bf800c08)
bf835f2d 6a02            push    2
bf835f2f 5f              pop     edi
bf835f30 e825acfcff      call    win32k!EnterCrit (bf800b5a)
bf835f35 a158aa9abf      mov     eax,dword ptr [win32k!gpsi (bf9aaa58)]
bf835f3a f6400208        test    byte ptr [eax+2],8
bf835f3e 0f8547ffffff    jne     win32k!NtUserBuildHwndList+0x1f (bf835e8b)
bf835f44 8b4d0c          mov     ecx,dword ptr [ebp+0Ch]
bf835f47 33db            xor     ebx,ebx
bf835f49 3bcb            cmp     ecx,ebx
bf835f4b 0f8542ffffff    jne     win32k!NtUserBuildHwndList+0x2b (bf835e93)
bf835f51 33c0            xor     eax,eax
bf835f53 395d14          cmp     dword ptr [ebp+14h],ebx
bf835f56 0f84e0000000    je      win32k!NtUserBuildHwndList+0x69 (bf83603c)
bf835f5c ff7514          push    dword ptr [ebp+14h]
bf835f5f e89439feff      call    win32k!PtiFromThreadId (bf8198f8)
bf835f64 8bf0            mov     esi,eax
bf835f66 3bf3            cmp     esi,ebx
bf835f68 0f8423010000    je      win32k!NtUserBuildHwndList+0x65 (bf836091)
bf835f6e 8b463c          mov     eax,dword ptr [esi+3Ch]
bf835f71 3bc3            cmp     eax,ebx
bf835f73 0f8418010000    je      win32k!NtUserBuildHwndList+0x65 (bf836091)
bf835f79 8b4004          mov     eax,dword ptr [eax+4]
bf835f7c 8b4008          mov     eax,dword ptr [eax+8]
bf835f7f 8b4038          mov     eax,dword ptr [eax+38h]
bf835f82 395d08          cmp     dword ptr [ebp+8],ebx
bf835f85 0f85dd000000    jne     win32k!NtUserBuildHwndList+0x70 (bf836068)
bf835f8b 895de4          mov     dword ptr [ebp-1Ch],ebx
bf835f8e 3bc3            cmp     eax,ebx
bf835f90 0f84ad000000    je      win32k!NtUserBuildHwndList+0xaa (bf836043)
bf835f96 395d10          cmp     dword ptr [ebp+10h],ebx
bf835f99 0f8572ffffff    jne     win32k!NtUserBuildHwndList+0xca (bf835f11)
bf835f9f 56              push    esi
bf835fa0 57              push    edi
bf835fa1 50              push    eax
bf835fa2 e861fdffff      call    win32k!BuildHwndList (bf835d08)   <- CALL BuildHwndList
bf835fa7 8bf0            mov     esi,eax
bf835fa9 8975e0          mov     dword ptr [ebp-20h],esi

发生异常时 eax 寄存器值为零。根据 InternalBuildHwndList 函数的指令序列得知 eax 寄存器存储的是 win32k!gptiCurrent 的值,win32k!gptiCurrent 是一个临界变量。在 NtUserBuildHwndList 函数中通过调用 win32k!EnterCrit 进入临界区,用来确保 USER 相关的各种全局资源能够独占访问。win32k!EnterCrit 通过调用 KeEnterCriticalRegion 进入临界区并通过 ExAcquireResourceExclusiveLite 函数对 gpresUser 资源实施共享锁定之后,调用 PsGetThreadWin32Thread 获取当前线程的线程信息结构体 tagTHREADINFO 指针并赋值给 win32k!gptiCurrent 变量。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
kd> u win32k!EnterCrit
win32k!EnterCrit:
bf800b5a ff1524cb98bf    call    dword ptr [win32k!_imp__KeEnterCriticalRegion (bf98cb24)]
bf800b60 6a01            push    1
bf800b62 ff3520ab9abf    push    dword ptr [win32k!gpresUser (bf9aab20)]
bf800b68 ff159ccb98bf    call    dword ptr [win32k!_imp__ExAcquireResourceExclusiveLite (bf98cb9c)]
bf800b6e ff1560cb98bf    call    dword ptr [win32k!_imp__PsGetCurrentThread (bf98cb60)]
bf800b74 50              push    eax
bf800b75 ff15f4d098bf    call    dword ptr [win32k!_imp__PsGetThreadWin32Thread (bf98d0f4)]
bf800b7b a318ae9abf      mov     dword ptr [win32k!gptiCurrent (bf9aae18)],eax
bf800b80 c3              ret

PsGetThreadWin32Thread 函数的指令非常简单:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
kd> u PsGetThreadWin32Thread
nt!PsGetThreadWin32Thread:
8052883a 8bff            mov     edi,edi
8052883c 55              push    ebp
8052883d 8bec            mov     ebp,esp
8052883f 8b4508          mov     eax,dword ptr [ebp+8]
80528842 8b8030010000    mov     eax,dword ptr [eax+130h]
80528848 5d              pop     ebp
80528849 c20400          ret     4

获取当前线程 KTHREAD + 0x130 位置的域的值并作为返回值返回。根据 Windows XP 的定义,该偏移位置存储的是 Win32Thread 指针。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
kd> dt _KTHREAD
nt!_KTHREAD
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 MutantListHead   : _LIST_ENTRY
   +0x018 InitialStack     : Ptr32 Void
   +0x01c StackLimit       : Ptr32 Void
   ...
   +0x12c CallbackStack    : Ptr32 Void
   +0x130 Win32Thread      : Ptr32 Void

然而在 InternalBuildWndList 函数中对 win32k!gptiCurrent 指针变量进行操作之前,并未判断该指针是否为空,直接操作则必然引发异常。事实上,在 Windows XP 操作系统中,Win32k 中的很多例程其默认为在调用自己之前,gptiCurrent 已经是一个有效的值,所以并不进行必要的判断。

然而如果当前线程不是 GUI 线程,如控制台应用程序进程的线程,它们的 Win32Thread 域始终是空值,如果不进行判断就直接在内核中调用 NtUserBuildWndList 等函数,就将直接引发前面提到的 BSOD 异常。幸运的是,用户层进程在通过系统服务调用位于 Win32k.sys 中的系统例程时,其通常通过 User32.dll 或 Gdi32.dll 等动态库模块中的函数来进行,此时该线程应已在内核通过 PsConvertToGuiThread 等函数将其转换成 GUI 线程。

在 Windows 中,所有的线程作为非 GUI 线程启动。如果某线程访问任意 USER 或 GDI 系统调用(调用号 >= 0x1000),Windows 将提升该线程为 GUI 线程(nt!PsConvertToGuiThread)并调用进程和线程呼出接口。

这样一来,通过常规方式从用户层到内核层的标准系统调用来调用 User 或 GDI 的系统服务时,操作系统负责处理相关的初始化和转换操作。但像在我们的驱动程序中执行全局的调用时,就需要对调用的环境(进程和线程)进行必要的判断,而不能轻易地擅自直接进行调用。

  • THE END -

文章链接: https://cloud.tencent.com/developer/article/2191132

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-04-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
大灰狼远控木马源码分析
大灰狼远程控制木马是一个较为常见的远控工具,不同的木马病毒团伙对其定制改造后发布了诸多变种。本章将从启动过程、通信协议、远控功能三个方面逆向分析该木马的实现原理。
范蠡
2021/12/08
6.8K0
大灰狼远控木马源码分析
Windows提权以及逆向
一个小白对于Windows提权以及逆向的学习,文章中说的思路可能有不对的,希望不要介意!!!
网e渗透安全部
2019/12/09
7990
Windows提权以及逆向
简单病毒样本分析
行为分析主要是通过系统监控软件,监控系统中各资源或环境的变化,比如监控注册表、监控文件、监控进程,以及监控网络等。当然了,还可以通过 HIPS 软件来监控病毒的行为。各类系统监控工具或者是 HIPS 软件都是在系统的不同层面上进行了不同方式的 HOOK。比如说在内核层进行 HOOK,在应用层进行 HOOK。HOOK 的方式也是多样化的,比如直接 HOOK API 函数,或者 HOOK SSDT 表中的内核函数。
码农UP2U
2020/08/26
2.2K0
CoInitialize浅析一
大家都知道程序中若要使用COM组件则必需要先调用CoInitialize,该函数主要是用来初始化COM执行环境。但这个函数的作用域是以线程为单位还是以进程为单位呢?或许大家已经通过測试程序摸索出答案,没错,是以线程为单位。今天我们就略微再深入一下,通过分析CoInitialize的详细实现来印证我们的想法。
全栈程序员站长
2022/09/07
7370
x32下逆向 PsSetCreateProcessNotifyRoutine 进程钩子
因自己工作,可能后面会写ark工具.所以周六周日没事就逆向了一下进程回调数组. 虽然资料很多.但是自己动手自己明白.总比别人给的好.
IBinary
2019/07/30
1.2K0
x32下逆向 PsSetCreateProcessNotifyRoutine 进程钩子
DllMain中不当操作导致死锁问题的分析--DisableThreadLibraryCalls对DllMain中死锁的影响
        《windows核心编程》作者在讨论DllMain执行序列化的时候,曾说过一个他的故事:他试图通过调用DisableThreadLibraryCalls以使得新线程不在调用DllMain从而解决死锁问题。但是该方案最后失败了。思考作者的思路,他可能一开始认为:因为线程要调用DllMain而加锁,于是windows在发现DllMain不用调用时就不用加锁了。本文将探讨DisableThreadLibraryCalls对DllMain死锁的影响。首先我们需要定位是什么函数调用了DllMain。(转载请指明出于breaksoftware的csdn博客)为了方便分析,我设计了以下代码
方亮
2019/01/16
1.7K0
【C语言】汇编角度剖析函数调用的整个过程
压栈操作,他会改变esp所指向的位置,从而适应栈帧空间的扩大,操作方式就是将操作数直接压栈到栈帧空间
举杯邀明月
2023/04/12
1.7K0
【C语言】汇编角度剖析函数调用的整个过程
CVE-2016-0095从PoC到Exploit
利用Vmware进行双机调试 使用管理员模式运行cmd bcdedit /copy {current} /d “Windwos7[DEBUG]” 开启调试bcdedit /debug ON和bcdedit /bootdebug ON 在Vmware的设备管理添加一个串口\\.\pipe\com_1 执行Windbg.exe -b -k com:port=\\.\pipe\com_1,baud=115200,pipe 注意 vmware 有个坑,默认添加打印机占用串口com1口,所以我们开启内核调试的串口就变
WeaponX
2018/07/11
1.1K0
Win32/AutoRun.Agent.NZ 蠕虫感染文件的简单分析和修复
最近几天上qq总是发现nod32 蹦出个什么发现“Win32/AutoRun.Agent.NZ 蠕虫”这么个东西的提示,刚开始还以为是浏览网页留下的也没在意,今天运行其他软件的时候也发现了这么个东东才知道情况不是自己想的那么简单了。找到被感染的程序和没有感染的做了下比较发现没什么大的差距,很明显不是捆绑了,那就是添加了新代码了,用Peid查看发现程序增加了一个区段
obaby
2023/02/28
4650
菜鸟 学注册机编写之 “MD5”
sc_office_2003_pro 高手不要见笑,仅供小菜玩乐,有不对或不足的地方还请多多指教,不胜感激!   
我是小三
2018/08/08
5270
菜鸟  学注册机编写之 “MD5”
1.7 完善自定位ShellCode后门
在之前的文章中,我们实现了一个正向的匿名管道ShellCode后门,为了保证文章的简洁易懂并没有增加针对调用函数的动态定位功能,此类方法在更换系统后则由于地址变化导致我们的后门无法正常使用,接下来将实现通过PEB获取GetProcAddrees函数地址,并根据该函数实现所需其他函数的地址自定位功能,通过枚举内存导出表的方式自动实现定位所需函数的动态地址,从而实现后门的通用性。
王 瑞
2023/07/05
2500
1.7 完善自定位ShellCode后门
获取网卡mac地址的汇编特征码
获取网卡mac地址一般需要用到IPHLPAPI模块,下面该模块用来获取mac地址的代码(内部名为: IPHLPAPI.GetAdaptersInfo),如果要查找该地址,可以搜索其前24byte的特征码:8B FF 55 8B EC 51 57 8B 7D 0C 85 FF 0F 84 E2 27 00 00 56 8D 45 FC 50 E8(测试无效,特征码会变)。可以用OD附加目标进程,然后按CTRL+N,查看IPHLPAPI的导出函数表,查找GetAdapterInfo地址。
用户2135432
2023/10/21
3960
通过 Windows 用户模式回调实施的内核攻击
这篇文章翻译自一篇多年之前的论文,原文作者是 Tarjei Mandt。原文系统地描述了 win32k 的用户模式回调机制以及相关的原理和思想,可以作为学习 win32k 漏洞挖掘的典范。早前曾经研读过,近期又翻出来整理了一下翻译,在这里发出来做个记录。原文链接在文后可见。
稻草小刀
2022/12/12
1.8K0
通过 Windows 用户模式回调实施的内核攻击
IE漏洞调试之CVE-2013-3893
前言 Windows平台的漏洞挖掘和安全研究中,IE始终是绕不开的话题。IE漏洞就跟adobe系列一样经典,是学习exploit、shellcode的绝佳途径。 在IE漏洞中,UAF即Use-After-Free是最为经典的一类。UAF可以这样简单理解:A先后调用B、C、D三个子函数,B会把A的某个资源释放掉;而D由于判断不严谨即使在B把A的资源释放后依然去引用它,比如某个指针,这时D引用了很危险的悬空指针;C是个什么角色呢?我们可以通过B分配数据。所以利用方法来了:构造奇葩的数据,让A调用B,B把A的某个
FB客服
2018/02/05
1.5K0
IE漏洞调试之CVE-2013-3893
x32下PsSetLoadImageNotifyRoutine的逆向
纯属兴趣爱好.特来逆向玩玩. PsSetLoadImageNotifyRoutine 是内核中用来监控模块加载.操作系统给我们提供的回调. 我们只需要填写对应的回调函数原型即可进行加监控. 既然可以进行监控.那么我们的回调函数存储在哪.这是个问题.所以特来逆向玩玩.
IBinary
2019/08/06
9360
windbg时间穿梭——利用Time Travel Debugging更方便地调试漏洞
下面使用windbg是新版的windbg,叫windbg preview,需要在微软商店(Microsoft Store)中下载安装
用户1423082
2024/12/31
3510
windbg时间穿梭——利用Time Travel Debugging更方便地调试漏洞
mov fs:[0],esp的含义
lea eax,SEH1[ebp] ;自己的异常处理函数地址 push eax ;把该异常处理函数地址压栈 push fs:[0] ;fs:[0]指向的是TIB[Thread information Block]结构中的 ;EXCEPTION_REGISTRATION 结构 mov fs:[0],esp ;让fs:[0]指向一个新的EXCEPTION_REGISTRATION 结构(就像链表插入一个新节点) mov esi,0 ;这两行指令就是用来处罚这个异常处理函数被调用的代码 mov eax,[esi];make a error for SEH
战神伽罗
2019/07/24
2.7K0
VC下提前注入进程的一些方法2——远线程带参数
        在前一节中介绍了通过远线程不带参数的方式提前注入进程,现在介绍种远线程携带参数的方法。(转载请指明出处)
方亮
2019/01/16
9200
汇编角度看函数堆栈调用
带着以下一个问题来探索: (1)形参的内存空间的开辟和清理是由调用方还是由被调用方执行的? (2)主函数调用函数结束后,主函数从哪里开始执行?从头开始还是从调用之后开始? (3)返回值是如何带出来的?
lexingsen
2022/02/24
7470
汇编角度看函数堆栈调用
CVE-2015-2546 UAF Analysis and Exploitation
This article will analyze a UAF vulnerability in win32k Window Manager (User) Subsystem in Windows: CVE-2015-2546. Similar to CVE-2017-0263 analyzed in the previous article, this vulnerability is use-after-free of popup menu tagPOPUPMENU object as well. The analyzing environment is Windows 7 x86 SP1 basic virtual machine.
稻草小刀
2022/12/12
4950
CVE-2015-2546 UAF Analysis and Exploitation
相关推荐
大灰狼远控木马源码分析
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验