libcHook
- libc函数符号hook
- libc函数参数、返回值打印和替换
- 主动调用libc读写文件
本章主要是对libc中的线程创建函数进行hook以拦截将要执行的函数,以及主动调用libc中创建文件的一系列函数
hook libc中的函数
这里我们以hook下面的反调试函数为例,一般反调试都是新开一个线程来执行的。那我们能不能直接hook线程创建函数来拦截反调试函数的执行呢
我们先来hook *pthread_create()*把它的参数和返回值打印出来,然后主动调用init函数看看它的执行效果。
function beginAnti(){
Java.perform(function(){
Java.choose("com.xiaoeryu.demoso1.MainActivity",{
onMatch: function(instance){
console.log("found instance")
instance.init()
}, onComplete:function(){
console.log("Search complete")
}
})
})
}
function hook_pthread(){
var pthread_create_addr = Module.findExportByName("libc.so", 'pthread_create')
console.log("pthread_create_addr => ", pthread_create_addr)
Interceptor.attach(pthread_create_addr, {
onEnter: function(args){
console.log("args => ", args[0], args[1], args[2], args[3])
}, onLeave: function(retval){
console.log("retval => ", retval)
}
})
}
setImmediate(hook_pthread)
以spawn模式启动进程看看*pthread_create()*是否hook成功
frida -U -f com.xiaoeryu.demoso1 -l libc_hook.js --no-pause
再主动调用*init()*执行
- 执行后成功打印出了参数和返回值,根据函数原型可以知道第三个参数是*detect_frida_loop()*函数的地址
- 那么怎么让*detect_frida_loop()*不执行从而过掉检测呢
替换参数和替换函数
hook *pthread_create()*方法,过滤一下如果偏移相同说明是同一个函数,替换掉第三个参数让他去执行一个无关紧要的函数
function hook_pthread(){ // 查找 libc.so 中 pthread_create 和 time 函数的地址(后面使用time函数的地址来替换原本要执行的函数的地址) var pthread_create_addr = Module.findExportByName("libc.so", 'pthread_create') var time_addr = Module.findExportByName("libc.so", 'time') // 打印 pthread_create 和 time 函数的地址 console.log("pthread_create_addr => ", pthread_create_addr) console.log("time_addr => ", time_addr) // 拦截 pthread_create 函数 Interceptor.attach(pthread_create_addr, { onEnter: function(args){ // 打印 pthread_create 函数的参数 console.log("args => ", args[0], args[1], args[2], args[3]) // 查找 libdemoso1.so 的基地址 var libdemoso1_addr = Module.findBaseAddress("libdemoso1.so") if(libdemoso1_addr != null){ // 打印 libdemoso1.so 的基地址 console.log("libdemoso1_addr => ", libdemoso1_addr) // 计算 detect_frida_loop 函数在 libdemoso1.so 中的偏移量 var dete_frida_loop_addr = args[2] - libdemoso1_addr console.log("detect_frida_loop_offset is => ", dete_frida_loop_addr) // 如果 detect_frida_loop 函数的偏移量为 126276,则将 pthread_create 的第三个参数替换为 time 函数的地址 if(args[2] - libdemoso1_addr == 126276){ args[2] = time_addr console.log("end args[2] => ", args[2]) } } }, onLeave: function(retval){ // 打印 pthread_create 函数的返回值 console.log("retval => ", retval) } }) }
过滤一下如果符合条件就替换掉*pthread_create()*让其直接返回不执行,不符合过滤条件就让其照旧执行
function replace_pthread(){ // 查找 libc.so 中 pthread_create 函数的地址 var pthread_create_addr = Module.findExportByName("libc.so", 'pthread_create') console.log("pthread_create_addr => ", pthread_create_addr) // 创建一个 NativeFunction 对象,用于调用 pthread_create 函数 var pthread_create = new NativeFunction(pthread_create_addr, 'int', ['pointer', 'pointer', 'pointer', 'pointer']) // 替换 pthread_create 函数 Interceptor.replace(pthread_create_addr, new NativeCallback(function(args0, args1, args2, args3){ // 打印替换后的 pthread_create 函数的参数 console.log("replace_pthread args: ", args0, args1, args2, args3) // 查找 libdemoso1.so 的基地址 var libdemoso1_addr = Module.findBaseAddress("libdemoso1.so") if(libdemoso1_addr != null){ // 打印 libdemoso1.so 的基地址 console.log("libdemoso1_addr => ", libdemoso1_addr) // 如果 detect_frida_loop 函数的偏移量为 126276,则返回 null if(args2 - libdemoso1_addr == args2-libdemoso1_addr){ console.log("detect_frida_loop_offset is => ", args2 - libdemoso1_addr) return null } } // 调用原始的 pthread_create 函数 return pthread_create(args0, args1, args2, args3) }, 'int', ['pointer', 'pointer', 'pointer', 'pointer'] )) }
这里需要注意的一点是,如果设备重启函数的偏移大概率会改变,这时候需要手动修改一下偏移
主动调用libc中的函数
这里调用libc中的一系列写文件的函数
function writeSomething(){
// 查找 libc.so 中 fopen、fputs、fclose 函数的地址
var fopen_addr = Module.findExportByName("libc.so", 'fopen')
var fputs_addr = Module.findExportByName("libc.so", 'fputs')
var fclose_addr = Module.findExportByName("libc.so", 'fclose')
console.log("fopen_addr => ", fopen_addr, "fputs_addr => ", fputs_addr, "fclose_addr => ", fclose_addr)
// 创建 NativeFunction 对象,用于调用 fopen 函数
var fopen = new NativeFunction(fopen_addr, 'pointer', ['pointer', 'pointer'])
// 创建 NativeFunction 对象,用于调用 fputs 函数
var fputs = new NativeFunction(fputs_addr, 'int', ['pointer', 'pointer'])
// 创建 NativeFunction 对象,用于调用 fclose 函数
var fclose = new NativeFunction(fclose_addr, 'int', ['pointer'])
// 指定文件名和模式
var fileName = Memory.allocUtf8String("/sdcard/hello.txt")
var mode = Memory.allocUtf8String("a+")
// 打开文件
var fp = fopen(fileName, mode)
// 指定要写入的内容
var context1 = Memory.allocUtf8String("hello world")
// 写入内容到文件中
fputs(context1, fp)
// 关闭文件
fclose(fp)
}
这里要需要用*Memory.allocUtf8String()将字符串以内存地址的方式传递给fopen()*使用
还需要注意的一个点是如果应用没有写文件权限是无法写入的,测试的时候可以把脚本注入com.android.settings等有文件读写权限的应用来测试
接下来尝试将libc.so库的所有导出函数全部写入文件中
function EnumerateAllExports(){
// 同步枚举所有模块
var modules = Process.enumerateModulesSync();
// 遍历所有模块
for(var i = 0; i < modules.length; i++){
// 获取当前模块的名称
var modele_name = modules[i].name;
// 枚举当前模块的所有导出函数
var exports = modules[i].enumerateExports();
// 遍历当前模块的所有导出函数
for(var j = 0; j < exports.length; j++){
// 将导出函数的信息写入文件
// 使用 writeSomething 函数将信息写入到指定的文件路径
// 导出函数的类型、名称和地址写入文件,以便后续分析
writeSomething("/sdcard/settings/" + modele_name + ".txt", "type: " + exports[j].type + " name: " + exports[j].name + " addr: " + exports[j].address + "\n");
}
}
}
- 稍微修改一下刚才的*writeSomething()*函数把路径名和写入内容改为传入的两个参数
附件
- 标题: libcHook
- 作者: xiaoeryu
- 创建于 : 2024-03-25 23:41:22
- 更新于 : 2024-04-13 17:40:01
- 链接: https://github.com/xiaoeryu/2024/03/25/libcHook/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论