转载自:https://www.anquanke.com/post/id/179748

《Dive into Windbg》是一系列关于如何理解和使用Windbg的文章,主要涵盖三个方面:

  • 1、Windbg实战运用,排查资源占用、死锁、崩溃、蓝屏等,以解决各种实际问题为导向。
  • 2、Windbg原理剖析,插件、脚本开发,剖析调试原理,便于较更好理解Windbg的工作机制。
  • 3、Windbg后续思考,站在开发和逆向角度,谈谈软件开发,分享作者使用Windbg的一些经历。

第三篇 《Explorer无法启动排查》

涉及知识点:消息机制、进程退出过程、MUI、国际化等。

起因

一台Windows服务器迁移后,发现资源管理器explorer启动不了。手动运行依然无法启动,在任务管理器能看到进程启动后消失,服务器是Server2008 R2,64位。

初步排查

关于进程退出,能想到其大致流程:

exit => ExitProcess => TerminateProcess => NtTerminateProcess => PspTerminateThreadByPointer => 线程内 PspExitNormalApc => PspExitThread =>
释放各种资源、通知调试器等 => KeTerminateThread => 交出调度权

_ExitProcess伪代码流程如下:

//from: https://bbs.pediy.com/thread-188064-1.htm#1287019
int __stdcall _ExitProcess(UINT ExitCode)
{int result; // eax@1char v2; // [sp+10h] [bp-DCh]@2UINT v3; // [sp+38h] [bp-B4h]@2CPPEH_RECORD ms_exc; // [sp+D4h] [bp-18h]@2result = __security_cookie;if ( !BaseRunningInServerProcess ){RtlAcquirePebLock();ms_exc.disabled = 0;NtTerminateProcess(0, ExitCode);LdrShutdownProcess();v3 = ExitCode;CsrClientCallServer(&v2, 0, 65539, 4);NtTerminateProcess(-1, ExitCode);ms_exc.disabled = -1;result = RtlReleasePebLock();}return result;
}

因为是在迁移机器,需要开关机,猜测是explorer产生已知异常导致的正常退出,可能是什么东西被破坏了,常见的像注册表配置、文件等,这种在关机时最可能会出现。

对于这种稳定复现的问题,先挂个调试器看看再说,因为此时资源管理器不在,用热键调出任务管理器,运行windbg,下面是具体调试过程:

直接g调试运行,发现进程直接退出,调试器中断在NtTerminateProcess,通过栈回溯可知是从main函数中的正常退出,跟之前猜想一样,说明这是程序内部已知的错误。

仔细观察发现,调试器捕获到一处异常:

(ae4.284): Unknown exception - code 000006ba (first chance)

难道跟这个异常有关?调试器没断下来,只在first chance,说明异常被内部SEH捕获了,没有到达second chance,还是看下异常是什么。使用sx命令查看当前windbg调试事件设置:

这里也说明了未知异常只在调试器收到second chance(即SEH不处理)时才break下来,输入sxe *,让其直接中断,重新调试运行。

观察异常堆栈,原来是音频服务RPC Server端抛了异常,不过这跟explorer无法启动应该没有关联。

输入!teb,看看LastError有没有什么线索:

LastStatusValue是c0000034,这里使用一款开源工具:OpenArk
https://github.com/BlackINT3/OpenArk

使用.err命令或者直接查看NTSTATUS。

找不到文件?还是先跟进explorer去看看,至少目前有一点线索。

步步追踪

在main函数中直接退出,想到WinProc的各种switch,很多应该都被折腾过。不过有符号已经能省很多事了,想想那些没符号全是各种条件断点的crack日子….

通过栈回溯找到调用ExitProcess的地方,ub查看上面的代码,观察哪个点最接近,也可在靠近GetCommandLineW等附近下断点,这样就不用从OEP去跟了。

对于这种必现问题,有两种方法:

1、反汇编代码,可尝试多次下断点、单步跟踪,找到可疑的地方,尤其是要检查一些返回值和LastError。

