GDA python 脚本自动化分析说明

一、简述

为了能够更加灵活的提供分析和信息交互,GDA从3.6版本开始提供了对python脚本的支持。GDA为分析人员提供了整个APK文件中的类和方法的相关信息,其中分别以列表和字典的方式来组织类和方法,提高类和方法的访问速度。当然使用脚本之前你必须打开了某个apk/dex/odex/oat/gda文件。


二、入口规范

GDA的所有脚本必须以GDA_MAIN函数为入口,并且你不需要导入任何GDA文件,为了方便调试,你可以导入GdaImport,GDA脚本引擎对此并不是必须的。GDA脚本引擎默认已经帮你处理了导入操作的很多细节。

Example:



脚本引擎会对apk中所有的类和方法进行分析,并且将这些分析好的数据传递给入口函数GDA_MAIN。GDA_MAIN只存在一个参数gda_obj,当然你可以修改该参数名称。通过该参数你可以访问这些类和方法,以及他所提供的接口。gda_obj实际上为类GDAInterface的实例化对象。该类提供了所有数据和API以供编程使用。

三、类GDAInterface提供的接口数据

为了让用户能够通过脚本的方式访问到所有的类和方法,我将GDA所分析得到的基本数据通过列表和字典来组织。其中该数据接口提供在gda_obj对象的DexList成员中,

该成员也是一个列表,因为部分APK中会存在多个dex文件,因此我用一个列表来存储之,如果只有一个dex文件,那么你只需要访问其第一个实体即可。

DexList的每个实体(GdaDex类)包含了三部分信息:Dex 头信息、类、方法。其中类和方法均以列表和字典的方式存储。


你可以根据该实体获取到该Dex文件的所有解析结果,详细可以参见GdaImport.py文件。


四、GDAInterface提供的接口函数:

为了能够访问到更加详细的信息,GDA提供了如下接口函数:

API

功能描述

GetAppString(dexId=0)

返回字符串,获取所有被引用过的字符串

log(str)

返回打印长度,打印字符串到gda窗口

GetPermission()

返回字符串,获取permission信息

GetCert()

返回字符串,获取签名信息

GetSupicous(dexId=0)

返回字符串,获取可疑行为信息

GetStringById(idx,dexId=0)

返回字符串,通过字符串index获取字符串

GetMethodNameById(idx,dexId=0)

返回字符串,通过方法的index获取方法名

SetClassName(idx,name,dexId=0)

返回bool,修改类名,同时会修改到UI界面上

SetMethodName(idx,name,dexId=0)

返回bool,修改方法名,同时会修改到UI界面上

GetClassCodeById(idx,dexId=0)

返回字符串,获取类代码(只包含方法和成员变量声明)

GetSmaliCodeById(idx,dexId=0)

返回字符串,获取方法的smali代码

GetJavaCodeById(idx,dexId=0)

返回字符串,获取方法的java代码

GetUrlString(dexId=0)

返回字符串,获取APK中的url字符串

FindClassId(descriptor,dexId=0)

返回int(index),通过类描述器,得到类的index

GetMethodDeclare(idx,dexId=0)

返回字符串,获取方法的声明

DumpBin(fileName,offset,size,dexId=0)

返回bool,根据偏移offset,及大小size, dump二进制数据到文件filename中

DumpData(offset,size,width=16,dexId=0)

返回字符串,以十六进制方式dump数据

WriteBinaryToDex(offset,bytes,buffLen,dexId=0)

返回bool,向当前dex文件的指定偏移出写入指定长度的数据

五、案例分析

1、最简单的样例-提取信息

下面是一个单纯用于获取信息的非常简单的脚本


通过gda_obj对象,调用gda_obj.GetPermission()函数用于当前apk所声明的权限信息,并该信息通过gda_obj.log()函数将其打印到GDA的界面上,然后再将权限信息写入到当前目录的out.txt文件中。效果如图:


是不是非常简单。

2、案例二:dump二进制数据

接下来我提供的第二例子是用于dump数据的样例。我想根据一个偏移地址dump指定大小的内存数据,该如何操作,比如获取字符串偏移表。就可以通过gda_obj对象找到dex,再根据dex得到dex header,接下来根据头部的stringIdsOff利用DumpData API就可以进行dump了。



当然你也可以利用DumpBin函数将数据直接dump到文件。你也可以利用WriteBinaryToDex向Dex文件的指定偏移写入数据。输出到GDA上显示如下:

其他案例:

3、案例三:找到一个方法所调用的方法


4、案例四:找到一个方法的调用者方法


这3和4两个案例可以用于打印向上和向下的调用图。

5、案例五:打印类和方法的反编译代码



6、案例6:反混淆脚本

该脚本利用某些程序员在配置混淆器时,为了方便调试而未对类文件名进行混淆,使得利用类文件名可以在一定程度上进行反混淆操作。利用GDA做为分析工具,可以使用短短的几行代码就可实现。

gda=gda_obj

Dex0=gda.DexList[0]

for classi in Dex0.ClassList:

sourceFileName=gda.GetStringById(classi.sourceFileIdx)

newName=sourceFileName[:-5]

oldName=classi.className

if len(oldName)<=2 and not(newName==oldName):

gda.SetClassName(classi.idx,newName)

反混淆前后对比:


这种方法有个问题,有时候一个类文件会包含多个类,尤其是内部类,这个时候通过这种方法就会反混淆出多个同名的类。如果需要更为精确的反混淆,可以利用GDA制作库签名来精确识别出方法名和类名。制作库签名:选中一个包名右键菜单可以制作一个该包名下所有方法的签名。


下次分析时如果怀疑使用了同样的库,可以在File菜单下点击”Load Flirt Sig”加载签名文件运行,还原方法名和类名。如图:


发布于 2018-12-11