Page 311 - 《软件学报》2024年第6期
P. 311
徐建 等: LibPass: 基于包结构和签名的第三方库检测方法 2887
的 TPL. 假设 APK 有 m 个包, TPL 本地库的规模 n, 且每个 TPL 最多有 1 个包, 在非混淆情形下, 可以通过包名匹
配确定 APK 中引入的 TPL, 其计算复杂度为 m×n . 当 n 较大时, 产生较高的比较代价, 尤其是在混淆情形下, 无法
借助于包名实现 TPL 检测, 不得不采用更细粒度的对象进行比较, 如以类为比较对象, 这进一步增加了计算复杂
度, 进而影响实用性. 本文旨在确保检测精度的前提下, 提升检测效率, 试图通过缩小 m 和 n 来达成这一目标, 为
此提出的一种基于包结构树和签名的检测方法, 命名为 LibPass, 其框架如图 2 所示, 整个框架由为 3 个关键组件
构成, 包括主模块识别、基于包结构树的候选 TPL 筛选和基于多级签名的检测方法.
LibPass 基于多级签名的检测
基于包结构树的 类签名生成
主模块识别 非主模块 候选 TPL 筛选 候选 TPL
根包解析 包结构树构建和整形 类权重分配
Android 应用 包依赖关系发现 包结构树签名 相似性度量
(*.apk)
Online
的包名、类名被混淆为无意义的短字符, 编译后的类通过扁平化操作移动到其他包中进行重组, 使得主模块与非
Offline
TPL 本地库
图 2 LibPass 的组成框架
主模块识别组件负责解耦待分析的 APK, 将其拆分为主模块和非主模块. 该组件引入根包的概念, 将 APK 的
包以更粗粒度的根包形式加以组织, 建立包依赖关系图, 通过依赖强度分析识别主模块. 假设主模块和非主模块分
别包含 m 1 和 m 2 个包, m 1 +m 2 = m , 通过主模块识别可以过滤掉 m 1 个包, 成对比较计算复杂度降为 m 2 ×n . 对于本
地库中的 TPL, 以离线方式对其预处理, 识别 TPL 的根包.
基于包结构树的候选 TPL 筛选组件负责从庞大的 TPL 本地库中快速搜索可能引入的 TPL. 对待分析 APK 的
根包, 将其表示为一棵包结构树. 为了对抗混淆操作以提升检测精度, 对包结构树进行了整形和重命名, 在此基础
上提出了一种包结构树签名方法. 对于 TPL 的根包, 同样为其生成包结构树、进行整形和重命名, 最后进行签名,
并存储在 TPL 本地库中. 于是, TPL 候选筛选问题转变为 APK 中根包签名与 TPL 中根包签名的哈希值比较问题,
有很多现有算法可以快速实现这一目标. 假设筛选出的 TPL 数目为 n , 有 n ≪ n , 因此, 细粒度比较对象上进行成
′
′
′
对比较的计算复杂度降为 m 2 ×n .
基于多级签名的细粒度检测组件负责为 APK 非主模块中的根包从候选 TPL 中确定引入的唯一一个 TPL. 该
检测过程不再以包作为比较对象, 而是以类为比较对象. 鉴于影响检测精度的主要因素是混淆, 提出了基于类内部
信息和类间依赖关系的类签名机制, 同时以 APK 为参照对象, 考虑加权成对比较方法, 以降低混淆对于检测精度
的影响. 对于给定的 APK, 通过 LibPass 最终输出该 APK 引入的所有 TPL.
2.1 主模块识别
Android 平台采用 Java 作为开发语言, 因此无论是主模块, 还是非主模块, 都是以层次包结构形式组织代码的.
通常, 每个模块都有自己特有的命名空间, 用于区分不同功能意图的模块. 然而, 在混淆情形下, 主模块和非主模块
主模块的边界模糊, 显著增加了 TPL 检测的难度. 因此, 有必要研究主模块识别方法实现主模块与引入的 TPL 的
分离, 从而减少待分析的包, 从而有利于提高检测性能和效率. 本文提出了一种基于元信息和包依赖关系的主模块
识别方法, 其基本思路是对于没有混淆的 APK, 采用基于元信息的方式, 而对于混淆的 APK 采用基于包依赖关系
的方法实现模块解耦. 采用这一思路的事实依据是研究报告 [34] 表明不少于 55% 的 APK 是混淆的. 因此, 针对混淆
和非混淆的情形, 采用不同的解决方法是直截了当的想法.
在非混淆的情形下, 每个 APK 解压后的 AndroidManifest.xml 文件中包含的关于“package”的元信息, 该信息