枚举各种

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); // new NativePointer
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());
//如果Java.vm.tryGetEnv()得不到env的话,可以试试将这段代码用Java.perform(){}包住

hooklibc读写文件

//frida API写文件
var ios = new File("/sdcard/hyq.txt","w");
ios.write("6666666");
ios.flush();
ios.close();
//hook libc写文件
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));
/*
* 0x1000 0x2000
* 0x2000 QQ285092564
*
* */
}

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 = env.getStringUtfChars(retval);
// console.log(hexdump(ret));
//addr --> 0
let env = Java.vm.tryGetEnv();
//let ret = GetStringUTFChars(env.handle, retval, ptr(0));
let ret = GetStringUTFChars(env.handle, retval);
console.log(hexdump(ret));
}
});
}

inlineHook

//就是把函数的首地址换成函数中的任一地址,并且不使用args,通常用来查看寄存器的值
//64位
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){

}
});
//32位
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,并不靠谱

  • 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基址