共计 2048 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
在 Android 安全领域,恶意软件检测一直是个棘手的问题。传统的静态分析方法,比如简单地检查应用申请的权限,已经越来越力不从心。现在的恶意软件开发者很聪明,他们会申请一大堆正常权限来掩盖自己的真实意图。

- 静态分析的局限性 :单纯看 manifest 文件或者 API 调用列表,很难发现恶意行为模式,因为恶意代码可能隐藏在看似无害的调用序列中。
- 传统方法的不足 :权限检测、字符串匹配这些方法误报率太高,比如一个文件管理器应用和恶意软件可能申请相同权限。
- FCG 的优势 :函数调用图(Function Call Graph,FCG)能完整展现应用内部的执行流程,通过分析调用关系可以识别出异常行为链,比如偷偷上传通讯录的调用路径。
技术实现
FCG 构建核心步骤
- 控制流分析 :首先需要解析 DEX 字节码,识别出每个方法(Method)内的基本块(Basic Block)和跳转关系。
- 跨过程调用处理 :特别注意 invoke-virtual 等指令,这些会触发跨方法的调用。
- 图构建 :把所有方法和它们之间的调用关系用图结构表示出来。
代码示例(Python + AndroGuard)
from androguard.misc import AnalyzeAPK
from networkx import DiGraph
def build_fcg(apk_path):
"""构建函数调用图"""
# 使用 AndroGuard 解析 APK
a, d, dx = AnalyzeAPK(apk_path)
# 创建有向图
fcg = DiGraph()
# 遍历所有方法
for method in dx.get_methods():
method_name = method.get_full_name()
fcg.add_node(method_name)
# 分析方法的字节码
for _, call, _ in method.get_xref_to():
called_method = call.get_full_name()
fcg.add_edge(method_name, called_method)
return fcg
算法优化
- 增量式构建 :对于大型应用,可以分模块构建 FCG 再合并
- 节点去重 :处理多 DEX 文件时,注意避免重复节点
- 性能分析 :构建 FCG 的时间复杂度通常是 O(n^2),需要优化
检测方案
图嵌入技术
现在主要有两种方法把 FCG 转换成机器学习模型能处理的特征:
- Graph2Vec:把整个图变成一个固定长度的向量
- 图神经网络 (GNN):可以保留图的结构信息,效果更好
PyTorch 代码示例
import torch
from torch_geometric.data import Data
def fcg_to_gnn_data(fcg):
"""将 FCG 转换为 GNN 输入格式"""
# 节点特征:这里简单用 API 敏感度作为示例
node_features = []
for node in fcg.nodes():
# 实际应用中这里应该有更复杂的特征设计
sensitive_score = 1 if "getDeviceId" in node else 0
node_features.append([sensitive_score])
# 边索引
edge_index = []
for src, dst in fcg.edges():
src_idx = list(fcg.nodes()).index(src)
dst_idx = list(fcg.nodes()).index(dst)
edge_index.append([src_idx, dst_idx])
return Data(x=torch.tensor(node_features, dtype=torch.float),
edge_index=torch.tensor(edge_index, dtype=torch.long).t().contiguous()
)
生产考量
性能优化
- 使用缓存:对相同库函数构建的 FCG 可以复用
- 并行处理:多 DEX 文件可以并行分析
- 内存管理:调整 JVM 参数,特别是分析大型应用时
误报处理
恶意软件常用的一些混淆手段:
- 反射调用:需要通过数据流分析来识别
- 动态加载:监控可疑的 DexClassLoader 使用
- 代码加密:结合动态分析来验证
避坑指南
在实际项目中,我们遇到过这些问题:
- 动态加载问题 :有些恶意代码运行时才加载,静态分析看不到
-
解决方案:结合动态分析,或者监控常见的动态加载模式
-
反射调用处理 :
- 方案一:保守策略,忽略所有反射调用
- 方案二:尝试解析常见反射模式
-
方案三:结合数据流分析推断可能的调用目标
-
多 DEX 合并 :
- 确保相同方法在不同 DEX 中被识别为同一个节点
- 注意处理跨 DEX 的调用关系
实践建议
对于想尝试这个方向的同学,我建议:
- 从 MalwareBazaar 数据集开始,它包含大量已分类的恶意样本
- 先用小样本测试 FCG 构建流程
- 逐步增加模型复杂度,从简单的图特征开始
- 一定要设置合理的评估指标,不要只看准确率
FCG 分析是个强大的工具,但也需要很多调优。希望这篇文章能帮你少走弯路。在实际应用中,通常需要结合静态和动态分析才能达到最好的效果。
正文完
