Page 312 - 《软件学报》2024年第6期
P. 312
2888 软件学报 2024 年第 35 卷第 6 期
可以用于识别 APK 的主模块. 在混淆情形下, 虽然 APK 中关于“package”的元信息仍然可以获取, 但是代码中包
名被混淆, 无法直接通过与元信息中的包名进行比较来辨别. 因此, 基于元信息的方式不再适用. 在混淆情形下, 尽
管标识符被混淆、类通过扁平化被重组, 但是包之间依赖关系仍然得以保持, 于是提出了基于包依赖关系的主模
块识别方法, 该方法将 APK 转换为一个包依赖关系图, 节点表示包, 边表示节点之间的依赖关系, 赋予边权重刻画
不同依赖关系的强度, 计算每个包的依赖强度, 将依赖强度最大的包作为主模块. 基于元信息和包依赖关系的主模
块识别过程如算法 1 所示.
算法 1. 主模块识别 (primary module identification, PMI).
输入: apk, 依赖强度阈值 δ d ;
输出: list.
1. boolean obfuscated = isSymbolObfuscated(apk);
2. ARPs arps = getAllRootPackages(apk, obfuscated);
3. if (!obfuscated)
以形参的形式出现在类
4. getPrimaryModulesFromMetaData();
5. else
6. pdg = constructPDG(arps);
7. list=sortModulesbyDependencyStrength(arps);
8. end if
9. return list.
算法 1 第 1 行函数调用 isSymbolObfuscated() 用于判断给定的 APK 是否经过了混淆. 尽管不同的混淆器支持
不同类型的混淆策略, 但是标志符混淆却是任何混淆器都支持的基本配置, 同时也是在绝大部分混淆 APK 中都加
以应用的混淆技术. 标志符混淆后的代码符号与程序员书写的代码符号相比是极其不自然的, 因此, 从符号的不自
然性角度来识别一个 APK 是否经过了混淆是可行的. 鉴于本文的重点是研究 TPL 检测方法, 而不是 APK 混淆检
测方法, 故直接应用基于 NLP 的混淆检测方法 [35] , 该方法使用交叉熵来捕获标识符的不自然性, 具有高准确性.
算法 1 第 2 行的函数调用 getAllRootPackages() 实现将 APK 划分为若干 ARP. 下面先给出不同类型包的定
义, 然后再给出根包划分方法.
定义 1. 应用程序子包 (application subpackage, ASP). 对于任意的包, 如果该包中有类或接口, 则称该包为应用
程序子包, asp = {C 1 ,...,C i ,...,C n1 ,I 1 ,...,I j ,...,I n2 } , 其中 C i 和 I j 分别表示该子包中的类和接口.
借助于子包的概念, 可以将 APK 理解为由若干相互之间存在依赖关系的子包构成的, 这里考虑以下 4 种情形
的依赖关系.
● 继承关系, 是指一个类 (称为子类、子接口) 保留并扩展了另外的一个类 (称为父类、父接口) 的功能.
● 实现关系, 是指一个类实现一个接口的功能, 实现是类与接口之间最常见的关系.
● 类属性关系, 是指一个类 B 的实例以类属性的形式出现在类 A 中, 则 B 是 A 的关联类, 体现了 A 与 B 之间
相对强的语义依赖性.
● 参数引用关系, 是指类 B A 的一个或多个方法声明中, 体现了相对弱的语义依赖性.
在以往的工作 [12] 中认为上述 4 种依赖关系的强度依次为继承、实现、类属性、参数引用, 为它们分配一个
∑ 4
,
依赖强度值 w i 1 ⩽ i ⩽ 4 , 满足 w i = 1 . 不失一般性, 设为 w 1 = 0.4, w 2 = 0.3, w 3 = 0.2, w 4 = 0.1 . 进一步地, 若引
i=1
∑ 5
入无依赖关系的情形, 并将其依赖强度设为 0, 记为 w 5 = 0 , 因此仍有 w i = 1 . 基于上述子包及其依赖关系可
i=1
以构建包依赖关系图来表征 APK, 如定义 2 所示.
定义 2. 包依赖关系图 (package dependency graph, PDG). 包依赖关系图是一个有向图, PDG =< V,E > , 其中 V
s ij
表示应用程序中包的集合, V = {p 1 ,..., p n } E 表示任意两个包之间以依赖关系为边的集合, E = {p i → p j |1 ⩽ i ,
,