Skip to content

GDA Python Scripts

charles2gan edited this page Nov 17, 2020 · 1 revision

GDA Python scripts

I. Briefly

In order to provide more flexible analysis and DATA exchange, GDA has provided Python scripting support since version 3.6. All the information about classes and methods in the DEX file is provided by lists and dictionaries of Python, in which classes and methods are organized in lists and dictionaries to improve the access speed of classes and methods. Of course, you have to load an APK/DEX/ODEX/oat/GDA DB file before using the script.

GDA will search the python2.7-3.7 installing directory. if not find, the message box will be popped and prompt you to choose the path where python*.dll file is.

II. Entrance Specification

All GDA scripts must have the GDA_MAIN function as entry function, and you don't need to import any GDA files in your python scripts. But, you can import GdaImport which is not necessary, just for debugging. By default, the GDA scripting engine has handled the details of the import operation for you.

Example:

def GDA_MAIN ( gda_obj):
    per='the apk permission:\n'
    #  per+=gda_obj.GetAppString()
    #  per+=gda_obj.GetCert()
    #  per+=gda_obj.GetUrlString()
    #  
    per+=gda_obj.GetPermission()
    gda_obj.log(per)
    tofile = open('out.txt','w')
    tofile.write(per)
    tofile.close()
    return 0

The script engine saves the information of all the classes and methods in the DEX to the only one parameter gda_obj of the entry function GDA_MAIN. Of course, you can change the parameter name. With this parameter, you are allowed to access them, as well as the interfaces GDA provide. Gda_obj is actually an instantiated object of the class GDAInterface defined by GDA. GDAInterface includes the Interface function and the object of the class GdaDex which stores the DEX header、classes and methods in APP.

III. Data Interfaces provided by GdaDex class

The member named DexList in the GDAInterface is a list of GdaDex objects for supporting MultiDex. If there is only one DEX file in your APK, then you only need to access the first item in the list. Every item is an instantiated object of the GdaDex. The corresponding relation of the DEX files and in DexList:

file name items of List
Classes.dex DexList[0]
Classes2.dex DexList[1]
Classes3.dex DexList[2]
Classes4.dex DexList[3]
... ...

Each entity of DexList (GdaDex class) contains three parts of information: Dex header information, classes, and methods. Classes and methods are stored in lists and dictionaries.

class GdaDex:
    def __init__(self):
        self.DexHeader = 0          #Class DexHeader instance 
        self.MethodTable = {}       #Methods Hash Table (dictionary<idx,MethodInfo>)
        self.ClassTable = {}        #Classes Hash Table (dictionary<idx,ClassInfo>)
        self.ClassList = []
        self.MethodList = []

You can get all the data of the Dex file based on the entity. See the GdaImport.py file for details.

GdaImport.py

# android-method infomation Class  
class MethodInfo:
    def __init__(self):
        idx  = 0                    #index to a method_id_item
        methodName = ""             #method name like OnCreate
        modifiedMethodName = ""     #the modified method name like OnCreate_
        methodFullName = ""         #method full name like com.gda.MyActivity.OnCreate
        methodPackage = ""          #package eg:com.gda.MyActivity
        MethodSignature = ""        #method signature eg:(IILandroid/sys/network;)I
        offset  = 0                 #the method binary code offset which is equal to codeOff of DexMehtod
        size = 0                    #the size of DexCode(the members and binary instruction data)
        regSize  = 0                #the register size
        permission = ""             #method execute permisson
        methodSmaliCode = ""        #smail code text
        methodJavaCode = ""         #java code text
        callorIdxList = []          #the callor list of the method
        refMethodIdxList = []       #the callee list of the method
        refStringIdxList  = []      #the string list that is used of method

v. The interface function provided by GDAInterface

In order to access more detailed information, GDA provides the following interface functions:

