☰
用Python打造安卓ROM分析与APK扫描工具
2026/10/20 1:45:16 网站建设 项目流程

刷了这么多年安卓ROM,我最大的一个感受是:改包这件事本身不难,难的是拿到包之后,根本不知道从哪里下手。每次拖下来一个官方包或者第三方包,我都想把里面所有APK挨个解包看一遍——谁在后台偷跑流量、哪个预装应用要了一堆根本用不上的权限、某个系统应用删了到底会不会影响开机,这些靠肉眼和手动敲命令去查,效率低得离谱。后来我干脆用Python写了个小工具,把ROM解包、APK扫描、应用分析这几件事串到一起,按键一敲,报告直接出来。这篇就聊聊这个工具的定位、设计思路、实现细节,以及我在反复改机过程中踩过的那些坑。

这个工具说到底就是一个“离线版的安卓玩机工作台”,不联网、不吃内存,专门服务三类人:喜欢精简ROM的玩机党、需要快速核实APK来源和权限的普通用户、以及刚开始接触安卓应用结构分析的新手。它给不了你动态调试那么深的能力,但能把“这个APK是谁、要了什么东西、能不能删、有没有幺蛾子”这些基础问题一次性回答清楚。

1. 为什么要自己做ROM分析与APK扫描工具

1.1 现成工具一大把,为什么还要重复造轮子

很多人第一反应是,安卓生态里分析APK的工具早就烂大街了,MT管理器、APK Analyzer、jadx、apktool、aapt,随便拿一个出来都能干活,为什么还要自己写一个?

说实话,这些工具单个拎出来都很强,问题出在“组合使用”上。我平时精简ROM的流程大概是:用payload-dumper或sdat2img把卡刷包解包,然后进system目录,把所有APK挨个看一遍,想搞清楚哪些能删。MT管理器在手机上确实方便,但是批量导出包名、权限、SDK版本就很吃力;Android Studio自带的APK Analyzer信息全,可一台老笔记本跑起来卡半天;aapt命令行效率高,可参数太多,记不住。最要命的是,分析完这个应用之后,想对比一下上个ROM版本里同一个APK有什么变化,还得手动查哈希、查版本号,完全没有一个统一入口。

自己做工具的本质,不是要重新发明“APK解析”这个轮子,而是把散落在一堆工具里的能力收拢成一个固定流程。我把所有命令行工具封装到后面,用一套脚本统一调用,输入一个APK路径,输出一份结构化的分析报告,再也不用在终端里一遍遍翻aapt的输出,也不需要为了看一个manifest去单独开一个图形工具。

1.2 工具的定位与核心使用场景

这个小工具的定位很明确:它是一个“离线扫描器+辅助决策工具”,不追求大而全,只把我在ROM修改和APK分析中最常用的能力做成可复用的模块。适用场景集中在四个方向。

第一个场景是ROM精简前的摸底。拿到卡刷包后,先解包,再对整个system分区的APK做批量扫描,生成一份清单,标出每个应用在哪个目录、占多大体积、是否引用了敏感权限、有没有明显的广告SDK特征,这样在动手删除之前就有了依据。

第二个场景是可疑APK的快速体检。从网上下载的安装包、聊天群里别人发的共享包、刷机包里面夹带的未知应用,丢进工具扫一遍,看它的包名、签名、权限、组件暴露情况,判断是不是官方出品,或者有没有明显的追踪/广告SDK痕迹。

第三个场景是升级包对比追踪。刷了新版ROM之后,想知道哪些系统应用被替换了、哪些新增了、哪些被删了,工具可以把两次扫描结果做差异对比,生成变更报告。

第四个场景是辅助理解APK结构。对安卓应用开发不熟的新手,拿到一份APK不知道里面哪些文件是什么,工具会把dex、资源、so库、assets、签名等信息拆开说明,也算一个学习入口。

边界我也划得很清楚:不做动态Hook,不做内存调试,不做太深的脱壳和混淆还原。静态分析有它的天花板,有些恶意行为只有跑起来才能暴露。工具的定位永远是“辅助分析”,而不是“自动判定”。

2. 工具的整体设计与技术选型

2.1 框架选型:为什么用Python做底座

选Python作为主语言,说实话没什么高大上的理由,就是因为写起来快、生态全、跨平台。整套工具里大量工作其实是“调用外部命令行工具+解析输出”,Python的subprocess和字符串处理能力干这个活非常顺手。而且很多第三方库可以直接上手,比如用zipfile读APK结构、用xml.etree解析AndroidManifest、用hashlib算文件哈希,几乎不折腾环境。

