首页
社区
课程
招聘
[原创] 某60,某加密,某盾,某迦,某乐逆向 - AI加garlic能力测试
发表于: 14小时前 537

[原创] 某60,某加密,某盾,某迦,某乐逆向 - AI加garlic能力测试

14小时前
537

garlic的arm64的native分析我写完了一段时间了,还在看雪发了三篇文章:

[原创] Arm静态分析引擎原理一 - 反汇编引擎码表设计

[原创] Arm静态分析引擎原理二 - 反汇编引擎实现

[原创] Arm静态分析引擎原理三 - 控制流构建基础

我是一个逆向分析的小白,为了测试garlic对android native的分析能力,我设计了下面的测试:
测试要求:

测试验收:

对比测试:

garlic的mcp-server在接受到分析xxx.apk的时候会做如下操作:

样例:

这个是两层加固,不知道是正常的包还是异常的包,不是在官网的apk下载下来的;

加载流程:

AI输出:

结论:这个是一个某60套着某加密的apk,感觉是个盗版;解压出来的dex在android运行的时候是一个壳,壳起来后解密dex后面的密文,释放出真实的业务逻辑;

加载流程:

AI输出:

结论:这个apk和上个apk一样,也是把密文存在了dex的后面,壳起来以后,跑了一堆加解密,解压出新的dex,然后用InMemoryDexLoader来加载真实的dex;

加载流程:

AI输出:

结论:这个apk是耗时最长的,涉及的重建几个so,然后用garlic分析重建的elf的工作,是隐藏的dex,隐藏的内容需要解密,解压,InMemoryDexLoader加载,没有vmp;

加载流程:

AI输出:

结论:这个apk没有隐藏的业务逻辑,复杂的地方在j2c保护的http签名的代码,纯静态没有办法分析(AI说的),动态抓包和模拟就可以了。

加载流程:

AI输出:

结论:这个apk也是将dex藏起来了,然后通过壳的初始化,加载native libs,native解密dex,通过InMemoryDexLoader启动app;

如果有人需要逆向过程,可以发我邮件,我打包calude的projects给你。

garlic的github: f3eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6F1k6h3!0U0j5h3&6S2j5X3I4W2i4K6u0r3k6$3q4J5L8r3W2U0i4K6u0o6 欢迎使用,特别欢迎PR

