Matrix-APKChecker分析

APKChecker分析

介绍

Matrix是腾讯开源的一个APM项目,对于android优化方面无从下手的话,感觉可以研究一下

这里主要分析其中一个模块APKChecker,这个模块和其它模块的关联性不是很大,可以单独提取出来、主要就是检测apk内重复资源文件,大小,未使用文件,
stl重复引入等功能。如果有自己的想法也可以进行扩展.

使用流程
  • 指定配置文件运行
    ./app/build/install/app/bin/app --config ./apk-checker-config.json
配置文件解析
1. 配置文件参数解析
  • –input 指定一个文件夹路径 apk文件的目录,–apk没有指定默认在这个目录里搜第一个后缀是apk的文件
  • –unzip 解压apk后的目录路径,不指定,默认是apk所在的路径
  • –apk 指定apk文件路径
  • –mappingTxt 指定类的混淆映射文件路径
  • –resMappingTxt 指定资源混淆映射文件路径
  • –output 输出结果文件路径,不指定默认是apk路径
  • –format 输出结果的形式.支持json 和html格式
  • –formatJar 这个比较吊了,可以拓展TaskJsonResult,TaskHtmlResult,这两个输出结果形式,配置一个jar包给一个注册类,动态加载.(感觉边角功能做的有点多了。)
  • –formatConfig 一个数组干嘛的呢
  • –log 指定日志等级 V D I W E,指定一个等级 小于这个等级的日志就没办法输出了
  • –options 一个数组JsonArray 每一个JsonObject必须指定一个name,对应一个ApkTask的任务名称。剩余的属性,是作为这ApkTask的参数,根据name利用TaskFactory创建任务清单
  • –rTxt -options里unUsedResource任务参数.指定的资源映射文件路径
2. 配置文件解析代码主要在ApkJob类 readConfigFile方法里
  • 你也可以通过命令行的形式指定这些参数
ApkTask
  • ApkTask主要就是具体apk检测项目的执行基类
  • ApkTask实现Callable接口,可以线程池执行得到执行结果TaskResult
  • TaskResult最后通过JobResult写入文件
  • ApkTask主要通过TaskFactory集中生成.
  • ApkTask相关的结构图
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2W0Ae2hy-1631069452664)(./apkarch.png)]
UnzipTask
  • 负责解压apk,读取类混淆文件和,资源混淆文件
ManifestAnalyzeTask
  • 负责Manifest.xml解析工作,主要利用apktool.jar里的 AXmlResourceParser类解析
  • 最后以键值对的形式保存到TaskJsonResult里面
CountClassTask
  • 对所有dex文件进行类分析
  • 解析每一个dex文件,以dex文件名为基础,解析dex里的包名,包对应的类的层次去解析
  • 这里注意代码混淆文件的利用,获取真正的类名。
CountRTask
  • 统计所有R类的数量,和R里面的资源数量
  • 解析每一个dex文件,先找到R类(也有可能是资源混淆之后的.R结尾),统计里面的R类字段的数量
DuplicateFileTask
  • 重复文件检测
  • 遍历解压后的文件夹,对每一个文件内容求md5值,md5作为key,文件名集合作为value
  • (感觉可以优化一下,没必要对所有文件内容都求md5,可以先对比收尾等字节的数据.过滤掉一部分)然后再,通过md5求剩下的文件
FindNonAlphaPngTask
  • 找到非透明的png文件
  • 主要遍历png 和.9.png文件
  • bufferedImage.getColorModel().hasAlpha()通过这个函数检测
MethodCountTask
  • 解析外部类对应的方法数
  • 解析内部类对应的方法数
MultiLibCheckTask
  • 多架构so库检测
  • 主要检测lib文件夹下是否存在多个目录
MultiSTLCheckTask
  • 检测so库是否重复引入stl库
  • nm命令对符号表进行过滤检测std::
 /** 
     *  -C, --demangle[=STYLE] 将低级符号名解码(demangle)成用户级名字,比如去除编译时添加的前置下划线,这样可以使得 C++ 函数名具有可读性。不同的编译器符号修饰风格不同,可以使用 =STYLE 参数来选择合适的解码风格
     *  -D, --dynamic:显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义
     *
     * */
    private boolean isStlLinked(File libFile) throws IOException, InterruptedException {
        ProcessBuilder processBuilder = new ProcessBuilder(toolnmPath, "-D", "-C", libFile.getAbsolutePath());
        Process process = processBuilder.start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line = reader.readLine();
        while (line != null) {
            String[] columns = line.split(" ");
            Log.d(TAG, "%s", line);
            if (columns.length >= 3 && columns[1].equals("T") && columns[2].startsWith("std::")) {
                return true;
            }
            line = reader.readLine();
        }
        reader.close();
        process.waitFor();
        return false;
    }

ResProguardCheckTask
  • 检测资源是否混淆
  • 检测是否配置资源混淆目录
  • 检测资源文件名称是否混淆过后的,不是混淆的就没用资源混淆
ShowFileSizeTask
  • 统计解压后的目录各个文件条目的大小
UnCompressedFileTask
  • 根据后缀名统计文件大小
  • 可以配置–suffix参数,指定后缀名
UnstrippedSoCheckTask
  • 检测是否剥离so文件的符号表
  • 通过nm命令查看符号表,是否存在符号表
private boolean isSoStripped(File libFile) throws IOException, InterruptedException {
        ProcessBuilder processBuilder = new ProcessBuilder(toolnmPath, libFile.getAbsolutePath());
        Process process = processBuilder.start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        String line = reader.readLine();
        boolean result = false;
        if (!Util.isNullOrNil(line)) {
            Log.d(TAG, "%s", line);
            String[] columns = line.split(":");
            if (columns.length == 3 && columns[2].trim().equalsIgnoreCase("no symbols")) {
                result = true;
            }
        }
        reader.close();
        process.waitFor();
        return result;
    }
UnusedAssetsTask
  • 查找未使用的asset 文件
  • 先找到所有asset文件存储绝对路径
  • 读取smali代码,查找const-string 声明的字符串,找到asset文件名结尾的assets路径
  • 所有assets文件路径 删除引用的路径 剩下的就是未引用的assets路径
UnusedResourceTask
  • 找到未使用的资源文件
  • 这里思路和UnusedAssetsTask思路一样
  • 就是smali找引用的时候,不仅仅通过const-string声明的变量,还有sget sput,和array-data指令去寻找资源id

github

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值