背景
用户反馈Qt程序启动即必现崩溃,用户环境为Dell笔记本电脑,具有英特尔&英伟达双显卡,win10 64位
分析
首先崩溃后,需要对用户提供的dmp文件进行分析
dmp分析
打开用户提供的dmp文件,发现栈帧被破坏导致堆栈无法正常显示
这里需要手动还原堆栈,首先输出!teb
拿到线程环境块,然后dps StackLimit StackBase
拿到栈上所有信息,输出到文件中
> .logopen ${path}stack.txt
> !teb
> dps StackLimit StackBase
> .logclose
一般进程启动都是从ntdll!__RtlUserThreadStart
这里开始,我们从这里进行栈底还原
029ff790 029ff7ec
029ff794 773280ce ntdll!__RtlUserThreadStart 0x2f
029ff5ec 029ff634
029ff5f0 00b73c7c YOUR_RPOGRAM_NAME!main 0x2fc
***
029ff1d0 00000000
029ff1d4 7a43d8ec Qt5Gui!QWindow::create 0xc [C:Usersqtworkqtqtbasesrcguikernelqwindow.cpp @ 651]
029ff184 02bd6718
029ff188 7a43d948 Qt5Gui!QWindowPrivate::create 0x58 [C:Usersqtworkqtqtbasesrcguikernelqwindow.cpp @ 527]
上面还原问题隐藏了业务敏感信息,不过问题大同小异
还原到这里基本上可以确定出问题的代码是在创建第一个窗口,同时也符合我们现网一直观测到的一个问题
代码语言:shell复制0f 0260d810 56f5552b 0260d824 0260d900 047ba960 atiumdag 0x2c77
10 0260d84c 56f5391f 047ba960 00000000 042807d0 atiu9pag 0x552b
*** WARNING: Unable to verify timestamp for aticfx32.dll
11 0260d868 77ae698c 0260d900 00000000 c0000001 atiu9pag 0x391f
12 0260d87c 77ae57e1 0260d900 0260dbe8 613fb634 aticfx32 0x3698c
*** WARNING: Unable to verify checksum for d3d9.dll
13 0260d888 613fb634 0260d900 082656b8 082656d8 aticfx32 0x357e1
14 0260dbe8 613fb431 e701114a 082656d8 082656b0 d3d9!CreateDeviceLHDDI 0x2dc
15 0260e440 613fa2cc e701114a 08265678 00000000 d3d9!D3D9CreateDirectDrawObject 0x1ae
16 0260e8f0 613fb146 08265380 00000000 00000000 d3d9!FetchDirectDrawData 0xc9
17 0260e924 613fc6d7 082651c0 08260810 00000001 d3d9!InternalDirectDrawCreate 0x22e
18 0260eb2c 613fc46f 00000020 00000000 0260ecd0 d3d9!CEnum::CEnum 0x479
19 0260ec74 61450a93 00000020 00000000 0260ec88 d3d9!Direct3DCreate9Impl 0x19d
*** WARNING: Unable to verify checksum for qwindows.dll
1a 0260ec8c 56fd7fe2 00000020 0260f4c4 047a7c68 d3d9!Direct3DCreate9 0x31
1b 0260eca0 56fd859c ffffffff 0260f5b0 00000000 qwindows!QDirect3D9Handle::QDirect3D9Handle 0x72 [C:Usersqtworkqtqtbasesrcpluginsplatformswindowsqwindowsopengltester.cpp @ 108]
1c 0260f498 56fa4367 0260f4c4 0260f6f8 0260f5b0 qwindows!GpuDescription::detect 0x6c [C:Usersqtworkqtqtbasesrcpluginsplatformswindowsqwindowsopengltester.cpp @ 128]
1d 0260f4e0 56fa2ae0 047a7c68 ffffffff 047a7c68 qwindows!QWindowsWindow::forcedScreenForGLWindow 0x47 [C:Usersqtworkqtqtbasesrcpluginsplatformswindowsqwindowswindow.cpp @ 1519]
1e 0260f588 56fa393f 0260f5e4 047a7c68 0260f5b0 qwindows!calcPosition 0x60 [C:Usersqtworkqtqtbasesrcpluginsplatformswindowsqwindowswindow.cpp @ 576]
1f 0260f6d0 56fa33a1 0260f850 047a7c68 0260f784 qwindows!WindowCreationData::create 0x46f [C:Usersqtworkqtqtbasesrcpluginsplatformswindowsqwindowswindow.cpp @ 800]
20 0260f710 56fab880 0260f850 047a7c68 0260f784 qwindows!QWindowsWindowData::create 0x71 [C:Usersqtworkqtqtbasesrcpluginsplatformswindowsqwindowswindow.cpp @ 1557]
*** WARNING: Unable to verify checksum for Qt5Gui.dll
21 0260f898 5780d948 047a7c68 02a54a40 047a7c68 qwindows!QWindowsIntegration::createPlatformWindow 0x300 [C:Usersqtworkqtqtbasesrcpluginsplatformswindowsqwindowsintegration.cpp @ 348]
22 0260f8e4 5780d8ec 00000000 00000000 0082b377 Qt5Gui!QWindowPrivate::create 0x58 [C:Usersqtworkqtqtbasesrcguikernelqwindow.cpp @ 527]
23 0260f8f0 0082b377 f4642ecd 047733f0 047733f0 Qt5Gui!QWindow::create 0xc [C:Usersqtworkqtqtbasesrcguikernelqwindow.cpp @ 651]
可以发现创建第一个窗口,qt会去加载d3d9的模块,然后加载到了显卡驱动,为什么要做这件事情呢?
review一下这里的代码,创建第一个窗口时,qt需要确认这个窗口需要出现在显示器的哪里,调用到了calcPosition
然后因为顶层窗口的原因,这里一定会走到forcedScreenForGLWindow
,这里是要去推测是否有多屏场景,需要出现在主屏,主要是为了解决多屏多显卡场景下的崩溃问题:QTBUG-50371
接着往下就会去遍历每个显示器,利用d3d的接口来实现,需要加载d3d9.dll
创建d3d9.dll!Direct3DCreate9函数创建句柄时就会加载英特尔驱动模块
创建句柄会最终调用到d3d9!CreateDeviceLHDDI上来创建设备驱动相关句柄,然后调用到显卡驱动模块内部
问题分析
用户出问题的模块在于Dell电脑上装了笔记本厂提供的定制英特尔显卡驱动(27版本)模块,导致的崩溃问题,类似问题也有大量的反馈:
英特尔-WPF应用崩溃、英特尔-QT崩溃、英特尔-D3d崩溃
基本上回复都是升级驱动版本,回滚驱动版本等操作修复问题,但是现网观测不仅仅英特尔驱动会导致我们崩溃,英伟达、AMD显卡模块同样会有问题,所以这里考虑屏蔽驱动模块的加载来解决问题,主要因为我们不会用到Opengl,去除风险较低
解决
因为这里d3d9.dll是显式调用LoadLibrary来实现加载,而我们防注入模块是Hook这个系统API来实现防注入,正好比较合适,增加d3d9.dll到我们防注入黑名单即可解决,后续考虑动态防注入名单配置来实现灰度。
参考
1、WinDbg手动修复堆栈_windbg 堆栈修复-CSDN博客
2、编程技术-Windbg调试栈溢出_游戏逆向|游戏安全|yxfzedu.com
3、手动遍历堆栈 - Windows drivers | Microsoft Learn
4、x64 手动堆栈重建和堆栈遍历 |Microsoft 学习