用Python打造安卓ROM分析与APK扫描工具

用Python打造安卓ROM分析与APK扫描工具 刷了这么多年安卓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:exportedtrue的组件等于把入口公开给其他应用如果这个组件还带有权限保护级别为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做初步体检这种“整合命令行工具输出统一报告”的思路完全可以直接抄作业不一定要用我的代码按自己的使用习惯搭一套也很快。最后再分享一个小习惯每次扫描完我都会把报告另存一份按日期命名归档时间长了就是一部完整的“玩机记录”想回溯任何一次操作都非常方便。