.
├── analysis.duckdb
├── cg
│   ├── call_graph_edge.csv
│   ├── call_graph_node.csv
│   ├── string_edge.csv
│   └── string_node.csv
├── decompiled
│   ├── AndroidManifest.xml
│   └── com
│       ├── example
│       │   └── helloworld
│       │       ├── BuildConfig.java
│       │       └── R.java
│       ├── ifeng
│       │   └── newvideo
│       │       └── StubWrapperProxyApplication.java
│       └── wrapper
│           └── proxyapplication
│               ├── AndroidNClassLoader.java
│               ├── CustomerClassLoader.java
│               ├── MultiDex.java
│               ├── MultiDexForMemoryDex.java
│               ├── MultiDexForTinker.java
│               ├── MultiDexForTinkerForMemoryDex.java
│               ├── ShareReflectUtil.java
│               ├── Util.java
│               └── WrapperProxyApplication.java
├── native_libs
│   └── arm64-v8a
│       ├── libapp.so
│       ├── libapp.so.cfg_edges
│       ├── libapp.so.cfg_nodes
│       ├── libapp.so.dissembly
│       ├── libapp.so.entries
│       ├── libapp.so.exports
│       ├── libapp.so.func_xref
│       ├── libapp.so.imports
│       ├── libapp.so.pc_xrefs
│       ├── libapp.so.strings
│       ├── libBugly_Native.so
│       ├── libBugly_Native.so.cfg_edges
│       ├── libBugly_Native.so.cfg_nodes
│       ├── libBugly_Native.so.dissembly
│       ├── libBugly_Native.so.entries
│       ├── libBugly_Native.so.exports
│       ├── libBugly_Native.so.func_xref
│       ├── libBugly_Native.so.imports
│       ├── libBugly_Native.so.pc_xrefs
│       ├── libBugly_Native.so.strings
│       ├── libc++_shared.so
│       ├── libc++_shared.so.cfg_edges
│       ├── libc++_shared.so.cfg_nodes
│       ├── libc++_shared.so.dissembly
│       ├── libc++_shared.so.entries
│       ├── libc++_shared.so.exports
│       ├── libc++_shared.so.func_xref
│       ├── libc++_shared.so.imports
│       ├── libc++_shared.so.pc_xrefs
│       ├── libc++_shared.so.strings
│       ├── libCtaApiLib.so
│       ├── libCtaApiLib.so.cfg_edges
│       ├── libCtaApiLib.so.cfg_nodes
│       ├── libCtaApiLib.so.dissembly
│       ├── libCtaApiLib.so.entries
│       ├── libCtaApiLib.so.exports
│       ├── libCtaApiLib.so.func_xref
│       ├── libCtaApiLib.so.imports
│       ├── libCtaApiLib.so.pc_xrefs
│       ├── libCtaApiLib.so.strings
│       ├── libdaemon_api20.so
│       ├── libdaemon_api20.so.cfg_edges
│       ├── libdaemon_api20.so.cfg_nodes
│       ├── libdaemon_api20.so.dissembly
│       ├── libdaemon_api20.so.entries
│       ├── libdaemon_api20.so.exports
│       ├── libdaemon_api20.so.func_xref
│       ├── libdaemon_api20.so.imports
│       ├── libdaemon_api20.so.pc_xrefs
│       ├── libdaemon_api20.so.strings
│       ├── libdaemon_api21.so
│       ├── libdaemon_api21.so.cfg_edges
│       ├── libdaemon_api21.so.cfg_nodes
│       ├── libdaemon_api21.so.dissembly
│       ├── libdaemon_api21.so.entries
│       ├── libdaemon_api21.so.exports
│       ├── libdaemon_api21.so.func_xref
│       ├── libdaemon_api21.so.imports
│       ├── libdaemon_api21.so.pc_xrefs
│       ├── libdaemon_api21.so.strings
│       ├── libdu.so
│       ├── libdu.so.cfg_edges
│       ├── libdu.so.cfg_nodes
│       ├── libdu.so.dissembly
│       ├── libdu.so.entries
│       ├── libdu.so.exports
│       ├── libdu.so.func_xref
│       ├── libdu.so.imports
│       ├── libdu.so.pc_xrefs
│       ├── libdu.so.strings
│       ├── libed25519.so
│       ├── libed25519.so.cfg_edges
│       ├── libed25519.so.cfg_nodes
│       ├── libed25519.so.dissembly
│       ├── libed25519.so.entries
│       ├── libed25519.so.exports
│       ├── libed25519.so.func_xref
│       ├── libed25519.so.imports
│       ├── libed25519.so.pc_xrefs
│       ├── libed25519.so.strings
│       ├── libffavc.so
│       ├── libffavc.so.cfg_edges
│       ├── libffavc.so.cfg_nodes
│       ├── libffavc.so.dissembly
│       ├── libffavc.so.entries
│       ├── libffavc.so.exports
│       ├── libffavc.so.func_xref
│       ├── libffavc.so.imports
│       ├── libffavc.so.pc_xrefs
│       ├── libffavc.so.strings
│       ├── libflutter.so
│       ├── libflutter.so.cfg_edges
│       ├── libflutter.so.cfg_nodes
│       ├── libflutter.so.dissembly
│       ├── libflutter.so.entries
│       ├── libflutter.so.exports
│       ├── libflutter.so.func_xref
│       ├── libflutter.so.imports
│       ├── libflutter.so.pc_xrefs
│       ├── libflutter.so.strings
│       ├── libgifimage.so
│       ├── libgifimage.so.cfg_edges
│       ├── libgifimage.so.cfg_nodes
│       ├── libgifimage.so.dissembly
│       ├── libgifimage.so.entries
│       ├── libgifimage.so.exports
│       ├── libgifimage.so.func_xref
│       ├── libgifimage.so.imports
│       ├── libgifimage.so.pc_xrefs
│       ├── libgifimage.so.strings
│       ├── libifeng_secure.so
│       ├── libifeng_secure.so.cfg_edges
│       ├── libifeng_secure.so.cfg_nodes
│       ├── libifeng_secure.so.dissembly
│       ├── libifeng_secure.so.entries
│       ├── libifeng_secure.so.exports
│       ├── libifeng_secure.so.func_xref
│       ├── libifeng_secure.so.imports
│       ├── libifeng_secure.so.pc_xrefs
│       ├── libifeng_secure.so.strings
│       ├── libijkffmpeg.so
│       ├── libijkffmpeg.so.cfg_edges
│       ├── libijkffmpeg.so.cfg_nodes
│       ├── libijkffmpeg.so.dissembly
│       ├── libijkffmpeg.so.entries
│       ├── libijkffmpeg.so.exports
│       ├── libijkffmpeg.so.func_xref
│       ├── libijkffmpeg.so.imports
│       ├── libijkffmpeg.so.pc_xrefs
│       ├── libijkffmpeg.so.strings
│       ├── libijkplayer.so
│       ├── libijkplayer.so.cfg_edges
│       ├── libijkplayer.so.cfg_nodes
│       ├── libijkplayer.so.dissembly
│       ├── libijkplayer.so.entries
│       ├── libijkplayer.so.exports
│       ├── libijkplayer.so.func_xref
│       ├── libijkplayer.so.imports
│       ├── libijkplayer.so.pc_xrefs
│       ├── libijkplayer.so.strings
│       ├── libijksdl.so
│       ├── libijksdl.so.cfg_edges
│       ├── libijksdl.so.cfg_nodes
│       ├── libijksdl.so.dissembly
│       ├── libijksdl.so.entries
│       ├── libijksdl.so.exports
│       ├── libijksdl.so.func_xref
│       ├── libijksdl.so.imports
│       ├── libijksdl.so.pc_xrefs
│       ├── libijksdl.so.strings
│       ├── libimagepipeline.so
│       ├── libimagepipeline.so.cfg_edges
│       ├── libimagepipeline.so.cfg_nodes
│       ├── libimagepipeline.so.dissembly
│       ├── libimagepipeline.so.entries
│       ├── libimagepipeline.so.exports
│       ├── libimagepipeline.so.func_xref
│       ├── libimagepipeline.so.imports
│       ├── libimagepipeline.so.pc_xrefs
│       ├── libimagepipeline.so.strings
│       ├── libjcore300.so
│       ├── libjcore300.so.cfg_edges
│       ├── libjcore300.so.cfg_nodes
│       ├── libjcore300.so.dissembly
│       ├── libjcore300.so.entries
│       ├── libjcore300.so.exports
│       ├── libjcore300.so.func_xref
│       ├── libjcore300.so.imports
│       ├── libjcore300.so.pc_xrefs
│       ├── libjcore300.so.strings
│       ├── liblocSDK7b.so
│       ├── liblocSDK7b.so.cfg_edges
│       ├── liblocSDK7b.so.cfg_nodes
│       ├── liblocSDK7b.so.dissembly
│       ├── liblocSDK7b.so.entries
│       ├── liblocSDK7b.so.exports
│       ├── liblocSDK7b.so.func_xref
│       ├── liblocSDK7b.so.imports
│       ├── liblocSDK7b.so.pc_xrefs
│       ├── liblocSDK7b.so.strings
│       ├── libmmkv.so
│       ├── libmmkv.so.cfg_edges
│       ├── libmmkv.so.cfg_nodes
│       ├── libmmkv.so.dissembly
│       ├── libmmkv.so.entries
│       ├── libmmkv.so.exports
│       ├── libmmkv.so.func_xref
│       ├── libmmkv.so.imports
│       ├── libmmkv.so.pc_xrefs
│       ├── libmmkv.so.strings
│       ├── libpag.so
│       ├── libpag.so.cfg_edges
│       ├── libpag.so.cfg_nodes
│       ├── libpag.so.dissembly
│       ├── libpag.so.entries
│       ├── libpag.so.exports
│       ├── libpag.so.func_xref
│       ├── libpag.so.imports
│       ├── libpag.so.pc_xrefs
│       ├── libpag.so.strings
│       ├── libpl_droidsonroids_gif.so
│       ├── libpl_droidsonroids_gif.so.cfg_edges
│       ├── libpl_droidsonroids_gif.so.cfg_nodes
│       ├── libpl_droidsonroids_gif.so.dissembly
│       ├── libpl_droidsonroids_gif.so.entries
│       ├── libpl_droidsonroids_gif.so.exports
│       ├── libpl_droidsonroids_gif.so.func_xref
│       ├── libpl_droidsonroids_gif.so.imports
│       ├── libpl_droidsonroids_gif.so.pc_xrefs
│       ├── libpl_droidsonroids_gif.so.strings
│       ├── libshell-super.com.ifeng.newvideo.so
│       ├── libshell-super.com.ifeng.newvideo.so.cfg_edges
│       ├── libshell-super.com.ifeng.newvideo.so.cfg_nodes
│       ├── libshell-super.com.ifeng.newvideo.so.dissembly
│       ├── libshell-super.com.ifeng.newvideo.so.entries
│       ├── libshell-super.com.ifeng.newvideo.so.exports
│       ├── libshell-super.com.ifeng.newvideo.so.func_xref
│       ├── libshell-super.com.ifeng.newvideo.so.imports
│       ├── libshell-super.com.ifeng.newvideo.so.pc_xrefs
│       ├── libshell-super.com.ifeng.newvideo.so.strings
│       ├── libsign.so
│       ├── libsign.so.cfg_edges
│       ├── libsign.so.cfg_nodes
│       ├── libsign.so.dissembly
│       ├── libsign.so.entries
│       ├── libsign.so.exports
│       ├── libsign.so.func_xref
│       ├── libsign.so.imports
│       ├── libsign.so.pc_xrefs
│       ├── libsign.so.strings
│       ├── libsmsdk.so
│       ├── libsmsdk.so.cfg_edges
│       ├── libsmsdk.so.cfg_nodes
│       ├── libsmsdk.so.dissembly
│       ├── libsmsdk.so.entries
│       ├── libsmsdk.so.exports
│       ├── libsmsdk.so.func_xref
│       ├── libsmsdk.so.imports
│       ├── libsmsdk.so.pc_xrefs
│       ├── libsmsdk.so.strings
│       ├── libstatic-webp.so
│       ├── libstatic-webp.so.cfg_edges
│       ├── libstatic-webp.so.cfg_nodes
│       ├── libstatic-webp.so.dissembly
│       ├── libstatic-webp.so.entries
│       ├── libstatic-webp.so.exports
│       ├── libstatic-webp.so.func_xref
│       ├── libstatic-webp.so.imports
│       ├── libstatic-webp.so.pc_xrefs
│       ├── libstatic-webp.so.strings
│       ├── libTaoBaoParamUtils.so
│       ├── libTaoBaoParamUtils.so.cfg_edges
│       ├── libTaoBaoParamUtils.so.cfg_nodes
│       ├── libTaoBaoParamUtils.so.dissembly
│       ├── libTaoBaoParamUtils.so.entries
│       ├── libTaoBaoParamUtils.so.exports
│       ├── libTaoBaoParamUtils.so.func_xref
│       ├── libTaoBaoParamUtils.so.imports
│       ├── libTaoBaoParamUtils.so.pc_xrefs
│       ├── libTaoBaoParamUtils.so.strings
│       ├── libutility.so
│       ├── libutility.so.cfg_edges
│       ├── libutility.so.cfg_nodes
│       ├── libutility.so.dissembly
│       ├── libutility.so.entries
│       ├── libutility.so.exports
│       ├── libutility.so.func_xref
│       ├── libutility.so.imports
│       ├── libutility.so.pc_xrefs
│       ├── libutility.so.strings
│       ├── libweibosdkcore.so
│       ├── libweibosdkcore.so.cfg_edges
│       ├── libweibosdkcore.so.cfg_nodes
│       ├── libweibosdkcore.so.dissembly
│       ├── libweibosdkcore.so.entries
│       ├── libweibosdkcore.so.exports
│       ├── libweibosdkcore.so.func_xref
│       ├── libweibosdkcore.so.imports
│       ├── libweibosdkcore.so.pc_xrefs
│       ├── libweibosdkcore.so.strings
│       ├── libwind.so
│       ├── libwind.so.cfg_edges
│       ├── libwind.so.cfg_nodes
│       ├── libwind.so.dissembly
│       ├── libwind.so.entries
│       ├── libwind.so.exports
│       ├── libwind.so.func_xref
│       ├── libwind.so.imports
│       ├── libwind.so.pc_xrefs
│       └── libwind.so.strings
└── report
    ├── report.html
    └── report.md

13 directories, 311 files
StubApp.attachBaseContext()
  ├─ DtcLoader.<clinit> → System.loadLibrary("jgdtc")   (DTC 引擎,运行时解密生成)
  ├─ interface5(app)                                     (native,壳初始化)
  ├─ 反射加载真实 Application "entryRunApplication"
  ├─ interface8(realApp, ctx)                            (native,解密 DEX)
  └─ System.loadLibrary("jiagu") → 释放 jiagu.so 到 /.jiagu/ 再加载

- 入口链:`JNI_OnLoad → interpreter_wrap_int64_t(0x46af0) → sub_44eb4(0x44eb4)`
- 15-opcode 类型化栈机,跳转表 @0x53968
- JNI_OnLoad 真实逻辑:缓存 Context + ActivityThread


- `classes.dex` 尾部(offset 18156 起,23MB)= 加密 payload(目录 `d6edc6c6` 结构 + 加密 blob)
- `assets/goodgoodstudy.zip` = 诱饵;`assets/data.bin` = PNG 图集;`.jgapp` = 16 字节标记



真正 DEX 解密在**第二阶段加密 .so**(`libdjiagu.so`/`libijmDataEncryption.so`/`libjgdtc.so`,运行时从密文解密生成)里,或由 `__arm_a_1`  程序触发(需修正其 funcptr/参数后仿真)。关键点:找到 `interface5`/`interface8` native 方法(RegisterNatives 注册表在加密代码中,libjiagu.so 明文里无 "interface5" 字符串)。
System.load(libvlplg.so)
  → Dynamic Linker:
    → INIT[0] (0xFD504): 代码/数据解密
    → INIT[1] (0xFD5B4): VMP 初始化
    → JNI_OnLoad (0xFD9D8): 注册 native methods
        
