news 2026/10/1 3:45:58

零基础Android脱壳实战:用frida-dexdump一键还原加固App业务代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
零基础Android脱壳实战:用frida-dexdump一键还原加固App业务代码

最近有个朋友拿着一个套了360加固的App来找我,问能不能把里面的业务逻辑还原出来看看。我说能,但需要先脱壳,他当时一脸懵:脱壳是什么?难不难?会不会把手机搞坏?我花了大概一个多小时,从环境搭建到dump出dex,带着他完整走了一遍,全程没让他写一行代码,最后他惊喜地发现业务类全都在。这篇文章就把这套完全可复制的流程写下来,给所有零基础想做Android反编译、脱加固壳的人一条能走通的路。

先说清楚,这是一篇技术学习与安全研究方向的实操教程,面向的目标是:你自己写的测试App、已开源App、或者你有明确授权做安全评估的样本。不要拿它去搞未经授权的破解和分析,技术工具没有原罪,但使用边界必须自己守住。

1. 先从根上理解:加固和脱壳到底是怎么回事

1.1 为什么用jadx打开加固App,看不到业务代码

正常情况下,一个APK就算代码写得再烂,你用jadx一拖,基本能把Java层逻辑看个八九不离十。Activity、Fragment、网络请求封装、工具类,清晰得很。但当你拖入一个套了360加固(或者其他同类加固方案)的APK时,画风突变:首页只有屈指可数的几个类,application节点被换成了一个壳入口,业务类一个都找不到。

这不是jadx坏了,而是加固的机制导致的。加固的本质是“加密存储 + 运行时解密”。开发者的真实dex会被加密成一个文件,塞进assets目录或者so库里。App启动后,壳代码先接管进程,做一堆环境检测和安全校验,然后才从加密文件里解密出真实的dex字节流,再通过DexClassLoader、InMemoryDexClassLoader这类机制,把真正的dex加载进ART虚拟机里执行。

也就是说,从磁盘上看,这个App是“空心”的;只有当它跑起来,内存里才会出现完整的真实dex。所以静态反编译当然什么都看不到,因为你看到的classes.dex只是壳的启动器,真正的业务逻辑被锁在了壳后面。

1.2 脱壳就是趁真实dex在内存里“活着”的时候抓住它

理解了加固原理,脱壳的思路就顺理成章了:既然真实dex最终必须出现在内存里,那我就在App运行期间,把这段内存抓下来,保存成dex文件,再丢回jadx反编译。这就是俗称的脱壳。

整个过程有三件事最核心:

  • 找到真实dex加载的时机(不能太早,壳还没解密;也不能太晚,某些临时dex可能已经被回收)
  • 定位dex在内存中的位置(靠文件特征、内存段扫描、Hook关键类加载逻辑)
  • 把内存字节完整保存下来,并保证dex文件的完整性(包括修复校验和等)

零基础路线最怕的就是第一关“时机”和第二关“定位”太玄学。好在社区已经有现成的工具把这两步做成了“一条命令”,你要做的不是发明轮子,而是学会正确使用轮子。

2. 工具选型:为什么零基础首选frida-dexdump

2.1 主流的脱壳方案,横向比个明白

在没有现成工具的时代,脱壳是件极其痛苦的事:你得先反编译壳本身,搞明白它的解密算法,然后写脚本手动调DexClassLoader,或者在IDA里动态调试so层逻辑。这对零基础无异于劝退。

现在主流方案大概分三类:

  • 基于Frida生态的工具,代表是frida-dexdump。核心思路是用Frida把JS脚本注入目标进程,然后扫描进程内存,按dex文件的魔法头特征把所有疑似dex的内存段全部dump下来。优点是操作简单、对大多数加固有效、不需要定制系统;缺点是对某些变种加固会失效,且dump出来的文件会有冗余噪声。
  • 基于定制ROM的方案,代表是FART、BlackDex。思路是在系统框架层做文章,在ART虚拟机加载类的时候主动脱壳。优点是兼容性极强,连很多VMP类加固都能扒出点东西;缺点是要给模拟器刷定制系统,光是环境搭建就可能劝退新手。
  • 基于静态分析硬破解的方案,比如直接还原壳的解密算法。这种方案对分析者的逆向功底要求极高,不推荐零基础尝试。

对于“零基础 + 第一次脱壳”这个场景,frida-dexdump是性价比最高的:环境要求低,模拟器就能跑;命令简单,一条命令搞定;社区活跃度高,遇到问题好搜答案。

2.2 frida-dexdump的原理,说人话版

frida-dexdump的原理其实不复杂。dex文件有固定的文件头,也就是我们常说的magic字节,通常是dex\n035\0或更高版本。无论壳怎么加密磁盘上的文件,只要它把dex解密后交给ART虚拟机执行,内存里就一定有一块区域符合dex文件的特征。

frida-dexdump做的事情就是:

  1. 通过Frida注入目标进程
  2. 扫描进程的所有内存段,寻找匹配dex文件头特征的内存区域
  3. 对候选内存块做合法性校验(比如dex文件头部字段是否合理、文件大小是否与头部声明匹配)
  4. 把验证通过的内存块原样保存成dex文件

这就是为什么你会看到它有时候会dump出很多个dex,其中一部分可能是系统框架的dex、壳自身的dex、还有你真正想要的业务dex。没关系,都保存下来,后面用jadx逐个看就行。

理解这一步,你后面排查问题就会容易得多:比如dump出来的全是一些无关系统类,你该知道是时机不对;比如发现dex很小,可能是dump到了不完整的dex碎片。

3. 环境准备:一张清单加几条命令

3.1 你需要的东西,一个都不能少

工欲善其事必先利其器,零基础脱壳的环境准备就四块:电脑端工具、模拟器环境、客户端Frida、设备端frida-server。每一块都必不可少,但它们都不难。

实际操作前,我建议你准备好这些:

  • 一台Windows 10/11或macOS电脑,内存别低于8GB
  • Genymotion模拟器(我强烈推荐用模拟器而不是真机,原因很简单:模拟器自带root,不用解锁BL、不用为root权限提心吊胆)
  • adb命令行工具,装Android Platform Tools即可
  • Python 3.8以上环境
  • Frida客户端、frida-tools、frida-dexdump,全部通过pip安装
  • 与电脑端Frida版本严格一致的frida-server,放进模拟器里运行

这里有个重点:frida-server的版本必须和电脑端Frida的版本一致。这是新手踩坑率最高的一步,后面我会专门讲。

3.2 安装过程详解,每一步都给你说清楚

第一步,安装Python和pip。Windows用户去官网下载Python安装包,安装时一定勾选Add Python to PATH。macOS可以用brew install python3。装完打开终端,执行python --version和pip --version确认能跑。

第二步,安装Frida工具链。终端执行:

pip install frida frida-tools frida-dexdump

装完以后,执行frida --version,记下版本号。比如我的结果是16.7.19,后面下载frida-server就要找16.7.19版本。

第三步,安装Genymotion并创建一台Android 9的虚拟设备。下载安装Genymotion Desktop客户端的时候,它会要求你安装VirtualBox,这个是底层依赖,按提示装就行。装好后启动设备,等它完全开机。

第四步,验证adb连接。终端执行adb devices,能看到设备就代表通信正常。如果看不到,大概率是adb路径没配好,或者Genymotion的adb bridge没启动。

第五步,下载frida-server。打开GitHub上frida官方仓库的releases页面,找到和刚才版本号完全一致的tag,下载android-x86_64的压缩包。Genymotion是x86架构,所以选x86_64;如果是真机,一般是arm64,别下错。

第六步,把frida-server推入模拟器并启动。依次执行:

adb push frida-server-16.7.19-android-x86_64 /data/local/tmp/frida-server adb shell chmod 755 /data/local/tmp/frida-server adb shell "su -c /data/local/tmp/frida-server &"

