|
|
# zTasker 崩溃 Dump 分析报告
> 文件:`zTasker.exe.35144.dmp`
> 大小:21.3 MB · MiniDump(13 个流)· 生成于崩溃后
> 分析方式:无 WinDbg 环境下,纯 Python(标准库)手写 MiniDump 解析器,解析异常流 / 系统信息 / 模块表 / 线程表 / 内存段,并基于 EBP 帧链重建调用栈
---
## 一、结论速览
| 项 | 值 |
|---|---|
| 异常类型 | `EXCEPTION_ACCESS_VIOLATION` (`0xC0000005`) |
| 崩溃类型 | **栈溢出 / 无限递归** |
| 崩溃线程 | **主 UI 线程**(TID `0x691C` / 26908,优先级 0) |
| 进程位数 | x86 / WOW64(在 64 位 Windows 上运行的 32 位进程) |
**根因**:`Utility.dll` 内部一处函数发生**无法收敛的直接自递归**(自调用返回点固定为 `Utility.dll + 0x4DD17`),递归深度约 **17,282 帧**,把 1 MB 的主线程栈彻底压满,最终一次栈底之下的写操作触发 ntdll 栈保护页处理 → 抛出访问越界异常。
崩溃发生在**主 GUI 线程**,调用链顶端是窗口消息派发(`user32!DispatchMessage → UI.dll → zTasker.exe → Utility.dll`),说明很可能是**窗口事件 / 通知回调的“重入”**触发了递归回环。
---
## 二、基本信息
| 项目 | 值 |
|---|---|
| 崩溃进程 | `zTasker.exe` 版本 **2.3.11.5434** |
| 进程 PID | 35144 (`0x8918`) |
| 崩溃线程 | 26908 (`0x691C`) — 主 UI 线程(优先级 0) |
| 进程位数 | x86 / WOW64 |
| 操作系统 | Windows 10.0 Build 19044 (21H2),8 逻辑处理器 |
| 加载模块数 | 162 个 |
---
## 三、异常与寄存器
| 项目 | 值 |
|---|---|
| 异常代码 | `0xC0000005` — EXCEPTION_ACCESS_VIOLATION |
| 异常地址 | `0x77CC5E7C` → `ntdll.dll + 0x45E7C`(栈探测 / 异常处理内部) |
| 违规操作 | 试图 **写入** 地址 `0x01200FFC` |
| 栈底 Esp | `0x01201000` → 违规地址位于 **Esp 之下 4 字节** |
| 判定 | 🔴 栈已耗尽 → 典型栈溢出 |
**寄存器:**
| 寄存器 | 值 | | 寄存器 | 值 |
|---|---|---|---|---|
| Eip | `0x77CC5E7C` | | Esp | `0x01201000` |
| Ebp | `0x01201078` | | Eax | `0x012010E4` |
| Ecx | `0x01310000` | | Edx | `0x0000002C` |
| Ebx | `0x01201080` | | Esi | `0x01310000` |
| Edi | `0x09E66A38` | | | |
> **为何是 ACCESS_VIOLATION 而非 STACK_OVERFLOW (`0xC00000FD`)**:
> 主线程栈被彻底压满、连栈保护页(guard page)也已耗尽,下一次压栈写到了 Esp 之下的地址,由 ntdll 内部抛出 `0xC0000005` 二次机会异常并被转储捕获。
---
## 四、重建调用栈(基于 EBP 帧指针,去重相邻)
```
栈范围: 0x01201000 .. 0x01300000 (1,044,480 字节 / 约 1 MB)
递归深度: 约 17,282 帧 | 自调用返回点 Utility.dll+0x4DD17 连续出现 17,261 次
#0 ntdll.dll +0x45E4E ← 异常分发 / 栈处理内部
#1 ucrtbase.dll+0x30166
#2 Utility.dll +0x862E2
#3 Utility.dll +0x33CC4
#4 Utility.dll +0x4E150
#5 Utility.dll +0x4DCE4
#6 Utility.dll +0x4DD17 ← 自递归返回点(重复 1.7 万次)
#7 Utility.dll +0x4D483
#8 Utility.dll +0x4856E
#9 zTasker.exe +0x2B9A14
#10 zTasker.exe +0x2DDFA4
#11 user32.dll +0x4139B ← DispatchMessage
#12 user32.dll +0x33FF2
#13 user32.dll +0x36193
#14 user32.dll +0x35EA0
#15 UI.dll +0x3FC2F
#16 zTasker.exe +0x3079D9
#17 zTasker.exe +0x40F49C
#18 kernel32.dll +0x200C9 ← BaseThreadInitThunk
#19 ntdll.dll +0x67B4E ← RtlUserThreadStart
#20 ntdll.dll +0x67B1E
#21 0x00000000
```
> 栈顶(#11~#17)是窗口消息派发,说明崩溃由一次 UI 事件 / 通知回调驱动;该回调进入 `Utility.dll` 后陷入自递归。
---
## 五、根因定位建议
1. **定位递归函数**:`Utility.dll + 0x4DD17`(自调用返回点)及其递归体
(`+0x862E2 / +0x33CC4 / +0x4E150 / +0x4DCE4 / +0x4D483 / +0x4856E`)
对应的源码。注意该 DLL 文件版本为 `0.0.0.0`,且转储未携带 PDB,
需用**该版本的 `.map` 文件**或**重新以 `/MAP` 或带 PDB 编译**来解析具体函数名。
2. **修复递归**:为该函数增加**最大递归深度保护**、**终止条件校验**,以及对空 / 异常数据、**循环引用**的提前返回(防御式编程)。
3. **排查“重入”**:因栈顶是窗口消息派发,重点检查
“**处理某事件时又同步触发了同一事件 / 通知**”的回环,例如
`SendMessage` 重入、事件订阅未去重、UI 更新回调互相调用。
4. **临时缓解**:可为主线程调大栈(链接选项 `/STACK`),但这只能延后崩溃,
必须修复递归本身;建议同时接入崩溃收集 / Application Verifier 复现触发场景。
---
## 六、值得注意的已加载模块
| 模块 | 说明(推断) |
|---|---|
| `zTasker.exe` (2.3.11.5434) | 主程序 |
| `Utility.dll` (0.0.0.0) | 🟠 崩溃根因所在的核心工具库 |
| `UI.dll` (0.0.0.0) | UI 框架(栈顶经其进入) |
| `HotKey` / `TimeSync` / `SndPlay` / `Mail` / `Http` / `Ping` / `ZipWrapper` / `asl` / `Volume` / `ModernZip` | 自动化动作模块(热键、定时、声音、邮件、HTTP、Ping、压缩…) |
| `opencv_core2410` / `opencv_highgui2410` / `opencv_imgproc2410` / `opencv_video2410` | OpenCV 2.4.10(图像 / 摄像头自动化) |
| `sqlite3.dll` / `libssl-3` / `libcrypto-3` | 数据库与 TLS |
| `1_SangforTcp` / `1_SangforNsp` / `sangforvpnlibcrypto-1_1` / `SangforUDProtectEx` | 深信服 VPN 相关组件 |
| `bass.dll` | 音频播放库 |
> 完整 162 个模块及偏移见随附的 `dump_analysis_zTasker_35144.txt`。
---
## 附:解析方法说明
- 无 WinDbg/cdb 环境,使用纯 Python(标准库)手写 MiniDump 解析器。
- 关键结构偏移:
- x86 `CONTEXT`:Eip@184,Esp@196,Ebp@180
- x64 `CONTEXT`:Rip@248,Rsp@152,Rbp@160
- `MINIDUMP_MODULE` = 108 字节;CvRecord(RSDS / PDB)在模块条目 offset 76 / 80
- 栈重建使用 **EBP 帧链**:`read32(frame)` = 上一层 EBP,`read32(frame+4)` = 返回地址
- 所有偏移均为“模块基址 + 偏移”,需配合该版本符号(PDB / MAP)才能得到函数名。
|
|