functionprettyMethod(method_id, withSignature) { const result = new StdString(); Java.api['art::ArtMethod::PrettyMethod'](result, method_id, withSignature ? 1 : 0); return result.disposeToString(); }
functionreadStdString(str) { if ((str.readU8() & 1) === 1) { // size LSB (=1) indicates if it's a long string return str.add(2 * Process.pointerSize).readPointer().readUtf8String(); } return str.add(1).readUtf8String(); }
functionattach(addr) { Interceptor.attach(addr, { onEnter: function (args) { this.arg0 = args[0]; // this }, onLeave: function (retval) { var modulemap = new ModuleMap() modulemap.update() varmodule = modulemap.find(retval) // var string = Memory.alloc(0x100) // ArtMethod_PrettyMethod(string, this.arg0, 1) if (module != null) { console.log('<' + module.name + '> method_name =>', prettyMethod(this.arg0, 1), ',offset=>', ptr(retval).sub(module.base), ',module_name=>', module.name) } else { console.log('<anonymous> method_name =>', readStdString(string), ', addr =>', ptr(retval)) } } }); }
functionhook_RegisterNative() { var libart = Process.findModuleByName('libart.so') var symbols = libart.enumerateSymbols() for (var i = 0; i < symbols.length; i++) { if (symbols[i].name.indexOf('RegisterNative') > -1 && symbols[i].name.indexOf('ArtMethod') > -1 && symbols[i].name.indexOf('RuntimeCallbacks') < 0) { //art::RuntimeCallbacks::RegisterNativeMethod(art::ArtMethod*, void const*, void**) attach(symbols[i].address) } }
}
functionmain() { hook_RegisterNative() }
setImmediate(main)
枚举内存中的 so 文件
用于查看目标 module 是否被正常加载, 使用 Process.enumerateModules() 将当前加载的所有 so 文件打印出来
1 2 3 4 5 6 7 8 9 10
functionhook_native(){ var modules = Process.enumerateModules(); for (var i in modules){ varmodule = modules[i]; console.log(module.name); if (module.name.indexOf("target.so") > -1 ){ console.log(module.base); } } }
获取指定 so 文件的基地址
1 2 3 4
functionhook_module() { var baseAddr = Module.findBaseAddress("libnative-lib.so"); console.log("baseAddr", baseAddr); }
获取指定 so 文件的函数
通过导出函数名定位 native 方法
1 2 3 4
functionhook_func_from_exports(){ var add_c_addr = Module.findExportByName("libnative-lib.so", "add_c"); console.log("add_c_addr is :",add_c_addr); }
functionmain(){ // get base address of target so; var libnative_lib_addr = Module.findBaseAddress("libnative-lib.so"); console.log("base module addr ->", libnative_lib_addr); if (libnative_lib_addr){ var add_addr1 = Module.findExportByName("libnative-lib.so", "_Z5r0addii"); var add_addr2 = libnative_lib_addr.add(0x94B2 + 1); // 32位需要加1 console.log(add_addr1); console.log(add_addr2); }
// 主动调用 var add1 = new NativeFunction(add_addr1, "int", ["int", "int"]); var add2 = new NativeFunction(add_addr2, "int", ["int", "int"]);
console.log("add1 result is ->" + add1(10, 20)); console.log("add2 result is ->" + add2(10, 20));
}
setImmediate(main);
/* base module addr -> 0xd430b000 0xd43144b3 0xd43144b3 add1 result is ->30 add2 result is ->30 */
functioninvoke_native_func() { var baseAddr = Module.findBaseAddress("libnative-lib.so"); console.log("baseAddr", baseAddr); var offset = 0x0000A28C + 1; var add_c_addr = baseAddr.add(offset); var add_c_func = new NativeFunction(add_c_addr, "int", ["int","int"]); var result = add_c_func(1, 2); console.log(result); } Java.perform(function () { // 获取 so 文件基地址 var base = Module.findBaseAddress("libnative-lib.so"); // 获取目标函数偏移 var sub_834_addr = base.add(0x835) // thumb 需要 +1 // 使用 new NativeFunction 将函数注册到 js var sub_834 = new NativeFunction(sub_834_addr, 'pointer', ['pointer']); // 开辟内存, 创建入参 var arg0 = Memory.alloc(10); ptr(arg0).writeUtf8String("123"); var result = sub_834(arg0); console.log("result is :", hexdump(result)); })
// hook 检测frida 的方法 functionmain() { // var exports = Process.findModuleByName("libnative-lib.so").enumerateExports(); 导出 // var imports = Process.findModuleByName("libnative-lib.so").enumerateImports(); 导入 // var symbols = Process.findModuleByName("libnative-lib.so").enumerateSymbols(); 符号
var pthread_create_addr = null; var symbols = Process.getModuleByName("libc.so").enumerateSymbols(); for (var i = 0; i < symbols.length; i++) { var symbol = symbols[i]; if (symbol.name === "pthread_create") { pthread_create_addr = symbol.address; console.log("pthread_create name is ->", symbol.name); console.log("pthread_create address is ->", pthread_create_addr); } }
Java.perform(function(){ // 定义方法 之后主动调用的时候使用 var pthread_create = new NativeFunction(pthread_create_addr, 'int', ['pointer', 'pointer','pointer','pointer']) Interceptor.replace(pthread_create_addr,new NativeCallback(function (a0, a1, a2, a3) { var result = null; var detect_frida_loop = Module.findExportByName("libnative-lib.so", "_Z17detect_frida_loopPv"); console.log("a0,a1,a2,a3 ->",a0,a1,a2,a3); if (String(a2) === String(detect_frida_loop)) { result = 0; console.log("阻止frida反调试启动"); } else { result = pthread_create(a0,a1,a2,a3); console.log("正常启动"); } return result; }, 'int', ['pointer', 'pointer','pointer','pointer'])); }) }
hook native 调用栈
1 2 3 4 5 6 7
Interceptor.attach(f, { onEnter: function (args) { console.log('RegisterNatives called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('\n') + '\n'); } });
Options: --version show program's version number and exit -h, --help show this help message and exit -D ID, --device=ID connect to device with the given ID -U, --usb connect to USB device -R, --remote connect to remote frida-server -H HOST, --host=HOST connect to remote frida-server on HOST -f FILE, --file=FILE spawn FILE -F, --attach-frontmost attach to frontmost application -n NAME, --attach-name=NAME attach to NAME -p PID, --attach-pid=PID attach to PID --stdio=inherit|pipe stdio behavior when spawning (defaults to “inherit”) --runtime=duk|v8 script runtime to use (defaults to “duk”) --debug enable the Node.js compatible script debugger -I MODULE, --include-module=MODULE include MODULE -X MODULE, --exclude-module=MODULE exclude MODULE -i FUNCTION, --include=FUNCTION include FUNCTION -x FUNCTION, --exclude=FUNCTION exclude FUNCTION -a MODULE!OFFSET, --add=MODULE!OFFSET add MODULE!OFFSET -T, --include-imports include program's imports -t MODULE, --include-module-imports=MODULE include MODULE imports -m OBJC_METHOD, --include-objc-method=OBJC_METHOD include OBJC_METHOD -M OBJC_METHOD, --exclude-objc-method=OBJC_METHOD exclude OBJC_METHOD -s DEBUG_SYMBOL, --include-debug-symbol=DEBUG_SYMBOL include DEBUG_SYMBOL -q, --quiet do not format output messages -d, --decorate Add module name to generated onEnter log statement -o OUTPUT, --output=OUTPUT dump messages to file
functionwriteFileNative(){ var addr_fopen = Module.findExportByName("libc.so", "fopen"); var addr_fputs = Module.findExportByName("libc.so", "fputs"); var addr_fclose = Module.findExportByName("libc.so", "fclose"); // 将 libc 的系统方法注册到 js 层 var fopen = new NativeFunction(addr_fopen, "pointer", ["pointer", "pointer"]); var fputs = new NativeFunction(addr_fputs, "int", ["pointer", "pointer"]); var fclose = new NativeFunction(addr_fclose, "int", ["pointer"]);
// 在 js 层主动调用 libc 的方法 // 不能直接将 js 的字符串传给 libc中的方法, 需要进行转换 var filename = Memory.allocUtf8String("/sdcard/reg.dat"); var open_mode = Memory.allocUtf8String("w"); var file = fopen(filename, open_mode); var buffer = Memory.allocUtf8String("content from frida"); var result = fputs(buffer, file); console.log("fputs ret: ", result);
// 关闭文件 fclose(file); }
hook 读写 std::string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
functionreadStdString(str){ var isTiny = (str.readU8 & 1) === 0; if (isTiny){ return str.add(1).readUtf8String(); } return str.add(2 * Process.pointerSize).readPointer().readUtf8String(); }
functionwriteStdString(str, content){ var isTiny = (str.readU8() & 1) === 0; if (isTiny){ str.add(1).writeUtf8String(content); }else{ str.add(2 * Process.pointerSize).readPointer().writeUtf8String(content); } }
functionhook_constructor() { if (Process.pointerSize == 4) { var linker = Process.findModuleByName("linker"); } else { var linker = Process.findModuleByName("linker64"); }
var addr_call_function =null; var addr_g_ld_debug_verbosity = null; var addr_async_safe_format_log = null; if (linker) { var symbols = linker.enumerateSymbols(); for (var i = 0; i < symbols.length; i++) { var name = symbols[i].name; if (name.indexOf("call_function") >= 0){ addr_call_function = symbols[i].address; } elseif(name.indexOf("g_ld_debug_verbosity") >=0){ addr_g_ld_debug_verbosity = symbols[i].address; ptr(addr_g_ld_debug_verbosity).writeInt(2);
functionget_self_process_name() { var openPtr = Module.getExportByName('libc.so', 'open'); var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
var readPtr = Module.getExportByName("libc.so", "read"); var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]);
var closePtr = Module.getExportByName('libc.so', 'close'); var close = new NativeFunction(closePtr, 'int', ['int']);
var path = Memory.allocUtf8String("/proc/self/cmdline"); var fd = open(path, 0); if (fd != -1) { var buffer = Memory.alloc(0x1000);
var result = read(fd, buffer, 0x1000); close(fd); result = ptr(buffer).readCString(); return result; }
return"-1"; }
functionmkdir(path) { var mkdirPtr = Module.getExportByName('libc.so', 'mkdir'); var mkdir = new NativeFunction(mkdirPtr, 'int', ['pointer', 'int']);
var opendirPtr = Module.getExportByName('libc.so', 'opendir'); var opendir = new NativeFunction(opendirPtr, 'pointer', ['pointer']);
var closedirPtr = Module.getExportByName('libc.so', 'closedir'); var closedir = new NativeFunction(closedirPtr, 'int', ['pointer']);
var cPath = Memory.allocUtf8String(path); var dir = opendir(cPath); if (dir != 0) { closedir(dir); return0; } mkdir(cPath, 755); chmod(path); }
functionchmod(path) { var chmodPtr = Module.getExportByName('libc.so', 'chmod'); var chmod = new NativeFunction(chmodPtr, 'int', ['pointer', 'int']); var cPath = Memory.allocUtf8String(path); chmod(cPath, 755); }
functiondump_dex() { var libart = Process.findModuleByName("libart.so"); var addr_DefineClass = null; var symbols = libart.enumerateSymbols(); for (var index = 0; index < symbols.length; index++) { var symbol = symbols[index]; var symbol_name = symbol.name; //这个DefineClass的函数签名是Android9的 //_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefE if (symbol_name.indexOf("ClassLinker") >= 0 && symbol_name.indexOf("DefineClass") >= 0 && symbol_name.indexOf("Thread") >= 0 && symbol_name.indexOf("DexFile") >= 0) { console.log(symbol_name, symbol.address); addr_DefineClass = symbol.address; } } var dex_maps = {}; var dex_count = 1;
console.log("[DefineClass:]", addr_DefineClass); if (addr_DefineClass) { Interceptor.attach(addr_DefineClass, { onEnter: function(args) { var dex_file = args[5]; //ptr(dex_file).add(Process.pointerSize) is "const uint8_t* const begin_;" //ptr(dex_file).add(Process.pointerSize + Process.pointerSize) is "const size_t size_;" var base = ptr(dex_file).add(Process.pointerSize).readPointer(); var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();
if (dex_maps[base] == undefined) { dex_maps[base] = size; var magic = ptr(base).readCString(); if (magic.indexOf("dex") == 0) {
var process_name = get_self_process_name(); if (process_name != "-1") { var dex_dir_path = "/data/data/" + process_name + "/files/dump_dex_" + process_name; mkdir(dex_dir_path); var dex_path = dex_dir_path + "/class" + (dex_count == 1 ? "" : dex_count) + ".dex"; console.log("[find dex]:", dex_path); var fd = new File(dex_path, "wb"); if (fd && fd != null) { dex_count++; var dex_buffer = ptr(base).readByteArray(size); fd.write(dex_buffer); fd.flush(); fd.close(); console.log("[dump dex]:", dex_path);
functionget_self_process_name() { var openPtr = Module.getExportByName('libc.so', 'open'); var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
var readPtr = Module.getExportByName("libc.so", "read"); var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]);
var closePtr = Module.getExportByName('libc.so', 'close'); var close = new NativeFunction(closePtr, 'int', ['int']);
var path = Memory.allocUtf8String("/proc/self/cmdline"); var fd = open(path, 0); if (fd != -1) { var buffer = Memory.alloc(0x1000);
var result = read(fd, buffer, 0x1000); close(fd); result = ptr(buffer).readCString(); return result; }
return"-1"; }
functionload_all_class() { if (Java.available) { Java.perform(function () {
var DexFileclass = Java.use("dalvik.system.DexFile"); var BaseDexClassLoaderclass = Java.use("dalvik.system.BaseDexClassLoader"); var DexPathListclass = Java.use("dalvik.system.DexPathList");
Java.enumerateClassLoaders({ onMatch: function (loader) { try { var basedexclassloaderobj = Java.cast(loader, BaseDexClassLoaderclass); var pathList = basedexclassloaderobj.pathList.value; var pathListobj = Java.cast(pathList, DexPathListclass) var dexElements = pathListobj.dexElements.value; for (var index in dexElements) { var element = dexElements[index]; try { var dexfile = element.dexFile.value; var dexfileobj = Java.cast(dexfile, DexFileclass); console.log("dexFile:", dexfileobj); const classNames = []; const enumeratorClassNames = dexfileobj.entries(); while (enumeratorClassNames.hasMoreElements()) { var className = enumeratorClassNames.nextElement().toString(); classNames.push(className); try { loader.loadClass(className); } catch (error) { console.log("loadClass error:", error); } } } catch (error) { console.log("dexfile error:", error); } } } catch (error) { console.log("loader error:", error); } }, onComplete: function () {
functionprint_dex_maps() { for (var dex in dex_maps) { console.log(dex, dex_maps[dex]); } }
functiondump_dex() { load_all_class();
for (var base in dex_maps) { var size = dex_maps[base]; console.log(base);
var magic = ptr(base).readCString(); if (magic.indexOf("dex") == 0) { var process_name = get_self_process_name(); if (process_name != "-1") { var dex_path = "/data/data/" + process_name + "/files/" + base.toString(16) + "_" + size.toString(16) + ".dex"; console.log("[find dex]:", dex_path); var fd = new File(dex_path, "wb"); if (fd && fd != null) { var dex_buffer = ptr(base).readByteArray(size); fd.write(dex_buffer); fd.flush(); fd.close(); console.log("[dump dex]:", dex_path);
} } } } }
functionhook_dex() { var libart = Process.findModuleByName("libart.so"); var addr_DefineClass = null; var symbols = libart.enumerateSymbols(); for (var index = 0; index < symbols.length; index++) { var symbol = symbols[index]; var symbol_name = symbol.name; //这个DefineClass的函数签名是Android9的 //_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefE if (symbol_name.indexOf("ClassLinker") >= 0 && symbol_name.indexOf("DefineClass") >= 0 && symbol_name.indexOf("Thread") >= 0 && symbol_name.indexOf("DexFile") >= 0) { console.log(symbol_name, symbol.address); addr_DefineClass = symbol.address; } }
console.log("[DefineClass:]", addr_DefineClass); if (addr_DefineClass) { Interceptor.attach(addr_DefineClass, { onEnter: function (args) { var dex_file = args[5]; //ptr(dex_file).add(Process.pointerSize) is "const uint8_t* const begin_;" //ptr(dex_file).add(Process.pointerSize + Process.pointerSize) is "const size_t size_;" var base = ptr(dex_file).add(Process.pointerSize).readPointer(); var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();
if (dex_maps[base] == undefined) { dex_maps[base] = size; console.log("hook_dex:", base, size); } }, onLeave: function (retval) {} }); }
}
var is_hook_libart = false;
functionhook_dlopen() { Interceptor.attach(Module.findExportByName(null, "dlopen"), { onEnter: function (args) { var pathptr = args[0]; if (pathptr !== undefined && pathptr != null) { var path = ptr(pathptr).readCString(); //console.log("dlopen:", path); if (path.indexOf("libart.so") >= 0) { this.can_hook_libart = true; console.log("[dlopen:]", path); } } }, onLeave: function (retval) { if (this.can_hook_libart && !is_hook_libart) { hook_dex(); is_hook_libart = true; } } })
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), { onEnter: function (args) { var pathptr = args[0]; if (pathptr !== undefined && pathptr != null) { var path = ptr(pathptr).readCString(); //console.log("android_dlopen_ext:", path); if (path.indexOf("libart.so") >= 0) { this.can_hook_libart = true; console.log("[android_dlopen_ext:]", path); } } }, onLeave: function (retval) { if (this.can_hook_libart && !is_hook_libart) { hook_dex(); is_hook_libart = true; } } }); }
hostname: localhost; Port: 11678; 勾选 Save network settings as default
frida 打印出目标 function 最终的地址, ida 中 g 到目标 function 地址, 查看是否是 thumb 指令集, 如果是 thumb 则 option + g, 将 T 修改为1, 再按 c;
File - Script file - 读取 ida trace 脚本, 需要更改目标 so 文件和 function 的起始地址和结束地址; 读取之后会出现断点; 快捷键: option F7;
frida 主动调用脚本, 检测断点是否触发
断点检测正常后, ida 中执行 starthook()命令, 进行 hook 操作; 执行suspend_other_thread()挂起其他线程(可选择)
ida 中 Debugger - tracing - tracing options - 设置 Trace file 路径 和 取消 Trace over debugger segments 的勾选
ida 中 Debugger - tracing - 勾选 instruction tracing
frida 主动调用触发断点
ida 点击运行按钮进行执行, 此时 ida 中黄色部分为已经执行完的指令, 点击 Debugger -tracing - tracing window 可以看到当前执行进度;
ida 添加自有 python 路径
修改路径下的文件 : /Applications/IDA Pro 7.0/ida.app/Contents/MacOS/python/init.py
1 2 3 4 5 6 7 8
# Prepare sys.path so loading of the shared objects works lib_dynload = os.path.join( sys.executable, IDAPYTHON_DYNLOAD_BASE, "python", "lib", "python2.7", "lib-dynload")
# added by kevin sys.path.insert(0, "/Users/zhangyang/anaconda3/envs/py2/lib/python2.7/site-packages")