MyApplication.attachBaseContext()
  → MyJni.load(Application, String) [native 0xFD700]:
    → 读取 37 个 chunk
    → 为每个 chunk 解密前 284 字节 → stub DEX
    → 37 x InMemoryDexClassLoader(stub)
    → 返回 true
    
App 后续运行:
  → 需要类时 → 自定义 ClassLoader.findClass()
  → VMP 解密对应 chunk 中的类数据
  → 返回完整类定义
DEX Decryption Handler (0xFDCF4)
  ├── LDR W2,[X3,#0x26C] W4,[X3,#0x27C] W5,[X3,#0x24C] W1,[X3,#0x25C]
  │   └── 从 chunk 子头解析后的结构体加载参数
  ├── LDR X0,[X20,#0x12]
  │   └── VM 状态偏移 0x12 (密钥流指针?)
  ├── BL sub_101ED8 (0x101ED8)  ← VM 解释器核心
  │   ├── PRNG 链 (mod_mul/mod_add/mod_sub × N)
  │   ├── TBZ W7,#0 → 验证 PRNG 输出
  │   ├── LDR W1,[X1,#0x2910] → 检查数据大小
  │   ├── BL sub_10719C (0x10719C) ← 数据处理
  │   │   ├── BL sub_FD440 (memcpy/setup)
  │   │   ├── BL sub_107FC4 (0x107FC4) ← 函数表分发
  │   │   │   └── BLR [X20,#0x4A*8] ← 动态分发
  │   │   └── STR W3,[X19,#0x23] ← 写回结果
  │   ├── BL sub_10721C (0x10721C) ← JNI 包装器
  │   │   └── BLR X8 → NewDirectByteBuffer?
  │   └── RET → 返回 DEX Decryption Handler
  ├── BLR [X23,#0x6E*8] ← 释放/清理
  └── TBNZ W1,#0 → 下一轮或结束
  
  
  | 地址                       | 描述                                |
| -------------------------- | ----------------------------------- |
| 0xFD504                    | INIT[0] - 代码解密                  |
| 0xFD5B4                    | INIT[1] - VMP 初始化                |
| 0xFD640                    | Key Generation 函数                 |
| 0xFD700                    | MyJni.load native 入口              |
| 0xFD9D8                    | JNI_OnLoad                          |
| 0x100088                   | 字符串解密 (读取 0x10D320)          |
| 0x107808/0x107818/0x107828 | PRNG mod_sub/mod_add/mod_mul        |
| 0x10D320                   | 密钥表 (8×uint32, 占位值, 永不更新) |
| 0x113568                   | 种子结构体 (BSS)                    |


- `/xxx/apks/tt/xor_key_284.bin` — 256 字节 XOR 密钥流
- `/xxx/apks/tt/unpacked_dex2/stub_0.dex` — 解密后的 284 字节 stub DEX
- `/xxx/apks/tt/frida_readbuf.js` — 捕获 stub DEX 的 Frida 脚本
- `/xxx/apks/tt/analyze_vm.py` — VM 字节码分析脚本
APK assets/ijiami.dat (17MB, 加密的 ZIP)
   │
   ├─ ① XOR 解密 ZIP 头/尾(隐藏 "PK\x03\x04" 魔术)
   │
   ├─ ② 得到标准 ZIP → 解压 "classes.dex" 条目(raw DEFLATE)
   │
   ├─ ③ 得到 57MB 的 ifd(= 字节替换加密的 DEX payload,含 40 字节头)
   │
   └─ ④ 字节替换 + K0 头部 XOR + 末尾 map_list K0 XOR
        → 9 个 App 真身 DEX(dex_3~dex_11,共 57,225,632 字节)
对本 App 的 DEX 保护,拆解后其实是**三层**:

1. **容器层**:ijiami.dat 是 ZIP(头尾 XOR 隐藏魔术)。
2. **压缩层**:classes.dex 条目用 raw DEFLATE 压缩。
3. **加密层**:57MB ifd 用字节替换 + K0 头部 XOR + map_list 特例。

三层全部离线可逆,最终一个纯 Python 脚本就能从 APK 直接还原 9 个 App 真身 DEX,**无需运行 App、无需 frida、无需 native 模拟**。

**重要结论**:`com.ijm.dataencryption.DETool` 类在 dex 里是 stub,真正组装这些参数、调用 dowork 的代码在**运行时被 native 壳替换**(爱加密的"stub + 运行时替换"手法)。dowork 的 256-bit key 派生(`6cf140de...`)是 native 侧的自定义派生,但对离线还原 DEX 完全无用——`dex_decrypt.py` 用硬编码 K0 + 数据相关替换已经绕过了它。


| 地址    | 函数      | 作用                                        |
| ------- | --------- | ------------------------------------------- |
| 0x572a8 | al()      | JNI 入口,DEX 解密 + 建 ClassLoader         |
| 0x5bd80 | sub_5bd80 | 写 ifd 文件(fopen "wb+" + fwrite + flock) |
| 0x34bac | 解压包装  | 调 0x65008/0x652a0/0x65468                  |
| 0x65008 | 解析 ZIP  | 解析容器                                    |
| 0x652a0 | 找条目    | 按名字找 "classes.dex"                      |
| 0x65468 | 解压条目  | raw DEFLATE(fcn.0006cbf4 windowBits=-15)  |
ShellApplication.attachBaseContext
  → Helper.a(context):创建 .cache/ .local/ .meta-inf/ .shell/ .jiagu/ 目录
  → 从 assets 提取加密载荷:
       maindata/res.data、meta-inf/enc.mf、maindata/xXxX.enc、cert0_enc
  → System.loadLibrary("xloader")  → native 层解密出真实 DEX
  → DexInstall 反射注入 ClassLoader
       (makeDexElements / makePathElements / makeInMemoryDexElements)
  → 通过 Instrumentation / Application 钩子启动 com.sf.base.MainApplication
完整证据链:
1. `com/Proxy/Helper.java` 中硬编码 `"d3d3Lm5hZ2Fpbi5jb20="` → base64 解码 = **`3c5K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3&6S2k6$3q4A6L8W2)9J5k6h3y4G2L8g2)9$3x3q4)9J5b7g2)9J5b7b7`.`.
2. 运行时创建 **`.jiagu/`**(加固)目录
3. 壳代码结构:
   - `com.Proxy.ShellApplication` / `Helper` / `VLibrary` / `H` / `Defines`
   - `com.Install.DexInstall`
   - `com.appsec.*`(a ~ k)
4. 专属 native 库:
   - `libxloader.so`(加载器)
   - `libnllvm.so`
   - `libKeyProvider.so`、`libMyKey.so`(内含 `"Example SO content for com.sf.activity"`)
   - `libentryexpro.so`、`libnMg.so`、`libdu.so`、`libwind.so`
   
   
## 三、安全防护机制

- **完整性校验(防篡改)**:`Helper.b()` 对 APK 的 `sourceDir` 做 CRC 校验,与预存值比对(XOR 取反),篡改即检测
- **反调试 / 反模拟器**:`Helper.a()` 读取 `/proc/self/exe` 检测 ELF 特征(字节 3 / 62);`com.appsec.*` 包负责反调试逻辑
- **字符串混淆**:`DexInstall.a()` 用 +3 偏移解密类名(如 `android.app.Application`、`android.app.Instrumentation`),配合大量反射调用   
原始 APK
  │
  │ ① 取出所有 dex 文件
  ├──────────────────────────────────────────────┐
  │                                              │
  │  classes.dex    classes2.dex    classes3.dex  │
  │        │              │              │        │
  │        │ ② NRV2D 压缩(UCL nrv2d,不加密)     │
  │        ▼              ▼              ▼        │
  │  packed_dex[0]  packed_dex[1]  packed_dex[2]  │
  │                                              │
  │ ③ 可选的方法/资源数据(本样本未用)             │
  │    hashmap[0..2]  = NRV2D 压缩 + XTEA 加密     │
  │    bytecode[0..2] = NRV2D 压缩 + XTEA 加密     │
  │                                              │
  │ ④ 全部打包进一个文件 assets/0OO00l111l1l      │
  └──────────────────────────────────────────────┘
  │
  │ ⑤ 注入壳 so(含解密密钥 + NRV2D 解压函数 + 反调试)
  │    libshell-super.com.ifeng.newvideo.so
  │
  │ ⑥ 替换 Application 为壳入口
  │    com.ifeng.newvideo.StubWrapperProxyApplication
  ▼
加固后的 APK
偏移        字段              大小     含义
0x00        nb_dex            u4       dex 文件个数(本样本 = 30x04        packed_dex[0]     变长    第 1 个 dex 的压缩数据
             ├ unknown1       u8      8 字节(用途不明,可能是随机数/校验)
             ├ uncomp_size    u4      NRV2D 解压后的字节数
             ├ comp_size      u4      压缩后的字节数
             ├ unknown2       u4      本样本恒为 0x457(=1111)
             └ data           comp_size  压缩数据(NRV2D 格式)
...         packed_dex[1]    变长    第 2 个 dex
...         packed_dex[2]    变长    第 3 个 dex
            hashmap[0..2]    变长    每个 = uncomp_size(u4) + comp_size(u4) + data
            bytecode[0..2]   变长    每个 = uncomp_size(u4) + comp_size(u4) + data
            
            
变量: src 源, dst 目标, 位缓冲 bitbuf, 上次偏移 last_off(初始=1)

主循环:
  if 读1bit == 1:
      literal: dst += src[读1字节]
  else:
      # ---- 读 offset (varlen 变长编码) ----
      w16 = 1
      loop:
          w16 = (w16 << 1) | 读1bit          # 累积
          if 读1bit == 1: break              # 判断 bit,1 终止
          w16 = (w16 << 1) - 2               # 继续时的 -2 调整
          w16 = w16 | 读1bit                 # 额外累积
      if w16 == 2:
          offset = last_off                  # 复用上次偏移
      else:
          low = src[读1字节]                  # 偏移低位字节
          offset = ((w16 << 8) - 768 + low) >> 1 + 1
          last_off = offset

      # ---- 读 length ----
      if w16 == 2:                           # 复用分支
          w16_prev = 读1bit << 1             # 额外读 1 bit
      else:                                  # 常规分支
          w16_prev = ((~low) & 1) << 1       # 由 low 的奇偶决定
      b0 = 读1bit                            # length 前置检查
      w1 = b0 | w16_prev
      if w1 != 0:
          length = w1                        # 短长度(1/2/3)
      else:
          # varlen 累积
          w16 = 1
          loop:
              w16 = (w16 << 1) | 读1bit
              if 读1bit == 1: break
          length = w16 + 2

      # ---- 拷贝 ----
      copylen = length + 1 + (1 if offset > 1280 else 0)
      从 dst[len-offset] 拷贝 copylen 字节到 dst 末尾
      
      
① 从 APK 提取 0OO00l111l1l / tosversion / libshell-super.*.so
② 解析 0OO00l111l1l → nb_dex + 3 个 packed_dex
③ 对每个 packed_dex.data 用 unicorn 模拟 so 里的 NRV2D 函数解压
④ 解压结果跳 16 字节头 → 按 dex 头 file_size 截断 → 完整 dex
⑤ adler32 校验 checksum 确认完整    


结果:
python legu_unpack.py com.ifeng.newvideo.apk [输出目录]
# 默认输出 ./unpacked/,得到 classes.dex / classes2.dex / classes3.dex
  • 在市场上找到一些加固厂商的apk
  • 包括:某60、某加密、某盾,某迦、某乐固
  • 加固产品的版本未知
  • 使用AI loop
  • 静态分析只用garlic,动态模拟用unicorn,动态分析用frida
  • 可以用模拟器,不能用真机
  • 测试系统:macmin m4
  • ai模型:deepseek-v4-pro
  • 客户端:cc switch
  • garlic version: 1.8 (未发布)
  • 禁止用objdump/capstone影响控制流
  • 如果有dex隐藏的,拿到dex -> 结束
  • 没有dex隐藏的,拿到请求签名 -> 结束
  • 有vmp的,分析出vm的指令集 -> 结束
  • 抛弃garlic,用jadx/r2/unicorn/ghidra等工具链
  • 对比token使用情况
  • 对比分析效率
  • 对比各个AI
  • 反编译所有的dex
  • 生成apk整体的call graph
  • 生成apk的string引用
  • 导入call graph到数据库,供AI查询
  • garlic内置的rosemary引擎会生成如下内容供ai使用
    1. 分析so的cfg
    2. 反汇编
    3. exports/imports
    4. 已知函数call graph
    5. pc xref导出
    6. strings导出
  • apk: com.fhyx.gamesstore.apk
  • md5: 4342e69622e37fce5b904c2a476bd303
  • so特征:libjiagu.so
  • apk: com.yiyou.ga
  • md5: 504ca64deaa7ea7c44ca58be3664f406
  • so特征:libvlplg.so
  • MyJni.load(Application, String) → 返回 true
  • apk: com.hexin.plat.android.ZheshangSecurity
  • md5: 1acc460d513540d9b0b6bf32c030b69f
  • so特征:ijiami.dat, libexec.so
  • apk: com.sf.activity
  • md5: 8a5ff65cf4e0f48b793259091b2e58e2
  • so特征:libxloader.so, libnllvm.so
  • apk: com.ifeng.newvideo
  • md5: 58bd5f262c3634addb533cb171b91971
  • so特征:libshell-super.com.ifeng.newvideo.so
  1. 分析so的cfg
  2. 反汇编
  3. exports/imports
  4. 已知函数call graph
  5. pc xref导出
  6. strings导出
  1. 本身设计了6个测试案例全部成功,AI对garlic的评价:apk的地图生成器,AI比较会舔,我挺受用。
  2. 发出来5个,有一个vmp的不发了,vmp的虚拟机的字节码可以完全被AI推理出来
  3. 对比测试:将garlic替换成jadx/ghidra/r2等工具,效率会慢,token消耗会变多
  4. 特别是ghidra,AI用java的脚本,非常消耗token
  5. 没有测试IDA,IDA肯定会成功,个人感觉IDA导出的内容给AI查询的时候会消耗很多token
  6. 在deepseek涨价之前,薅了一波
  • 在市场上找到一些加固厂商的apk

  • 冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。

    最后于 14小时前 被neocanable编辑 ,原因: 改个标题
    收藏
    免费 27
    打赏
    分享
    最新回复 (8)
    雪    币: 4590
    活跃值: (7797)
    能力值: ( LV3,RANK:20 )
    在线值:
    发帖
    回帖
    粉丝
    2
    感谢分享
    13小时前
    0
    雪    币: 21
    能力值: ( LV1,RANK:0 )
    在线值:
    发帖
    回帖
    粉丝
    3
    感谢分享
    12小时前
    0
    雪    币: 158
    活跃值: (2571)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    4
    感谢分享
    12小时前
    0
    雪    币: 6
    能力值: ( LV1,RANK:0 )
    在线值:
    发帖
    回帖
    粉丝
    5
    yyds
    10小时前
    0
    雪    币: 1050
    活跃值: (1505)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    6
    1
    7小时前
    0
    雪    币: 3032
    活跃值: (5440)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    7
    666
    2小时前
    0
    雪    币: 539
    活跃值: (9086)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    8
    13026511289@163.com,大佬求一份
    2小时前
    0
    雪    币: 7143
    活跃值: (7265)
    能力值: ( LV2,RANK:10 )
    在线值:
    发帖
    回帖
    粉丝
    9
    66
    6分钟前
    0
    游客
    登录 | 注册 方可回帖
    返回