各位读者你们好啊,今天一起讨论下hook so 中的函数的相关 api。
函数Hook
Native hook 主要使用的 api 是 interceptor:
https://frida.re/docs/javascript-api/#interceptor
可以通过符号来hook:
Interceptor.attach(Module.getExportByName('libc.so', 'read'), {
onEnter(args) {
this.fileDescriptor = args[0].toInt32();
},
onLeave(retval) {
if (retval.toInt32() > 0) {
/* do something with this.fileDescriptor */
}
}
});
也可以通过地址偏移来hook:
var nativelibmodule = Process.getModuleByName("libnative-lib.so");
var subf0ac = nativelibmodule.base.add(0xf0ac);
Interceptor.attach(subf0ac, {
onEnter: function (args) {
console.log("secret key: ", hexdump(args[1]), '\n length:', args[2]);
}, onLeave: function () {
}
});
拿到了函数地址后,甚至还可以直接调用该函数:
var offset = 0xF658;
var module = Process.getModuleByName("libnative-lib.so");
var sub_address = module.base.add(offset);
var arg1_address = Memory.alloc(10);
let arg2_address = Memory.alloc(10);
arg1_address.writeUtf8String(arg1);
arg2_address.writeUtf8String(arg2);
var hooked_sub_f658 = new NativeFunction(sub_address, 'pointer', ['pointer', 'pointer']);
hooked_sub_f658(arg1_address, arg2_address);
NativeFunction
https://frida.re/docs/javascript-api/#nativefunction
NativeFunction可以描述一个Native函数,与Java函数的签名是一样的,参数为:
-
函数地址
-
返回值类型
-
参数类型列表
支持的类型有:
void
pointer
int
uint
long
ulong
char
uchar
size_t
ssize_t
float
double
int8
uint8
int16
uint16
int32
uint32
int64
uint64
bool
其中,pointer 类型是指针类型,它使用 NativePointer 来描述。
NativePointer
https://frida.re/docs/javascript-api/#nativepointer
比如,当我们需要打印 jstring
类型的参数,javascript 是没有这个类型的,就需要先做一些转换:
Java.vm.getEnv().getStringUtfChars(argxxx, null).readCString()
上面的代码中,Java.vm.getEnv().getStringUtfChars(argxxx, null)
返回的就是一个 NativePointer 类型:
https://github.com/frida/frida-java-bridge/blob/main/lib/env.js
Env.prototype.getStringUtfChars = proxy(169, 'pointer', ['pointer', 'pointer', 'pointer'], function (impl, str) {
return impl(this.handle, str, NULL);
});
function proxy (offset, retType, argTypes, wrapper) {
let impl = null;
return function () {
if (impl === null) {
impl = new NativeFunction(vtable(this).add(offset * pointerSize).readPointer(), retType, argTypes, nativeFunctionOptions);
}
let args = [impl];
args = args.concat.apply(args, arguments);
return wrapper.apply(this, args);
};
}
frida 针对 NativePointer
提供了很多的 api,具体可看文档,其中就有 readCString
。
替换返回值与参数
frida 文档里面说了,retval 是一个 NativePointer
的衍生对象,包含了原始的值,直接使用 replace
方法即可替换返回值:
-
You may call
retval.replace(1337)
to replace the return value with the integer1337
, -
or
retval.replace(ptr("0x1234"))
to replace with a pointer.
举个例子,比如我想替换字符串,可以使用 JNI 中的任意函数达成目标,如果你熟悉 JNI 开发就最好不过了:
onLeave:function(retval) {
var newRetval = Java.vm.getEnv().newStringUtf("nice to meet you");
retval.replace(newRetval);
}
onEnter(args)
里面的 args 是一个 NativePointer
类型的数组(这里感觉有点奇怪,一个 int 参数也是 NativePointer 类型???),想要替换参数,直接构建一个新参数替换即可:
onEnter:function(args) {
var args2 = Java.vm.getEnv().newStringUtf("nice to meet you");
args[2] = args2;
}
替换方法
使用 Interceptor.replace
来实现,工作模式与 inline hook 很相似,方法参数:
-
地址
-
替换后的实现
-
返回值类型
-
参数类型列表
const openPtr = Module.getExportByName('libc.so', 'open');
const open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
Interceptor.replace(openPtr, new NativeCallback((pathPtr, flags) => {
const path = pathPtr.readUtf8String();
log('Opening "' + path + '"');
// 调用了原来的方法
const fd = open(pathPtr, flags);
log('Got fd: ' + fd);
return fd;
}, 'int', ['pointer', 'int']));
查看so中的导出函数
使用 Module 的 enumerateExports
函数即可。
二手的程序员
欢迎关注二手的程序员,这里主要分享逆向相关的知识。专注于完整系列,让知识不再碎片化。不定时更新,也欢迎关注我的博客:lyldalek.top
公众号