AI 自动化逆向(五)控制组:区分「没有」和「没取到」
本文是「AI 自动化逆向」系列第 5 篇。上一篇:人在 AI 逆向流程里到底做什么;下一篇预告:AI 解一个 9 万条目的资源容器。
前言
上一篇把"人的动作"拆成五类,其中"定判据"只举了四个例子就收住了——因为每一个都值得单独一篇。从这一篇开始逐个拆开。
第一个,也是整个系列的第一个"审核协议":怎么区分「没有」和「没取到」。
这两件事在输出上完全同形:都是空的、零的、"未发现"的。而它们的含义相反——一个是关于世界的结论(真的没有),一个是关于仪器的故障(没取到)。能把它们分开的工具只有一个:控制组。
(同前几篇:文中"AI"指执行分析的模型,"我"指设定目标、审结果的人;内容均已脱敏。)
一、AI 在这里踩的坑:一份"全零"的输出
AI 写过一个搜索脚本:拿一批函数去运行时里搜对应位置,每个函数打印"找到几个片段"。
它跑出来的结果是这样的——8 行,格式一致,每个数字都是 0:
FUNC_VA fragments=0 FUNC_VA fragments=0 ……(共 8 行)读起来完全像一个真实结论:“这些函数都没有可恢复的片段。”不报错、没有异常值、行数对得上、语气一致——你没有任何理由怀疑它。
但实际上:8 行里有 5 行是需要定位的目标,另外 3 行是特意塞进去的控制组——其中有一个样本,本该有 90 个片段。
问题不在目标身上,在取材环节:脚本用一个不存在的字段名去取数据,.get()不报错、返回空,于是整批结果都变成了"空"。如果那 3 个控制组不在,这 8 行会被当成结论写进文档。
人应该加的检查:任何"某集合里一个都没有"的结论,必须至少有一个已知非空的同类样本作担保。空结果与取材失败在输出上完全同形——唯一能把它们分开的,就是那份"担保"。
二、过程:空结果的三种真身,四个真实案例
把项目里所有"空结果"排一遍,只有三种真身:
- 真的空——世界就是这样;
- 样本没进来——取材坏了:字段名错、路径错、范围错;
- 规则压根没跑——查询或规则根本没起步:被静默吞掉、参数被改写,或者结构上就起不了步。
控制组的全部使命,就是把 2 和 3 从 1 里分出去。四个案例,各对应一种真身。
案例 1:控制组戳破"全零"(真身 2:取材坏了)
就是上面那 8 行。流程很朴素:
- 脚本打印全 0;
- 控制组里那个"本该有 90 个片段"的样本,报 0;
- ⇒ 不是目标的性质,是取材环节错了。修好之后,5 个目标全部搜到。
这次事故留下两条规矩:"一个都没有"必须有已知非空的担保;以及用一个数据结构之前,先把它的字段名列出来(print(list(d[0].keys()))),不要凭记忆写字段名。
案例 2:两次"测了个寂寞"的阴性对照(真身 3:规则起不了步)
AI 要给一条"恢复片段"的规则量化假阳性率,需要一个"肯定不是代码"的对照区域。它选了两次:
- 第一次:拿保护段当对照 →恢复出 0 个片段;
- 第二次:拿数据段里的字符串块当对照 →还是 0 个。
看起来是"规则不乱报"——其实是规则根本起不了步:恢复算法只在"已知指令边界"上起步,而那两块区域里没有任何已知边界。测到的是"规则没跑",不是"规则很干净"。
对照区域必须落在被测规则的作用域内。选对照之前先问一句:这条规则在这里至少要能跑起来吗?跑不起来的对照,"0"是假象。
(顺带:那次唯一有价值的产出——“假阳性只可能出现在’被误认为指令边界’的字节上”——恰恰是从"对照为什么起不了步"里读出来的。废掉的对照也说话。)
案例 3:先用已知答案验方法,再看目标(正对照的标准形态)
这个方法本身有风险:拿"幸存指令的助记符和指令长度"去运行时里搜——凭什么信它?AI 的做法是先跑控制组:
- 两个已知答案的样本:一个能恢复出 6 个可用片段,一个能恢复出 12 个——全票给出同一个偏移,且与已知地址逐一吻合;
- 一个已知不适用的样本(函数体基本被保护壳吞掉,只恢复出 1 个片段、0 命中)——它留在表里,告知的是方法的边界,不是失败。
控制组全票之后,才拿这个方法去定那个未知的目标——一次命中。
注意第三个样本:控制组不是"全绿才合格"。一个诚实的控制组里,应该同时有"已知答对的"和"已知答不了的"——后者量出方法的边界。
案例 4:连"查询"本身都会静默失败(真身 3 的另一种:动作没发生)
一次进程枚举:脚本"顺利跑完",返回空列表——读起来是"机器上没这个进程"。真相是:命令经一层 shell 转手,参数被改写了,查询语法错了,等于什么都没查。
修法写进了纪律:同一个查询,先拿一个"已知存在"的对象跑一遍——它明明在,你就必须看得见它。看不见,问题在仪器,不在世界。
(同族还有更粗暴的一种:异常被except直接吞成空列表——连"查询失败"这四个字都被吞掉了。"空"这个输出,一个字都不许单独信,除非它有担保。)
三、判据与验证:把控制组写成四条规矩
1. 空结果必须配"已知非空"的担保,且走同一条代码路径。担保样本要和目标经过同一段代码,不然它担保不了任何东西。(换个说法:先确认分母不是 0,再谈分子。)
2. 阴性对照必须落在被测规则的作用域内。先确认"规则在这里能起步",再谈"规则干不干净"。
3. 仪器先验存在物。任何查询、正则、枚举,先拿一个"已知存在"的对象跑同一路径:它必须被看见。这条也适用于人自己的眼睛——"看起来没有"之前,先证明"看"这个动作真的发生了。
4. 三值,不是布尔。结果有三种:找到干净的/找到过、但判它不合格/什么都没找到。前两个在旧输出里同形、含义相反——要分列,而不是压成一个"0"。这条是前面那条判据链在收口阶段才逼出来的:“找到过但判它是垃圾” ≠ “没找到”。
再加一条元规矩:控制组的形态要跟着问题走。要"验证一个方法"→ 用已知答案的控制组;要"量一个规则的假阳性"→ 用作用域内的阴性对照;要"确认仪器活着"→ 用已知存在物。三种控制组管三件事,不能互相替代。
四、结论
结论可以压缩成一张"空结果审查清单":
| 输出是"空 / 零 / 未发现"时 | 先问 |
|---|---|
| 真的没有? | 有没有一个"已知非空"的同类样本,跑同一条路? |
| 还是没取到? | 取材的字段、路径、范围,打印出来核对过吗? |
| 还是没跑? | 规则在对照区域里能起步吗?仪器拿已知存在物验过吗? |
AI 特别容易栽在这里,原因也简单:它默认自己的工具是对的——上面所有的"空",在它的输出里没有一处长得像错误。把"没有"和"没取到"分开,不靠聪明,靠那个被塞进去的已知样本。
五、边界声明
- 本文只讨论AI 自动化逆向中的判据设计与审核方法,不涉及任何具体产品、厂商或服务的分析结论。
- 文中所有地址、偏移、常量、字段名一律为占位符或示意值,
不代表任何真实目标中的取值;示例代码为说明判据而编写,不构成可运行的针对特定目标的工具。 - 文中提到的比例与数字均来自作者项目内的测量记录,仅用于说明方法失效的方式。
- 请勿将本文内容用于未授权的用途。
六、收尾
"没有"必须被担保,否则它什么都不是。下一篇换一个完全相反的场景——一个"九万多条目、百分之百全过"的漂亮结果,看它凭什么敢说自己是对的。
下一篇计划聊:AI 解一个 9 万条目的资源容器。
转载请注明出处:
https://lizhiyong.blog.csdn.net/article/details/167814341