frida hook so层方法大全
文章轉(zhuǎn)載,僅供學(xué)習(xí),如有需要請(qǐng)支持原文章創(chuàng)作:https://kevinspider.github.io/fridahookso/
1.感謝
2. frida env
https://github.com/frida/frida-java-bridge/blob/master/lib/env.js
3.IDA 判斷 Thumb 指令集和 Arm 指令集
- IDA - Options - General - number of opcode bytes - 設(shè)置為 4
- 此時(shí)查看 IDA VIew 中 opcode 的長(zhǎng)度, 如果出現(xiàn) 2 個(gè)字節(jié)和 4 個(gè)字節(jié)的, 說(shuō)明為 thumb 指令集
- 如果都是 4 個(gè)字節(jié)的, 說(shuō)明是 arm 指令集;
- 在 Thumb 指令集下, inline hook 的偏移地址需要進(jìn)行 +1 操作;
4.枚舉內(nèi)存中的 so 文件
用于查看目標(biāo) module 是否被正常加載, 使用 Process.enumerateModules() 將當(dāng)前加載的所有 so 文件打印出來(lái)
function hook_native(){var modules = Process.enumerateModules();for (var i in modules){var module = modules[i];console.log(module.name);if (module.name.indexOf("target.so") > -1 ){console.log(module.base);}} }5. 獲取指定 so 文件的基地址
function hook_module() {var baseAddr = Module.findBaseAddress("libnative-lib.so");console.log("baseAddr", baseAddr); }6.獲取指定 so 文件的函數(shù)
6.1通過(guò)導(dǎo)出函數(shù)名定位 native 方法
function hook_func_from_exports(){var add_c_addr = Module.findExportByName("libnative-lib.so", "add_c");console.log("add_c_addr is :",add_c_addr); }6.2通過(guò) symbols 符號(hào)定位 native 方法
function find_func_from_symbols() {var NewStringUTF_addr = null;var symbols = Process.findModuleByName("libart.so").enumerateSymbols();for (var i in symbols) {var symbol = symbols[i];if (symbol.name.indexOf("art") >= 0 &&symbol.name.indexOf("JNI") >= 0 &&symbol.name.indexOf("CheckJNI") < 0){if (symbol.name.indexOf("NewStringUTF") >= 0) {console.log("find target symbols", symbol.name, "address is ", symbol.address);NewStringUTF_addr = symbol.address;}}}console.log("NewStringUTF_addr is ", NewStringUTF_addr);Interceptor.attach(NewStringUTF_addr, {onEnter: function (args) {console.log("args0",args[0])console.log("args0", args[0], hexdump(args[0]));console.log("args1", args[1], hexdump(args[1]));var env = Java.vm.tryGetEnv();if (env != null) {// 直接讀取 c 里面的 charconsole.log("Memory readCstring is :", Memory.readCString(args[1]));}else{console.log("get env error");}},onLeave: function (returnResult) {console.log("result: ", Java.cast(returnResult, Java.use("java.lang.String")));var env = Java.vm.tryGetEnv();if (env != null) {var jstring = env.newStringUtf("修改返回值");returnResult.replace(ptr(jstring));}}}) }6.3通過(guò)地址偏移 inline-hook 任意函數(shù)
function main(){// 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位需要加1console.log(add_addr1);console.log(add_addr2);}// 主動(dòng)調(diào)用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 */7.通過(guò) Intercept 攔截器打印 native 方法參數(shù)和返回值, 并修改返回值
- onEnter: 函數(shù)(args) : 回調(diào)函數(shù), 給定一個(gè)參數(shù) args, 用于讀取或者寫入?yún)?shù)作為 NativePointer 對(duì)象的指針;
- onLeave: 函數(shù)(retval) : 回調(diào)函數(shù)給定一個(gè)參數(shù) retval, 該參數(shù)是包含原始返回值的 NativePointer 派生對(duì)象; 可以調(diào)用 retval.replace(1234) 以整數(shù) 1234 替換返回值, 或者調(diào)用retval.replace(ptr("0x1234")) 以替換為指針;
- 注意: retval 對(duì)象會(huì)在 onLeave 調(diào)用中回收, 因此不要將其存儲(chǔ)在回調(diào)之外使用, 如果需要存儲(chǔ)包含的值, 需要制作深拷貝, 如 ptr(retval.toString())
8.通過(guò) Intercept 攔截器替換原方法
function frida_Interceptor() {Java.perform(function () {//這個(gè)c_getSum方法有兩個(gè)int參數(shù)、返回結(jié)果為兩個(gè)參數(shù)相加//這里用NativeFunction函數(shù)自己定義了一個(gè)c_getSum函數(shù)var add_method = new NativeFunction(Module.findExportByName('libhello.so', 'c_getSum'), 'int',['int','int']);//輸出結(jié)果 那結(jié)果肯定就是 3console.log("result:",add_method(1,2));//這里對(duì)原函數(shù)的功能進(jìn)行替換實(shí)現(xiàn)Interceptor.replace(add_method, new NativeCallback(function (a, b) {//h不論是什么參數(shù)都返回123return 123;}, 'int', ['int', 'int']));//再次調(diào)用 則返回123console.log("result:",add_method(1,2));}); }9.so 層方法注冊(cè)到 js 中, 主動(dòng)調(diào)用
new NativeFunction(address, returnType, argTypes[, options])- address : 函數(shù)地址
- returnType : 指定返回類型
- argTypes : 數(shù)組指定參數(shù)類型
- 類型可選: void, pointer, int, uint, long, ulong, char, uchar, float, double, int8, uint8, int16, int32, uint32, int64, uint64; 參照函數(shù)所需的 type 來(lái)定義即可;
10.hook libart 中的 jni 方法
jni 全部定在在 /system/lib(64)/libart.so 文件中, 通過(guò)枚舉 symbols 篩選出指定的方法
function hook_libart() {var GetStringUTFChars_addr = null;// jni 系統(tǒng)函數(shù)都在 libart.so 中var module_libart = Process.findModuleByName("libart.so");var symbols = module_libart.enumerateSymbols();for (var i = 0; i < symbols.length; i++) {var name = symbols[i].name;if ((name.indexOf("JNI") >= 0) && (name.indexOf("CheckJNI") == -1) && (name.indexOf("art") >= 0)) {if (name.indexOf("GetStringUTFChars") >= 0) {console.log(name);// 獲取到指定 jni 方法地址GetStringUTFChars_addr = symbols[i].address;}}}Java.perform(function(){Interceptor.attach(GetStringUTFChars_addr, {onEnter: function(args){// console.log("args[0] is : ", args[0]);// console.log("args[1] is : ", args[1]);console.log("native args[1] is :",Java.vm.getEnv().getStringUtfChars(args[1],null).readCString());console.log('GetStringUTFChars onEnter called from:\n' +Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n');// console.log("native args[1] is :", Java.cast(args[1], Java.use("java.lang.String")));// console.log("native args[1] is :", Memory.readCString(Java.vm.getEnv().getStringUtfChars(args[1],null)));}, onLeave: function(retval){// retval const char*console.log("GetStringUTFChars onLeave : ", ptr(retval).readCString());}})}) }11.hook libc 中的系統(tǒng)方法
/system/lib(64)/libc.so 導(dǎo)出的符號(hào)沒(méi)有進(jìn)行 namemanline , 直接過(guò)濾篩選即可
// hook libc.so var pthread_create_addr = null;// console.log(JSON.stringify(Process.enumerateModules())); // Process.enumerateModules() 枚舉加載的so文件 var symbols = Process.findModuleByName("libc.so").enumerateSymbols(); for (var i = 0; i < symbols.length; i++){if (symbols[i].name === "pthread_create"){// console.log("symbols name is -> " + symbols[i].name);// console.log("symbols address is -> " + symbols[i].address);pthread_create_addr = symbols[i].address;} }Interceptor.attach(pthread_create_addr,{onEnter: function(args){console.log("args is ->" + args[0], args[1], args[2],args[3]);},onLeave: function(retval){console.log(retval);} });}libc.so 中方法替換
// hook 檢測(cè)frida 的方法 function main() {// var exports = Process.findModuleByName("libnative-lib.so").enumerateExports(); 導(dǎo)出// var imports = Process.findModuleByName("libnative-lib.so").enumerateImports(); 導(dǎo)入// var symbols = Process.findModuleByName("libnative-lib.so").enumerateSymbols(); 符號(hào)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(){// 定義方法 之后主動(dòng)調(diào)用的時(shí)候使用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反調(diào)試啟動(dòng)");} else {result = pthread_create(a0,a1,a2,a3);console.log("正常啟動(dòng)");}return result;}, 'int', ['pointer', 'pointer','pointer','pointer']));}) }12.hook native 調(diào)用棧
Interceptor.attach(f, {onEnter: function (args) {console.log('RegisterNatives called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');} });13.jnitrace
13.1安裝
jnitrace: https://github.com/chame1eon/jnitrace
python` : `pip install jnitrace13.2基礎(chǔ)用法
ndk 開發(fā)是沒(méi)有辦法脫離 [libc.so](http://libc.so) 和 [libart.so](http://libart.so) 進(jìn)行開發(fā), 所以只要降維打擊, 通過(guò) trace 的方式就可以監(jiān)控到 so 層
啟動(dòng)命令
jnitrace [options] -l libname packagename例如: jnitrace -l [libnative-lib.so](http://libnative-lib.so) com.example.myapplication
必要參數(shù)
- -l libname : 指定要trace的.so文件, 可以同時(shí)trace多個(gè).so文件, 直接使用 *來(lái)trace所有的.so文件; 如: -l libnative-lib.so -l libanother-lib.so or -l *
- packagename : 指定要trace的package name
可選參數(shù)
- -m: 指定是spawn還是attach
- -b: 指定是fuzzy還是accurate
- -i : 指定一個(gè)正則表達(dá)式來(lái)過(guò)濾出方法名, 例如: -i Get -i RegisterNatives 就只會(huì)打印出名字里包含Get或者RegisterNatives的JNI methods
- -e 和i相反,同樣通過(guò)正則表達(dá)式來(lái)過(guò)濾,但這次會(huì)將指定的內(nèi)容忽略掉
- -I <string>trace導(dǎo)出的方法,jnitrace認(rèn)為導(dǎo)出的函數(shù)應(yīng)該是從Java端能夠直接調(diào)用的函數(shù),所以可以包括使用RegisterNatives來(lái)注冊(cè)的函數(shù),例如I stringFromJNI -I nativeMethod([B)V,就包括導(dǎo)出名里有stringFromJNI,以及使用RegisterNames來(lái)注冊(cè),并帶有nativeMethod([B)V簽名的函數(shù)。
- -o path/output.json,導(dǎo)出輸出到文件里。
- -p path/to/script.js,用于在加載jnitrace腳本之前將指定路徑的Frida腳本加載到目標(biāo)進(jìn)程中,這可以用于在jnitrace啟動(dòng)之前對(duì)抗反調(diào)試。
- -a path/to/script.js,用于在加載jnitrace腳本之后將指定路徑的Frida腳本加載到目標(biāo)進(jìn)程中
- -ignore-env,不打印所有的JNIEnv函數(shù)
- -ignore-vm,不打印所有的JavaVM函數(shù)
啟動(dòng)方式
默認(rèn)使用 spawn 啟動(dòng), 可以通過(guò) -m attach 設(shè)置通過(guò) attach 啟動(dòng)
jnitrace -m attach -l[libnative-lib.so](http://libnative-lib.so) com.kevin.demoso1設(shè)置回溯器
默認(rèn)情況下使用 accurate的精確模式來(lái)進(jìn)行回溯, 可以通過(guò) -b fuzzy 修改為模糊模式
jnitrace -l [libnative-lib.so](http://libnative-lib.so) -b fuzzy com.kevin.demoso1監(jiān)控指定規(guī)則的方法
用于指定應(yīng)該跟蹤的方法名, 該選項(xiàng)可以多次提供;
jnitrace -l libnative-lib.so -i RegisterNatives com.kevin.demoso1只過(guò)濾出RegisterNatives相關(guān)的內(nèi)容
忽略指定規(guī)則的方法
用于指定在跟蹤中應(yīng)被忽略的方法名, 這個(gè)選項(xiàng)可以被多次提供;
忽略以Find開頭的所有方法;
jnitrace -l libnative-lib.so -e ^Find com.kevin.demoso13.3 jnitace 計(jì)算偏移地址
0x8e4f3b1 是方法 initSN 方法的絕對(duì)地址
0xd8e4e000 是 libmyjni.so 基地址
使用使用 initSN()V的絕對(duì)地址 0xd8e4f3b1 減去 libmyjni.so 的基地址 0xd8e4e000 , 得到偏移 0x13B1
g 進(jìn)行跳轉(zhuǎn)到 0x13B1 即可進(jìn)入方法
14.frida trace
文檔地址: https://frida.re/docs/frida-trace/
options
Usage: frida-trace [options] targetOptions:--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基礎(chǔ)使用
frida-trace [options] packagename啟動(dòng)模式
默認(rèn)使用 attach 模式, 可以指定 -f packageName 使用 spawn 模式啟動(dòng)
frida-trace -U -i strcmp -f com.gdufs.xman文件輸出
frida-trace -U -i "strcmp" -f com.gdufs.xman -o xman.json-o filepath 指定輸出的文件路徑, 方便內(nèi)容過(guò)多時(shí)進(jìn)行查看
trace 任意 function
frida-trace -U -i "strcmp" com.example.demoso1trace 任意 module
frida-trace -U -I "libnative-lib.so" com.example.demoso1根據(jù)地址進(jìn)行 trace
frida-trace -U -a "libnative-lib.so!0x9281" com.example.demoso1批量 trace
源碼地址: https://github.com/Pr0214/trace_natives
ps: 需要切換到 frida14 版本
- 1.將traceNatives.py丟進(jìn)IDA plugins目錄中
在ida 的python console中運(yùn)行如下命令即可找到plugins目錄:os.path.join(idaapi.get_user_idadir(), "plugins")
- 2.IDA中,Edit-Plugins-traceNatives –> IDA輸出窗口就會(huì)顯示如下字眼:使用方法如下: frida-trace -UF -O C:\Users\Lenovo\Desktop\2021\mt\libmtguard.txt
15.frida-hook-libart
下載地址: https://github.com/lasting-yang/frida_hook_libart
hook art
frida -U --no-pause -f package_name -l hook_art.jshook_RegisterNatives
frida -U --no-pause -f package_name -l hook_RegisterNatives.jshook_artmethod
init libext first time
adb push lib/libext64.so /data/local/tmp/libext64.so adb push lib/libext.so /data/local/tmp/libext.so adb shell su -c "cp /data/local/tmp/libext64.so /data/app/libext64.so" adb shell su -c "cp /data/local/tmp/libext.so /data/app/libext.so" adb shell su -c "chown 1000.1000 /data/app/libext*.so" adb shell su -c "chmod 777 /data/app/libext*.so" adb shell su -c "ls -al /data/app/libext*"use hook_artmethod.js
frida -U --no-pause -f package_name -l hook_artmethod.js # or frida -U --no-pause -f package_name -l hook_artmethod.js > hook_artmethod.logfrida-fart-hook
-
首先拷貝fart.so和fart64.so到/data/app目錄下,并使用chmod 777 設(shè)置好權(quán)限,然后就可以使用了。
-
如果目標(biāo) app 沒(méi)有 sdcard 權(quán)限則需要手動(dòng)添加; 或者可以修改 frida_fart_hook.js 中的源碼, 將 savepath 改為 /data/data/應(yīng)用包名/;
-
該frida版fart是使用hook的方式實(shí)現(xiàn)的函數(shù)粒度的脫殼,僅僅是對(duì)類中的所有函數(shù)進(jìn)行了加載,但依然可以解決絕大多數(shù)的抽取保護(hù)
-
需要以spawn方式啟動(dòng)app,等待app進(jìn)入Activity界面后,執(zhí)行fart()函數(shù)即可。如app包名為com.example.test,則frida -U -f com.example.test -l frida_fart_hook.js --no-pause ,然后等待app進(jìn)入主界面,執(zhí)行fart()
-
高級(jí)用法:如果發(fā)現(xiàn)某個(gè)類中的函數(shù)的CodeItem沒(méi)有dump下來(lái),可以調(diào)用dump(classname),傳入要處理的類名,完成對(duì)該類下的所有函數(shù)體的dump,dump下來(lái)的函數(shù)體會(huì)追加到bin文件當(dāng)中。
16.frida 文件寫入(frida/hook libc)
frida api 寫入文件
function writeFile(){var file = new File("/sdcard/reg.dat", "w");file.write("content from frida");file.flush();file.close(); }frida 定義 NativeFunction 寫入文件
function writeFileNative(){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 的系統(tǒng)方法注冊(cè)到 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 層主動(dòng)調(diào)用 libc 的方法// 不能直接將 js 的字符串傳給 libc中的方法, 需要進(jìn)行轉(zhuǎn)換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);// 關(guān)閉文件fclose(file);}17.hook 讀寫 std::string
function readStdString(str){var isTiny = (str.readU8 & 1) === 0;if (isTiny){return str.add(1).readUtf8String();}return str.add(2 * Process.pointerSize).readPointer().readUtf8String(); }function writeStdString(str, content){var isTiny = (str.readU8() & 1) === 0;if (isTiny){str.add(1).writeUtf8String(content);}else{str.add(2 * Process.pointerSize).readPointer().writeUtf8String(content);} }18.hook so 文件加載后馬上 hook
//第一種方式(針對(duì)較老的系統(tǒng)版本) var dlopen = Module.findExportByName(null, "dlopen"); console.log(dlopen); if(dlopen != null){Interceptor.attach(dlopen,{onEnter: function(args){var soName = args[0].readCString();console.log(soName);if(soName.indexOf("libc.so") != -1){this.hook = true;}},onLeave: function(retval){if(this.hook) { dlopentodo();};}}); }//第二種方式(針對(duì)新系統(tǒng)版本) android 8.1 使用該方法 var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); console.log(android_dlopen_ext); if(android_dlopen_ext != null){Interceptor.attach(android_dlopen_ext,{onEnter: function(args){var soName = args[0].readCString();console.log(soName);if(soName.indexOf("libc.so") != -1){this.hook = true;}},onLeave: function(retval){if(this.hook) {dlopentodo();};}}); }function dlopentodo(){//todo ... }19.hook libc kill
function replaceKILL(){var kill_addr = Module.findExportByName("libc.so","kill");Interceptor.replace(kill_addr, new NativeCallback(function(arg0, arg1){console.log("arg0=> ", arg0);console.log("arg1=> ", arg1);console.log('libc.so!kill called from:\n' +Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n');},"int",["int","int"])) }20.hook init_array
//應(yīng)用以32位在64位終端環(huán)境下運(yùn)行 //adb install --abi armeabi-v7a <path to apk>function get_call_function() {var call_function_addr = null;var symbols = Process.getModuleByName("linker").enumerateSymbols();for (var m = 0; m < symbols.length; m++) {if (symbols[m].name == "__dl__ZL13call_functionPKcPFviPPcS2_ES0_") {call_function_addr = symbols[m].address;console.log("found call_function_addr => ", call_function_addr)hook_call_function(call_function_addr)}} }function hook_call_function(_call_function_addr){console.log("hook call function begin!hooking address :=>",_call_function_addr)Interceptor.attach(_call_function_addr,{onEnter:function(args){if(args[2].readCString().indexOf("base.odex")<0){console.log("============================")console.log("function_name =>",args[0].readCString())var soPath = args[2].readCString()console.log("so path : =>",soPath)var soName = soPath.split("/").pop();console.log("function offset =>","0x"+(args[1]-Module.findBaseAddress(soName)).toString(16))console.log("============================")}},onLeave:function(retval){}}) }setImmediate(get_call_function) function hook_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;}else if(name.indexOf("g_ld_debug_verbosity") >=0){addr_g_ld_debug_verbosity = symbols[i].address;ptr(addr_g_ld_debug_verbosity).writeInt(2);} else if(name.indexOf("async_safe_format_log") >=0 && name.indexOf('va_list') < 0){addr_async_safe_format_log = symbols[i].address;} }}if(addr_async_safe_format_log){Interceptor.attach(addr_async_safe_format_log,{onEnter: function(args){this.log_level = args[0];this.tag = ptr(args[1]).readCString()this.fmt = ptr(args[2]).readCString()if(this.fmt.indexOf("c-tor") >= 0 && this.fmt.indexOf('Done') < 0){this.function_type = ptr(args[3]).readCString(), // func_typethis.so_path = ptr(args[5]).readCString();var strs = new Array(); //定義一數(shù)組 strs = this.so_path.split("/"); //字符分割this.so_name = strs.pop();this.func_offset = ptr(args[4]).sub(Module.findBaseAddress(this.so_name)) console.log("func_type:", this.function_type,'\nso_name:',this.so_name,'\nso_path:',this.so_path,'\nfunc_offset:',this.func_offset );}},onLeave: function(retval){}})} } function main() {hook_constructor(); } setImmediate(main);21.frida dump
document: https://github.com/lasting-yang/frida_dump
21.1 frida dump so
function dump_so(so_name) {Java.perform(function () {var currentApplication = Java.use("android.app.ActivityThread").currentApplication();var dir = currentApplication.getApplicationContext().getFilesDir().getPath();var libso = Process.getModuleByName(so_name);console.log("[name]:", libso.name);console.log("[base]:", libso.base);console.log("[size]:", ptr(libso.size));console.log("[path]:", libso.path);var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";var file_handle = new File(file_path, "wb");if (file_handle && file_handle != null) {Memory.protect(ptr(libso.base), libso.size, 'rwx');var libso_buffer = ptr(libso.base).readByteArray(libso.size);file_handle.write(libso_buffer);file_handle.flush();file_handle.close();console.log("[dump]:", file_path);}}); }21.2 frida dump dex
function get_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"; }function mkdir(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);return 0;}mkdir(cPath, 755);chmod(path); }function chmod(path) {var chmodPtr = Module.getExportByName('libc.so', 'chmod');var chmod = new NativeFunction(chmodPtr, 'int', ['pointer', 'int']);var cPath = Memory.allocUtf8String(path);chmod(cPath, 755); }function dump_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;//這個(gè)DefineClass的函數(shù)簽名是Android9的//_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefEif (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);}}}}},onLeave: function(retval) {}});} }var is_hook_libart = false;function hook_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) {dump_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) {dump_dex();is_hook_libart = true;}}}); }setImmediate(dump_dex);21.3 frida dump dex class
function get_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"; }function load_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 () {}})console.log("load_all_class end.");});} } var dex_maps = {};function print_dex_maps() {for (var dex in dex_maps) {console.log(dex, dex_maps[dex]);} }function dump_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);}}}} }function hook_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;//這個(gè)DefineClass的函數(shù)簽名是Android9的//_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefEif (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;function hook_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;}}}); }setImmediate(hook_dex);22.指針運(yùn)算符和讀寫 API
22.1 hook so readPointer()
Java.perform(function () {var libc_addr = Process.findModuleByName("libc.so").base;console.log("libc address is " + libc_addr);// 0x10 轉(zhuǎn)為十進(jìn)制為 16, 讀取console.log(libc_addr.readByteArray(0x10));// readPointer(), 從此內(nèi)存位置讀取 NativePointerconsole.log("pointer size", Process.pointerSize);console.log("readPointer() is " + libc_addr.readPointer());console.log("Memory.readPointer()" + Memory.readPointer(libc_addr.add(Process.pointerSize)));})22.2 hook so writePointer()
Java.perform(function () {var libc_addr = Process.findModuleByName("libc.so").base;console.log("libc_addr : " + libc_addr);// 分配四個(gè)字節(jié)的空間地址const r = Memory.alloc(4);// 將 libc_addr 指針寫入剛申請(qǐng)的 r 中r.writePointer(libc_addr);// 讀取 r 指針的數(shù)據(jù)var buffer = Memory.readByteArray(r, 4);console.log(buffer);})//libc_addr : 0x7da7fdf000// 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF // 00000000 00 f0 fd a7 ....22.3 hook so readS32(), readU32()
從指定內(nèi)存地址讀取有符號(hào)或者無(wú)符號(hào) 8/16/21/etc 或浮點(diǎn)數(shù)/雙精度值, 并將其作為數(shù)字返回;
Java.perform(function () {var libc_addr = Process.findModuleByName("libc.so").base;console.log(hexdump(libc_addr));console.log(libc_addr.readS32(), (libc_addr.readS32()).toString(16));console.log(libc_addr.readU32(), (libc_addr.readU32()).toString(16));})22.4 hook so writeS32(), writeU32()
將有符號(hào)或無(wú)符號(hào)8/16/32/等或浮點(diǎn)數(shù)/雙精度值寫入此內(nèi)存位置
Java.perform(function () {// 開辟四個(gè)字節(jié)的內(nèi)存空間const r = Memory.alloc(4);r.writeS32(0x12345678);console.log(r.readByteArray(0x10));})<!-- 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 00000000 78 56 34 12 7d 00 00 00 98 c0 bb a8 7d 00 00 00 xV4.}.......}...-->22.5 hook so readByteArray(), writeByteArray()
Java.perform(function () {// 定義一個(gè)需要寫入的字節(jié)數(shù)組var arr = [ 0x72, 0x6F, 0x79, 0x73, 0x75, 0x65];//這里申請(qǐng)以arr大小的內(nèi)存空間var r = Memory.alloc(arr.length);// 將 arr 寫入 r 中r.writeByteArray(arr);// Memory.writeByteArray(r, arr); 同樣可以寫入console.log("memory readbyteArray: ")console.log(r.readByteArray(arr.length));console.log(Memory.readByteArray(r, arr.length));})22.6 hook so readCString(), writeUtf8String()
Java.perform(function () {// 開辟內(nèi)存空間 存有字符串var r = Memory.allocUtf8String("你好,世界");// 讀取內(nèi)存中的字符串console.log(hexdump(r));console.log(r.readCString());// 往內(nèi)存中寫入新的字符串r.writeUtf8String("Hello,World");console.log(hexdump(r));console.log(r.readCString())})23.hook 獲取 jni array
// 獲取 jbytesArray 的指針 var arg1Ptr = Java.vm.getEnv().getByteArrayElements(this.arg1, null) // 獲取到指針后可以直接 hexdump 打印 console.log("arg1Ptr",hexdump(arg1Ptr)); // 如果是字符串可以直接轉(zhuǎn) console.log("arg1Ptr",arg1Ptr.readCString());24.ida 動(dòng)態(tài)調(diào)試
1.將 ida 中的 android-server 推入到手機(jī)中
adb push /Applications/IDA\ Pro\ 7.0/ida.app/Contents/MacOS/dbgsrv/android_server /data/local/tmp/asandroid_server 負(fù)責(zé)調(diào)試 32 位的app, android_server64 負(fù)責(zé)調(diào)試 64 位的app, 改名為 as 可以防止部分 android_server 名稱檢測(cè)
2.給 android_server 增加權(quán)限
adb shell su chmod +x /data/local/tmp/as3.進(jìn)行端口轉(zhuǎn)發(fā) adb forward tcp:11678 tcp:11678
4.啟動(dòng) android_server 并指定端口為 11678
5.調(diào)試之前先注入 frida
6.新建一個(gè) ida 界面
7.Debugger - Remote ARMLinux/Android debugger
8.hostname: localhost; Port: 11678; 勾選 Save network settings as default
9.frida 打印出目標(biāo) function 最終的地址, ida 中 g 到目標(biāo) function 地址, 查看是否是
10.thumb 指令集, 如果是 thumb 則 option + g, 將 T 修改為1, 再按 c;
File - Script file - 讀取 ida trace 腳本, 需要更改目標(biāo) so 文件和 function 的起始地址和結(jié)束地址; 讀取之后會(huì)出現(xiàn)斷點(diǎn); 快捷鍵: option F7;
11.frida 主動(dòng)調(diào)用腳本, 檢測(cè)斷點(diǎn)是否觸發(fā)
12.斷點(diǎn)檢測(cè)正常后, ida 中執(zhí)行 starthook()命令, 進(jìn)行 hook 操作; 執(zhí)行suspend_other_thread()掛起其他線程(可選擇)
13.ida 中 Debugger - tracing - tracing options - 設(shè)置 Trace file 路徑 和 取消 Trace over debugger segments 的勾選
14.ida 中 Debugger - tracing - 勾選 instruction tracing
15.frida 主動(dòng)調(diào)用觸發(fā)斷點(diǎn)
16.ida 點(diǎn)擊運(yùn)行按鈕進(jìn)行執(zhí)行, 此時(shí) ida 中黃色部分為已經(jīng)執(zhí)行完的指令, 點(diǎn)擊 Debugger -tracing - tracing window 可以看到當(dāng)前執(zhí)行進(jìn)度;
25.ida 添加自有 python 路徑
修改路徑下的文件 : /Applications/IDA Pro 7.0/ida.app/Contents/MacOS/python/init.py
# 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")26.idapython 腳本調(diào)試
在 pycharm 中開啟調(diào)試
在需要調(diào)試的腳本中添加斷點(diǎn)
在 idapython 的__init__.py文件中添加自有的 python 路徑;
先在 pycharm 中打開調(diào)試監(jiān)聽, 在 ida 中運(yùn)行要調(diào)試的腳本即可;
27.jni_helper
- 進(jìn)入目錄 ~/androidFxxk/idaTools/jni_helper
- java -jar JadxFindJNI/JadxFindJNI.jar <apk.path> <output.json>
- ida 中Script File運(yùn)行 jni_help腳本, 路徑 ~/androidFxxk/idaTools/jni_helper/ida/jni_helper.py
- 導(dǎo)入剛才生成的 output.json 文件即可自動(dòng)識(shí)別
總結(jié)
以上是生活随笔為你收集整理的frida hook so层方法大全的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 聚美app之 _sign参数分析
- 下一篇: 某次元app之data参数分析