我保留了命令行优先的设计,主程序入口只接受参数,比如python romkit.py scan xxx.apk、python romkit.py rom /path/to/rom.zip,这样在服务器上、在WSL里、在Windows Terminal里都能跑,也方便写进自动化脚本。后来加了一个简单Web界面,用Flask起本地端口,浏览器打开就能点选目录、看报告。这个界面纯粹是为了“给不太会用命令行的朋友用”,核心逻辑全部在命令行层,界面只是壳。

依赖方面,Python环境本身只需要几个基础库。像androguard这种重库我特意没有作为硬依赖,因为有些机器上安装它挺费劲的,而且它的输出格式我不完全满意。我更信任自己封装的解析逻辑:先用aapt2拿标准信息,再叠加自定义特征规则,这样可控性更强。说实话,工具是自己用的,少一个重依赖就少一个崩溃点。

2.2 核心引擎:把aapt/apktool/jadx这些“老伙计”包起来

工具虽然是我自己写的,但底下干活的还是那些经过大量验证的开源命令行工具。我把它们统一放到工具的tools/目录下,并做了一层封装,好处是版本可控,不会因为系统里装了什么乱七八糟的版本导致输出格式不稳定。

主要有这么几个:

aapt2负责提取APK基础信息和资源表,包括包名、版本、SDK版本、权限、启动Activity等。它输出的badging格式是key-value形式的,解析起来非常稳定。这里我要特别说一句,APK分析首选aapt2而不是老版aapt,因为新版打包工具生成的APK在aapt下解析时偶尔会出现资源引用不完整的情况,而aapt2对AAPT2编译的产物兼容性最好。

apktool负责反编译资源文件和AndroidManifest.xml。虽然Java/Kotlin代码它解不出来,但拿到明文格式的Manifest是静态分析的基础。apktool还有一个常见用途是回编译,在修改资源或者smali之后重新打包,不过我这套工具在扫描场景下只用它的d命令。

jadx负责反编译dex得到Java代码。分析一个APK是否藏了可疑逻辑,光看权限不够,最好能搜一下代码里有没有敏感关键词,比如爬取通讯录、上传文件、读取IMEI相关的接口调用。jadx支持命令行批处理,我把它嵌入到一个“深度分析”模式里,平时扫描默认不开,因为反编译耗时太长。

除了这几个,还有zipalign和apksigner管对齐和签名,keytool管生成密钥,payload-dumper-go、simg2img、sdat2img、lpunpack这些管ROM镜像解包。每个工具在配置文件中登记路径和版本,工具启动时先做一次“自检”,缺什么就直接告诉你,不会让你跑一半才报错。

2.3 信息收集维度:APK要扫哪些东西才算“分析”

一开始我犯过一个错误,就是把包名、版本号、权限列出来就觉得“分析完了”。后来被一个伪装成系统工具的应用上了一课,它申请权限看起来平平无奇,实际上把搜集到的信息加密外发。所以这个工具后来扩展到了下面的字段维度。

维度具体字段分析价值
基础信息包名、应用名、versionName、versionCode判断是不是官方包、对比版本变化
兼容性minSdk、targetSdk、支持的ABI预判在特定设备上的安装兼容性
权限权限列表、危险权限数、权限分组定位权限滥用点,比如一个手电筒应用请求短信权限
组件activity/service/receiver/provider查看导出组件、后台服务、开机广播
签名签名方案、证书指纹、签名者DN判断APK来源链条是否一致
文件特征体积、dex数量、lib目录、assets名识别加固壳、广告SDK、异常文件
固件关联所在分区目录、所属厂商目录判断系统应用能否安全精简
指纹SHA-256、MD5记录文件完整性,用于后续对比

权限这块我单独加了一个“风险分”逻辑,不是有权限就报风险,而是按组合来。比如说一个应用同时申请了读取短信、定位、读取联系人、获取安装列表这几个权限,而且它的核心功能只是一个本地文件管理,那这个风险分就会飙升。单纯列权限列表没有意义,用户看着一长串也不知道问题在哪,不如直接给出一个可解释的评分。

3. 核心功能拆解与实操细节

3.1 功能一:ROM包解析与系统应用清单生成

ROM包解析是整个工具里最“脏”的活,因为不同刷机包的镜像格式五花八门。老一点的卡刷包用的是system.new.dat.br加transfer.list,新机型很多直接给payload.bin,还有一部分是raw的system.img或者sparse分块镜像。我最初只支持了dat.br,后来遇到一个用sparse分块镜像的包直接傻眼,才意识到格式兼容必须一开始就考虑。

