资讯详情

资讯详情

AI 自动化逆向(二)AI 会相信它自己上一轮写的表

AI 自动化逆向二AI 会相信它自己上一轮写的表本文是「AI 自动化逆向」系列第 2 篇。上一篇最贵的错误是「看起来像结论的错答案」下一篇预告我把一个逆向项目交给了 AI它做到了什么程度。前言上一篇的陷阱 4/5 只写了一页末尾留了一个扣子AI 有一条判据在同一个问题上连续推翻自己五次从这段是数据一路改到这段是夹层而每一个中间结论都曾经写进过文档。这一篇把那条链完整讲开。这条链是在一句很简单的目标下跑出来的把剩余的未解条目逐个给出明确结论。它最终也做到了——107 个受保护函数第一次全部有了定案68 个可继续挖、39 个判定为纯桩、0 个纯数据有名字的从 37 涨到 43三个被误撤的条目全部恢复。但过程是六轮工作五次推翻。每一次推翻之前它交出的东西都长得像结论——格式完整的表格、自洽的数字、跑得通的回归脚本五次里每一次都曾经是它手里最好的答案。同上一篇文中AI指执行分析的模型我指设定目标、审结果的人。所有地址、常量、数值一律脱敏。一、AI 在这里踩的坑任务的起点很不起眼给这批受保护函数做命名。AI 手里有一张清单——它自己早先产出的——每个条目带起点和大小。它默认[起点, 起点大小)就是函数体然后跑进每个区间里提取字符串引用、API 引用作为这个函数引用了什么的证据。产出很快、很整齐107 个条目、一张规整的表、每条结论都带着格式一致的证据行。问题就出在这里那张表是它自己造的所以它默认表是对的。人对自己的手抄表有天然的不信任AI 对自己生成的表天生信任它从来不问一句这张表本身成立吗。裂缝来自一次普通的审计对全部 107 个条目逐字节解码统计每个区间能按顺序解出指令的字节比例。解码率条目数≥90%8450–90%120–50%1020%1222 个条目的函数体解不出来。最差的一条数千字节里只有0.4%能按顺序解码——把它打开看第一条指令就是一个跳进保护段的跳转跳转之后全是垃圾。但比22 个更要命的是分布的形状从 0.005 到 0.45 之间没有天然断层。AI 当时注意到了这一点却把它解释成阈值有任意性——于是它拍了一个阈值建了一个闸门函数级解码率低于阈值的条目判定函数体不是代码把从里面提取的证据整批撤回。复盘到这里这一篇的题目已经出现了它在老表的上面又摞了一张新表。人应该加的检查任何某函数体内有 X的结论先过一关——那段字节本身是不是代码且不能用函数级的单一比例来判为什么不行见第 3 轮。以及阈值选不出来不是小事它是模型不对的信号。二、过程六轮工作五次推翻第 1 轮建闸门整批撤回闸门装上后全部引用被分成两拨干净引用76条落在 11 个正常条目里可疑引用41条落在 5 个垃圾条目里。41 条被整批撤回。同一轮里闸门自己先崩了一次第一版对不在清单里的地址返回判为数据——它自己的对照组从 26 个崩到 2 个因为一大批正常的普通函数被当成数据拦掉了。修法就一句话“我不认识和我知道它是坏的”必须分开。净结果有点反直觉有名字的条目 37 → 39丢了 4 个、补回 6 个。撤回证据的同时它做了一件对的事——把名字的来源拆开了有些名字从来就不依赖读函数体那些名字被单独标注成不是因为能读到它的函数体。这一步防的是将来有人把已撤回的证据悄悄挂在独立来源的名字后面洗白。第 2 轮一个分界线上的条目里捞出了两段真逻辑它顺着一条待办去读一对无参布尔函数的实现。结果发现两件事其中一个是纯桩——十几个字节全是跳转和ret没有一条真逻辑真正在做判断的是保护段里的代码。谓词这个标签只对了一半。另一个的尾部藏着两段真逻辑——用一种新写法捞出来的在每个偏移都尝试解码保留以ret结尾、且不含跳进保护段的转移的连续段。捞出来的一段直读就是; 尾部捞出的第二段示意 cmp dword ptr [SLOT_A], 2 ; 全局槽 A 2 je 继续 xor al, al ret ; A ! 2 → 返回假 继续: setg al ; al (signed char)SLOT_B 0 ret ; 返回 (B 0)语义完整return (A 2) (B 0)。注意它是从一段没有天然断层的灰区字节里捞出来的。⇒ 第一次推翻上一轮说这些区间的函数体是数据——这里明明有语义自洽的真代码。难得的是它当场自己把风险写下来了这个捞片段的规则会产生假阳性垃圾里也能随机撞出以ret结尾的短段这一例是靠语义自洽人工确认的不是自动可判的而且只在 1 个条目上验证过——是不是系统性问题还没测。第 3 轮模型错了——不是数据是夹层下一轮它把捞片段跑遍了全部 22 个可疑条目11 / 22 夹着带证据的真代码共恢复255 个片段没有一个条目是零片段——连最差的那条也能捞出东西来。最能说明问题的是反差条目解码率恢复片段数其中带证据解码率最低的那条0.0052817解码率与含真代码多少完全不成正比——最低分拿到第二名多的证据。还有一条决定性的解码率最低的那个区间里有一整条完整的if-else 链逐个比较 8 个名字jne串联语义完全自洽。上一篇陷阱 6 的那条反例就是它。⇒ 第二次推翻不是数据 or 代码的二分是夹层。保护壳把原始指令逐条换成跳进保护段的桩但原始字节留在原地于是幸存的真指令夹在桩与桩之间被字节流隔断——线性解码必然失步。低解码率不等于不是代码而是大部分不是代码但剩下的部分可能是。同两轮的判决一起下来上一轮那批整批撤回是过度撤回闸门机制废弃判据的单位从函数换成片段。它还自己给这条链做了一个总复查——一份很难得的自我诊断有的轮次是模型错拿函数级比例判是不是代码有的是方法错把整段区间的字节流当指令读有的是预期错还有一次是发现得太晚——夹层已经露头却没有立刻推广。共同根因只有一个一直想找一个函数级的二元判据而真实结构是区间内的分层混合。该早点问的是“这段字节是均匀的还是分层的”第 4 轮重建——单位从函数换成片段重建的方案是片段 区间双证据优先用捞出来的片段作证据捞不到片段的回退到整区间提取但单独降级标注。有没有代码和有多少证据是两个正交信号不合成一个布尔。重建里它又踩了一步而且是熟悉的一脚第一版把带证据的片段数 0直接判数据——66 个条目被判数据。荒谬。66 个里包含解码率 100%、单纯不引用任何字符串的干净代码“没有证据被当成了没有代码”。抓住这一步的不是感觉是已知真值回归拿 17 个此前由独立方法命名的函数当真值测新判据是否还找得到它们的证据——结果片段法会漏掉 3 个。于是定下片段优先、无片段回退整区间并把回退证据标成低置信。它自己把这条标成本轮最有价值的方法论。⇒ 第三次推翻整批撤回改判——3 个条目被证明是误撤、已恢复。全撤和全留是同一种错。结果指标闸门版重建后未解1713高置信名字1925有名字合计3943被判纯数据2200/107 被判纯数据——107 个区间全部含代码片段或干净解码只是可读比例不同。还有一个工程动作值得单独记被废弃的闸门被改成**“调用即抛异常”**——将来任何一个脚本想悄悄启用旧判据都会当场炸掉。错误的判据不会自己消失必须让它无法静默复活。第 5 轮量化假阳性——那批垃圾串的真身一个洞一直挂着是它自己记的捞片段的规则会产生假阳性率是多少没测。第 2 轮就警告过。它先做阴性对照结果两次都失败拿保护段当肯定不是代码的对照 → 恢复出0 个片段换成数据段里的字符串块 →还是 0 个。看起来是规则不乱报其实——恢复规则只在已知指令边界起步而那两块区域里没有任何已知边界。测到的是规则压根没跑不是规则很干净。它自己指出这和第一轮没有天然断层是同一类错误——度量设计本身无效而不是结果好。然后才是量化全部片段里23.8% 带垃圾特征。判据两条——含编译器几乎不会产出的指令aaa/iretd/xlatb这类call/jmp的立即数目标不在另一套独立扫描得到的指令起点集合里。真阳性回归那 8 个名字片段8/8 全部通过加过滤器后真证据零损失——丢掉的全是垃圾串。⇒ 第四次推翻恢复出来的片段 证据的默认被推翻。顺带它把上一篇陷阱 4 里那条假证据的真身也查清了那些形如!Q的短乱码串是误码片段把随机字节当成指令、又把随机的立即数解析成了字符串地址——不是数据里的巧合是误码的产物。同一轮里它还把自己刚写的另一个判据否掉了一次用片段数 0判无代码误伤了一个靠指针恒等式命名的函数。修法“找到过、但判它是垃圾” ≠ “没找到”——这是三值不是布尔。第 6 轮最后一块拼图——干净代码这一类不存在重建时它曾把 7 个条目判为解码率 100%、代码全可读、只是不引用任何字符串标注为静态到顶。下一轮逐个做结构检验7/7 全部是纯桩——各含2–9 条跳进保护段的跳转、零可恢复片段、无标准函数序言。那个解码率 100%是垃圾解出来的上一篇陷阱 7 的那段反汇编就是它。⇒ 第五次推翻单看解码率永远证明不了这段是代码——它对垃圾偏高而且在最难的情形纯桩上给出最漂亮的值。判是不是桩要用合取可恢复片段 0且跳进保护段的跳转 0。干净函数的零片段伴随零跳转桩的零片段伴随 2–9 个。终态账13 个未解分成7 个纯桩 6 个可继续挖的混合区间全量 107 个三分——68 可挖 / 39 桩 / 0 纯数据。最后是这个项目里最有用的一个结论桩 ≠ 不可命名。39 个桩里27 个已经有了名字或类型全部来自非函数体来源——指针恒等式、调用者证据、数据槽与表。函数体不可读不等于函数不可命名13 个未解里的那 7 个桩缺的正是这些非函数体证据。三、判据与验证前面是它翻了什么这一节是下次靠什么不翻。1. 阈值选不出来时先怀疑模型别怀疑阈值。分布没有天然断层不是阈值有任意性的借口而是模型选错了的信号。22 个条目的解码率从 0.005 到 0.45 平滑分布——那是夹层结构在说不存在一条线能把它们切干净。2. 判是不是 X不要用单一量压成一个是/否。这条链里同一种动作反复出现了六次#单一量 → 是/否后果1函数级解码率 → “这段是数据”22 个条目整段判错41 条证据整批撤回其中 3 个是误撤2“清单里没有” → “判为数据”对照组从 26 崩到 2当场折断3整区间当成指令读 → 提取体内证据把字节流读成指令既漏真串又造假串4强片段数 0 → “判为数据”66 个条目被判数据含解码率 100% 的干净代码5片段数 0 → “判为无代码”误伤一个靠指针恒等式命名的函数6零片段 解码率 100% → “判为干净代码”7 个其实是纯桩正确的形态是合取或多值判是不是桩用两个信号的合取表达能读出来多少用两个数值列而不是一个布尔列。3. 新判据必须用已知真值回归。66 这个荒谬值是靠拿 17 个独立命名的函数当真值现形的片段法会漏 3 个也是靠它发现的。看起来合理不是验收标准能通过已知样本才是。4. 阴性对照必须落在规则的作用域内。两次恢复出 0 个的对照都因为区域里没有任何已知指令边界而根本起不了步——测到的是规则没跑不是规则很干净。对照区域必须落在被测规则的射程里否则它只是一块更安静的 0。5. 撤回与断言同罪。整批撤回和整批断言是同一种布尔化。撤掉 3 个真函数的那次和给 22 个区间贴错标签的那次是同一条链上的两个方向。撤回也要逐条复核。6. 废弃的判据要上锁。被废掉的闸门被改成调用即抛异常——任何脚本想悄悄启用旧判据都会当场炸掉。错误的判据必须失去静默复活的能力。7. “找到过但判它是垃圾” ≠ “没找到”。三个状态找到了干净证据 / 找到了但判为垃圾 / 什么都没找到。前两个在旧输出里长得一样含义相反。人加的检查这条链里所有的推翻都是按规矩跑出来的检查干掉的而这些规矩能被立出来是因为流程里有一条底线——产出必须附带能被机器证伪的检查。人不需要懂夹层和片段只需要在每一轮问一句“这个结论该被什么检查打脸”——然后要求它把那个检查跑出来。四、结论五次被推翻的判词一张总账被推翻的判词它出自哪一轮被什么推翻“低解码率 不是代码”第 1 轮第 2 轮从一个灰区条目里捞出真逻辑第 3 轮全量否证数据 or 代码的二分第 1 轮夹层模型桩 字节流 幸存指令“整批撤回是对的”第 1 轮3 个条目被证明误撤、恢复“恢复出来的片段 证据”第 3–4 轮假阳性率量化23.8%过滤后零真证据损失“干净代码这一类存在”第 4 轮7/7 全是纯桩终态107 68 可挖 / 39 桩 / 0 纯数据名字 43 个高置信 2539 个桩里 27 个靠非函数体证据命名。这一整条链是静态结论——零运行时验证是它在每一页里自己重复标注的限制。三句话收束AI 会把自己上一轮的产出当事实。所以链条上的每一步都必须重新验证它的输入而不只是它的输出。分层的现实会被压成分明的布尔。拦住这件事的不是更聪明的提问是合取 多值 已知真值回归这类能被执行的检查。产能与自信是同一个东西的两面。六轮、几十个脚本、每次全量回归重跑——这是 AI 的产能五次推翻、每次都格式完整——这是 AI 的自信。五、边界声明本文只讨论AI 自动化逆向中的判据设计与审核方法不涉及任何具体产品、厂商或服务的分析结论。文中所有地址、偏移、常量、字段名一律为占位符或示意值不代表任何真实目标中的取值示例代码为说明判据而编写不构成可运行的针对特定目标的工具。文中提到的比例与数字均来自作者项目内的测量记录仅用于说明方法失效的方式。请勿将本文内容用于未授权的用途。六、收尾AI 从一个自己造的假前提出发用六轮把它拆干净——顺带把怎么判断一段字节是不是代码这件事从手感变成了判据。下一篇计划聊我把一个逆向项目交给了 AI它做到了什么程度。转载请注明出处https://lizhiyong.blog.csdn.net/article/details/167813706
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →