如果你做过Linux软件适配,一定经历过那种"毫无头绪"的时刻:程序跑起来,窗口没弹,控制台没输出,进程就没了。没有 Segmentation fault,没有 abort,没有 core dump——就像一个三无病人,你说"你哪里不舒服"?它不理你。
我在如意玲珑社区做应用适配时,就遇到了这样一个病例。一个 Electron 应用——uTools,在经过签名注入 section 并重新打包 deb 后,运行即静默终止。没有任何报错,没有任何线索。
更令人困惑的是:
三个线索像三条断头路,彼此矛盾。于是我决定——让 LLM 来帮忙。这不是一次"AI替我干活"的故事,而是一次"人给方向、AI做验证、来回迭代逼近真相"的协作实验。
故事从一个简单的问题开始。
我把 uTools 的 deb 包解压、做了一些签名注入操作、重新打包,运行。然后——什么都没发生。没有窗口,没有 stderr,没有任何日志。echo $? 返回 1。
echo $?
对于有过 debug 经验的人来说,exit(1) 比 SIGSEGV 可怕得多。SIGSEGV 告诉你"我挂了,这是挂的地方";exit(1) 告诉你"我不高兴,但我不告诉你为什么"。
exit(1)
SIGSEGV
当时的我已经有一些已知线索:
.sig
我打开了 opencode,开始了第一轮尝试——让 DeepSeek V4 Pro 帮我看看。
和 LLM 协作的第一步通常是:你先描述问题,它给出一个分析计划,然后跑一堆命令。DeepSeek 拿到了 /usr/bin/utools 这个路径——等等,/usr/bin/utools 是一个指向 /opt/apps/u.utools.utools/files/uTools/utools 的符号链接。DeepSeek 立刻兴奋了:
/usr/bin/utools
/opt/apps/u.utools.utools/files/uTools/utools
"libffmpeg.so => not found!"
然后它开始写长篇大论:RPATH 问题、库缺失、加载器失败……但我知道 libffmpeg 在 $ORIGIN 目录里,动态加载不会有问题。
libffmpeg
$ORIGIN
人类第一次纠正:那不是问题,直接用 GDB。
DeepSeek 调头了,但调得不彻底。它开始分析 strace 输出,发现了 exit_group(1) 调用——"用户态代码调用了 exit",结论是"程序自己决定退出的"。这个结论没错,但和"为什么退出"差了十万八千里。
exit_group(1)
换 Qwen 3.5-122b 上。这次 AI 学乖了?不,它换了一个方向沉浸——"ASAR 完整性验证"。
Qwen 花了大量时间去解压 resources/app.asar,研究 Electron 的内嵌模块,分析 app-update.yml。它甚至发现了 leveldown 依赖的文件在 app.asar.unpacked 里不完整——"可能是 asar 提取失败导致运行崩溃!"
resources/app.asar
app-update.yml
leveldown
app.asar.unpacked
方向其实不对——原始二进制在没有这个缺失文件的情况下能跑,为什么签名版本就跑不了?但 AI 没有问自己这个问题。它沉浸在自己的推理链里,一路走到底。
人类第二次纠正:二进制运行就退出,跟缺失的 leveldown 文件没关系。
GLM-5 上场后,怀疑点变成了 DISPLAY 环境变量和 window manager。"XDG_SESSION_TYPE=tty,DISPLAY=localhost:11.0,这是个远程/SSH环境,没有图形界面——大概率是缺少 GUI 环境导致的 exit(1)。"
但已知线索明确写着"原始二进制在同一环境可以启动"。人类第三次纠正:"同一台机器,同一个环境。"
这三个回合下来,我产生了一个很深的感受:LLM 的"自信感"是一种幻觉。每个模型都能写出一篇逻辑自洽的技术分析,但只要初始假设偏离一点点,后面的推理就是千里之遥。它不是故意的——它只是不知道自己在犯错。
那是不是 LLM 就没用?恰恰相反。三次失焦的价值在于:每次错误的假设都排除了一个方向。排除法,是 debug 最重要的策略之一。
371 轮对话穿梭了 4 个模型,我整理了一个小观察笔记:
没有完美的模型,但有互补的模型。交叉验证,是和人协作的重要方法论。但真正的转折点——不是来自模型,而是来自实验室。
在 AI 跑了 100 多轮对话、贴了几十个 readelf 输出之后,我做了一个事:在 linyaps 包格式实验平台上亲手打包了一次。
不是 pull request,不是按规范流程走——而是故意搞破坏。
我构建了两个版本:
binary.withDebugSymbols
binary.withoutDebugSymbols
结果出来了:
有调试符号的 → 正常运行 剥离调试符号的 → 静默闪退
那一刻,前面所有的 .sig 签名分析、V8 Snapshot 校验分析、asar 完整性分析——全都需要翻盘。
之前我们一直以为是"签名注入"的问题,但实验证明:剥离 .symtab 和 .strtab 这两个 section 才是根因。
.symtab
.strtab
而这个实验,AI 是做不了的——它没有物理环境,没有应用容器,没有 deb 构建工具链。它只能在已有的二进制文件上做静态分析。真正一刀切进核心的,是那个坐在电脑前、按下 Enter 键的人。
我把实验结果给了 GLM-5,说:"方向变了,不是签名,是符号。重新分析。"
方向一旦正确,AI 的价值就爆发了。
我给定了两个目录——一个包含调试符号,一个剥离了调试符号——让 GLM-5 做差异分析。以下是我看到的:
秒级对比 16980 个符号。 人类手动比对?一天。AI?几秒钟。
对比结果显示:
符号数量完全一致:16980 差异不在符号内容上
那差在哪?GLM 开始逐行排查 section 结构:
.gnu_debuglink
然后 GLM 做了一件让我刮目相看的事——它反汇编了 V8 的 VerifyChecksum 函数。不是简单的符号解引用,而是给出了反汇编代码的具体逻辑:
VerifyChecksum
_ZN2v88internal8Snapshot14VerifyChecksumEPKNS_11StartupDataE (0x3a25bd0) ├─ 读取 snapshot_blob.bin 头部 ├─ 调用 Checksum (0x3a256a0) 计算实际校验和 ├─ 与 GetExpectedChecksum (0x34e2de0) 的结果比较 └─ 不匹配 → PrintF 输出 → 进程退出
它还发现了 Electron Fuses 的值:0x1237ad0 位置,二进制值为 1011000。这个值告诉我们:ASAR 完整性验证是关闭的——所以 not asar 的问题。
0x1237ad0
1011000
然后它挖到了 .gnu_debuglink 的变更细节:
withDebugSymbols 的 .gnu_debuglink CRC: CRC31 (0xXXXXXXXX) withoutDebugSymbols 的 .gnu_debuglink CRC: CRC32 (0xYYYYYYYY)
两个 CRC 算法和值都不同。这是剥离符号时,objcopy --only-keep-debug 重写了 .gnu_debuglink 的结果。
objcopy --only-keep-debug
这就是 AI 的高光时刻。它从一个已知的目录结构和几十个反汇编函数里,理出了一张完整的校验流程图——V8 Snapshot → Electron Fuses → .gnu_debuglink CRC → 未知完整性校验的层级关系。人类可以在宏观方向上做判断,但要逐个去查反汇编里的 45 行代码、16980 个符号、十几个 section 的 CRC,那会是一个漫长到不敢开始的过程。
AI 不是决策者,它是微观分析的加速器。
但这时候,我们还没找到终极答案。因为上面的分析虽然好,但都是"可能"。我们还没能确认到底是哪个校验触发了闪退。《三体》里有句话:"弱小和无知不是生存的障碍,傲慢才是。" 但在二进制分析里,"不够精确的假设"才是。我们需要一个实验来锁死结论。
故事的最后一转来自人类的一个新问题:
"等一下,我用 deb 版本也试了一下。deb 的 withoutDebugSymbols 两个目录 都有 16980 个符号,但 deb 版同样闪退。这说不通啊——如果根因是符号,那 deb 版符号没丢,为什么也闪退?"
withoutDebugSymbols
AI 迅速对比了 deb 版的两个目录,发现:
"withoutDebugSymbols" 这个命名具有误导性——它实际上没剥离符号,只是多了一个签名 section。
那 deb 版闪退的原因就变成了:添加 .sig section 导致了什么?
人类提出了一个假设性追问:"是否uTools根本不允许二进制文件发生 section 层面的变化?"
AI 拉了一张表:
这不是巧合。
两个完全不同的打包方案、两个完全不同的修改方式(一个是删 section,一个是加 section),结果一模一样:静默闪退。
共同点只有一个:section 数量/结构变化了。
结论:uTools 的主二进制存在 ELF Section 头表完整性保护机制。无论增减,section 数量一旦偏离 38,程序即触发静默退出。
回顾前面的分析,这个校验点大概率不在 V8、不在 Electron、不在 ASAR——它位于更底层,可能是 ELF 加载阶段的自检代码,在 main 函数执行前就已经完成了完整性判断。
同时这也解释了为什么前几轮 AI 的分析总是差一点:因为我们一直在查 V8 Snapshot 校验、查 Electron Fuses、查 bytecode checksum——通通是"上层机制"。而真正的保护位于一个更隐蔽的层次:ELF Section 头表本身。
这份排除了的清单,本身就是一套完整的二进制逆向分析 checkList:
snapshot_blob.bin
v8_context_snapshot.bin
IsOnlyLoadAppFromAsar
resources.pak
V8 VerifyChecksum
如果你在分析一个 Electron 二进制闪退问题,这个单子可以帮你直接跳过一大半假设。
回看整个 371 轮对话(大约 880 万 token)的调试过程,人和 AI 的角色非常清晰:
人类给方向 ──→ AI 做分析 ──→ AI 给结论 ──→ 人类提出质疑 ↑ │ └──────────────── 下一个假设 ──────────────┘
螺旋式逼近。每次循环排除一批可能性,最终收敛于真相。
很多人觉得用 LLM debug 就是写好 prompt,然后躺平等结果。这不对。AI 需要的是你告诉它"什么值得看、什么可以忽略"。
比如第二次 Qwen 沉迷 asar 分析时,我如果不说"方向不对",它可能会继续分析 50 轮 asar 的结构。你是那个说"这里停一下,往那边看"的人。
DeepSeek 说"libffmpeg 缺失"的时候语气非常肯定。Qwen 说"asar 完整性验证导致闪退"的时候也非常肯定。GLM 说"窗口管理器问题"的时候同样肯定。
每个模型都能写出一篇逻辑自洽的分析,但正确的概率并不高。你必须去验证,而不是接受。
371 轮对话穿梭了 4 个模型。如果只用一个模型,很多错误方向不会被发现。多模型有一个隐藏的好处:当两个独立的模型给出相同的结论时,置信度会大幅提升。反之,当一个模型推翻另一个模型的结论时——那比单模型的任何输出都更有信息量。
AI 不可替代的:
人类不可替代的:
如果你打算用 LLM 帮你分析类似的二进制问题,不要期待它从头到尾替你 debug。你应该这样做:
这个故事最终找到了结论:uTools 存在 ELF Section 头表完整性保护,section 数量偏离 38 即触发静默退出。但比结论更重要的是一路上的协作方式——AI 加速了每一步微观分析,而人类提供了每一步的方向和实验验证。
这不是"AI 替我 debug"的故事,而是"AI 和我一起 debug"的故事。最好的 debug 不是 AI 替你思考,而是 AI 帮你加速思考。就像你在走廊的这头喊一声"那边有没有问题?",AI 瞬间跑过去看,跑回来告诉你"有,是这个"。但决定"要看哪边"的那个人,还是你。
本文为如意玲珑社区技术博客系列。事实验证来源:uTools-debug-end.md(371 轮 AI 对话,约 880 万 token 输入),utools-debug-1.md(首轮分析记录,约 100 万 token)。所有 GDB 命令、section 数量、fuse 值、符号数量均为真实实验数据。
人类提供直觉和方向,AI 提供算力和微观扫描
最后还是失败了~
Featured Collection
Popular Events
前言
如果你做过Linux软件适配,一定经历过那种"毫无头绪"的时刻:程序跑起来,窗口没弹,控制台没输出,进程就没了。没有 Segmentation fault,没有 abort,没有 core dump——就像一个三无病人,你说"你哪里不舒服"?它不理你。
我在如意玲珑社区做应用适配时,就遇到了这样一个病例。一个 Electron 应用——uTools,在经过签名注入 section 并重新打包 deb 后,运行即静默终止。没有任何报错,没有任何线索。
更令人困惑的是:
三个线索像三条断头路,彼此矛盾。于是我决定——让 LLM 来帮忙。这不是一次"AI替我干活"的故事,而是一次"人给方向、AI做验证、来回迭代逼近真相"的协作实验。
第1章 谜题浮现:一个"没报错"的闪退
故事从一个简单的问题开始。
我把 uTools 的 deb 包解压、做了一些签名注入操作、重新打包,运行。然后——什么都没发生。没有窗口,没有 stderr,没有任何日志。
echo $?返回 1。对于有过 debug 经验的人来说,
exit(1)比SIGSEGV可怕得多。SIGSEGV告诉你"我挂了,这是挂的地方";exit(1)告诉你"我不高兴,但我不告诉你为什么"。当时的我已经有一些已知线索:
.sigsection(PKCS7 格式的 X.509 数字证书)我打开了 opencode,开始了第一轮尝试——让 DeepSeek V4 Pro 帮我看看。
第2章 弯路也是路:AI的三次失焦
Round 1:DeepSeek——"让我看看你的 libffmpeg"
和 LLM 协作的第一步通常是:你先描述问题,它给出一个分析计划,然后跑一堆命令。DeepSeek 拿到了
/usr/bin/utools这个路径——等等,/usr/bin/utools是一个指向/opt/apps/u.utools.utools/files/uTools/utools的符号链接。DeepSeek 立刻兴奋了:然后它开始写长篇大论:RPATH 问题、库缺失、加载器失败……但我知道
libffmpeg在$ORIGIN目录里,动态加载不会有问题。人类第一次纠正:那不是问题,直接用 GDB。
DeepSeek 调头了,但调得不彻底。它开始分析 strace 输出,发现了
exit_group(1)调用——"用户态代码调用了 exit",结论是"程序自己决定退出的"。这个结论没错,但和"为什么退出"差了十万八千里。Round 2:Qwen——"让我看看你的 asar"
换 Qwen 3.5-122b 上。这次 AI 学乖了?不,它换了一个方向沉浸——"ASAR 完整性验证"。
Qwen 花了大量时间去解压
resources/app.asar,研究 Electron 的内嵌模块,分析app-update.yml。它甚至发现了leveldown依赖的文件在app.asar.unpacked里不完整——"可能是 asar 提取失败导致运行崩溃!"方向其实不对——原始二进制在没有这个缺失文件的情况下能跑,为什么签名版本就跑不了?但 AI 没有问自己这个问题。它沉浸在自己的推理链里,一路走到底。
人类第二次纠正:二进制运行就退出,跟缺失的 leveldown 文件没关系。
Round 3:GLM——"一定是窗口管理器的问题"
GLM-5 上场后,怀疑点变成了 DISPLAY 环境变量和 window manager。"XDG_SESSION_TYPE=tty,DISPLAY=localhost:11.0,这是个远程/SSH环境,没有图形界面——大概率是缺少 GUI 环境导致的 exit(1)。"
但已知线索明确写着"原始二进制在同一环境可以启动"。人类第三次纠正:"同一台机器,同一个环境。"
这三个回合下来,我产生了一个很深的感受:LLM 的"自信感"是一种幻觉。每个模型都能写出一篇逻辑自洽的技术分析,但只要初始假设偏离一点点,后面的推理就是千里之遥。它不是故意的——它只是不知道自己在犯错。
那是不是 LLM 就没用?恰恰相反。三次失焦的价值在于:每次错误的假设都排除了一个方向。排除法,是 debug 最重要的策略之一。
插章:多模型轮战观察
371 轮对话穿梭了 4 个模型,我整理了一个小观察笔记:
没有完美的模型,但有互补的模型。交叉验证,是和人协作的重要方法论。但真正的转折点——不是来自模型,而是来自实验室。
第3章 人类的关键一手:实验台的价值
在 AI 跑了 100 多轮对话、贴了几十个 readelf 输出之后,我做了一个事:在 linyaps 包格式实验平台上亲手打包了一次。
不是 pull request,不是按规范流程走——而是故意搞破坏。
我构建了两个版本:
binary.withDebugSymbols:保留所有调试符号binary.withoutDebugSymbols:剥离调试符号结果出来了:
那一刻,前面所有的
.sig签名分析、V8 Snapshot 校验分析、asar 完整性分析——全都需要翻盘。之前我们一直以为是"签名注入"的问题,但实验证明:剥离
.symtab和.strtab这两个 section 才是根因。而这个实验,AI 是做不了的——它没有物理环境,没有应用容器,没有 deb 构建工具链。它只能在已有的二进制文件上做静态分析。真正一刀切进核心的,是那个坐在电脑前、按下 Enter 键的人。
我把实验结果给了 GLM-5,说:"方向变了,不是签名,是符号。重新分析。"
第4章 AI的爆发:从45度行到全局图
方向一旦正确,AI 的价值就爆发了。
我给定了两个目录——一个包含调试符号,一个剥离了调试符号——让 GLM-5 做差异分析。以下是我看到的:
秒级对比 16980 个符号。 人类手动比对?一天。AI?几秒钟。
对比结果显示:
那差在哪?GLM 开始逐行排查 section 结构:
.symtab:被剥离(符号表,从有到无).strtab:被剥离(字符串表,从有到无).gnu_debuglink:两个版本都存在,但 CRC 值不同然后 GLM 做了一件让我刮目相看的事——它反汇编了 V8 的
VerifyChecksum函数。不是简单的符号解引用,而是给出了反汇编代码的具体逻辑:它还发现了 Electron Fuses 的值:
0x1237ad0位置,二进制值为1011000。这个值告诉我们:ASAR 完整性验证是关闭的——所以 not asar 的问题。然后它挖到了
.gnu_debuglink的变更细节:两个 CRC 算法和值都不同。这是剥离符号时,
objcopy --only-keep-debug重写了.gnu_debuglink的结果。这就是 AI 的高光时刻。它从一个已知的目录结构和几十个反汇编函数里,理出了一张完整的校验流程图——V8 Snapshot → Electron Fuses →
.gnu_debuglinkCRC → 未知完整性校验的层级关系。人类可以在宏观方向上做判断,但要逐个去查反汇编里的 45 行代码、16980 个符号、十几个 section 的 CRC,那会是一个漫长到不敢开始的过程。AI 不是决策者,它是微观分析的加速器。
但这时候,我们还没找到终极答案。因为上面的分析虽然好,但都是"可能"。我们还没能确认到底是哪个校验触发了闪退。《三体》里有句话:"弱小和无知不是生存的障碍,傲慢才是。" 但在二进制分析里,"不够精确的假设"才是。我们需要一个实验来锁死结论。
第5章 拼图的最后一角:Section完整性保护
故事的最后一转来自人类的一个新问题:
"等一下,我用 deb 版本也试了一下。deb 的
withoutDebugSymbols两个目录 都有 16980 个符号,但 deb 版同样闪退。这说不通啊——如果根因是符号,那 deb 版符号没丢,为什么也闪退?"AI 迅速对比了 deb 版的两个目录,发现:
.sig.sig(12KB PKCS7 签名)"withoutDebugSymbols" 这个命名具有误导性——它实际上没剥离符号,只是多了一个签名 section。
那 deb 版闪退的原因就变成了:添加
.sigsection 导致了什么?人类提出了一个假设性追问:"是否uTools根本不允许二进制文件发生 section 层面的变化?"
AI 拉了一张表:
.symtab,.strtab).sig)这不是巧合。
两个完全不同的打包方案、两个完全不同的修改方式(一个是删 section,一个是加 section),结果一模一样:静默闪退。
共同点只有一个:section 数量/结构变化了。
结论:uTools 的主二进制存在 ELF Section 头表完整性保护机制。无论增减,section 数量一旦偏离 38,程序即触发静默退出。
回顾前面的分析,这个校验点大概率不在 V8、不在 Electron、不在 ASAR——它位于更底层,可能是 ELF 加载阶段的自检代码,在 main 函数执行前就已经完成了完整性判断。
同时这也解释了为什么前几轮 AI 的分析总是差一点:因为我们一直在查 V8 Snapshot 校验、查 Electron Fuses、查 bytecode checksum——通通是"上层机制"。而真正的保护位于一个更隐蔽的层次:ELF Section 头表本身。
第6章 技术深水区:我们排除了什么
这份排除了的清单,本身就是一套完整的二进制逆向分析 checkList:
snapshot_blob.bin和v8_context_snapshot.bin在两个版本中大小和内容完全一致1011000明确显示 ASAR 完整性验证关闭,IsOnlyLoadAppFromAsar也是关闭resources.pak在两个版本间完全一致.gnu_debuglinkCRCV8 VerifyChecksum校验的是 snapshot blob,不是二进制本身.sig签名本身如果你在分析一个 Electron 二进制闪退问题,这个单子可以帮你直接跳过一大半假设。
第7章 协作复盘:谁做了什么
回看整个 371 轮对话(大约 880 万 token)的调试过程,人和 AI 的角色非常清晰:
人类贡献了什么?
AI 贡献了什么?
协作节奏
螺旋式逼近。每次循环排除一批可能性,最终收敛于真相。
第8章 与LLM协作的反思
1. 不要当"提示词工程师",要当"指挥官"
很多人觉得用 LLM debug 就是写好 prompt,然后躺平等结果。这不对。AI 需要的是你告诉它"什么值得看、什么可以忽略"。
比如第二次 Qwen 沉迷 asar 分析时,我如果不说"方向不对",它可能会继续分析 50 轮 asar 的结构。你是那个说"这里停一下,往那边看"的人。
2. AI 的"自信感"是一道门槛
DeepSeek 说"libffmpeg 缺失"的时候语气非常肯定。Qwen 说"asar 完整性验证导致闪退"的时候也非常肯定。GLM 说"窗口管理器问题"的时候同样肯定。
每个模型都能写出一篇逻辑自洽的分析,但正确的概率并不高。你必须去验证,而不是接受。
3. 交叉验证的价值
371 轮对话穿梭了 4 个模型。如果只用一个模型,很多错误方向不会被发现。多模型有一个隐藏的好处:当两个独立的模型给出相同的结论时,置信度会大幅提升。反之,当一个模型推翻另一个模型的结论时——那比单模型的任何输出都更有信息量。
4. AI + 人类的不可替代组合
AI 不可替代的:
人类不可替代的:
5. 一条实际的建议
如果你打算用 LLM 帮你分析类似的二进制问题,不要期待它从头到尾替你 debug。你应该这样做:
尾声
这个故事最终找到了结论:uTools 存在 ELF Section 头表完整性保护,section 数量偏离 38 即触发静默退出。但比结论更重要的是一路上的协作方式——AI 加速了每一步微观分析,而人类提供了每一步的方向和实验验证。
这不是"AI 替我 debug"的故事,而是"AI 和我一起 debug"的故事。最好的 debug 不是 AI 替你思考,而是 AI 帮你加速思考。就像你在走廊的这头喊一声"那边有没有问题?",AI 瞬间跑过去看,跑回来告诉你"有,是这个"。但决定"要看哪边"的那个人,还是你。
本文为如意玲珑社区技术博客系列。事实验证来源:uTools-debug-end.md(371 轮 AI 对话,约 880 万 token 输入),utools-debug-1.md(首轮分析记录,约 100 万 token)。所有 GDB 命令、section 数量、fuse 值、符号数量均为真实实验数据。