枚举各种
let imports = Process.findModuleByName("libxiaojianbang.so").enumerateImports(); for(let i =0; i<imports.length; i++){ let _import = imports[i]; if(_import.name.indexOf("pthread_create") !== -1){ console.log(JSON.stringify(_import)); } }
let exports = Process.findModuleByName("libxiaojianbang.so").enumerateExports(); for(let i =0; i<exports.length; i++){ let _export = exports[i]; if(_export.name.indexOf("Java_com_xiaojianbang_ndk_NativeHelper_add") !== -1){ console.log(JSON.stringify(_export)); } }
let symbols = Process.findModuleByName("libart.so").enumerateSymbols(); for(let i=0; i<symbols.length; i++){ let _symbol = symbols[i]; if(_symbol.name.indexOf("NewStringUTF") !== -1){ console.log(JSON.stringify(_symbol)); } }
let modules = Process.enumerateModules(); for(let i =0; i<modules.length; i++){ console.log(JSON.stringify(modules[i])); }
|
hook导出函数
let add = Process.findModuleByName("libxiaojianbang.so").findExportByName("Java_com_xiaojianbang_ndk_NativeHelper_add"); Interceptor.attach(add,{ onEnter: function(args){ console.log(args[0]); console.log(args[1]); }, onLeave: function(retval){ console.log("retval: ", retval.toInt32()); } });
|
hook任意函数
在导入表、导出表、符号表里找不到的函数,地址需要自己计算
so基址+函数在so中的偏移[+1]
获取so基址,也就是模块基址
Process.findModuleByName("libart.so"); Process.getModuleByName("libc.so"); Process.enumerateModules(); Process.findModuleByAddress(address); Process.getModuleByAddress(address); Module.findBaseAddress("libart.so");
|
let address = Module.findBaseAddress("libxiaojianbang.so"); let func = address.add(0x1520); console.log("func: ", func); Interceptor.attach(func,{ onEnter:function(args){ console.log(args[2]); console.log(args[3]); console.log(args[4]); }, onLeave:function(retval){ console.log(retval.toInt32()); } });
|
ARM64函数地址的计算
so基址+函数在so中的偏移
ARM32函数地址的计算
指定应用安装成32位
adb install --abi armeabi-v7a C:\Users\17704\Desktop\HookDemo.apk
|
存在Thumb指令和ARM指令,地址为2字节或者4字节对齐
处理器在执行命令时,会将地址的最低位,设置到CPSR寄存器的T位上,因此Thumb2指令的地址通常需要+1
总结:
如果是Thumb指令,函数地址:so基址+函数在so中的偏移+1
如果是ARM指令,函数地址:so基址+函数在so中的偏移
hook需要+1,读内存不需要加
let address = Module.findBaseAddress("libencryptlib.so"); let func = address.add(0x1FA38); Interceptor.attach(func, { onEnter: function (args) { console.log('args[0]: ', hexdump(args[0])); console.log('args[1]: ', args[1].readCString()); console.log('args[2]: ', args[2].toInt32()); this.args3 = args[3]; }, onLeave: function (retval) { console.log('this.args3: ', hexdump(this.args3)); } });
|
修改数值参数和返回值
var soAddr = Module.findBaseAddress("libxiaojianbang.so"); console.log("soAddr", soAddr); var add = soAddr.add(0x1520); Interceptor.attach(add, { onEnter: function (args) { args[2] = ptr(1000); console.log(args[2].toInt32()); console.log(args[3]); console.log(args[4]); }, onLeave: function (retval) { retval.replace(1000); console.log(retval.toInt32()); } });
|
读写内存
var base = Module.findBaseAddress("libxiaojianbang.so"); console.log("soAddr: ", base); console.log(hexdump(base.add(0x9A0))); console.log(base.add(0x9A0).readCString()); console.log(base.add(0x9A0).readByteArray(10)); console.log(Memory.readByteArray(base.add(0x9A0), 16)); console.log(hexdump(base.add(0x9A0),{offset:0, length: 16, header: false, ansi: true})); base.add(0x9A0).writeByteArray();
function stringToBytes(str){ return hexToBytes(stringToHex(str)); }
function stringToHex(str) { return str.split("").map(function(c) { return ("0" + c.charCodeAt(0).toString(16)).slice(-2); }).join(""); }
function hexToBytes(hex) { for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16)); return bytes; }
function hexToString(hexStr) { var hex = hexStr.toString(); var str = ''; for (var i = 0; i < hex.length; i += 2) str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); return str; } Memory.protect(base.add(0x9A0), 0x1000, 'rwx'); base.add(0x9A0).writeByteArray([0x00, 0x20, 0x00, 0x00]); base.add(0x9A0).writeInt(0x1000); base.add(0x9A0).writeByteArray(stringToBytes('ruyi\0')); let nativePointer = Memory.alloc(13); nativePointer.writeUtf8String('xiaojianbang'); console.log(nativePointer.readByteArray(13)); let nativePointer = Memory.allocUtf8String('xiaojianbang'); console.log(nativePointer.readByteArray(13));
|
hook_dlopen
function hook_dlopen(addr, soName, callback) { Interceptor.attach(addr, { onEnter: function(args){ var name = args[0].readCString(); if(name.indexOf(soName) !== -1) this.hook = true; }, onLeave: function(retval){ if(this.hook) callback(); } }); } var dlopen = Module.findExportByName(null, "dlopen"); var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); hook_dlopen(dlopen, "libxiaojianbang.so", hookfunc); hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", hookfunc);
function hookfunc() { let func; let imports = Process.findModuleByName("libxiaojianbang.so").enumerateImports(); for(let i = 0; i < imports.length; i++){ if(imports[i].name.indexOf("_Z7bssFuncv") !== -1){ func = imports[i].address; break; } } if (func !== null) { Interceptor.attach(func,{ onEnter: function(args){ console.log("_Z7bssFuncv is called"); }, onLeave: function(retval){
} });
} }
|
so层主动调用任意函数
var soAddr = Module.findBaseAddress("libxiaojianbang.so"); var funcAddr = soAddr.add(0x135C); var jstr2cstr = new NativeFunction(funcAddr, 'pointer', ['pointer', 'pointer']); var env = Java.vm.tryGetEnv(); console.log("env: ", JSON.stringif(env)); var jstring = env.newStringUtf("hyq"); var retval = jstr2cstr(env, jstring); console.log("retval: ", retval.readCString());
|
hooklibc读写文件
var ios = new File("/sdcard/hyq.txt","w"); ios.write("6666666"); ios.flush(); ios.close();
var addr_fopen = Module.findExportByName("libc.so", "fopen"); var addr_fputs = Module.findExportByName("libc.so", "fputs"); var addr_fclose = Module.findExportByName("libc.so", "fclose"); console.log("addr_fopen: ", addr_fopen, "addr_fputs: ", addr_fputs, "addr_fclose: ", addr_fclose);
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"]);
var filename = Memory.allocUtf8String("/data/data/com.xiaojianbang.app/hyq.txt"); var open_mode = Memory.allocUtf8String("w"); var file = fopen(filename, open_mode); console.log("fopen: ", file);
var buffer = Memory.allocUtf8String("666\n"); var retval = fputs(buffer, file); console.log("fputs: ", retval); fclose(file);
|
反编译dex
java -jar baksmali-2.5.2.jar d clesses.dex
回编译dex
java -jar smali-2.5.2.jar a smali(smali要改成目录名字)突然忘记怎么用了
替换函数
function hook_dlopen(addr, soName, callback) { Interceptor.attach(addr, { onEnter: function(args){ var name = args[0].readCString(); if(name.indexOf(soName) !== -1) this.hook = true; }, onLeave: function(retval){ if(this.hook) callback(); } }); } var dlopen = Module.findExportByName(null, "dlopen"); var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); hook_dlopen(dlopen, "libxiaojianbang.so", hookfunc); hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", hookfunc);
function hookfunc() { let func; let imports = Process.findModuleByName("libxiaojianbang.so").enumerateImports(); for(let i = 0; i < imports.length; i++){ if(imports[i].name.indexOf("_Z7bssFuncv") !== -1){ func = imports[i].address; break; } } if (func !== null) { Interceptor.replace(func,new NativeCallback(function () { console.log('_Z7bssFuncv is replaced!'); }, 'void', ['void'])); } }
|
hook_pthread_create
检测函数需要实时运行,那么可能用pthread_create开启线程
hook pthread_create来查看开启了哪些子线程,把和检测相关的子线程干掉
let pthread_create_addr = Module.findExportByName("libc.so", "pthread_create"); Interceptor.attach(pthread_create_addr, { onEnter: function (args) { let soName = Process.findModuleByAddress(args[2]).name; if (soName.indexOf('libxiaojianbang.so') !== -1) { Interceptor.replace(args[2], new NativeCallback(function () { console.log(' is replaced!'); }, 'void', ['void'])); } } });
|
构造二级指针
function hook_dlopen(addr, soName, callback) { Interceptor.attach(addr, { onEnter: function(args){ var name = args[0].readCString(); if(name.indexOf(soName) !== -1) this.hook = true; }, onLeave: function(retval){ if(this.hook) callback(); } }); } var dlopen = Module.findExportByName(null, "dlopen"); var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext"); hook_dlopen(dlopen, "libxiaojianbang.so", hookfunc); hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", hookfunc);
function hookfunc() { let xiugaiStr = Module.findExportByName("libxiaojianbang.so", "_Z9xiugaiStrPPc"); console.log((xiugaiStr)); let func = new NativeFunction(xiugaiStr, "pointer", ['pointer']);
let strAddr = Memory.allocUtf8String("xiaojianbang"); console.log(hexdump(strAddr)); let params = Memory.alloc(8); params.writePointer(strAddr); console.log("======================================="); console.log(hexdump(params)); console.log("======================================="); let retval = func(params); console.log(hexdump(retval));
}
|
hookJNI函数
let NewStringUTFAddr = null; let GetStringUTFCharsAddr = null; let Symbols = Process.findModuleByName("libart.so").enumerateSymbols(); for (let i = 0; i < Symbols.length; i++) { if (Symbols[i].name.indexOf('NewStringUTF') !== -1 && Symbols[i].name.indexOf('CheckJNI') === -1) { console.log(Symbols[i].name, Symbols[i].address); NewStringUTFAddr = Symbols[i].address; } }
if (NewStringUTFAddr !== null) { Interceptor.attach(NewStringUTFAddr,{ onEnter: function(args){ console.log(args[1].readCString()); }, onLeave: function(retval){ console.log("retval: ", retval); } }); }
|
主动调用JNI函数
let NewStringUTFAddr = null; let GetStringUTFCharsAddr = null; let Symbols = Process.findModuleByName("libart.so").enumerateSymbols(); for(let i = 0; i < symbols.length; i++) { if(Symbols[i].name.indexOf("NewStringUTF")!==-1 && Symbols[i].name.indexOf("CheckJNI") === -1){ console.log(Symbols[i].name, Symbole[i].address); NewStringUTFAddr = Symbols[i].address; } if(Symbols[i].name.indexOf("GetStringUTFChars") !== -1 && Symbols[i].name.indexOf("CheckJNI") === -1){ console.log(Symbols[i].name, Symbols[i].address); GetStringUTFCharsAddr = Symbols[i].address; } } if(NewStringUTFAddr !== null){ Interceptor.attach(NewStringUTFAddr,{ onEnter: function(args){ console.log(args[1].readCString); }, onLeave: function(retval){ let env = Java.vm.tryGetEnv(); let ret = GetStringUTFChars(env.handle, retval); console.log(hexdump(ret)); } }); }
|
so层函数打印栈
let NewStringUTFAddr = null; let GetStringUTFCharsAddr = null; let Symbols = Process.findModuleByName("libart.so").enumerateSymbols(); for (let i = 0; i < Symbols.length; i++) { if (Symbols[i].name.indexOf('NewStringUTF') !== -1 && Symbols[i].name.indexOf('CheckJNI') === -1) { console.log(Symbols[i].name, Symbols[i].address); NewStringUTFAddr = Symbols[i].address; } if (Symbols[i].name.indexOf('GetStringUTFChars') !== -1 && Symbols[i].name.indexOf('CheckJNI') === -1) { console.log(Symbols[i].name, Symbols[i].address); GetStringUTFCharsAddr = Symbols[i].address; } } let GetStringUTFChars = new NativeFunction(GetStringUTFCharsAddr, 'pointer', ['pointer', 'pointer']);
if (NewStringUTFAddr !== null) { Interceptor.attach(NewStringUTFAddr,{ onEnter: function(args){ console.log(args[1].readCString()); console.log(Thread.backtrace(this.context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\n') + '\n'); }, onLeave: function(retval){ let env = Java.vm.tryGetEnv(); let ret = GetStringUTFChars(env.handle, retval); console.log(hexdump(ret)); } }); }
|
inlineHook
var nativePointer = Moudle.findBaseAddress("libxiaojianbang.so"); var hookAddr = nativePointer.add(0x1524); Interceptor.attach(hookAddr,{ onEnter: functon(){ console.log("onEnter w8: ", this.context.x8 & 0xFFFF); }, onLeave: function(retval){ } });
var nativePointer = Module.findBaseAddreass("libxiaojianbang.so"); var hookAddr = nativePointer.add(0x11A0); Interceptor.attach(hookAddr,{ onEnter: function(){ console.log("onEnter r0: ", this.context.r0); }, onLeave: function(retval){ } });
|
如何确认native函数注册在哪个so
静态分析查看静态代码块中加载的so,并不靠谱
hook系统函数来得到绑定的native函数地址,然后再得到so地址
- jni函数静态注册,可以hook dlsym
- jni函数动态注册,可以hook RegisterNatives
快速定位jni静态注册函数
var dlsymAddr = Module.findExportByName(null, "dlsym"); Interceptor.attach(dlsymAddr, { onEnter: function(args){ this.funcName = args[1].readCString(); }, onLeave: function(retval){ var module = Process.findModuleByAddress(retval); if(module){ console.log(module.name + " " + this.funcName + " ", retval, retval.sub(module.base)); } } });
|
快速定位jni动态注册函数
function hook_RegisterNatives() { var symbols = Process.getModuleByName("libart.so").enumerateSymbols(); var RegisterNatives_addr = null; for (let i = 0; i < symbols.length; i++) { var symbol = symbols[i]; if(symbol.name.indexOf("CheckJNI") == -1 && symbol.name.indexOf("RegisterNatives") != -1) { RegisterNatives_addr = symbol.address; } } console.log("RegisterNatives_addr: ", RegisterNatives_addr);
Interceptor.attach(RegisterNatives_addr, { onEnter: function (args) {
var env = Java.vm.tryGetEnv(); var className = env.getClassName(args[1]); var methodCount = args[3].toInt32();
for (let i = 0; i < methodCount; i++) { var methodName = args[2].add(Process.pointerSize * 3 * i).readPointer().readCString(); var signature = args[2].add(Process.pointerSize * 3 * i).add(Process.pointerSize).readPointer().readCString(); var fnPtr = args[2].add(Process.pointerSize * 3 * i).add(Process.pointerSize * 2).readPointer(); var module = Process.findModuleByAddress(fnPtr); console.log(className, methodName, signature, fnPtr, module.name, fnPtr.sub(module.base)); }
}, onLeave: function (retval) { } });
} hook_RegisterNatives();
|
hookinitarray
function main() { function hook_dlopen(addr, soName, callback) { Interceptor.attach(addr, { onEnter: function (args) { var soPath = args[0].readCString(); if(soPath.indexOf(soName) != -1) hook_call_constructors(); }, onLeave: function (retval) { } }); } var dlopen = Module.findExportByName("libdl.so", "dlopen"); var android_dlopen_ext = Module.findExportByName("libdl.so", "android_dlopen_ext"); hook_dlopen(dlopen, "libxiaojianbang.so", inlineHook); hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", inlineHook);
var isHooked = false; function hook_call_constructors() { var symbols = Process.getModuleByName("linker64").enumerateSymbols(); var call_constructors_addr = null; for (let i = 0; i < symbols.length; i++) { var symbol = symbols[i]; if(symbol.name.indexOf("__dl__ZN6soinfo17call_constructorsEv") != -1){ call_constructors_addr = symbol.address; } } console.log("call_constructors_addr: ", call_constructors_addr); Interceptor.attach(call_constructors_addr, { onEnter: function (args) { if(!isHooked) { hook_initarray(); isHooked = true; } }, onLeave: function (retval) { } }); }
function hook_initarray(){ var xiaojianbangAddr = Module.findBaseAddress("libxiaojianbang.so"); var func1_addr = xiaojianbangAddr.add(0x1C54); var func2_addr = xiaojianbangAddr.add(0x1C7C); var func3_addr = xiaojianbangAddr.add(0x1C2C); Interceptor.replace(func1_addr, new NativeCallback(function () { console.log("func1 is replaced!!!"); }, 'void', []));
Interceptor.replace(func2_addr, new NativeCallback(function () { console.log("func2 is replaced!!!"); }, 'void', []));
Interceptor.replace(func3_addr, new NativeCallback(function () { console.log("func3 is replaced!!!"); }, 'void', [])); } } main();
|
sohook封装
function print_arg(addr){ var module = Process.findRangeByAddress(addr); if(module != null) return hexdump(addr) + "\n"; return ptr(addr) + "\n"; } function hook_native_addr(funcPtr, paramsNum){ var module = Process.findModuleByAddress(funcPtr); Interceptor.attach(funcPtr, { onEnter: function(args){ this.logs = []; this.params = []; this.logs.push("call " + module.name + "!" + ptr(funcPtr).sub(module.base) + "\n"); for(let i = 0; i < paramsNum; i++){ this.params.push(args[i]); this.logs.push("this.args" + i + " onEnter: " + print_arg(args[i])); } }, onLeave: function(retval){ for(let i = 0; i < paramsNum; i++){ this.logs.push("this.args" + i + " onLeave: " + print_arg(this.params[i])); } this.logs.push("retval onLeave: " + print_arg(retval) + "\n"); console.log(this.logs); } }); }
|
inlinehook获取so基址