你们都知道,Android Studio
开发工具自带了Analyze Apk
,能够很方便的分析Apk文件。具体位于菜单build >> Analyze APK...
路径下,点击后能够选择目标路径下的Apk文件,甚至能够直接将目标APK文件直接拖拽到Android Studio
中,不到几秒中时间,立刻就会生成对应的分析结果。html
例如,微信Apk分析结果是这样的:
java
又如,支付宝Apk分析结果: android
瞬间感受本身很niubility,有没有?git
有时候,咱们也常常用它来分析本身的Apk,例如,生成的安装包到底长什么样子,里面的资源/代码构成,Manifest
中配置是否如预期,又或者方法数,等等。可是,一次忽然的机会,发现本身开发的Ap分析不了,一直处于Parsing Manifest
状态。es6
一脸懵逼,有木有? github
这个问题曾经困惑了我很多时间,以前也没有具体去研究过。如今又遇到了。瞬间想到鲁迅说的一句话:安全
技术路上,会遇到不少看似莫名其妙的问题。
细心探究,解决了,就是成长。
无视它并避让过去,看似绕过了问题,
实际上失去了一次很好的技术历练的机会。
而且下次极可能还会遇到相似的,甚至同样的问题,
长期看将是困难和停滞。
复制代码
既然先辈都这样说了,那,硬着头皮解一下?bash
如今给人的感受是Analyze APK
执行过程当中直接停住了,后者长时间一直在分析。但无论怎样,毕竟是在AS中的操做,先查一下对应的AS日志,看看有没Parsing Manifest
或相关的日志信息,能够起到帮助的。微信
Help >> Show Log in Finder
,打开日志,对应时间点看了又看,没找到Parsing Manifest
直接相关的,不过,找到了控件显示先关的日志:app
2019-08-08 19:21:25,323 [entQueue-0] INFO - ools.idea.apk.viewer.ApkEditor - Disposing ApkEditor with ApkViewPanel: com.android.tools.idea.apk.viewer.ApkViewPanel@7a608115
2019-08-08 19:21:25,323 [entQueue-0] INFO - s.idea.apk.viewer.ApkViewPanel - Cleared Archive on ApkViewPanel: com.android.tools.idea.apk.viewer.ApkViewPanel@7a608115
复制代码
从日志里面能够看出来,AS中对应的Analyze Apk
相关的类名有ApkEditor
、ApkViewPanel
,包名是com.android.tools.idea.apk.viewer
。AS日志部分的有效信息只有这么多了。
AS日志中没有,那有没有可能存在有效的信息输出在了系统其它的地方?因而,直接花点时间,系统全局搜索下。
/ grep -rnl "Parsing Manifest" *
复制代码
输出信息中有一些警告信息之类的,最终在输出信息中找到了个相关的:
Applications/Android Studio.app/Contents/plugins/android/lib/android.jar
复制代码
看目录名,大概是AS插件中对应的Android相关的lib工具包。找到对应位置,用JD-GUI打开对应的jar文件,具体看下一下。
Parsing Manifest
,的确能够定位到具体的
ApkViewPanel
类,且包名与上面AS日志中都能对的上,但字节码反解成java过程当中有内部错误。尝试着用用jadx打开,由于android.jar包还挺大,时间比较长,、最终虽然
ApkViewPanel
部份内容能够显示,但内部依然有部份内部错误没法显示,且
Parsing Manifest
不能直接显示。
不过不要紧,咱们试着去找找源码看看。搜索对应包名:com.android.tools.idea.apk.viewer
,选择java类别,很快,咱们找到了对应的源码位置。
正好,AS就是JetBrains主导的产品,Perfect!
Parsing Manifest
的位置:
setAppInfo()
方法中,将对应的控件内容从原来的Parsing Manifest
改为了对应的包名和版本号等信息。
显然,在代码myNameComponent.append("Parsing Manifest");
与setAppInfo(result);
之间,程序出了问题。
apkParser
对象对应的
ApkParser
类,还有
Archives
类。继续跟踪
ApkParser
类,发现其主要也是一个外壳性质的类,
apkParser.constructTreeStructure()
方法主要流程来到以下所示位置:
ArchiveTreeStructure
类,仍是以前的
Archives
类,这两个关键线索上的类都不是在这个项目中。根据代码文件中的
import
导入,很快,咱们发现,线索被定向到了
com.android.tools.apk.analyzer
包中。
从包名上来看,com.android.tools.apk.analyzer
应该是Android Tools
中带的一个工具。来到项目iml
文件,咱们发现与之相关的构件。其中,组名是:com.android.tools.apkparser
,构件名是:apkanalyzer
。
至此,咱们先总结下问题的缘由。
AS中自带的Analyze APK,其实是经过集成了插件实现,而插件内部,又经过调用了 Android Tools中的名叫apkanalyzer
的工具实现的分析。所以,想要追溯出现问题的缘由,咱们须要再去对应追踪下apkanalyzer
。
若是熟悉Android Tools,咱们对应去tools目录下找找,很快便能找到apkanalyzer
。及时不熟悉,不知道目录位置也不要紧,打不了全局搜索下。
终于,对应的工具本体出如今咱们面前。
实际上,若是对Google Developer比较熟悉,或者直接在上面搜索下,也能直接在Analyze APK
页面上找到核心信息,直接指向工具本体—apkanalyzer
。
developer.android.com/studio/buil…
啊哈,饶了半天,原来官方文档上直接有啊,哭了,有木有?
一样的,经过反编译工具查看apkanalyzer.jar
代码终究不太方便,且内部也有很多INNER ERROR
。因而,咱们继续去GitHub上找找。
GitHub上搜索到的apkanalyzer
相关的零零散散,好像都是我的的,不太官方,也不符合咱们的预期。怎么办呢?
源码不够,Google Source来凑!
直接Google Source搜索可能的关键字,立刻获得告终果。
显然,这正是咱们须要的。
但此时,若是直接源码跟踪下去,仍是有难度的。
apkanalyzer
做为一个工具,是独立的。在实际使用时能够直接脱离AS环境,Google Developer官网上也有专门的篇幅进行了介绍。
developer.android.com/studio/comm…
实际使用时,咱们经过不一样的命令行命令及参数,能够获得咱们指望的结果,如用来分析APK基本属性,Manifest,dex或资源等。
由此,咱们能够多试几个,反正AS中Analyze APK
最终用的也是它。在必定的命令上,结果确定是同样的。也就是说,经过命令行直接执行apkanalyzer
,确定也会有问题,但有个好处时,命令行执行每每都能抛出对应的错误日志。
有了进一步的错误日志提示,就有了异常栈和关键性的真正的错误缘由信息。
那咱们就试一试吧。
➜ bin apkanalyzer -h apk file-size Corn-dev-debug.apk
46.9MB
➜ bin apkanalyzer apk summary Corn-dev-debug.apk
com.corn 10300 10.3.0.0
➜ bin apkanalyzer manifest print Corn-dev-debug.apk
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="10300"
android:versionName="10.3.0.0"
package="com.mymoney"
platformBuildVersionCode="27"
platformBuildVersionName="8.1.0">
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="26" />
<uses-permission
android:name="android.permission.GET_ACCOUNTS"
android:maxSdkVersion="22" />
...
...
...
复制代码
说明直接分析Manifest文件都是没有问题的。
➜ bin apkanalyzer dex list Corn-dev-debug.apk
classes7.dex
classes6.dex
classes5.dex
classes4.dex
classes3.dex
classes2.dex
classes.dex
复制代码
➜ bin apkanalyzer resources configs --type drawable Corn-dev-debug.apk
anydpi-v21
anydpi-v26
default
watch-v20
v21
v23
ldpi-v4
mdpi-v4
ldrtl-mdpi-v17
hdpi-v4
ldrtl-hdpi-v17
xhdpi-v4
ldrtl-xhdpi-v17
xxhdpi-v4
ldrtl-xxhdpi-v17
xxxhdpi-v4
ldrtl-xxxhdpi-v17
复制代码
➜ bin apkanalyzer files list Corn-dev-debug.apk
Exception in thread "main" java.util.zip.ZipError: invalid END header (bad central directory offset)
at com.sun.nio.zipfs.ZipFileSystem.zerror(ZipFileSystem.java:1605)
at com.sun.nio.zipfs.ZipFileSystem.initCEN(ZipFileSystem.java:1045)
at com.sun.nio.zipfs.ZipFileSystem.<init>(ZipFileSystem.java:130)
at com.sun.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:117)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:326)
at java.nio.file.FileSystems.newFileSystem(FileSystems.java:276)
at com.android.utils.FileUtils.createZipFilesystem(FileUtils.java:538)
at com.android.tools.apk.analyzer.Archives.openInnerZip(Archives.java:48)
at com.android.tools.apk.analyzer.ArchiveTreeStructure.create(ArchiveTreeStructure.java:100)
at com.android.tools.apk.analyzer.ArchiveTreeStructure.create(ArchiveTreeStructure.java:65)
at com.android.tools.apk.analyzer.ApkAnalyzerImpl.filesList(ApkAnalyzerImpl.java:803)
at com.android.tools.apk.analyzer.ApkAnalyzerCli$Action$6.execute(ApkAnalyzerCli.java:430)
at com.android.tools.apk.analyzer.ApkAnalyzerCli.run(ApkAnalyzerCli.java:163)
at com.android.tools.apk.analyzer.ApkAnalyzerCli.main(ApkAnalyzerCli.java:130)
复制代码
终于,在用命令显示Apk内全部文件列表的时候出现了问题。而且有对应的调用栈信息抛出。
从调用栈中咱们发现,命令行的调用方式,是经过ApkAnalyzerCli
中的main
方法去接收命令参数的。在ApkAnalyzer.jar
同级的目录中,咱们发现了有对应的ApkAnalyzerCli.jar
,其做用,就是基于ApkAnalyzer.jar
基础上封装的一个Client
,以方便程序被外部调用执行,如经过命令行的方式等。
而且,忽然间发现,此处的栈信息与以前GitHub上JetBrains/android项目中分析到的源码位置相同~!!
at com.android.tools.apk.analyzer.ArchiveTreeStructure.create(ArchiveTreeStructure.java:100)
复制代码
看来,这就是真实的缘由所在了。
ArchiveTreeStructure
主要做用是分析apk文件中的档案文件树形结构,且从最终抛出的错误信息能够看出:Apk包中zip文件出现的问题,zip文件头部信息无效。
java.util.zip.ZipError: invalid END header (bad central directory offset)
复制代码
抓住这一关键点,那就好办了。直接搜索整个项目中的.zip
文件,发现还真有很多。而且存在于assets目录下。主要存放的是一些资源。
直接解压缩.zip文件,发现有问题,果真,此处有问题的.zip文件致使apkanalyzer在分析Apk过程当中,分析到这些.zip文件出现了问题。
.zip格式显然是不符合.zip规范的,那么,具体是什么问题呢?
查找到项目使用到这些文件的代码位置。
泪奔了,有木有?!
到如今位置,总体逻辑已经很清晰了。项目中由于存在移除了表示zip格式的头字节的zip文件,致使在使用Android Studio Analyze APK
分析Apk时,出现程序错误,从而只显示Parsing Manifest
。
究竟对不对呢,能够以简单方式核验下。
1,经过移除此类有问题的.zip文件,从新打包,发现可使用Android Studio Analyze APK
进行分析了,直接使用apkanalyzer
命令行分析时,也木有问题。
2,使用二进制修改工具,将这类有问题的.zip文件对应的四个字节的头信息给补上。
此处推荐使用 Hex Friend 工具,能够直接以十六进制修改对应的二进制文件内容。
504B0304
,用来表示的是对应的zip格式。
因而,咱们手动补上试试。保存后,发现.zip文件能够经过系统工具解压了。
替换后再次打包验证,发现能够Android Studio Analyze APK
能够正常分析,apkanalyzer
能够直接使用。
项目中之因此此处将zip文件头四个字节去除存放,当时主要考虑是安全性问题。而后经过代码的形式在拷贝过程当中,去补上对应的字节信息,至关于进行了修正。
如今既想不影响原有逻辑,同时又能愉快的使用Android Studio Analyze APK
进行分析,怎么办呢?
经过分析,咱们发现,apkanalyzer
抛出的异常,是在对zip文件进行分析的时候出现的,既然.zip文件自己,文件头标识.zip格式的字节已经被去除,那程序是如何识别其为.zip文件的呢,大几率是直接经过文件后缀了。发现文件后缀为.zip,就直接按照zip文件格式进行了处理。
对应的调用栈上,咱们没有找到Archives
类的openInnerArchive
方法,这应该是对应的jar包版本问题。不过不要紧,沿着对应的调用过程分析,咱们找到了以下的逻辑。
显然,此处的判断逻辑与猜想一致,直接是按照文件名称中的格式后缀去匹配的。
那换一种思路,若是这类文件自己,不是zip后缀,程序极可能就不会执行到zip文件的分析判断上,是否是就能够了呢?
说干就干,直接将项目中此类非规范化的zip文件格式替换,例如替换成一种本身随意想的格式,就叫.tfc
吧,而后程序中作好可能的逻辑修正。
试试。果真,再次打包,分析,OK,彻底木有问题!
其实从反面来想,若是咱们不想别人经过apkanalyzer
来分析咱们的Apk,能够经过此类技巧,直接放一个不符合规范的zip文件在assets目录中。例如本文中的将zip文件头四个字节去除。
若是本身想分析本身的Apk,加对应的文件移除便可。这样在必定程度上,防防一些开发者,提升一下安全的门槛,仍是能够的。
是否是有点,尬?
实际上,咱们发现不少App在提升反编译门槛时,都采起的相似的思路,经过分析对应的反编译工具的源码,在可能对应有逻辑漏洞的地方去设防,使得反编译程序运行到对应位置出现错误,以此达到反编译失败目的。
如针对ApkTool反编译工具使用的一些常见方法等。
有时候,在内部项目或不太严格场景下的技术实现时,直接对文件后缀进行格式校验,也许也就能够了。可是,在一些通用的框架、工具或有外部交互及严格需求场景的状况下,对文件格式的断定,不该该只是简单的判断文件格式后缀。由于文件格式后缀是很容易被任意修改的,并不具备事实上的文件格式约束力。在文件格式后缀判断的基础上,必要时,增长上对文件事实上的格式判断逻辑,是颇有必要的。在必定程度上来讲,本例中发现的问题,其实也偏偏是apkanalyzer
工具自身隐藏的一个bug。
apkanalyzer
是Android开发过程当中,用来分析Apk中颇有用的一个工具,常常被用到。由于其集成在AS中后,足够简单轻便,且能必定程度上知足咱们分析Apk的须要。
项目在不断的迭代,维护和开发,项目中的技术问题也会不时出现,对每个技术问题,其实都是一次很好的技术历练机会。认真分析,不断探因,最终会终有所获。
通常的产品开团队,面对这类问题,每每习惯的选择绕开,由于在相对繁忙的需求开发过程当中,无暇此顾。其实,对整个项目组来讲,长期来看,这是一个严重的问题。这类技术性问题,就像厨房中的小强,若是发现了不一一解决之,最终会致使不断的繁衍,终有一日,屋里的主人,会被细菌病毒侵袭。
与其积重难返,不如时时解决之。
end~