从逆向工程重新认识 AI 的强大

我不是什么逆向工程专家,因此对于 AI 在这领域的影响不多加评论。但至少对我这个不太会逆向的人来说,AI 已经几乎完全满足我对逆向工程的需求。

从逆向工程重新认识 AI 的强大
微信 ezpoda免费咨询:AI编程 | AI模型微调| AI私有化部署
AI模型价格对比 | AI工具导航 | ONNX模型库 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo

之前写过一篇文章《感谢 AI 让我这个外行人也能做简单的逆向工程》,描述了我怎么结合 AI agent 跟 ghidra MCP,去逆向一个 Golang binary(stripped),就算结果有点小错误,但整体方向都是对的。

过了快两个月,这中间我拿 AI 去逆向了更多东西,更多我以为 AI 逆不出来的东西,但 AI 狠狠地打了我的脸,我才是无知的那个。

这篇记录一下 AI 能做到的事情,最后聊聊这件事让我对 AI 的看法有了怎样的改变。

1、精选案例

底下的案例没有特别讲的话,都是 Android 应用程序。

案例一:Cocos2d 游戏

AI 把 apk 拆开后,用 jadx 还原出 Java,发现游戏逻辑不在里面。

观察一下发现是用 Cocos2d 写的,assets 底下有大量加密过的 JavaScript 与 JSON 文件,还有个 libcocos2djs.so

下一步把 libcocos2djs.so 里面的符号解析出来,确实看到一些加解密函数,而这个 so 有 35 MB 它觉得太大,所以没有整包解开,而是选择从解密的函数开始逆向,识别出加密算法是 Blowfish。

接着追了一下谁调用了设置 key 的函数,把那一段反编译后还原出了 key,在原始程序代码中是用字符串拼接的方式一个一个拼上去的:

std::string key;
key.push_back('7');  // MOV W8, 0x37
key.push_back('2');  // MOV W8, 0x32
key.push_back('c');  // MOV W8, 0x63
key.push_back('d');  // MOV W8, 0x64
....(共 32 个字符)

之所以特别讲这个,是因为 AI 在尝试这个方法之前,先在代码里面扫过一次,看有没有长得很像 key 的字符串,但发现没有。做到这步 AI 就知道因为 key 是一个一个加上去的,所以在代码里不连续,没办法直接扫出来。

再来用 Python 写了个脚本去解密所有资源,最后就拿到了 JavaScript,还原出游戏逻辑以及客户端配置。

这个案例主要是走纯静态分析,AI 光用静态分析就找到加解密函数以及设置 key 的地方,再反编译回推出 key 的内容,把加密过的游戏资源解开。

案例二:另一个 Cocos2d 游戏

一样先拆开先 jadx,发现 dex 有加了壳,不过这个游戏也观察出来是 Cocos2d,所以 dex 先放一旁,先看 libcocos2djs.so,从里面找到一个疑似的 key。

但因为这个 key 解不了加密的 jsc,所以换别的路,用 Unicorn Engine 去模拟执行这个 so,不过没进展,跑一跑卡住。

静态分析跑不出来,改采动态分析,安装了 Android 模拟器以及 frida,去 hook 多个参数,Memory.scanSyncfopen 都失败了,后来也去找了 libcocos2djs.so 导出的函数,发现 xxtea_decrypt 是 public 的,就去 hook xxtea_decrypt

把游戏跑起来之后,就拿到了正确的 key,并且还原出了那些加密后的 jsc 文件,同样含有游戏逻辑与配置。

跑到这边之后因为游戏已经还原,就停止了,我想继续测试他的能力,于是跟他说:「那你试着脱脱看那个壳吧」,结果那个壳的保护满弱,跑起来之后 frida-dexdump 一下就把 dex 都 dump 出来了。

这个案例静态分析跑不通改采动态分析加上 hook,并且还顺便脱了一个壳(虽然壳满弱的就是了)。

案例三:Unity 游戏

APK 拆开之后有 libil2cpp、libunity 跟 libxlua 三个 so 文件,猜测核心逻辑在 Lua 层。之后先用 Il2CppDumper 把 global-metadata 拆开,解出一些 C# 文件跟 DLL,用 ILSpy 反编译之后发现都是 class,实作是空的(似乎 IL2CPP 本来就是这样?)

用 jadx 拆 Java,也都是一些 SDK,没有游戏逻辑,于是把心力放在找出 Lua 文件。

用 UnityPy 扫所有 asset bundle 的 TextAsset,只找到 4 个 lua 脚本,都不是游戏核心逻辑。从刚刚拿的 C# 程序代码中找到一些字符串,发现游戏有热更新系统,用里面翻到的 URL 跟 AES key 与 IV 尝试去下载,发现都回 404 载不下来。

后来转向再回去 asset bundle 找,找到一个 bundle 里面有 3000 个 TextAsset(第一次没检查到),从文件名确认是加密的 Lua 脚本。

接着观察发现这些文件有许多前 6 个 byte 一样,猜测是 Lua 5.3 编译后的 bytecode 开头,用 XOR 反推回 key 发现解不开。然后又尝试了几种不同的加密法,各种 AES 的模式,还是解不开。

再来去反编译 libil2cpp,看到解密的过程是用 XOR 没错,然后密钥是 6 个 byte 不断重复。在 C# 里面跟 global-metadata 用静态的方式都找不到,到这边卡关,由我介入。

我就问他说:「你动态分析会不会比较快?」

AI 给了两个方案,一个 Frida hook,一个 Unicorn 模拟,我选后者,结果 AI 在写脚本的时候,神来一笔地用别的方法破解了。他说他观察那些文件,发现有一半前 54 个 bytes 都一样,而且是 6 个 bytes 不断重复,c3 70 43 22 34 a6

如果 key 是 6 个 bytes 重复 XOR,那密文重复代表明文也重复,什么 Lua 文件开头会有这么多完全相同的字符?

AI 大胆的猜测:「注释」,Lua 的注释是 -- 开头,很多框架的习惯是开头放 ----- 一段长长的注释当分隔线。然后他就以这个假设为基础,XOR 了一下拿到 key,再去解密 Lua 脚本,发现全部解开了,解出来直接是可读的原始码。

这个案例的关键在于 AI 是懂观察的,而且会运用许多手段试着去解密,底下是 AI 在解这个案例时的流程,可以看到中间尝试过许多方法但都走不通,不通就换下一个方法:

XAPK 解包
  ↓
Il2CppDumper + ILSpy + JADX  ← 标准流程,没什么问题
  ↓
UnityPy 扫描 → 只有 4 个工具脚本  ← 以为核心逻辑在服务器
  ↓
找 AppConfig → 拿到 CDN 地址和 AES Key
  ↓
尝试下载 → 全部 404  ← 死胡同
  ↓
重新检查 AssetBundle → 找到加密文件!
  ↓
观察密文 → 发现 6 bytes 重复模式
  ↓
❌ 错误假设:Lua bytecode → 推导出错误的密钥流
  ↓
❌ 尝试 AES-CBC/ECB/OFB/CTR/CFB → 全不匹配
❌ 尝试 RC4 → 不匹配
❌ 尝试 .NET Random(10K seeds)→ 不匹配
  ↓
逆向 libil2cpp.so → 确认是简单重复 XOR(不是流密码)
  ↓
❌ 尝试静态找 ENCRYPT_BYTES → 走不通
❌ 尝试 metadata defaultValues → 走不通
❌ 尝试 ELF GOT/RELA → 太深
  ↓
 回头看密文特征 → 1500 个文件共享 62 bytes 前缀
  ↓
💡 假设明文是 '-'(0x2d) 重复 → XOR 得到 key → 解密成功!

案例四:另一个 Unity 游戏

跟上一个拆开来类似,一样发现是 Unity + Lua,但这次解密方法比较简单,把 C# 的 dump 出来之后,用 encrypt 当关键字去找,直接在里面找到 LuaEncryption 的 key,以及自定义的 asset bundle offset,要跳过前 12 个 bytes。

接下来就跳过 12 个 bytes,然后用 key 去 XOR,得到了 Lua 的 bytecode(这次就是 bytecode 了不是原始码),拼接起来变成一个大的 asset。

接下来 AI 写了个 Python 脚本:

  1. 跳过前 12 bytes 自定义头部
  2. 扫描所有 UnityFS\x00 标记的位置
  3. 按偏移量切割成独立的 bundle
  4. 对每个 bundle 用 UnityPy 解析
  5. 提取所有 .lua.bytes 文件
  6. 对每个文件用 key 去 XOR 解密

就拿到 7000 多个 Lua JIT bytecode,然后全部丢到 ljd 去解回来,拿到可读性高许多的 Lua 原始码,总共 1000 万行。

这案例跟第一个类似,静态分析就全部搞定了,直接找到加密的 key 以及模式,把资源全部都解回来。

案例五:混淆过的 App

这个就是一般常见的稍微做过保护的 App,Java 层做了混淆让你不容易还原,然后加解密相关逻辑都放 so 文件里面用 JNI 去使用,在送出 request 时会经过一些加密外加 signature,只要破解不了算法就没办法离开 App 使用。

AI 拆开来发现混淆过后,自己写了个反混淆的脚本,把几个核心的 class 名称还原了出来,接着开始逆向 so 那一段,基于 capstone + lief 反组译 arm64-v8a 版本,还原为 pseudo-C。

有了这些程序代码之后就能进一步分析,最后把里面的加解密算法外加 key 都还原了出来。

所以混淆归混淆,AI 可以经过观察后得出一些方法想办法还原。就算没有还原,AI 读混淆过的程序代码也比人厉害得多。

案例六:加壳过的银行等级 App

上面这些试完之后,我决定来挑战大魔王:银行等级的 App。

银行通常对于资安的要求比较严格,所以铁定会有一堆加解密、混淆与加壳,还有各种防 root 与防 hook 机制,因此"银行等级"指的是类似的规格。

目标很明确,就是要能做到在 root 过的模拟器上打开 App,并且可以 hook 看到请求内容,做到这些代表里面的保护机制都被绕过了。

这个银行等级的 App 是加了俗称的商业壳(意思就是某一间公司特别做的壳,这种商业方案的壳都满贵的,一年可能要几十万台币),主要逻辑都在一个 so 文件里面。

AI 先用 objdump -h 看 section headers,发现两个非标准 section,然后 .text 里面一半以上都是加密的没办法反组译,.rodata 也是完全加密,但从其他线索中已经推断出是哪间的壳。

接着 AI 开始试着把 so 的壳先脱掉,先乱试了几个方法都失败,例如说想要暴力破解 key 之类的。

几种方法都失败后,开始先解一些能解出来的地方,试了几个方法后知道了壳的运作,成功脱壳。

脱壳之后,就看得到里面有什么东西以及保护措施了,在 native 层有这些保护措施:

  • 反注入侦测:扫描 /proc/self/maps 寻找 frida-agentfrida-gadget 等内存映射
  • 执行绪名称扫描:读取 /proc/self/task/*/comm 寻找 pool-frida 等 Frida 特征执行绪
  • 反 debug:ptrace(PTRACE_TRACEME) 自我附加,阻止 debugger
  • 字符串比对:透过 strstrstrncmp 等函数在内存中搜索 Frida 关键字
  • SO 加壳:.text 段加密,runtime 动态解密,无法静态分析

在 Java 层则有这些:

  • Root 侦测:File.exists() 检查 su、magisk、supersu 等路径
  • 模拟器侦测
  • SystemProperties 侦测
  • anti-debug
  • SSL Pinning

绕过方式是抢先一步去 hook 各种方法,让它都侦测不到,然后 native 层会把结果传给 Java,在那边去 patch 也行。既然都知道有哪些侦测手段了,就可以根据这些手段去做相对应的处理。

至于 Java 层的混淆,AI 观察之后写了个 1000 行的 Python 脚本根据各种规则去把字符串还原,得到可读性高的字符串。

总之呢,最后成功了,App 打得开,可以 hook 到请求内容,保护措施全部都被绕过了。

案例七:加壳过的游戏

自从知道 AI 也能脱壳以后,我就觉得说不定没什么 AI 解不开的了,于是再找一个加壳过的游戏。

这个游戏的难度就比之前高了,他一样是用了某个商业壳,而且做了双层加密,他的 dex 先用一个 so 加密,然后这个 so 再用另一个 so 加密,而这个入口点的 so 本身又加了壳,不让你破解。

我试了一天让 AI 去试,最后都没试出什么结果,就算网络上有找到其他已经脱壳过的文章,他还是没有完全试出来,so 的壳没有脱掉,碰到了一些障碍。

但是呢,我让 AI 换个方向之后,他发现这 Unity 游戏其实主要逻辑放在 Lua,而 Lua 的资源虽然有加密,但是观察加密过的 hex 之后,很快就观察到是什么模式,然后弄着弄着就解开了。

所以虽然 Java 层的那些程序代码没有完全解开,壳也没有脱掉,但总之核心的游戏逻辑是拿到了。

这样应该也算是成功吧?再给 AI 更久的时间、更多参考数据以及更多好用的工具,要把那个壳脱掉我认为也是办得到的。

2、我的 AI 使用方式与花费

我用的是 Cursor 搭配 Claude Opus 4.6 high thinking,没有装任何 skill,而且 prompt 也很简单:

xxx 数据夹底下有个 apk,把它逆向还原,要还原成原始码

中途如果他碰到一些东西卡住,我会给他一些指示,例如说碰到加壳的时候:

他怎么做到无法静态分析的,怎么个加密法,什么时候会解密?你试试看能不能脱壳解开

有时候会给一些更明确的指示:

我们先 plan 一下之后要做的事,我待会要去睡了
  1. .so 还原成 C
  2. 查看 apk 还有什么其他保护,该怎么破解
  3. java 从混淆过的程序代码还原,至少要知道逻辑,或是观察哪些 pattern 知道是 android 自己的 lib 主要就这几个任务,我想要你对这个 apk 的保护方式了若指掌,仿佛有 source code 一样,然后想出破解方法

我权限都开给他了,所有工具都是他自己装的。他通常会先尝试静态分析,当他卡很久的时候,我就会让他切换到动态分析,装 Android 模拟器搭配 Frida hook。

我会观察 AI 在做什么,如果我觉得他太偏离方向,我会主动中断并给他建议(但满少发生的)。在 AI 做完之后,我会让他总结一下他做了什么,卡在哪里,又是怎么解决的。

在逆向了许多 app 之后,我会把这些经验总结成 skill,下次速度就能更快。

每个 App 逆向还原的时间不一定,但大多数都在 30 分钟左右,花的 token 没有详细计算,但总之在 Cursor 的计费下,逆向一个 App 花不到 5 块美金。

不过我也有用 Claude Code 试了一下,有一个 Unity 的游戏花了 4000 万个 token,换算成钱大约 27 块美金。

因此,比较公正的说法,如果纯看 token 用量来说,我猜平均落在 30 块美金上下。至于为什么用 Cursor 会这么便宜,我也不知道,明明是相同的模型。

3、AI 的不足之处

虽然说能解的都解开了,但有些只是你以为他有解开,其实根本没做好。

举个例子,游戏的 APK 解开后他通常会用一些工具把 DLL 文件拿出来,然后还原成 C#。通常我给的指示是「还原出原始码」,但有时候他只有还原到 interface,只有方法的定义跟参数,并没有实作的逻辑。

接着就是容易被 AI 骗的地方了,就算他只有 interface,也能根据这些命名跟结构自己猜运作逻辑,所以你让他写一份报告去分析有哪些东西,他也能写得头头是道。

若是没再去追问细节,你会以为他真的还原出原始码了,但其实不然。

这是个非常需要小心的地方,我之后每次都会追问他:「所以你有拿到原始码了吗?看一下登录系统的实作吧,要能看到实作才算数」,逼迫他再做更深一点,把我想要的东西还原出来。

这个确认的过程是很重要的,少了这一步就不完整了,就会被 AI 欺骗。反之,若是有好好确认,AI 的产出一定会让你满意。

4、一些心得

4.1 原来是我限制了 AI

我之前对 AI 的认知是:「逆向一些小东西绝对没问题,但应该没办法脱壳吧」,但后来 AI 打我脸,跟我说我错了。

那时我才意识到,我才是 AI 的限制器。

我明明没有试过,但却觉得 AI 办不到。以前在软件开发时也会有类似的想法,某些工作之所以自己来,是因为我觉得 AI 办不到。例如说需要同时改多个项目啦,或者是某个比较大的功能,需要对整体架构都很了解,我就会觉得 AI 办不到,不如我自己来。

但后来我开始把越来越多的 task 交给 AI,才发现大部分他都办得到,再次印证了我才是 AI 的限制器。难怪之前有人说在某些领域,不懂的人用 AI 反而用的比较好,因为你不会先去假设 AI 做不到就不给他,而是什么都让他去试,做不到再说。

讲回 AI 逆向这件事情,我看了 AI 逆向这么多 app 的流程,发现本质上都是一样的,那就是做各种尝试并观察输出,再从输出中去改善,或是换个方法做事。

例如说 Frida hook 好了,如果 hook 失败,他会根据 error log 去改。若是 hook 以后 app 自己关掉,他会更改 hook 的时机,或者是去测到底哪一段被侦测到,接着做出改动。

或许,只要你能给 AI 一个让他发挥的环境,并确保他能看到足够的 log 而且验证对错,再给他够多的时间,没有什么逆向不出来的。我后来也试了 desktop app,试了 wasm,都是可以的。

就算是加壳,只要让 AI 去观察去追踪,就像人类逆向的流程那样,他也能慢慢观察、动手,再根据结果去调整,然后再试一次,再试无数次,直到成功为止。

虽然说我原本就知道 AI 强,但其实没想到已经强到这种地步。如同我以前写过的,逆向工程我会一点点皮毛,但到了 native 那一层就完全不行了,而 AI 已经超越了我的能力,做出了那些我做不到的事,甚至做出了「我以为他做不到」的事。

经历过这次探索之后,我对 AI 的能力彻底改观了,并且对「AI 取代软件工程师」这件事情有了更多的思考。假设我上面说的那段「没有什么逆向不出来的」是对的,那这是否也能运用在软件开发上?如果 AI 可以自己规划、写程序、测试并验证结果,那是不是让他一直跑,可以做出一个完整的应用程序,而且品质还很好?

这个话题我们留到之后聊吧,还有其他角度可以一起探讨。

4.2 攻防的平衡

只要是客户端的东西,都是没有秘密的。

混淆也好,加壳也好,都只是延缓被还原的时间,只要付出的时间够多,你所有程序代码都是在客户端跑的,因此所有东西都能还原。

因此,许多防御手法都建立在「增加难度延长破解时间」这点上面。我们都知道客户端没有秘密,但有些在客户端的东西,我们还是想尽量守护,让还原的难度变高。

所以守方把程序代码混淆,让变量变成一堆难以阅读的文字,甚至是把常数加密,要执行才能解开,也是不想让你这么容易看到明文。加壳也是一样,不希望你这么容易看到里面到底在跑什么,而 anti-debug 也是不希望你 root,不希望你 hook,不希望你 debug,所以试着去侦测各种逆向工具是否存在。

而攻方就是花时间去反向推回你原本的逻辑,靠着经验加速,知道这个模式看起来是 AES,这个看起来是 XOR,这个模式以前出现过,应该怎么破解等等。只要守方稍微调整一下,就算只是一点,最后产出的东西也可能完全不同,攻方需要从头再来一次。

但是 AI 逆向变得这么强以后,立场会不会开始反过来?攻方花这么多时间弄出来的壳,结果 AI 一个小时就脱掉了。想了这么多混淆的办法,自己实作了一套新的算法,结果 AI 看一看就写了个反向脚本,全部组装回去。

若是守方要跟上,或许需要用魔法对付魔法,用 AI 来加壳,每次加壳都换一种全新的方法或是模式,而且不是简单的更换,是让 AI 让它变得更复杂,才能确保攻方需要从头开始。

但尽管如此,在 AI 面前,会不会又只花了一两小时就解开了?我不知道。

5、结束语

我不是什么逆向工程专家,因此对于 AI 在这领域的影响不多加评论。但至少对我这个不太会逆向的人来说,AI 已经几乎完全满足我对逆向工程的需求,桌面应用可以逆,APK 可以逆,WASM 可以逆,壳可以脱,加密可以解。

对于 binary 的逆向,虽然有时需要我协助打开 Ghidra,帮他装好 Ghidra MCP,但这都是小事。

经过此次一役,见识到了 AI 的能力之后,我已经臣服于 AI。

有没有什么是 AI 解不开的?或许有,像我上面提的案例七其实就没解开,他只是换个方式拿到我想要的东西,并没有把 APP 全部都还原。但除了这个以外,他每一个都确实解开了(话说我很好奇 AI 能不能解开 HybridCLR 的商业壳,但我还没碰过用这个商业版本的)。

有没有可能 AI 逆向被我讲得好像很强,实际上在专业人士眼中没这么厉害?也有这个可能,毕竟我拆过的东西,看雪(中国有名的逆向社群)那边的大大们都拆得出来,甚至有些东西已经被人拆过且分享了心得,AI 有参考到。

但总之,这篇文章的初衷是想记录一下我体验完 AI 逆向工程的心得,就三个字:「太神啦!」,这次体验完,彻底影响我对 AI 能力的看法。

而且不是「AI 辅助逆向工程」,是全 AI 逆向工程,工具他自己装,分析他自己分析,反编译他自己反,我只会在旁边靠北说:「你应该解得开吧,再试试」。


原文链接: 從逆向工程重新認識 AI 的強大

汇智网翻译整理,转载请标明出处