工具的rom_unpack模块会先扫描压缩包内容,识别镜像类型,然后自动选择解包工具。具体流程是:检测到payload.bin就用payload-dumper-go;检测到system.new.dat.br就先用brotli解压dat,再用sdat2img结合transfer.list生成system.img;遇到sparse分块镜像就调用simg2img合并;如果是Android 10以后常见的super.img,就用lpunpack按分区槽位提取。这里最容易踩的坑是只解包system分区,忽略了product、vendor、odm这些分区,实际上很多预装应用和权限白名单都藏在product和vendor里,精简和扫描的时候漏掉它们,后面刷完机就会出现各种奇怪问题。

解包完成后,工具会扫描system/app、system/priv-app、product/app、product/priv-app、vendor/app等目录,对每个APK做基础信息提取,生成一个全量应用清单CSV。CSV里除了包名和APK路径,还会带上“精简安全度”这一列,规则很简单:系统核心组件、设置项依赖、输入法、桌面启动器、帐号管理这类标记为高风险;壁纸、演示视频、厂商定制商店、第三方合作应用标记为可精简候选。这个标记不是绝对的,但它能帮我在几百个预装应用里快速锁定目标。

3.2 功能二:APK批量扫描与异常特征识别

批量扫描是工具使用频率最高的功能。把一整个目录丢进去,工具会对里面的每个APK执行扫描,输出JSON和HTML两种报告,HTML适合鼠标点击查看,JSON方便后续脚本二次处理。

异常特征识别是我重点打磨的部分。主要分四类。

第一类是权限类异常。前面提到的风险分就在这里起效,结合targetSdk版本还能做更细判断。比如targetSdk大于等于23的应用,运行时权限需要动态申请,如果一个应用没有对应的权限声明框,却在代码里反射调用危险API,那就有绕过权限机制的嫌疑。

第二类是组件暴露。Manifest里android:exported="true"的组件等于把入口公开给其他应用,如果这个组件还带有权限保护级别为normal甚至不设权限,那问题就比较明显。工具会列出所有导出的组件,标出哪些没有自定义权限保护。

第三类是签名异常。包括只使用v1签名方案、多APK之间签名不一致、证书指纹和官方发布版本不匹配。这里我有个习惯,把官方应用商店里抓到的APK作为基准,把它们的证书指纹存下来,之后扫到同名包先做对比。

第四类是加固和广告特征。通过检测lib目录下常见的加固库文件名、classes.dex加密特征、assets里典型的SDK配置文件名,判断是否加壳,以及是否嵌入了广告/统计SDK。实测下来,这个识别对常见商业壳和主流广告SDK命中率不错,但对定制变种就无能为力了。

我还加了一个“伪系统应用”提示逻辑:如果一个APK的包名看起来像是系统应用,但它不在系统分区路径下,或者签名和系统密钥不一致,就会单独标记出来。这个功能在检查第三方ROM里是否被塞入奇怪包的时候特别有用。

3.3 功能三:ROM精简辅助与签名重打包

精简辅助功能是ROM修改里最实用的一个模块,但也最需要谨慎。它的核心操作就是拿着3.1生成的应用清单,勾选删哪些,工具自动在解包目录里执行删除,并同步清理关联文件。关联文件是个关键,删APK不删odex/vdex是精简后卡开机最常见的元凶之一。

以Android 13为例,一个系统应用目录下可能有Base.apk、Arm64/Base.odex、Arm64/Base.vdex,如果只删Base.apk,刷机后系统在编译阶段找不到对应dex,很可能直接引导失败。工具的删除逻辑是先解析APK所在的整个目录,把所有同名校验文件、编译产物一并清空,再检查/system/etc/permissions下面有没有对应的权限白名单xml,有的话提示你确认这个XML里还声明了哪些其他应用。

删完后就是重打包。重打包环节涉及三个操作:把修改过的system目录重新生成镜像、对齐签名、封装成卡刷包。签名这一步需要注意,ROM里如果存在vendor分区并且启用了AVB校验,那么重打包后的system必须和vbmeta的形象信息匹配,否则设备会拒绝启动。我用的方案是把vbmeta的verity开关关掉或者重新签名,具体的命令取决于设备平台,但核心原则是:修改system之前先确认AVB状态,不然刷了就是砖。

