Skip to content

Instantly share code, notes, and snippets.

@Areizen
Last active October 28, 2022 17:47
Show Gist options
  • Save Areizen/a1b2b52b44bdb6460dd956cf0bcb439f to your computer and use it in GitHub Desktop.
Save Areizen/a1b2b52b44bdb6460dd956cf0bcb439f to your computer and use it in GitHub Desktop.
import argparse
import json
predefined_struct = {
"UnityEngine_Vector3_o" : [("float","x"),("float","y"),("float","z")]
}
def extract_class_methods(methods_informations, clazz):
"""
We check if the signature start with the class we want to hook, if so
we add the current method to the array
:param methods_informations: list of method objects
:param clazz: class to hook
:return array(methods): return filtered methods
"""
clazz_methods = []
for method in methods_informations:
method_name = method["Name"]
if(method_name.startswith(f'{clazz}$$')):
#print(method_name)
clazz_methods.append(method)
return clazz_methods
def extract_signature(method):
"""
Extract the signature to have an understable way to get them
"""
if( "." not in method["Name"] and "<" not in method["Name"]):
signature = {
"initial" : method["Signature"],
"addr" : method["Address"],
"method_name" : method["Name"],
"return" : "void",
"parameters" : []
}
# extract all type/name associations
parameters = method["Signature"].split('(')[1] .split(')')[-2].split(",")
return_ = method["Signature"].split(" ")[0]
if("*" in return_):
return_ = "pointer"
elif("_t" in return_):
return_ = return_.replace("_t","")
signature["return"] = return_
for parameter in parameters:
parameter = parameter.strip()
if(len(parameter.split(" ")) == 2):
type_, name = parameter.split(" ")
# make type usable by Frida
if("*" in type_):
type_ = "pointer"
name = name + "_ptr"
signature["parameters"].append({"type":type_,"name":name})
elif("_t" in type_):
type_ = type_.replace("_t","")
signature["parameters"].append({"type":type_,"name":name})
elif(type_ in predefined_struct):
for type_,name in predefined_struct[type_]:
signature["parameters"].append({"type":type_,"name":name})
else:
signature["parameters"].append({"type":type_,"name":name})
return signature
def generate_hook_method(signature):
name = signature["method_name"].split("$$")[1]
types_parameter = list(map(lambda x: x["type"], signature["parameters"]))
names = list(map(lambda x: x["name"], signature["parameters"]))
console_logs = ""
for i in range(len(types_parameter)):
console_logs += f'\tconsole.log("\\t - {names[i]} : " + {names[i]} );\n'
#print(types_parameter)
hooks = f"""
// {signature["initial"]}
var {name}_pointer = il2cpp_addr.add({hex(signature["addr"])})
const {name} = new NativeFunction({name}_pointer, "{signature["return"]}",{types_parameter});
Interceptor.replace({name}_pointer,
new NativeCallback(function({",".join(names)}) {{
console.log("[+] {name} : ");
{console_logs}
{name}({",".join(names)});
}},
"{signature["return"]}",{types_parameter})
);
"""
return hooks
def generate_hooks(methods):
base = """
var library_name = "libil2cpp.so";
var library_loaded = 0;
var base_address = 0;
// frida -U -l <this_file> -f <package> --no-pause
function hookFunction(){
var il2cpp_addr = Module.findBaseAddress(library_name);
"""
for method in methods:
signature = extract_signature(method=method)
if(signature):
base += generate_hook_method(signature=signature)
base+="""
}
// First Step : waiting for the application to load the good library
// https://android.googlesource.com/platform/system/core/+/master/libnativeloader/native_loader.cpp#746
//
// OpenNativeLibrary is called when you loadLibrary from Java, it then call android_dlopen_ext
Interceptor.attach(Module.findExportByName(null, 'android_dlopen_ext'),{
onEnter: function(args){
// first arg is the path to the library loaded
var library_path = Memory.readCString(args[0]);
if( library_path.includes(library_name)){
console.log("[...] Loading library : " + library_path);
library_loaded = 1;
}
},
onLeave: function(return_val){
// if it's the library we want to hook, hooking it
if(library_loaded == 1){
console.log("[+] Loaded")
library_loaded = 0;
hookFunction();
}
}
})
"""
print(base)
def main(script, clazz):
# Loading script.json
il2cpp_informations = json.load(open(script,"rb"))
methods_informations = il2cpp_informations["ScriptMethod"]
# Extract the method used by the class we want to hook
clazz_methods = extract_class_methods(methods_informations=methods_informations, clazz=clazz)
hooks = generate_hooks(methods=clazz_methods)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Generate Frida Hooks for a given Unity application")
parser.add_argument("script", help="script.json file")
parser.add_argument("clazz", help="Class to hook functions")
args = parser.parse_args()
main(script=args.script, clazz=args.clazz)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment