一、背景

大家都知道,Android Studio开发工具自带了Analyze Apk,可以很方便的分析Apk文件。具体位于菜单build >> Analyze APK...路径下,点击后可以选择目标路径下的Apk文件,甚至可以直接将目标APK文件直接拖拽到Android Studio中,不到几秒中时间,马上就会生成对应的分析结果。

例如,微信Apk分析结果是这样的:

又如,支付宝Apk分析结果:

瞬间感觉自己很niubility,有没有?

有时候,我们也经常用它来分析自己的Apk,例如,生成的安装包到底长什么样子,里面的资源/代码构成,Manifest中配置是否如预期,又或者方法数,等等。但是,一次突然的机会,发现自己开发的Ap分析不了,一直处于Parsing Manifest状态。

一脸懵逼,有木有?

二、探因

这个问题曾经困惑了我不少时间,之前也没有具体去研究过。现在又遇到了。瞬间想到鲁迅说的一句话:

技术路上,会遇到很多看似莫名其妙的问题,细心探究,解决了,就是成长。
无视它并避让过去,看似绕过了问题,实际上失去了一次很好的技术历练的机会,
并且下次很可能还会遇到类似的,甚至一样的问题,长期看将是困难和停滞。

既然先辈都这样说了,那就,那硬着头皮解一下?

2.1 AS日志

现在给人的感觉是Analyze APK执行过程中直接停住了,后者长时间一直在分析。但不管怎样,毕竟是在AS中的操作,先查一下对应的AS日志,看看有没Parsing Manifest或相关的日志信息,可以起到帮助的。

Help >> Show Log in Finder,打开日志,对应时间点看了又看,没找到Parsing Manifest直接相关的,不过,找到了控件显示先关的日志:

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相关的类名有ApkEditorApkViewPanel,包名是com.android.tools.idea.apk.viewer。AS日志部分的有效信息只有这么多了。

2.2 系统全局搜索

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不能直接显示。

2.3 GitHub定位与源码分析

不过没关系,我们试着去找找源码看看。搜索对应包名:com.android.tools.idea.apk.viewer,选择java类别,很快,我们找到了对应的源码位置。

github.com/JetBrains/a…

正好,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


2.4 工具本体-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搜索可能的关键字,马上得到了结果。

显然,这正是我们需要的。

但此时,如果直接源码跟踪下去,还是有难度的。

2.5 apkanalyzer查因

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.apk46.9MB➜  bin apkanalyzer apk summary Corn-dev-debug.apkcom.corn    10300   10.3.0.0➜  bin apkanalyzer manifest print Corn-dev-debug.apk<?xml version="1.0" encoding="utf-8"?>        .........

说明直接分析Manifest文件都是没有问题的。

➜  bin apkanalyzer dex list Corn-dev-debug.apkclasses7.dexclasses6.dexclasses5.dexclasses4.dexclasses3.dexclasses2.dexclasses.dex复制代码
➜  bin apkanalyzer resources configs --type drawable  Corn-dev-debug.apkanydpi-v21anydpi-v26defaultwatch-v20v21v23ldpi-v4mdpi-v4ldrtl-mdpi-v17hdpi-v4ldrtl-hdpi-v17xhdpi-v4ldrtl-xhdpi-v17xxhdpi-v4ldrtl-xxhdpi-v17xxxhdpi-v4ldrtl-xxxhdpi-v17
➜  bin apkanalyzer files list Corn-dev-debug.apkException 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.(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)

看来,这就是真实的原因所在了。

2.6 项目查因

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规范的,那么,具体是什么问题呢?

查找到项目使用到这些文件的代码位置。


泪奔了,有木有?!

2.7 核验

到现在位置,整体逻辑已经很清晰了。项目中因为存在移除了表示zip格式的头字节的zip文件,导致在使用Android Studio Analyze APK分析Apk时,出现程序错误,从而只显示Parsing Manifest

究竟对不对呢,可以以简单方式核验下。
1,通过移除此类有问题的.zip文件,重新打包,发现可以使用Android Studio Analyze APK进行分析了,直接使用apkanalyzer命令行分析时,也木有问题。

2,使用二进制修改工具,将这类有问题的.zip文件对应的四个字节的头信息给补上。
此处推荐使用 Hex Friend 工具,可以直接以十六进制修改对应的二进制文件内容。

Hex Friend打开zip文件后,发现文件头字节中,确实不符合zip规范。zip文件头四个字节是固定的,504B0304,用来表示的是对应的zip格式。


于是,我们手动补上试试。保存后,发现.zip文件可以通过系统工具解压了。

替换后再次打包验证,发现可以Android Studio Analyze APK可以正常分析,apkanalyzer可以直接使用。

三、解决

项目中之所以此处将zip文件头四个字节去除存放,当时主要考虑是安全性问题。然后通过代码的形式在拷贝过程中,去补上对应的字节信息,相当于进行了修正。

现在既想不影响原有逻辑,同时又能愉快的使用Android Studio Analyze APK进行分析,怎么办呢?

通过分析,我们发现,apkanalyzer抛出的异常,是在对zip文件进行分析的时候出现的,那换一种思路,如果这类文件本身,不是zip文件格式,程序很可能就不会执行到zip文件的分析判断上,是不是就可以了呢?

说干就干,直接将项目中此类非规范化的zip文件格式替换,例如替换成一种自己随意想的格式,就叫.tfc吧,试试。

果然,再次打包,分析,OK,完全木有问题!

四、启示

其实从反面来想,如果我们不想别人通过apkanalyzer来分析我们的Apk,可以通过此类技巧,直接放一个不符合规范的zip文件在assets目录中。例如本文中的将zip文件头四个字节去除。

如果自己想分析自己的Apk,加对应的文件移除即可。这样在一定程度上,防防一些开发者,提高一下安全的门槛,还是可以的。

是不是有点,尬?

五、结语

apkanalyzer是Android开发过程中,用来分析Apk中很有用的一个工具,经常被用到。因为其集成在AS中后,足够简单轻便,且能一定程度上满足我们分析Apk的需要。

项目在不断的迭代,维护和开发,项目中的技术问题也会不时出现,对每一个技术问题,尤其是一些偏向于“疑难杂症”性的技术问题,其实都是一次很好的技术历练机会。认真分析,不断探因,最终会终有所获。

一般的产品开团队,面对这类问题,往往习惯的选择绕开,因为在繁重的需求开发过程中,无暇此顾。其实,对整个项目组来说,长期来看,这是一个严重的问题。这类技术性问题,就像厨房中的小强,如果发现了不一一解决之,最终会导致不断的繁衍,终有一日,屋里的主人,会被细菌病毒侵袭。

码文不易。希望读到这的您能分享和关注一下我,以后还会分享Android知识点及解析,您的支持就是我最大的动力!


更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. 一款常用的 Squid 日志分析工具
  3. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  4. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
  5. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  6. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  7. Android内存泄露自动检测神器LeakCanary
  8. Android本地视频播放器开发--ffmpeg解码视频文件中的音频(2)
  9. Android中使用POI加载与显示word文档

随机推荐

  1. android使用滚动视图
  2. android UI跨线程操作
  3. android aosp 下载源代码
  4. Android资料备注
  5. Android(安卓)软键盘遮挡Dialog
  6. Android: Attaching Sources to Librarie
  7. Android(安卓)Market URL
  8. Android(安卓)自定义渐变背景
  9. Android(安卓)的大牛的博客 提供给大家参
  10. [图解]Android下Invalid layout of java.