libcHook

xiaoeryu Lv5
  • 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()*不执行从而过掉检测呢

替换参数和替换函数

  1. 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)
            }
        })
    }
    
  2. 过滤一下如果符合条件就替换掉*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 进行许可。
评论