如果提示Permission denied,别急,先adb shell进去,输入id看自己是不是root用户。Genymotion不同镜像版本的su行为不太一样,有些进去就是root,有些必须su。如果su -c不行,直接/data/local/tmp/frida-server &也许就成功了。

第七步,验证Frida连通。电脑终端执行frida-ps -U,如果能看到模拟器里的进程列表,说明Frida客户端和服务端通信正常,环境全部就绪。

4. 实操全流程:一条命令把壳拔下来

4.1 找到目标App的包名

脱壳前先得让工具知道该操作哪个App。包名就是Android里每个应用唯一的身份标识,比如com.example.app。获取方法有几种:

  • 用aapt命令:aapt dump badging xxx.apk | grep package
  • 用jadx打开APK,看AndroidManifest.xml里的package属性
  • 或者最简单的,模拟器里安装好App,用frida-ps -U列出进程名,找到自己目标

顺带观察一个细节:如果Manifest里application的name被换成了com.stub.StubApp这类带stub关键词的类,基本可以确认这包确实加了加固,而且这个类在后续分析里也是个重要线索。

4.2 启动脱壳命令

找到包名后,在电脑终端执行:

frida-dexdump -U -f com.example.app -o dump

这里解释一下参数含义:-U表示连接通过USB/adb连接的目标设备;-f表示spawn方式,也就是由Frida来拉起这个App,而不是去attach一个已经在跑的进程;dump是输出文件夹名。

执行后你会看到类似下面的输出:

Start: 15:30:00 Spawned com.example.app. PID: 2345 Dumping dex files... [INFO] Dumping 4 dex files ... Done: 15:30:05

几秒钟到几十秒不等,视App大小和dex数量而定。完成后,当前目录会生成一个dump文件夹,里面放了若干个dex文件。这些就是壳加载到内存里的所有dex原始数据。

4.3 用jadx验证脱壳成果

打开jadx,把dump目录里的dex文件拖进去,然后按包名路径找你的业务类。如果能看到com.example.app下的Activity、Fragment、网络请求类等,恭喜,脱壳成功。

如果看到的全是壳类或系统类,大概率是dump时机太早,壳还没把真实dex解密加载完。我遇到这种情况的补救办法是:先手动正常打开App,等页面完全加载,然后用attach模式重新来一次:

frida-dexdump -U -n com.example.app -o dump2

-n参数指定的是进程名称。这种方法能覆盖到App运行中后期才按需加载的dex,特别是那些用到某个功能才动态加载进来的模块。

4.4 备选方案:如果frida-dexdump收获寥寥

frida-dexdump也不是万能的。某些加固方案做了更强的混淆和反调试,或者dex在内存中不是连续完整存在,而是分片加密、按方法动态解密执行,这时候扫描整段内存就变得困难。

遇到这种情况,可以尝试进阶路线:用Frida脚本主动遍历ClassLoader列表,沿着ClassLoader对象里的pathList字段,找到Element数组,再从Element里拿DexFile对象,反射调用相关方法把dex内容取出来。这个方案的本质是“通过ART虚拟机自身的加载信息来还原dex”,对一部分frida-dexdump无能为力的加固有效。

不过这个方案对新手并不友好,需要你懂一点Java反射和Frida脚本的语法。第一次不建议碰它,先把基础方案跑通再说。真要深入了,后面可以单独写一篇进阶篇。

5. 脱壳之后的三个坎,你得提前知道

5.1 第一坎:dump出来的可能是odex/vdex,不是标准dex

Android 8.0以上,ART虚拟机的运行时dex格式可能不是我们熟悉的classes.dex,而是odex/vdex这些衍生格式。jadx虽然能解析一部分,但偶尔会报错打不开。

遇到这种情况,思路是先把文件转换成标准dex。社区里常见的工具是vdexExtractor、oat2dex等。转换时要注意:不同的工具对不同Android版本的兼容性不同,Genymotion的Android 9和Android 7生成的文件格式有差异,需要试错。不过大多数常规App在Android 9上dump出来的依然是可以直接解析的标准dex,这个问题只在部分场景出现,不用太焦虑。

