news 2026/9/23 14:37:37

米斗APP逆向分析:360壳脱壳与核心逻辑还原实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
米斗APP逆向分析:360壳脱壳与核心逻辑还原实战

1. 米斗APP逆向分析的整体思路与方案选型

1.1 为什么选择从加固识别入手

拿到一个APK,第一步永远不是急着拖进反编译工具,而是先搞清楚它到底穿了什么“衣服”。米斗APP这个样本,我最初用常规的apktool反编译,出来的classes.dex只有几百KB,打开一看全是壳代码——典型的DEX整体加固特征。再用jadx-gui直接打开APK,搜索Application类的attachBaseContext方法,发现里面调用了大量反射和native方法,基本可以确认是360数字壳

为什么先做加固识别?因为不同加固方案的脱壳策略完全不同。360壳、腾讯御安全、梆梆、爱加密,它们的DEX加载时机、内存布局、反调试手段都不一样。你如果连壳都没认准就上手脱,轻则脱出来的DEX残缺不全,重则触发反调试导致设备被标记。我个人的习惯是:先看AndroidManifest.xml里的application标签,再看assets目录下有没有可疑的.so或加密文件,最后用frida挂载看DexClassLoader的调用栈。这三步走完,基本能锁定加固厂商。

1.2 脱壳方案对比:内存dump vs 主动调用

确认是360壳之后,脱壳路线有两条:内存dump主动调用。内存dump的思路是在DEX被加载到内存但还未被完全加密时,把内存中的DEX结构体抠出来;主动调用则是通过HookDexFileloadDexopenDexFile方法,在系统加载DEX的瞬间拦截并保存。

我最终选了内存dump为主、主动调用为辅的组合方案。原因很直接:360壳对DexFile相关方法做了inline hook,单纯主动调用容易被绕过;而内存dump虽然粗暴,但只要找到合适的时机——通常是attachBaseContext执行完毕、onCreate开始之前——就能拿到相对完整的DEX。具体工具上,frida-dexdumpFRIDA-DEXDump这两个脚本我都试过,前者对360壳的兼容性更好,后者在多DEX场景下更稳。

注意:脱壳环境建议用真机而不是模拟器。360壳对x86模拟器的检测非常敏感,我一开始在AVD上跑,frida刚attach上去进程就崩了。换到一台Android 10的物理机后,同样的脚本一次跑通。

1.3 工具链的取舍与版本坑

工具版本这块我踩过不少坑。frida建议用15.2.216.1.4这两个版本,太新的版本对老壳的兼容性反而差,太老的版本又不支持Android 12以上的设备。frida-dexdump用GitHub上star最多的那个Python脚本就行,但它依赖frida-tools的版本,装的时候最好用pipenv隔离环境,不然很容易和系统里的其他Python包冲突。

反编译工具方面,jadx用来快速看Java层逻辑,GDA用来分析native层和壳的交互,IDA Pro则是在需要深入.so文件时才会用到。这里要提一句:不要迷信单一工具。同一个方法,jadx反编译出来可能是乱码,换GDA就能看到清晰的逻辑,这是因为不同工具对DEX指令的解析策略不同。多工具交叉验证是逆向分析的基本功。

2. 360数字壳的核心细节与脱壳实操要点

2.1 360壳的DEX加载流程拆解

360数字壳的DEX加载大致分三个阶段:壳DEX启动解密真实DEX替换DEX数组。第一阶段,壳的Application类在attachBaseContext里通过System.loadLibrary加载一个叫libjiagu.so的native库,这个库负责后续所有解密操作。第二阶段,native层从assets目录或APK的尾部读取加密的DEX数据,用AES或自定义算法解密到内存。第三阶段,通过反射修改ActivityThreadmPackages字段,把解密后的DEX路径塞进去,让系统以为这就是原始的DEX。

理解这个流程对脱壳至关重要。因为你要找的“脱壳时机”,就是在第二阶段完成、第三阶段刚开始的那个窗口期。太早,DEX还没解密完;太晚,壳可能已经做了二次加密或内存抹除。我实测下来,在frida里Hookandroid.app.InstrumentationcallApplicationOnCreate方法,在这个方法执行前做内存搜索,成功率最高。

2.2 内存dump的具体操作与参数调优

具体操作上,我用的命令是:

frida -U -f com.example.midou -l dexdump.js --no-pause

