# 2026-10-21 游戏崩溃排查 本记录区分转储直接证实的故障、代码审查发现的问题和实机复测结果。原始转储只在本机分析,没有提交给第三方。自动导航完整功能仍未验收,不据此启用自动飞行。 ## 转储结论 | 本机时间 | 游戏 PID / 崩溃线程 | 自有加载器 | 异常位置 | | --- | --- | --- | --- | | 08:13:36 | 23104 / 4596 | 0.2.0-dev,PE 时间戳 `6ad802e9`,映像大小 `75000` | 游戏 RVA `552072`,`0xC0000005` | | 08:15:44 | 63768 / 18140 | 已回退的 0.1.0,PE 时间戳 `6ad7e392`,映像大小 `29000` | 游戏 RVA `55207D`,`0xC0000005` | 游戏 EXE 两次均为 r334473,SHA-256 `6891f70a021fa9f3a0e00458c284f48d1226e68ae7fc02ad48081aed47857e93`。加载器版本通过转储模块的 PE 时间戳和映像大小与已保存 DLL 交叉核验;转储自身不包含 DLL 文件的 SHA-256。 两处属于同一函数 RVA `551FC0`:内存分配器分配新块时从空闲链表摘取节点。第一次链表指针含非规范地址,第二次链表节点的下一项为零,随后写入地址 `0x18` 触发异常。**已确认空闲链表损坏;尚未确认更早的哪次写入破坏了它。** 这不是仅凭异常代码判断内存不足。 调用栈由 Windows 自带 DbgEng 从异常上下文展开。游戏没有完整私有符号;调试器显示的远距离导出函数名称不能当作真实函数名,故本报告使用游戏 RVA。反编译通过固定构建间的唯一代码匹配核对,未改动游戏 EXE。 第二次使用旧加载器仍崩溃,不能把首次崩溃直接认定为新物理挂钩造成。现有转储也不能排除其他组件或启动时序的影响。没有为了继续运行而跳过游戏检查或覆盖分配器错误。 ## 已修正的代码问题 这些是独立查实的实现缺陷或边界风险,**不等同于已经证明了上述两次崩溃的写入来源**。 - 原加载器以 `/MT` 链接静态 CRT,却在 DllMain 中调用 `DisableThreadLibraryCalls`。已保留线程通知;微软明确要求静态 CRT DLL 不禁用它们,见 [官方说明](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-disablethreadlibrarycalls)。 - 使用 CRT 的服务线程改用 `_beginthreadex`,并在真正的系统 DirectInput 初始化成功后启动;服务失败不改变原始 DirectInput 返回值。见 [线程运行时说明](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/beginthread-beginthreadex)。 - 安装和状态查询只启用通信。导航挂钩需要单独的 `navigation.observe.start` 请求;独立研究 DLL 也改为显式导出启动,DllMain 不创建线程。 - 服务运行期间固定自有模块的生命周期,避免游戏释放输入包装 DLL 后线程或回调继续执行已卸载代码。停用与退出后清理仍由安装管理器执行。 - 原生输入恢复改为每次调用独立保存,并再次验证实体编号、生命周期、输入对象及物理对象关联。原调用销毁或回收对象时不向旧地址恢复数值,玩家修改过的轴也不覆盖。 - 姿态查询使用游戏已注册的接口编号,不再逐项探测固定的 512 个条目;当前玩家失效时立即撤销该帧有效性。 - 原生安装失败不再重复尝试同一进程的部分安装,线程创建失败会停用自有挂钩。失败的重新预检也会清除 C# 缓存能力。 - 生成匹配 DLL 的 PDB、MAP 和只含版本、PID、线程及生命周期阶段的本机日志。诊断工具的连接超时以错误退出,不再制造自身的未处理异常转储。 ## 复测与验收状态 已保存的启动对照均未发送飞行指令,也没有读取屏幕: | 对照 | 证据 | 结果 | | --- | --- | --- | | 移除自有加载器,保留原汉化 | `startup-no-toolbox-01.json` | 连续存活 60 秒,正常关闭 | | 重新安装原 0.1.0 | `startup-old-plugin-01.json` | 连续存活 60 秒,正常关闭 | | 重新安装原始故障 DLL 0.2.0-dev | `startup-nav-plugin-original-01.json` | 连续存活 60 秒,正常关闭 | | 加固版首轮 | `startup-hardened-01.json` | 连续存活 60 秒;状态查询后原生入口仍与磁盘一致 | | 最终 DLL 第 1、2 轮 | `startup-hardened-final-01.json`、`startup-hardened-final-02.json` | 每轮连续存活 60 秒,均核对同一 DLL 哈希 | | 最终 DLL 强制结束后重启 | `startup-hardened-final-after-force.json` | 连续存活 60 秒,没有新增崩溃转储 | 因此故障尚未稳定复现,不能用一次成功启动宣布根因已经消除。最终加固 DLL 的重复启动、显式观测结果和文件哈希另见 `artifacts/crash-analysis-20261021/acceptance.json`;缺少该文件或其中未通过的项目均不算验收通过。 显式观测已连续存活 84 秒,初始化成功,未发送驾驶轴或跃迁命令。随后一次 `CloseMainWindow` 未在 15 秒内结束进程,另外保存了诊断转储。展开栈及原生活动检查证明游戏仍在 `MainMenu` 正常更新(5 秒内 1199 次更新),不能把该关闭请求超时当成插件死锁。经同一自然游戏线程核对定义、缓存及 `0x0101` 可用状态后,调用一次原始 `QuitApplication`,进程结束,Journal 写入 `Shutdown`,没有新增崩溃转储。此项只验证主菜单中的原生退出,不代表游戏内重进已完成;大退仍按用户要求直接结束核对身份的进程。 核心回归共 155 项:151 通过,0 失败,4 项环境集成检查跳过。原生独立测试覆盖整数/SSE/标志保留,以及对象回收、玩家输入变化、物理关联变化和页面失效时的输入恢复边界。独立分配的测试内存结果不冒充实际自动飞行通过。 ## 本机证据 原始转储的工作区副本和 SHA-256: - `20261021_081336_MiniDump.dmp`:`98b632d2452c4231d4bc17abeb90d432ba2b8d071af46fb0ddc14286ba9fa252` - `20261021_081544_MiniDump.dmp`:`1ad65f48025e67c73f92962020482a2f952b303ed1d13146ce12520da53f6942` 目录:`artifacts/crash-analysis-20261021/`。`metadata.json` 保存异常、模块及寄存器;`*-debugger.txt` 保存本机展开栈;`allocator-map-d-to-e.json` 与 `allocator-decompile/` 保存分配器核对;`startup-*.json` 保存实际进程与组件哈希。`stackPointerCandidates` 只是原始栈指针候选,不能当作展开后的栈。 本次没有修改汉化 DLL、资源包或游戏 EXE。抓包保持被动,游戏退出后等待同一安装再次启动。 测试结束后,选定游戏及本次官方启动器均已关闭,自有开发加载器及安装记录已移除;下一版源码、DLL 和匹配符号保留在工作区,没有作为生产版本发布。`cleanup.json` 逐项核对游戏 EXE、汉化 DLL 与资源包仍为原哈希。被动采集守护已续开,最多 120 分钟、1024 MiB,当前等待所选游戏启动;最新目录和期限见 `capture-renewal.json` 及相应 `status.json`。