-
Notifications
You must be signed in to change notification settings - Fork 528
GDA Python Scripts
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.
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.
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.
# 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
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 |
- 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?
- 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 ofDexClass
object through thegda_obj
object, then get DEX header, and then call theDumpData()
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.
- 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.