其中dexdump.jsfrida-dexdump的核心脚本。这里有几个参数需要根据设备情况调整:

  • 扫描范围:默认是0x00xFFFFFFFF,但在64位设备上这个范围太大,容易OOM。我一般改成从0x700000000x7FFFFFFF,这是Android应用DEX常见的映射区间。
  • 匹配特征:DEX文件头是dex\n035dex\n037,但360壳有时会把文件头改掉。我加了一个模糊匹配,同时搜索dex\ndex\n0两种模式。
  • 超时时间:默认3秒,但米斗APP的DEX有多个,解密耗时较长,我调到了8秒。

跑完之后,脚本会在/data/local/tmp下生成一堆.dex文件。这时候别急着高兴,先检查文件大小和数量。米斗APP正常应该有3个DEX,主DEX大概4MB左右。如果脱出来只有几百KB,说明时机不对,得重新调整Hook点。

2.3 脱壳后的DEX修复与验证

脱出来的DEX往往不是完美的。常见问题有三个:文件头损坏DEX尾部缺失指令集错乱。文件头损坏的话,用010 Editor手动把前8个字节改成dex\n035\0就行。DEX尾部缺失比较麻烦,需要用dexfixer这类工具尝试修复,但成功率不高,很多时候只能重新脱。

验证DEX是否完整,我一般用两个方法:一是用dexdump命令看能否正常解析出类列表,二是用jadx打开看关键类(比如MainActivity)的方法体是否完整。如果jadx里看到的方法全是native或者空实现,那说明DEX还是壳的,真实代码没脱出来。

实操心得:脱壳过程中,手机最好开飞行模式,关掉所有后台应用。我遇到过好几次因为系统自动更新或其他应用抢占内存,导致脱出来的DEX不完整。另外,frida-server要以root权限运行,并且和frida客户端的版本严格一致,否则连不上。

3. 米斗APP核心逻辑的逆向分析与还原

3.1 从入口Activity追踪业务逻辑

DEX脱干净之后,用jadx打开,先找AndroidManifest.xml里注册的入口Activity。米斗APP的入口是com.midou.app.SplashActivity,里面主要做初始化工作和路由跳转。顺着SplashActivity往下追,会看到一个RouterManager类,它根据服务器下发的配置决定跳转到登录页还是主页。这个设计在现在的APP里很常见,目的是为了动态控制功能入口,方便做A/B测试或灰度发布。

继续追RouterManager的调用链,会发现它依赖一个ConfigManager,而ConfigManager的数据来源是一个叫/api/v1/config的接口。到这里,Java层的逻辑基本清晰了:APP启动后先请求配置接口,拿到配置后再决定后续流程。这个过程中,网络请求用的是OkHttp,但做了证书绑定(SSL Pinning),直接抓包会失败。

3.2 native层关键函数的定位与分析

Java层逻辑不复杂,说明核心逻辑大概率在native层。用GDA打开libjiagu.so,搜索JNI_OnLoad,找到注册的native方法列表。其中有一个叫nativeCheckSign的方法,一看就是做签名校验的。还有一个nativeGetKey,返回一个字符串,大概率是用于网络请求加密的密钥。

分析nativeGetKey时,我用IDA Pro加载.so,定位到函数入口,发现它内部调用了AES_set_decrypt_keyAES_cbc_encrypt,说明密钥是AES解密出来的。密钥的密文硬编码在.rodata段,IV是固定的16字节。把密文和IV抠出来,用Python的pycryptodome库解密,得到一串32位的字符串——这就是网络请求的AES密钥。

from Crypto.Cipher import AES import binascii key = binascii.unhexlify('你的密文hex') iv = binascii.unhexlify('你的IV hex') cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(binascii.unhexlify('你的密文数据')) print(plaintext)

3.3 网络协议与数据加密的还原

拿到AES密钥后,抓包就简单了。用mitmproxy配合fridassl unpinning脚本,绕过证书绑定,就能看到明文请求。米斗APP的请求体是JSON,但关键字段(如userIdtoken)用AES加密后再Base64编码。响应体也是同样的处理。这意味着你光抓包没用,还得把加解密逻辑还原出来。

我写了一个Python脚本模拟它的加解密流程,核心就是上面那段AES代码。需要注意的是,它的AES模式是CBC,填充方式是PKCS7,这些在IDA里看AES_cbc_encrypt的调用参数就能确认。另外,它的token有有效期,过期后会返回401,需要重新登录获取。这个逻辑在TokenInterceptor类里,用jadx能直接看到。