2、wt直接跟踪,有符号的话,根据函数名找到可疑的地方,结合方法1。

Tips:用bu命令下断点,然后保存到workspace,下次调试继续生效,注意windbg根据符号偏移下断点可能会有bug,以后有机会再来探讨,若有需要可以关掉ASLR。

跟踪发现是CreateDesktopAndTray函数返回NULL,导致退出。

这是第一个关键点,进入函数,发现是因为explorer!c_tray+0x8为NULL导致,一般情况我们可能会考虑内存访问断点来找,不过这种情况下,执行失败后explorer!c_tray可能不会赋值。

观察explorer!CTray::SyncThreadProc线程,c_tray是该线程的一个参数,那么线程应该在初始化c_tray信息,继续跟踪。

最后发现SyncThreadProc调用SHFusionCreateWindowEx函数创建窗口失败了,窗口句柄是NULL,调用之前和调用之后,LastStatusValue都是c0000034,说明开始发现的这个值有误。

跟进SHFusionCreateWindowEx,单步走过CreateWindowEx。有意思的事发生了,得到的窗口句柄rax却不是0,为什么?

说明这个函数内部递归调用,查看栈回溯如下:

说到Windows消息机制,就想到了KeUserModeCallback、KiUserCallbackDispatcher、KernelCallbackTable、LoadLibrary注入、消息钩子等等。
感兴趣的可以看看《KeUserModeCallback用法详解》https://bbs.pediy.com/thread-104918.htm。
NT6之后虽有些不同,但实现思路是一致的。

看到这个_fnINLPCREATESTRUCT蛋疼的命名,我便想起了David Culter看到GUI部门的表情,还是ntoskrnl内部颇有美感。

_fnINLPCREATESTRUCT这个函数便在分发WM_CREATE消息,具体可以看看KernelCallbackTable表,在这个点可以做一些拦截,什么消息钩子、窗口子类化。

接下来思路就清晰了,肯定是某个窗口创建失败了,在调用CreateWindowExW处下条件断点。

bu *** ".if(rax == 0){}.else{gc}"

断下来后,继续查看TEB LastStatus,最后发现是c00b0006错误。

15105 ERROR_MUI_FILE_NOT_LOADED <--> c00b0006 STATUS_MUI_FILE_NOT_LOADED

提示MUI文件不存在,到系统Windows目录查看,explorer.exe.mui果然不在,拷贝之,再次验证,explorer启动成功。。。

MUI文件本身也是PE文件,里面含有语言资源,为了解决国际化,动态分离程序和语言文件,这个和”大名鼎鼎”的LPK有关联。

MUI可以参考:https://docs.microsoft.com/en-us/windows/desktop/intl/overview-of-mui

结束

很多时候,看似一些难缠的问题背后往往原因很简单。积累经验,总结反思,培养测量意识,善用调试跟踪工具,自动化去解决问题。

比如这次我可以在TEB的LastError用内存访问断点,也可以用Procmon监视文件访问,解决问题的方法基本是:猜想 + 论证 =>…=> 解决。

Thanks for reading。

参考资料:
Google
MSDN
Windbg Help
WRK

