本系列主要从隐私权限、第三方sdk、个人信息,三个方面展开对APP的隐私问题静态分析,主要探讨在没有成熟工具的情况下,如何使用androguard对一个APK文件进行分析。
本文主要涉及androguard介绍、APK文件基本信息和隐私权限的静态检查
阅读本文需要对python和Android有基础了解
androguard工具介绍
Androguard是一个开源的Android应用程序分析工具,主要用于对APK文件进行静态和动态分析。它提供了多种功能,可以帮助安全研究人员、逆向工程师和开发人员分析Android应用程序的内部结构和行为。
主要功能包括:
- 反编译与分析
- Androguard可以将APK文件反编译成可读的Smali代码,这是Android应用程序的汇编语言表示形式。
- 它还能够将Smali代码转换回Java代码,使得代码审查更加容易。
- 静态分析
- Androguard允许用户检查APK的文件结构、资源、权限请求和Android清单文件中声明的组件。
- 可以分析APK中使用的第三方库和依赖。
- 动态分析
- 尽管Androguard主要用于静态分析,但它也能辅助动态分析。例如,可以提取APK的DEX文件并在Dalvik虚拟机或ART运行时中执行。
- 自动化分析
- 提供了Python API,允许用户编写脚本来自动化和批量处理APK的分析。
APK解析过程:
Androguard通过以下步骤对APK进行解析:
- 加载APK文件
- Androguard会加载APK文件,包含了应用程序的所有资源、代码和配置信息
- Androguard会解析APK中的AndroidManifest.xml文件,这是Android应用程序的配置文件,其中包含了应用程序的组件、权限请求和其他重要信息。
- 提取DEX文件
- Androguard能够提取APK中的DEX(Dalvik Executable)文件,这是Android应用程序的核心代码文件,它包含了应用程序的字节码和相关的数据。
- 对提取的DEX文件进行分析,包括查找和识别类、方法和字段等信息。
- 反编译Smali代码
- Androguard可以将DEX文件中的字节码反编译成Smali代码,这使得逆向工程师可以查看和理解应用程序的工作原理。
- Smali是一种文本格式的汇编语言,用于表示Android应用程序的字节码指令序列。Smali代码可以反编译自APK中的DEX文件,用于分析和理解应用程序的具体实现。
AnalyzeAPK解析APK示例:
AnalyzeAPK函数通常返回三个主要对象,即 a, d, dx。它们分别代表以下内容:
- a 是一个表示整个APK文件的对象。它通常包含了APK的各种元数据信息,比如应用程序的名称、版本号、包名、权限请求、清单文件内容等。在Androguard中,通常可以通过 a.get_app_name()、a.get_package() 等方法来获取这些信息。
- d 是一个表示APK中的Dex文件的对象。Dex文件包含了应用程序的字节码和相关的数据。通过 d.classes,可以访问到Dex文件中反编译后的类信息,包括类的方法、字段等。
- dx检测包体基本信息(隐私合规相关)。
d = DalvikVMFormat(a.get_dex())
dx = VMAnalysis(d)
---------以上由AI辅助生成------
参考材料如下
https://github.com/androguard/androguard
https://androguard.readthedocs.io/en/latest/
检测包体基本信息(隐私合规相关)
#解析包体,解析获得a、d、dx。
androguard analyze XX.apk
#获取包名、名称、版本号、图标
a.get_package()
a.get_app_name()
a.get_androidversion_name()
a.get_app_icon()
#获取sdk版本信息
a.get_min_sdk_version()
a.get_max_sdk_version()
a.get_target_sdk_version() # 版本不得小于28,部分渠道要求不得低于30
#获取应用声明的系统权限,manifest文件的
a.get_permissions()
#获取代码中动态请求的权限,requestpermission()函数
#获取第三方sdk。
#manifest中services name\activity name\provider name\receiver name.
#dex文件中的classes,遍历目录文件名
a.get_services()\get_activities()\get_providers()\get_receivers()
dexss=list(dx.classes.keys())
new_dess = [item[1:].replace("/", ".") for item in dexss]
检测隐私权限
静态检查可以从两方面进行,方法1为主,方法2仅作参考
1. menifest文件中的声明
通过androguard对apk文件进行解析,主要加载APK文件的manifest文件,使用a.get_permissions()获取已声明的文件,并对比最新的隐私权限列表(可以看我之前的文章),找出目标内容
2. requestPermission函数
因为Android6之后危险权限需要动态申请、检查等,通过androguard对apk的dex进行解析,使用dx.get_methods()获取各个方法,并查找是否含有requestPermission以及权限特征“android.permisson.XXX",并输出权限代码和源代码
注:这个方法缺陷明显,External类型无法解析,且可能是第三方引入但应用无法使用,且因混淆等原因可能查不到对应代码。故该方法只能作为方法1的对比检查。(可以进一步优化,使用d来解析methods)
参考https://developer.aliyun.com/article/1129350 参考了此文,但检查结果和APP实际不太一样,这个方法我还没深刻领会
参考https://blog.csdn.net/ybdesire/article/details/52629142
import sys
import os
import androguard
import pandas as pd
from androguard.misc import AnalyzeAPK
from loguru import logger
logger.remove(handler_id=None)
def check_level(apkPerDict):
perLevel_file = r"危险权限表地址"
level_data = pd.read_csv(perLevel_file)
dangerous_list = level_data['permission'].values.tolist()
dangerous_name = level_data['name'].values.tolist()
dangerous_level = level_data['level'].values.tolist()
for apk_per in apkPerDict.keys():
for dangerous in dangerous_list:
i = dangerous_list.index(dangerous)
if apk_per == ('android.permission.') + dangerous:
apkPerDict[apk_per] = str(dangerous_name[i] + dangerous_level[i])
return apkPerDict
def check_permission(apk):
permissions_list = apk.get_permissions()
apk_permissions = dict.fromkeys(permissions_list)
apkperLevels = check_level(apk_permissions)
print("权限列表:")
for key, value in apkperLevels.items():
if value != None:
print(key + '----------------------------' + str(value))
# 只能静态解析apk自己的方法,来自外部库或系统的方法无法静态分析
# 混淆、源码不可用等,均可能影响结果
# 危险权限均需要使用requestpermission进行动态申请
def check_permissionmathod(dx):
ExternalMethod_num = 0
reslut_detail = []
reslut = []
for method in dx.get_methods():
meth = method.get_method()
try:
c = meth.get_code()
code = c.get_bc()
n = code.get_instructions()
for idx, instruction in enumerate(n):
method_c = instruction.get_output()
if "requestPermissions" in method_c:
n = meth.get_class_name() + "-" + meth.get_name() + "-" + str(meth.get_descriptor())
#print("字节码类名方法名参数" + n)
# print("完整指令" + instruction.get_name() + " " + method_c)
try:
s = meth.get_source()
#print("源代码" + s)
pattern = r'android\.permission\.[^\s,;]*'
matches = re.findall(pattern, s)
if matches:
w = str("FIND::" + n + "-" + s)
reslut_detail.append(w)
for match in matches:
match = match.replace('"', '').replace(')', '')
reslut.append(match)
except TypeError as e:
print("Error:", e)
#print("\n")
ExternalMethod_num = ExternalMethod_num + 1
except AttributeError as e:
ExternalMethod_num = ExternalMethod_num + 1
print("外库与系统方法数" + str(ExternalMethod_num))
print("敏感权限信息如下(仅参考):")
reslutA = list(set(reslut))
for e in reslutA:
print(e)
print("源代码")
for a in reslut_detail:
print(a)
if __name__ == '__main__':
file_path = sys.argv[1]
file_path = file_path.replace("\\", "/") + "/"
apks = [f for f in os.listdir(file_path) if f.endswith(".apk")]
print("开始分析")
for apk in apks:
print(f"应用程序名称: {apk}")
apk, dex, dx = AnalyzeAPK(os.path.join(file_path, apk))
# 从manifest文件中发现危险权限
check_permission(apk)
# 从dex文件的方法中发现危险权限的申请行为
check_permissionmathod(dx)
pass
以上代码及涉及的辅助库表,后续均会维护在集中地址,欢迎持续关注本系列