5.2 第二坎:脱壳成功但代码被混淆过

脱壳只是把“门”打开了,但门后面还有一道“雾”。很多App在加固之前会先做ProGuard/R8混淆,类名、方法名、变量名全变成a、b、c、d。你看到这种代码别以为脱壳失败了——脱壳解决的是“代码在不在内存里”的问题,混淆解决的是“代码好不好读”的问题,这是两个完全不同的层面。

应对混淆代码,我的经验是:先把jadx里按包名排序,肯定能找到一些没被混淆的工具类或三方库类;再通过AndroidManifest里的入口Activity和Service,找到那些必须保留名字的类,这些类是手动梳理逻辑的锚点;最后用jadx的导出源码功能,把可读的Java源码导出来,放到Android Studio或IDEA里做交叉搜索,效率会高不少。

5.3 第三坎:字符串加密和动态加载

有些App对关键字符串做了一层加密,运行时会解密后拼接使用。你在静态反编译的代码里看到的是一堆类似new String(Base64.decode("xxx"))的代码,或者是一堆native方法调用。这类保护已经不属于加固脱壳的范畴,更多是代码层面的对抗,需要结合动态调试和hook工具去还原。

对零基础读者来说,这三个坎可以放一放,先把脱壳这条主链路跑通,再按需进阶。

6. 常见问题与排查技巧实录

6.1 问题速查表

现象可能原因解决办法
frida-ps -U 提示unable to connect电脑端Frida和frida-server版本不一致两边版本号必须严格一致,重新下载frida-server并推入设备启动
spawn后App秒退目标App存在Frida检测机制尝试attach模式(-n参数),或换一台更干净的设备环境
adb devices列表是空的adb服务异常或模拟器未完全启动执行adb kill-server和adb start-server,重启adb服务
dump目录只有1个很小的dexdump时机太早,壳还没来得及解密真实dex改用attach模式,或先手动打开App让页面完全加载再脱壳
jadx无法打开dump文件文件是odex/vdex等非标准dex格式用vdexExtractor等工具转换后再用jadx打开
看到大量a、b、c类名加固之外还做了代码混淆这不算脱壳失败,按混淆分析思路处理
dump下来的dex有很多,内部全是系统类扫描到了ART虚拟机的框架dex不奇怪,逐个查看,业务dex通常集中在和目标包名有关的路径下

6.2 我踩过的三个坑,希望你绕开

第一个坑:frida-server版本不匹配。这是新手最容易遇到的。我有一台电脑上Frida客户端更新到16.7版本,模拟器里的frida-server还停留在16.6,结果frida-ps直接报unable to connect。排查了十分钟才想到版本问题。解决方式就是重新下载16.7的frida-server,推上去重启。强烈建议:在环境配置完成后,先在模拟器里跑一次frida-ps,确认没问题再开始脱壳。

第二个坑:Genymotion的root权限在不同镜像上有差异。有些镜像进去就是root,有些必须su -c。遇到Permission denied先别慌,adb shell进去跑一下id看看当前用户,再决定启动方式。如果su路径不统一,也可以试试在Genymotion的shell里直接以root执行。

第三个坑:dump时机拿捏不准。加固App启动后,壳代码要先解密、加载、校验,真实dex进入内存是需要时间的。我第一次实操时,App刚spawn完就立刻dump,结果只得到一个壳的dex,差点以为工具坏了。后来改成让App多跑几秒、甚至打开几个页面后再脱壳,结果完全不一样。如果你也遇到这个问题,优先考虑是不是时机没选对。

7. 脱壳技能还能怎么用在真实场景里