《Dive into Windbg系列》Explorer无法启动排查相关推荐

  1. RabbitMQ系列之【启动过程中遇到问题及解决方案】

    RabbitMQ系列之[启动过程中遇到问题及解决方案] 参考文章: (1)RabbitMQ系列之[启动过程中遇到问题及解决方案] (2)https://www.cnblogs.com/feixiabl ...

  2. 开机explorer无法启动,无法进入桌面

    开机explorer无法启动,无法进入桌面,在任务管理器里运行explorer也无法启动,安全模式也一样 开机explorer无法启动,无法进入桌面,在任务管理器里运行explorer也无法启动,安全 ...

  3. Netty 源码解析系列-服务端启动流程解析

    netty源码解析系列 Netty 源码解析系列-服务端启动流程解析 Netty 源码解析系列-客户端连接接入及读I/O解析 五分钟就能看懂pipeline模型 -Netty 源码解析 1.服务端启动 ...

  4. [CLR团队公告]CLR基础研究团队:精品系列推广活动启动

    博客园CLR基础研究团队|CLR团队精品系列 CLR团队精品系列推广活动启动 发布日期:2007.9.14 发布人:Anytao 各位队友.园友: 大家好,CLR基础研究团队,经过1-2个月的发展,队 ...

  5. ansible学习系列之顺利启动后台程序

    文章目录 ansible系列文章 场景 环境 原因 尝试 添加`nohup`指令 `source`环境变量 结果 总结 参考链接 随缘求赞 ansible系列文章 ansible学习系列之tags的使 ...

  6. ElasticSearch启动该正常无法连接或无法正常启动排查方案

    ElasticSearch启动该正常无法连接或无法正常启动排查方案 文章目录 ElasticSearch启动该正常无法连接或无法正常启动排查方案 一.8.2版本及以上默认https链接 二. JDK版 ...

  7. android packagemanagerservice目录,Android重学系列 PackageManagerService的启动与安装(下)

    前言 PMS installStage PMS中的安装步骤 void installStage(String packageName, File stagedDir, IPackageInstallO ...

  8. kubelet 无法启动排查

    问题描述 使用ansible安装Kubernetes,最后出现如所示报错,提示kubelet启动异常 TASK [kube-node : 轮询等待kubelet启动] **************** ...

  9. Spark系列之Spark启动与基础使用

    title: Spark系列 第三章 Spark启动与基础使用 3.1 Spark Shell 3.1.1 Spark Shell启动 安装目录的bin目录下面,启动命令: spark-shell $ ...

最新文章

  1. session一致性架构设计极简教程
  2. 将一个Excel文件分隔成多个
  3. HttpServletRequest简述
  4. android ui stencil kit 下载,实用的iOS6/iPhone5 GUI/iPad PSD以及其它版本素材
  5. 使用NBAR更有效的识别与封堵网络应用
  6. 怎么学好python leetcode的题目太难了_为什么leetcode中的python解法过于pythonic,而忽略了算法题主要关注的复杂度问题?...
  7. 计算机生成兵力方法,计算机生成兵力平台体系结构技术研究
  8. TensorFlow精进之路(九):TensorFlow编程基础
  9. 数据结构上机实践第二周项目2- 程序的多文件组织
  10. nyoj412 Same binary weight(bitset类运用)
  11. vivoy67Android7升级包,vivoy67刷机包
  12. iOS 音频播放时听筒及扬声器切换
  13. php接dicom,DICOM医学图像处理:WEB PACS初谈四,PHP DICOM Class
  14. 2A锂电池充电管理芯片,具有恒压/恒流充电模式的充电管理 IC
  15. Lingo教育版免费申请流程
  16. RK3288 Android5.1 串口接收数据不是一次性收到问题
  17. 选课系统软件测试计划规划,职业生涯规划测评系统 测评软件
  18. 论文Express | 谷歌DeepMind最新动作:使用强化对抗学习,理解绘画笔触
  19. python 课程设计题目_数据分析师能力培养:业务与技术的完美结合
  20. 判断图有无环_21考研有机化学打卡第四题——芳香性判断

热门文章

  1. matlab的GUI设计
  2. Maven打包pom里面配置exclude 排除掉环境相关的配置文件
  3. 在Android上设置imageview的透明背景
  4. 考研复试——概率论(2)
  5. 样机模板素材哪家强?Top4都在这!
  6. Linux c 停车场管理系统
  7. 刷爆了!程序员都在点赞的Python学习图谱!你安利了吗?
  8. 白盒测试的用例设计方法
  9. 中国网络安全相关政策法规(2020第一季度)
  10. vue项目使用大华摄像头怎样初始化_【译】如何使用Vue捕获网络摄像头视频