androguard是Android恶意软件分析工具,主要用于APK的静态分析。相比于IDA/VTS之类的工具,androguard的扩展性更强,开发者可以将其作为自己项目中的一个模块。本文详细讲解怎么在自己的py代码中调用androguard。
androguard目前最新的2.0版本还不兼容python3。所以下面的过程都在python2.7上进行。python3的用户可以参考这篇文章配置一个virtualenv的pythong2环境。
安装androguard
本文的环境为:Windows 10 + python 2.7.11 + androguard 2.0
安装androguard的步骤为:
- (1) 下载androguard2.0
- (2) 安装androguard2.0
python setup.py install
- (3) 安装其他依赖
python中导入androguard
从androguard源码中可以看出,其androguard/就是一个包(从目录下含有init.py可以看出)。而包是可以在python导入,并使用其方法的。
从androguard的源码androlyze.py中也可以看到,这个包是可以被导入的。
所以我们只要在代码中声明androguard的位置,并将需要的androguard模块导入,即可使用androguard的函数。
import os
import sys
# 根据自己py代码的位置,确定Androguard-2.0的路径
androguard_module_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'Androguard-2.0/androguard' )
if not androguard_module_path in sys.path:
sys.path.append(androguard_module_path)
# 导入核心的三个模块
from androguard.core.bytecodes import apk
from androguard.core.bytecodes import dvm
from androguard.core.analysis import analysis
使用androguard
def get_androguard_obj(apkfile):
a = apk.APK(apkfile, False, "r", None, 2)
#获取APK文件对象
d = dvm.DalvikVMFormat(a.get_dex())
#获取DEX文件对象
x = analysis.VMAnalysis(d)
#获取分析结果对象
return (a,d,x)
sp = '1.apk'
if __name__=='__main__':
ao = get_androguard_obj(sp)
x = ao[2]
pkgs = x.get_tainted_packages()
for pkg in pkgs.get_packages():
print(pkg)
#打印package name
-------------------------------------------------------------------------
Androguard的文档写的很粗略,很多API的用法都是查源码得到的,真是源码里的注释都比文档写的详细。
APK静态分析,主要用Androguard里的androlyze.py。下面从APK级别,到package级别,和class级别,讲解用androlyze.py静态分析APK最常用的方法。
androlyze.py使用方法
使用androlyze.py之前,要先安装配置好Androguard环境(参考这里)。
然后,在终端提示符下,执行androlyze.py -s
就会进入androlyze的Shell交互环境。一般就是在这个交互环境中执行不同的命令,来分析APK文件。
分析之前,首先要初始化三个对象:APK文件对象
,DEX文件对象
,分析结果对象
。
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
APK文件对象
从Androguard源码中可以看出,创建APK文件对象
的类其实就是androguard.core.bytecodes.apk.APK
。这个类用于访问APK文件中的所有元素。从类中的一些方法中也可以看出它的功能:
- 获取manifest文件:get_AndroidManifest()
- 判断APK是否有效:is_valid_APK()
- 获取APK文件名:get_filename()
- 获取APP名:get_app_name()
- 获取package名:get_package()
- 获取android版本名:get_androidversion_code()
- 获取APK中的文件列表:get_files()
- 获取APK中所有activity名称列表:get_activities()
- 获取APK中的主activity名称:get_main_activity()
- 获取APK中所有service名称列表:get_services()
- 获取APK中所有receiver名称列表:get_receivers()
- 获取APK中所有provider名称列表:get_providers()
- 获取APK中签名:get_signature()
除了这些信息,还能获取APK权限相关的信息,看看下面这些函数就知道它的重要性了。
- get_permissions()
- get_requested_permissions()
- get_declared_permissions()
- get_certificate()
如果只想大致看看APK的基本信息,有一个简单的函数就能满足你,show()
。它会将APK内部的文件、权限、Activity相关的信息显示给你。
DEX文件对象
创建该对象的类为androguard.core.bytecodes.dvm.DalvikVMFormat
,源码位于这里。这个类的主要功能是解析APK文件中classes.dex
,并获取其相关信息。同样的,只需要用创建的DEX文件对象的show()
方法,就能显示classes.dex
中的基本信息。
- 获取APK/DEX文件中的所有类:get_classes()
- 获取APK/DEX文件中的所有方法:get_methods()
- 获取APK/DEX文件中的所有成员变量:get_all_fields()
- 获取APK/DEX文件中的所有字符串:get_strings()
分析结果对象
创建该对象的类为androguard.core.analysis.analysis.VMAnalysis
,源码位于这里。VMAnalysis
类的主要功能,就是分析DEX文件对象。比如根据字符串,搜索APK中的package:dx.get_tainted_packages().search_packages('')
。该类其它功能详见源码。
APK级别的分析
APK级别的分析,可以用APK文件对象
提供的方法,来获取manifest文件、APK的签名、权限等相关信息。除了直接用APK文件对象
,还能根据下面的例子,来获取、查找APK中含有的类和package。
获取整个APK中所有的class
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
注意结果中的$
是用于分隔外部类
和内部类
的,按照[外部类]$[内部类]
的格式。
根据string,来search package,并打印search得到的结果
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
res[0]
就是搜索结果中的第一个package。由show_Path()结果可知,搜索到的Landroid/support/v4/widget/SearchViewCompatHoneycomb
(注意这其实是一个class),就是我们需要的结果。
获取整个APK中所有的methods
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
package级别的分析
根据分析结果对象
类的说明,package是可以用get_tainted_packages()
来获取的。但在我本机上实验发现两个问题:
get_tainted_packages()
获取的是class级别的信息,比如:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
但其得到的Ljava/io/FileNotFoundException
是一个class,并不是package。
get_tainted_packages()
获取的是所有tainted package
,并非所有package。关于什么是tainted package
,在stackoverflow, androguard开源github, 看雪论坛都发帖问了这个问题,给Androgurad的作者也发了邮件询问,已经得到回复。
根据我的理解,只要将class信息(比如Ljava/io/FileNotFoundException
)中的class(FileNotFoundException
)去掉,就是package了(Ljava/io
)。所以我用下面的方式来获取APK中的所有package。
根据class,获取所有package。
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
调用某个特殊API的class-method信息
假设要查询这个API(class: ContextCompat, method: buildPath)被APK中哪些类调用
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
可知这个API被三个地方调用:
* 类Landroid/support/v4/content/ContextCompat中的方法getExternalCacheDirs代码中调用过这个API
* 类Landroid/support/v4/content/ContextCompat中的方法getExternalFilesDirs代码中调用过这个API
* 类Landroid/support/v4/content/ContextCompat中的方法getObbDirs代码中调用过这个API
class级别的分析
获得整个类的反编译后的samli代码
- 1
- 2
- 3
- 1
- 2
- 3
获取类的Java代码
- 1
- 2
- 3
- 1
- 2
- 3
获取类的直接方法个数
- 1
- 2
- 3
- 1
- 2
- 3
注意这里方法只包括类自己创建的方法,里面当然是包括了构造函数的,但不包括继承的方法。
获取类的虚拟方法个数
- 1
- 1
获取类的静态变量个数
- 1
- 1
获取类的实例变量个数
- 1
- 1
获取类的(private, public)信息
- 1
- 1
获取类的名称
- 1
- 1
获取父类的名称
- 1
- 1
function级别的分析
获取method对象
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
获取method的访问属性(public static)
- 1
- 2
- 1
- 2
获取method的大小
函数对象在dex文件中占的空间。
- 1
- 2
- 1
- 2
获取method的Java代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
获取method的描述符
- 1
- 2
- 3
- 1
- 2
- 3
对比它的Java代码,就能知道其含义。
获取method的基本信息
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5