工具里内置了一个“自动打包”脚本,把用户选定的system目录做成new.dat.br镜像,再从原始刷机包里提取META-INF目录,最后生成一个新的zip。这个新zip我一般不直接刷,而是先在模拟器或者备用机上验证一轮,等确认系统能起来再刷主力机。

3.4 功能四:APK对比与升级变更追踪

这个功能其实是后来加的。有一段时间我频繁刷更新包,手头留着几十个版本的ROM,想看看某次系统更新到底改了什么,结果发现靠人类记忆根本不可行。于是我加了一个简单的“基线库”功能:每次解包后,工具会把所有APK的包名、版本号、SHA-256哈希、大小、签名指纹记录到一个SQLite数据库里,并且打上ROM版本标签。

之后扫描新ROM时,可以指定和某个基线做对比,输出新增、删除、更新、签名变化四类结果。一个很有价值的场景是发现“同版本号但哈希不同”的APK,这通常意味着APK被二次打包或者签名密钥更换了,在第三方ROM里见到这种情况要格外警惕。

对比报告我用的是纯HTML页面,左右两栏展示旧版和新版的信息,变更项高亮显示,下面列出具体的权限差异和组件差异。虽然技术上没什么特别,但用起来是真的省事,尤其是帮朋友检查“某某精简版ROM到底改了什么”的时候,直接把差异甩过去,比口头扯半天有效得多。

4. 实操过程:从刷机包到应用分析报告

4.1 一次完整的ROM修改流程演示

拿我最近处理的一个通用LineageOS包举例,整个流程可以复现一遍。

第一步,解包。假设ROM包叫lineage-20.0-xxx-signed.zip,我先运行工具的命令行入口:

python3 romkit.py rom unpack lineage-20.0-xxx-signed.zip -o ./workspace

工具自动检测到payload.bin,调用payload-dumper-go把system、product、vendor等分区解到./workspace下。解包完成后,会提示发现system分区4100MB、product分区1200MB、vendor分区900MB。这一步会花几分钟,磁盘IO是瓶颈,建议放在固态硬盘上。

第二步,生成应用清单。

python3 romkit.py apk scan ./workspace/system/app ./workspace/system/priv-app -o report.html

扫描会遍历所有APK,提取包名、权限、SDK版本、签名指纹,输出HTML报告。我一般还会加一个--risk参数,让它把风险分高、可疑特征多的应用排在最前面。

第三步,人工筛选。打开报告,逐个看标红的项目,结合自己的需求决定删除列表。比如我习惯把Wallpaper、LiveWallpapers、厂商自带浏览器、几个第三方工具app列入删除候选,同时保留GMS全家桶、输入法和系统设置相关组件。这里有一个从我实际翻车里总结的经验:判断能否删除时,不要只看包名,要看它有没有被其他系统应用引用。最直接的办法是用apktool解包一个核心系统UI或SystemUI,查它Manifest里的queries和调用关系,如果它引用了你要删的应用,那删了轻则功能缺失重则无限重启。

第四步,执行精简。工具读取我保存的prune_list.txt,自动删除对应目录并清理odex/vdex。删除后会自动跑一遍依赖检查,如果发现某个权限白名单被“删空”,会提示我确认。

第五步,重打包。工具自动调用img2sdat把system目录打包成system.new.dat.br,组装卡刷包,输出signed.zip。整个过程我不用手动敲一条复杂的命令行,原来要折腾半小时的活,现在一两分钟就出来了。

整个流程跑完之后,我还会做一遍“虚拟检查”,把新打包的ROM再解包一次,用差异对比功能核对删除列表是否全部生效、有没有误删。这个习惯帮我避免了至少两次“底包忘放关键文件直接变砖”的事故。

4.2 常见问题与排查技巧实录

我把工具使用过程中最常遇到的问题整理成了下面这张表,基本覆盖了解包、扫描、精简、打包的全链路。

问题现象可能原因解决办法
解包payload.bin时提示“invalid rom table”payload.bin被加密或下载不完整重新检查文件完整性,确认来源可靠;少数厂商加了私有头,需要对应工具版本
system.new.dat.br解压后sdat2img失败分块格式和transfer.list不匹配,或者是稀疏镜像先确认dat文件版本,用simg2img直接转换;检查transfer.list是否有损坏
APK扫描报告里包名为空APK资源损坏或使用了特殊混淆先用aapt2单独dump badging看是否有输出;也可能是APK本身不完整
精简后开机卡在Logo删除了系统核心依赖回滚删除列表,重点检查SystemUI、framework-res、SettingsProvider、PackageInstaller等
重打包后刷机提示verification failedAVB/dm-verity校验失败重新签名vbmeta,或关闭AVB校验;不要只签system而不管vbmeta
签名后的APK安装提示“与现有应用签名不同”替换系统应用时签名证书不一致用系统平台密钥重新签名,或先卸载旧应用再安装
扫描时遇到超大APK内存溢出zipfile直接读入内存改为按ZIP Entry流式读取,只解压需要的文件
反编译jadx耗时太长dex体量大、混淆严重只对重点APK启用深度分析,批量扫描保持轻量
删除priv-app后功能异常对应的privapp-permissions白名单没删检查system/etc/permissions下对应XML,谨慎处理
打包后的zip刷机后多分区不识别分区槽位(A/B)结构处理错误确认lpunpack时使用的super分区布局参数正确

