堆栈反混淆与报告格式
基本信息
| 属性 | 值 |
|---|---|
| 映射来源 | https://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/conf/methods.csv |
| 本地缓存 | bettercrashes-stackdeobfuscator-methods.csv(工作目录下) |
| 期望 SHA-256 | 63de115ead3e848e529de81e81ada9dff694e50fb5c6d92ec1e9037fe50ca191 |
| 开关 | stacktraceDeobfuscation(默认 true) |
| 实现类 | vfyjxf.bettercrashes.utils.StacktraceDeobfuscator |
映射获取与校验
StacktraceDeobfuscator.init(File)(:43-114):
- 文件已存在 → 直接读,不联网
- 文件不存在 → 下载,边写边用 Guava
Hashing.sha256()累计摘要 - 摘要与硬编码值不符 → 删文件、
return,放弃反混淆 - 下载/解析异常 →
logger.warn后return,放弃反混淆 - 全部成功才赋值
srgMcpMethodMap(注释:确保映射是完整的)
CSV 解析:跳表头,按第一个逗号切出 srgName,
再从其后取到下一个逗号切出 mcpName。
DEBUG_IN_DEV(硬编码 false)控制映射方向:
false 时 SRG → MCP(生产),true 时反向(仅开发自测)。
堆栈改写
deobfuscateThrowable(Throwable)(:116-125)用 ArrayDeque 广度遍历
cause 链 与全部 suppressed 异常,对每个异常调用
deobfuscateStacktrace 重建整个 StackTraceElement[]。
deobfuscateMethodName 在映射表为 null 时原样返回(:139-141),
所以反混淆失败时行为退化为无操作。
报告格式改写
CrashReportMixin @Overwrite 了两个方法:
getCompleteReport()(:108-130):
- 头部
//+ 一行 witty comment(见下) - 额外插入
Time: yyyy-MM-dd HH:mm:ss z一行(原版没有) - 用
Throwable.printStackTrace的输出替换原版getCauseStackTraceOrString,目的是打印 cause 与 suppressed(注释明说) - 分隔线为 87 个
-(for (int i = 0; i < 87; i++)) - 末尾统一把制表符替换成 4 空格
getSectionsInStringBuilder(StringBuilder)(:143-150):
先把所有 section 逐个追加,每个后面补一个 \n,最后追加主 category。
与原版的差异是每个 section 之间多一个空行。
Witty comment 的彩蛋
betterCrashes$getVanillaFixComment()(:152-162):
有 1% 概率(Math.random() < 0.01)且已识别出嫌疑 mod 时,
取第一个 mod 的 authorList.get(0)(作者名),
返回 "I blame " + author + ".";否则回落到原版
CrashReport.getWittyComment()。
源码缺陷(源码核对 2026-10-01 审计)
- ⚠️ 性能缺陷(高影响):
CrashReportMixin.java:73-79中StacktraceDeobfuscator.deobfuscateThrowable(cause)(第 78 行) 在if (BetterCrashesConfig.stacktraceDeobfuscation)之外,无条件执行。 只有init(...)(第 76 行)被开关保护。 ⇒ 即使玩家把开关设为false,异常树中每个 throwable 的StackTraceElement[]仍会被全量重建(每个元素 new 一个新对象), 只是方法名原样返回。关闭开关省不掉这份开销。 - ⚠️ 映射文件写在进程工作目录(
new File(String.format("%s-stackdeobfuscator-methods.csv", MODID)),CrashReportMixin.java:76),不是mcDataDir。 ⇒ 路径依赖启动方式,IDE 与启动器下位置不同。 - ⚠️ 源码缺陷(死代码):
StacktraceDeobfuscator.java:150-156保留了public static void main(String[] args)测试入口, 里面init(new File("methods.csv"))后直接遍历srgMcpMethodMap.entrySet()—— 若 init 失败(映射为空)此行 NPE。 - ⚠️ 关闭反混淆时
deobfuscateThrowable仍会遍历 cause + suppressed 全树, 深层异常链较多的崩溃点上会明显加重崩溃瞬间的卡顿。