常见问题:脱壳后的DEX里,有些方法被nop掉了,或者指令被替换成了goto。这是360壳的“指令抽取”保护。遇到这种情况,要么用frida在运行时Hook这些方法,直接读返回值;要么用IDA分析native层的对应实现。我一般优先选前者,因为快。

4. 逆向过程中的典型问题与排查技巧实录

4.1 反调试检测的绕过方法

360壳的反调试手段主要有三种:ptrace检测frida检测调试端口检测。ptrace检测是检查/proc/self/status里的TracerPid字段,如果不是0就说明被调试了。绕过方法是在frida脚本里Hookfopenopen,把TracerPid的值改成0。frida检测是扫描进程内存里有没有frida相关的字符串,比如frida-agentgum-js-loop。这个可以用frida--rename参数把agent改名,或者用magisk模块隐藏frida。

调试端口检测是检查adb是否开启。这个最简单,脱壳时把USB调试关掉,用frida-U参数通过USB连接就行。但要注意,有些壳会检测adb的默认端口5555,如果发现端口开放就主动退出。我一般用adb tcpip换个端口,或者直接用frida-H参数走网络连接。

4.2 DEX脱壳不完整的修复思路

脱壳不完整是家常便饭。我遇到最多的情况是:DEX文件头正常,但string_idsmethod_ids区域全是0。这说明壳在内存里把DEX的某些区域抹掉了。修复思路有两个:一是从/proc/pid/maps里找到DEX的内存映射,用dd命令直接dump整块内存,再用dexfixer重建DEX结构;二是用fridaMemory.readByteArray在DEX加载的瞬间读取,这时候数据还没被抹。

第二种方法成功率更高,但需要精确的Hook点。我一般Hookdalvik.system.DexFile<init>方法,在方法进入时读取this对象的mCookie字段,这个字段指向DEX在内存中的起始地址。然后根据DEX文件头里的file_size字段,读取整个DEX。这个方法对360壳特别有效,因为它是在DEX完全加载后才做抹除的。

4.3 常见问题速查表

问题现象可能原因排查方法解决方案
frida attach后进程崩溃壳检测到frida查看logcat是否有SIGSEGV用magisk隐藏frida,或改名agent
脱出的DEX只有几百KBHook时机太早检查DEX文件头是否完整调整Hook点到onCreate之前
jadx打开DEX全是native方法指令抽取保护用GDA查看方法体运行时Hook或IDA分析native层
抓包全是乱码数据加密搜索AES相关字符串还原AES密钥和IV
请求返回401token过期检查请求头里的token重新登录获取新token
反编译报错Invalid DEXDEX文件头损坏用010 Editor查看前8字节手动修复为dex\n035\0

4.4 独家避坑技巧

第一个技巧:脱壳前先备份原始APK。我见过太多人脱壳脱到一半,把原始APK覆盖了,结果连壳都找不回来。备份的时候连split APK一起备份,有些APP的核心DEX在split包里。

第二个技巧:用多个frida脚本交叉验证frida-dexdumpFRIDA-DEXDump各有优劣,我一般两个都跑一遍,对比脱出来的DEX数量和大小。如果一致,说明脱干净了;如果不一致,以大的那个为准,小的那个大概率是残缺的。

第三个技巧:关注/data/data/包名/下的文件变化。有些壳会把解密后的DEX临时写到应用私有目录,脱壳时用inotifywait监控这个目录,能直接抓到DEX文件。这个方法对腾讯御安全和梆梆壳特别有效,360壳偶尔也能用。

第四个技巧:不要忽视oatvdex文件。Android 8.0以上,系统会对DEX做预编译,生成oatvdex文件。这些文件里可能包含完整的DEX代码,直接用vdexExtractor就能提取。我遇到过好几次,壳脱不出来,但从vdex里直接拿到了完整DEX。

5. 逆向分析后的延伸思考与合规提醒

5.1 从逆向结果看APP的安全设计

米斗APP的整体安全设计在同类产品里算中等偏上。它用了360壳做基础防护,网络层做了证书绑定和AES加密,native层有签名校验和密钥保护。但它的弱点也很明显:AES密钥硬编码在.so里,虽然做了简单的字符串混淆,但用IDAstrings窗口一搜就能找到线索。另外,它的token有效期太长,我测试时发现一个token能用72小时,这给了攻击者很大的窗口期。