API Description
GetAppString (dexId = 0) returns a string, to retrieve all referenced strings
Log(str) returns the print length, and prints the string to the GDA window
GetPermission() returns a string, to obtain permission information
GetCert() returns a string, to obtain signature information
GetSupicous(dexId=0) returns a string, to obtain suspicious behavior information
GetStringById(idx, dexId=0) returns a string, and retrieves the string through the string index
GetMethodNameById(idx, dexId=0) returns the string, and gets the method name through the index of the method
SetClassName(idx, name, dexId=0) returns boolean value, modifies the class name, and modifies it to the UI interface
SetMethodName (idx, name, dexId=0) returns boolean value, modifies the method name, and modifies it to the UI interface
GetClassCodeById (idx, dexId=0) returns the string, to get the class code (including only method and member variable declarations)
GetSmaliCodeById(idx, dexId=0) returns the string, to get the SmalI code of the method
GetJavaCodeById(idx, dexId=0) returns the string, to get the Java code for the method
GetUrlString(dexId=0) returns the string, to get the URL string in the APK
FindClassId(descriptor, dexId=0) returns int (index), and gets the index of the class through the class descriptor
GetMethodDeclare(idx, dexId=0) returns the string, to get the declaration of the method
DumpBin(fileName, offset, size, dexId=0) returns a boolean value, according to offset, and size, dump binary data into the file filename
DumpData(offset, size, width=16, dexId=0) returns a string, to dump data in hexadecimal form
WriteBinaryToDex(offset, bytes, buffLen, dexId=0) returns a boolean value, writes data of specified length to the specified offset of the current DEX file

V. Case Analysis

  1. The simplest example - extracting information Here's a very simple script for getting information
def GDA_MAIN ( gda_obj):
    per='the apk permission:\n'
    #  per+=gda_obj.GetAppString()
    #  per+=gda_obj.GetCert()
    #  per+=gda_obj.GetUrlString()
    #  
    per+=gda_obj.GetPermission()
    gda_obj.log(per)
    tofile = open('out.txt','w')
    tofile.write(per)
    tofile.close()
    return 0

Through the gda_obj object, you can call the function 'gda_obj.GetPermission()' to obtain the permission information in the AndroidMainfest.xml file. If succeeds,the permission would be printed on the RichEditor of GUI through the function gda_obj.log (), and then it would be written to the out.txt file of the current directory. The result is as follows: Is it very simple?

  1. Case 2: Dump binary data Next, the second example is a sample for dumping data. I want to dump any data according to specify the offset and the size of memory, how to do it, such as getting the stringIds table. You can find the reference of DexClass object through the gda_obj object, then get DEX header, and then call the DumpData() according to the stringIdsOff of the Dex header.
def GDA_MAIN ( gda_obj):
    gda =gda_obj
    dex0=gda.DexList[0]
    head=dex0.DexHeader
    out="the string Ids off:\n\n"
    #dump hex data
    out+=gda.DumpData(head.stringIdsOff,128,16)
    gda.log(out)
    #dump bin to file
    # gda.DumpBin("bin.data",head.stringIdsOff,128)
    #modify the dex file
    #bytes = b'\xe4\xba\xba'
    #gda.WriteBinaryToDex(0,bytes,len(bytes))

Of course, you can also call the 'DumpBin()` to dump the data to the file. You can also call WriteBinaryToDex() to write data back into the Dex file by the specified offset address. The output to GDA is shown as follows:

Other cases: 3. Case 3: Find the callees of a method 4. Case 4: Finding the callers of a method

Case 3 and 4 cases can be used to print Call graph.

  1. Case 5: Printing Decompiled code of classes and methods

Case 6: Deobfuscation script The script can recover the obfuscated APK by class file name. In GDA, it only takes a few lines of code to work.

def GDA_MAIN ( gda_obj):
    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)

Effection of Deobfuscation:

There is a problem with the class file name. Sometimes a class file contains multiple classes, especially internal classes. At this time, there are multiple classes renamed the same name. but you can make some library signatures to accurately identify method names and class names. Make library signature: Select a right-click menu of package name to make a signature of all methods under the package name.

If the same library is suspected to be used in your next analysis, you can click "Load Flirt Sig" in the File menu item to load the signature file to run and rename the method and class. As shown in the following Fig:

Ok, thank you for reading, if you have some bug to report, please send it to me.