首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
编程技术
发新帖
1
2
[原创]无调试符号的崩溃分析:从汇编到结构体推断
发表于: 2025-10-17 18:16
1761
[原创]无调试符号的崩溃分析:从汇编到结构体推断
_THINCT
2025-10-17 18:16
1761
# 程序崩溃分析报告 ## 背景 突然接到一个任务:分析程序启动后崩溃的问题。 **遇到的困难:** - 程序使用 **MinGW** 编译,不带 PDB 调试符号文件 - 崩溃处无法直接定位到源码信息 - 只能通过反汇编代码进行分析 ## 1. 崩溃现场 - 反汇编代码 ```assembly .text:00000000004A7C8F mov rdx, rsi .text:00000000004A7C92 call rdi ; QString::operator=(QString const&) .text:00000000004A7C94 mov r12, cs:off_6F6D20 ; ⚠️ 崩溃点 .text:00000000004A7C9B lea rdx, [rsi+8] .text:00000000004A7C9F lea rcx, [r12+8] .text:00000000004A7CA4 call rdi ; QString::operator=(QString const&) .text:00000000004A7CA6 lea rdx, [rsi+10h] .text:00000000004A7CAA lea rcx, [r12+10h] .text:00000000004A7CAF call rdi ; QString::operator=(QString const&) .text:00000000004A7CB1 lea rdx, [rsi+18h] .text:00000000004A7CB5 lea rcx, [r12+18h] .text:00000000004A7CBA call rdi ; QString::operator=(QString const&) .text:00000000004A7CBC lea rdx, [rsi+20h] .text:00000000004A7CC0 lea rcx, [r12+20h] .text:00000000004A7CC5 call rdi ; QString::operator=(QString const&) .text:00000000004A7CC7 lea rdx, [rsi+28h] .text:00000000004A7CCB lea rcx, [r12+28h] .text:00000000004A7CD0 call rdi ; QString::operator=(QString const&) .text:00000000004A7CD2 lea rdx, [rsi+30h] .text:00000000004A7CD6 lea rcx, [r12+30h] .text:00000000004A7CDB call rdi ; QString::operator=(QString const&) .text:00000000004A7CDD lea rdx, [rsi+38h] .text:00000000004A7CE1 lea rcx, [r12+38h] .text:00000000004A7CE6 call rdi ; QString::operator=(QString const&) .text:00000000004A7CE8 lea rax, aDefault_9 ; "default" ``` ### 1.1 崩溃位置 **崩溃指令:** ```assembly .text:00000000004A7C94 mov r12, cs:off_6F6D20 ``` 程序在访问全局变量地址 `off_6F6D20` 时崩溃。该指令试图从全局偏移地址加载指针到 `r12` 寄存器,但访问失败。 ## 2. 反汇编代码特征分析 ### 2.1 识别出 8 次连续的 QString 赋值操作 | 赋值次序 | 偏移量 | 汇编指令 | |---------|--------|----------| | 第1次 | 0x0 | `lea rcx, [r12+0]` | | 第2次 | 0x8 | `lea rcx, [r12+8]` | | 第3次 | 0x10 | `lea rcx, [r12+10h]` | | 第4次 | 0x18 | `lea rcx, [r12+18h]` | | 第5次 | 0x20 | `lea rcx, [r12+20h]` | | 第6次 | 0x28 | `lea rcx, [r12+28h]` | | 第7次 | 0x30 | `lea rcx, [r12+30h]` | | 第8次 | 0x38 | `lea rcx, [r12+38h]` | ### 2.2 关键特征 #### 固定间隔 8 字节 - 每个偏移量相差 **8 字节** - 这正好是 64 位系统中 `QString` 对象的大小 - `QString` 本质上是指针包装类 #### 结构体赋值的典型模式 这种连续、固定偏移的成员赋值,是 **C++ 结构体拷贝构造或赋值操作**的特征。编译器会为每个成员生成单独的赋值代码。 ### 2.3 初步推断 这是一个包含 **8 个 QString 成员的结构体赋值操作**。 ## 3. 源代码定位与结构体反推 ### 3.1 在源码中寻找匹配的赋值模式 根据反汇编代码的特征(8 个 QString 成员的连续赋值),在源码中搜索类似的赋值模式。 找到了 `taskform.cpp` 中 `on_btn_save_2_clicked()` 函数的代码: ```cpp void taskForm::on_btn_save_2_clicked() { ToolParam tp; tp.model = ui->cb_toolmodel->currentText(); tp.tool_accuracy = tool_accuracy; tp.tool_basic = tool_basic; tp.inertial_coefficient = ui->le_inertial_coefficient->text(); tp.unscrew_speed = ui->le_unscrew_speed->text(); tp.display_dev = ui->le_display_dev->text(); tp.tool_compnate = ui->le_tool_compnate->text(); tp.tool_flexibility = ui->le_tool_flexibility->text(); // ... } ``` **关键发现:** - 对 `ToolParam` 类型的对象 `tp` 进行赋值 - 共赋值 **8 个成员** - 所有成员都是 **QString 类型** ### 3.2 反推 ToolParam 结构体定义 根据上述代码,可以反推出 `ToolParam` 结构体的定义应该是: ```cpp typedef struct _ToolParam { QString model; // 偏移 0x0 - 第1个QString QString tool_accuracy; // 偏移 0x8 - 第2个QString QString tool_basic; // 偏移 0x10 - 第3个QString QString inertial_coefficient; // 偏移 0x18 - 第4个QString QString unscrew_speed; // 偏移 0x20 - 第5个QString QString tool_flexibility; // 偏移 0x28 - 第6个QString QString tool_compnate; // 偏移 0x30 - 第7个QString QString display_dev; // 偏移 0x38 - 第8个QString } ToolParam; ``` ### 3.3 验证推断 将反推出的结构体与反汇编代码进行对比: | 反汇编偏移 | 推断的成员 | 类型 | 匹配情况 | |-----------|-----------|------|---------| | 0x0 | model | QString | ✅ | | 0x8 | tool_accuracy | QString | ✅ | | 0x10 | tool_basic | QString | ✅ | | 0x18 | inertial_coefficient | QString | ✅ | | 0x20 | unscrew_speed | QString | ✅ | | 0x28 | tool_flexibility | QString | ✅ | | 0x30 | tool_compnate | QString | ✅ | | 0x38 | display_dev | QString | ✅ | **完美匹配!** 在工程中找到了完全一致的结构体定义。 - 成员数量:8 个 ✅ - 成员类型:全部为 QString ✅ - 偏移量:与反汇编代码一致 ✅ ### 3.4 定位问题代码 根据反推出的结构体定义,继续在源码中查找 `ToolParam` 的使用位置。 在 `taskform.cpp` 的构造函数中找到问题代码: ```cpp // taskform::taskForm() 构造函数 int tool_model = (int(toolinfo.tool_type._value)) / 10; QList<QString> models; int index = -1; for (int i = 0; i < tps.count(); ++i) { if (tps.at(i).model.contains(QString::number(tool_model))) { index = i; } } qDebug() << "--->index = " << index; curtps = tps.at(index); // ⚠️ 当 index=-1 时,访问越界 qDebug() << "index=" << index << tool_model << tps.at(index).model; if (index != -1) { int i = ui->cb_toolmodel->findText(tps.at(index).model); ui->cb_toolmodel->setCurrentIndex(i); } ui->cb_toolmodel_2->addItem(tps.at(index).model); ``` **问题代码:** ```cpp curtps = tps.at(index); // 当 index=-1 时触发数组越界 ``` ## 4. 崩溃原因分析 ### 4.1 内存访问流程 ```assembly mov r12, cs:off_6F6D20 ; 从全局地址 off_6F6D20 加载目标对象指针 ``` - `off_6F6D20` 是全局变量地址,对应源码中的 `curtps` - `r12` 后续被用作目标对象的基址 - **崩溃原因**:这个地址无效(空指针/野指针/已释放内存) ### 4.2 根本原因链 1. **程序启动初始化**:`taskForm` 构造函数执行 2. **查找工具型号**:遍历 `tps` 列表寻找匹配的 `tool_model` 3. **未找到匹配项**:`index` 保持为 `-1` 4. **数组越界访问**:`tps.at(-1)` 触发未定义行为 5. **野指针赋值**:将无效对象赋值给全局变量 `curtps` 6. **延迟崩溃**:后续代码访问 `curtps` 时,尝试访问无效内存地址 7. **程序崩溃**:访问 `off_6F6D20` 时触发内存访问异常 ### 4.3 问题代码流程图 ``` 启动程序 ↓ taskForm 构造函数 ↓ 查找 tool_model 匹配项 ↓ 未找到 (index = -1) ↓ tps.at(-1) ← 数组越界 ↓ 返回无效对象/野指针 ↓ curtps = [无效对象] ← 污染全局变量 ↓ 后续访问 curtps ↓ mov r12, cs:off_6F6D20 ← 尝试加载无效地址 ↓ 程序崩溃 ``` ## 5. 结论 ### 5.1 崩溃定位 程序在执行 **ToolParam 结构体赋值操作**时崩溃,具体位置为: - **文件**:`taskform.cpp` - **函数**:`taskForm::taskForm()` 构造函数 - **代码行**:`curtps = tps.at(index);` ### 5.2 技术原因 这段反汇编代码正在执行一个包含 **8 个 QString 成员的 ToolParam 结构体赋值操作**: - **源对象**:`tps.at(index)` - 由于 `index=-1` 导致越界访问 - **目标对象**:全局变量 `curtps`(对应 `off_6F6D20`) - **崩溃时机**:尝试将无效对象赋值给全局变量时,访问无效内存地址 ### 5.3 分析方法总结 在没有调试符号的情况下,通过以下手段成功定位问题: 1. **反汇编代码模式识别**:识别出 8 次固定间隔的 QString 赋值 2. **源码搜索**:在代码中找到匹配的赋值模式(`on_btn_save_2_clicked` 函数) 3. **结构体反推**:根据源码中的赋值操作反推出 `ToolParam` 结构体定义 4. **验证假设**:确认反推的结构体与反汇编代码完全匹配 5. **定位崩溃代码**:根据结构体类型找到问题代码位置 6. **逻辑分析**:分析构造函数中的数组访问逻辑,确认 `index=-1` 会触发越界访问 **关键技巧:** 在没有直接获得结构体定义的情况下,通过实际使用代码反推数据结构,这是逆向分析的核心思路。 --- ## 6. 修复建议 ```cpp // 修复前 curtps = tps.at(index); // index=-1 时崩溃 // 修复后 if (index != -1) { curtps = tps.at(index); } else { // 处理未找到匹配项的情况 qWarning() << "未找到匹配的工具型号,使用默认配置"; // 使用默认值或第一个配置 if (!tps.isEmpty()) { curtps = tps.at(0); } } ```
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于
2025-10-17 18:27 被_THINCT编辑 ,原因:
#基础知识
#开发技巧
#其他
收藏
・
1
点赞
・
2
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
wx_晨梦
非常支持你的观点!
2026-7-16 10:56
我的小拇指啊
感谢你的积极参与,期待更多精彩内容!
2025-10-17 19:31
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
1
)
TkBinary
雪 币:
6550
活跃值:
(13289)
能力值:
(RANK:385 )
在线值:
发帖
26
回帖
526
粉丝
249
关注
私信
TkBinary
5
2
楼
成就感满满
2025-10-20 10:15
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
_THINCT
80
发帖
132
回帖
20
RANK
关注
私信
他的文章
[原创]msvcp140 / vcruntime140 版本劫持导致 OpenCV 启动崩溃 0xC0000005
321
[原创]WinDbg Dump 分析完整指南
3743
[原创]无调试符号的崩溃分析:从汇编到结构体推断
1761
[原创]为什么我选择windbg而不是visual studio?
4439
重新认识线程sleep
1723
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部