这里面最想强调的一点是“精简后卡开机”这种问题。新手的通病是觉得删得越多越流畅,实际上系统里很多应用之间存在隐式依赖,尤其是一些提供ContentProvider或Service给系统UI调用的应用,比如DocumentsUI、ExtShared等,虽然看着不起眼,但删了之后系统会持续报错,最终导致反复重启。所以我在工具的删除动作里加了一个强制备份机制,每次删除前先把APK压缩进一个备份归档,出问题还能一键恢复。

5. 实战心得与避坑清单

5.1 工具设计层面容易踩的坑

先说说写这个工具本身踩的那些坑。第一点,千万不要把外部工具的路径写死在代码里。我一开始图省事,直接调用系统的aapt,结果换一台电脑就找不到命令。后来改成在配置文件中登记工具路径,启动时自动检测并在PATH中查找,找不到就给出明确提示,终于不再被环境问题折磨。

第二点,解析外部工具输出时,要留足日志。aapt2或者apktool偶尔会有诡异输出,如果不把原始stdout/stderr保存下来,出了问题根本没法排查。我的每个模块都会把命令行执行日志写到logs/目录,既能用来定位问题,也能在报告里追溯数据来源。这里多说一句,我看到很多人写类似工具时只解析输出不保留日志,一旦报告和一个APK的实际内容对不上,连查都没法查。

第三点,权限扫描不能只做“有/无”判断。危险权限的表述太粗略,同一个权限在不同targetSdk下风险等级完全不一样。我在风险分计算里加入了targetSdk上下文,还引入了权限组合规则,这样报告才更有参考价值。

第四点,静态扫描报告一定要带“免责声明”。工具只能发现已知特征,不能证明APK绝对安全。我在报告顶部写得很清楚:这是一份静态分析结果,仅供安全评估参考,不代表应用真实行为。这句话不光是给用户看的,也是提醒我自己,分析工具永远是辅助。

5.2 玩机操作层面的血泪教训

最后说点玩机层面的经验,这些比工具本身更值钱。玩机第一原则是备份。哪怕是精简ROM这种看起来风险不高的操作,也一定要先把原包完整备份好,最好再留一份系统完整镜像。我见过太多人精简到一半发现少了关键库,又找不到原始文件,最后只能重新下载整个ROM。

第二原则是“分批精简、逐步验证”。别一次删几十个应用然后直接刷机,刷完机器起不来你根本不知道是哪一个删错了。正确做法是先删三五个明显安全的,刷机验证没问题,再继续删下一批。这个原则是我在一次次卡Logo中总结出来的。

第三原则是签名密钥一定要长期保存。不管是给APK重签还是给ROM打包,签名用的是同一把私钥,如果搞丢了,后续所有在线升级都得重新刷全量包,甚至涉及应用数据迁移。

第四原则是不要迷信“第三方优化版”。很多来路不明的“精简版ROM”“去广告版APK”其实就是拿现成工具二次打包,再塞点自己的私货。我写这个工具的一大动力,就是能在刷入之前先看看里面到底有什么。尤其是那些连签名证书都和官方不一致的包,十有八九有猫腻。

回过来看,这个小工具本身并不复杂,真正有价值的是把一套我自己反复验证过的流程固化成了工具。每次拿到新ROM,第一件事先跑一遍扫描生成报告,再决定动不动刀。现在我把它一直放在移动硬盘里,换电脑也不影响使用。如果你平时也经常折腾安卓ROM或者需要对APK做初步体检,这种“整合命令行工具+输出统一报告”的思路完全可以直接抄作业,不一定要用我的代码,按自己的使用习惯搭一套也很快。最后再分享一个小习惯:每次扫描完,我都会把报告另存一份按日期命名归档,时间长了就是一部完整的“玩机记录”,想回溯任何一次操作都非常方便。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询