从防御角度看,如果我是这个APP的开发者,我会把AES密钥改成动态下发,每次启动时从服务器获取,并且用白盒加密保护。签名校验也应该放到native层,并且和关键业务逻辑绑定,而不是单独一个nativeCheckSign方法。当然,这些都是后话,逆向分析的目的不是攻击,而是理解它的安全边界在哪里。

5.2 逆向分析的合规边界

这里必须说清楚:逆向分析仅限用于学习研究和安全评估。我写这篇东西,目的是分享技术思路和实操方法,不是教人去破解或盗取数据。实际操作中,我全程用的是自己的测试账号,没有触碰任何真实用户数据。如果你要分析某个APP,请确保你有合法的授权,或者至少是在自己的设备上、用自己的账号做研究。

另外,脱壳后的DEX和还原的密钥,不要公开传播,更不要用于商业用途。我一般分析完就把这些文件删掉,只保留分析笔记。技术本身没有对错,关键在于用它的人。

5.3 后续可以深入的方向

如果你对米斗APP的逆向还有兴趣,可以继续往这几个方向挖:一是它的so文件里还有几个未分析的native方法,比如nativeEncryptDatanativeDecryptData,大概率是更底层的加密逻辑;二是它的assets目录下有一个config.dat文件,看起来是加密的,可以尝试用脱壳时拿到的密钥解密;三是它的推送和埋点逻辑,这部分用了第三方SDK,分析起来相对独立。

我个人在实际操作中的体会是,逆向分析最耗时的不是脱壳本身,而是脱壳后的代码梳理和逻辑还原。一个中等规模的APP,脱壳可能只要半小时,但把核心业务逻辑理清楚,往往需要好几天。所以耐心和细致比工具更重要。最后再分享一个小技巧:分析过程中随时记笔记,把关键类名、方法名、密钥、接口地址都记下来,不然过两天再回头看,你自己都忘了当时是怎么找到的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 14:33:50

免费小游戏平台怎么选?CrazyGames、itch.io、Poki深度推荐

1. 为什么我推荐这三款免费小游戏平台1.1 免费游戏平台的生态现状这些年周围朋友经常问我一个问题&#xff1a;想找个地方玩游戏&#xff0c;不想下载几十个G的大型客户端&#xff0c;也不想一打开就跳充值弹窗&#xff0c;到底有什么靠谱的选择&#xff1f;说实话&#xff0c;…

作者头像 李华
网站建设 2026/9/23 14:33:10

Spring Boot + MySQL + ECharts 实现路口流量调查统计分析系统

1. 这套系统到底在干什么1.1 交叉路口流量调查的真实痛点先别急着打开源码&#xff0c;把需求吃透比什么都重要。交叉路口行人、非机动车流量调查&#xff0c;说白了就是交通管理部门、城市规划部门要搞清楚一个路口到底有多少人走路、多少辆电动车和自行车经过、集中在什么时间…

作者头像 李华
网站建设 2026/9/23 14:32:55

国台酒经销商代理合作模式全解析

2026年&#xff0c;对于正在寻找酱酒代理机会的酒类经销商而言&#xff0c;贵州国台数智酒业集团股份有限公司&#xff08;品牌简称&#xff1a;国台酒&#xff09;是一个值得认真评估的合作对象。作为政府授牌的茅台镇第二大酿酒企业&#xff0c;国台酒历经二十余年发展&#…

作者头像 李华
网站建设 2026/9/23 14:32:39

ZCode静默上传Git历史的技术真相与风险解析

1. 项目概述&#xff1a;一场被代码提交记录戳穿的信任裂痕“智谱 ZCode 静默上传 Git 历史”——这十个字不是技术文档的标题&#xff0c;而是一份事故通报的导语。它背后没有炫酷的AI模型演示&#xff0c;没有流畅的IDE插件动画&#xff0c;只有一行被开发者反复翻查、最终在…

作者头像 李华
网站建设 2026/9/23 14:31:41

Vim 从入门到实践:一篇文章理清模式、命令与配置

我得先讲个真实观察&#xff1a;如果你去翻各搜索引擎里 vim 相关的高频问题&#xff0c;常年霸榜的一定是"vim 如何保存退出""vim 怎么到底端""linux vim 保存和退出"这一类最基础的操作。一个编辑器的基础操作成了大家最常搜索的内容&#xff…

作者头像 李华