这部分算是给愿意深入的人一个方向提示。脱壳之外,大多数安全分析和逆向流程里,你会频繁遇到类似的手段:抓包、Hook、动态调试、so层分析。脱壳解决的是“看见Java层代码”的问题,但这只是第一步。你用frida-dexdump成功一次之后,顺手可以试试frida自带的一些Hook能力,比如hook一个方法看参数和返回值,简单且收获感极强。

还有一个很实际的应用场景:App升级后,你之前积累的分析笔记失效了。很多商业App每次发版都会重新加固、重新混淆,但包名和核心类名不会大变。有了脱壳这条流水线,你可以快速把新版本的dex导出来,和自己之前的笔记做diff,看看到底改了什么。这对做合规分析、版本对比、历史追溯都很有用。

我自己在做一些App的隐私行为审查时,标准流程就是:脱壳 → 反编译 → 导出Java → 在IDE里全局搜索隐私相关API调用 → 模块级梳理行为路径。脱壳是这套流程里最前置、最有“破防感”的一步,也是最容易让新人获得成就感的一步。

说到底,脱壳这件事真不难,难的是环境偶尔抽风、时机不对、工具版本打架,这类问题解决多了,你的排查能力自然就长上来了。我个人的体会是,第一次跑通frida-dexdump看到业务类出现的那一刻,你会觉得前面所有的环境折腾都值了。后面如果你想继续深挖,可以试着写自己的Frida脱壳脚本、研究不同加固厂商的加载时机差异,甚至尝试对着FART源码理解系统层脱壳的原理。路很长,但起点就是眼前这条命令。

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

Pi Agent工具提示词优化实战:从12800到1152的降本指南

先说个前阵子踩的坑。我用 Pi Agent 做跨模块重构,会话跑到一半,模型开始频繁丢上下文,回答越来越敷衍。一开始我以为是长会话的老毛病,后来把会话的 token 明细拉出来一看,问题清楚得吓人:系统提示词里光工…

作者头像 李华
网站建设 2026/10/1 3:45:33

ST7701驱动开发实战:MIPI DSI点屏、初始化序列与花屏排查

简介:这份资源面向嵌入式Linux显示驱动开发者,提供ST7701/ST7701S液晶控制器的C/C驱动程序及配套资料,帮助开发者将屏幕快速集成到MTK、展讯等硬件平台。压缩包共6个文件、约5.3MB,以3份PDF规格与应用笔记、2份C语言驱动源码和1份…

作者头像 李华
网站建设 2026/10/1 3:45:16

Kubernetes Service与Ingress:分层流量模型、原理与排障实践

聊Kubernetes的流量负载,几乎每个刚接触K8s的人都会被Service和Ingress卡住。我不止一次在群里看到有人问:“我已经建了Service,为什么外部还是访问不了?”或者“Ingress到底算不算Service的一种?”这些问题背后&#…

作者头像 李华
网站建设 2026/10/1 3:44:38

代码静态验证工具实战:从ESLint到CI的工程规范落地

代码静态验证工具,听起来像是“给代码做体检”的玩意儿,但真在团队里推起来,会发现它远不止体检那么简单。我自己的感受是,它更像是在代码评审和CI流水线之间,加了一道没人情味的、但极其稳定的自动化闸门。过去两年&a…

作者头像 李华
网站建设 2026/10/1 3:44:36

深分页性能优化:从OFFSET到游标分页的实践指南

去年我做的一个内容社区项目,列表页突然开始收到用户投诉:翻到一百多页的时候,页面加载要十几秒,有些用户直接卡死。运营那边反馈后台查询超时,数据库CPU负载冲到90%以上。我当时第一反应是“加索引啊”,但…

作者头像 李华
网站建设 2026/10/1 3:43:42

LightGBM实战水电站入库流量预测:从特征工程到滚动回测全流程

简介:面向水利数据分析与机器学习初学者,这份资料提供基于Python和LightGBM的水电站入库流量预测完整方案,适合科研人员、工程师用于调度决策或竞赛复现。项目针对历史入库流量、降雨预报及遥测站降雨等多维数据,完整覆盖数据清洗…

作者头像 李华