Frida-syscall-interceptor

简介: Frida-syscall-interceptor

一、目标


现在很多App不讲武德了,为了防止 openat 、read、kill 等等底层函数被hook,干脆就直接通过syscall的方式来做系统调用,导致无法hook。


应对这种情况有两种方案:

  • 刷机重写系统调用表来拦截内核调用
  • inline Hook SWI/SVC指令


我们今天采用第二种方法,用frida来实现

  • 内联汇编SWI/SVC做系统调用, syscall
  • frida inline hook
  • hook syscall
  • frida ArmWriter
  • frida typescript project


二、步骤

inline Hook 原理

.

备份SWI/SVC部分的指令,重写成为跳转指令

.  跳转到我们新的代码空间,把之前备份的指令执行一下。然后执行我们自己的逻辑。 (打印参数之类的)

.  跳回原程序空间,继续往下跑65.png


重写成为跳转指令


这次hook使用 frida ArmWriter 来实现,用 putBranchAddress 函数写个跳转指令,需要花20个字节,我们先看看修改之前的情况。66.png


// 我们定位的锚是 svc指令, 0x9374C1F8  它前面还有8个字节的指令这里一并替换
const address = syscallAddress.sub(8);
// 备份这20个字节,马上它们就要被替换了
const instructions = address.readByteArray(20);
if (instructions == null) {
    throw new Error(`Unable to read instructions at address ${address}.`);
}
// 把旧的20个字节打印出来
console.log(" ==== old instructions ==== " + address);
console.log(instructions);
// 开始替换成跳转指令,跳转的地址是 createCallback 里面创建的新的代码空间地址。
Memory.patchCode(address, 20, function (code) {
    let writer = null;
    writer = new ArmWriter(code, { pc: address });
    writer.putBranchAddress(createCallback(callback, instructions, address.add(20), syscallAddress));
    writer.flush();
});
// 把新的指令打出来对比下
console.log(" ==== new instructions ==== " + address);
const instructionsNew = address.readByteArray(20);
console.log(instructionsNew);

跑一下结果

==== old instructions ==== 0x937621f0
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
00000000  07 c0 a0 e1 42 71 00 e3 00 00 00 ef 0c 70 a0 e1  ....Bq.......p..
00000010  01 0a 70 e3                                      ..p.
 ==== new Code Addr ====
0xa9b83000
 ==== retAddress ====
0x93762204
 ==== new instructions ==== 0x937621f0
           0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
00000000  01 80 2d e9 04 00 9f e5 04 00 8d e5 01 80 bd e8  ..-.............
00000010  00 30 b8 a9                                      .0..

指令是修改成功了,但是修改的对不对呢?  这时候需要祭出 IDA,Attach一下我们的demo。对比一下。


作者:fenfei331

链接:https://juejin.cn/post/6973849358278230046

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


65.png


新的代码空间地址是 0xa9b83000。  从我们修改的指令来看,它会跳转到 0xa9b83000, 木有问题。


然后返回的地址是 0x93762204, 恰好也是要回来的地址。


执行备份指令,和我们自己的逻辑


执行备份指令比较简单,但是我们自己的逻辑可不能用Arm汇编来写,frida已经帮我们想好了,可以创建一个 NativeCallback, 执行备份指令之后,直接可以跳转到 firida的 NativeCallback。 听起来很牛的样子。

// Hook 逻辑,这里只打印 参数
hookSyscall(address, new NativeCallback(function (dirfd, pathname, mode, flags) {
    let path = pathname.readCString();
    log('Called openat hook');
    log('- R0: ' + dirfd);
    log('- R1: ' + path);
    log('- R2: ' + mode);
    log('- R3: ' + flags);
    return 0;
}, 'int', ['int', 'pointer', 'int', 'int']));
......
// 创建一个新的代码空间,放我们自己的代码
let frida = Memory.alloc(Process.pageSize);
// 开始写程序了
writer = new ArmWriter(code, { pc: frida });
// 执行备份的指令
writer.putBytes(instructions);
// 寄存器入栈,这里把r0也入栈了
// FF 5F 2D E9 STMFD  SP!, {R0-R12,LR} 寄存器入栈  
writer.putInstruction(0xE92D5FFF);
// 00 A0 0F E1 MRS R10, CPSR
// 00 04 2D E9 STMFD SP!, {R10}    // 状态寄存器入栈
writer.putInstruction(0xE10FA000);
writer.putInstruction(0xE92D0400);
// instructions.size = 20  + 5条指令
// 修改lr寄存器,保障执行我们自己的逻辑之后还能回来继续向下执行。
writer.putLdrRegAddress("lr",frida.add(20 + 5*4));
writer.putBImm(callback);
// 00 04 BD E8  LDMFD SP!, {R10}   // 状态寄存器出栈     
// 0A F0 29 E1  MSR CPSR_cf, R10
writer.putInstruction(0xE8BD0400);
writer.putInstruction(0xE129F00A);
// FF 5F BD E8 LDMFD  SP!, {R0-R12,LR}    寄存器出栈 
writer.putInstruction(0xE8BD5FFF);
// 我回来了 0x93762204
writer.putBranchAddress(retAddress);
writer.flush();


再跑一下,

Called openat hook
- R0: 86
- R1: /proc/self/maps                                                                                  
- R2: 0
- R3: 0


三、总结

本文来自github.com/AeonLucid/f… ,(对,就是AndroidNativeEmu的作者。  Orz) ,作者实现了 arm64下面的hook,我们把arm32的hook补上了,所以调试的时候需要在 arm32的手机上去调试。


frida 脚本采用 typescript project, 调试和编译脚本的时候需要参照

github.com/oleavr/frid…  。


frida-syscall-interceptor和frida-agent-example在同级目录。


生成js文件时,会提示ArmWriter的putBranchAddress函数找不到,其实这个函数是存在的,只是库文件没有更新, 手工在 declare class ArmWriter 里面增加一下 putBranchAddress 的声明。


我们的实现只是把参数和结果打印出来了,在我们自己的 NativeCallback 中并不能方便的去修改这些入参和返回值。这个就给大家留作业了。  参照  

github.com/zhuotong/An… 的原理,那就想怎么玩就怎么玩。

69.png


每每剖开自己写过的代码,里面都应有血流出来。


TIP: 本文的目的只有一个就是学习更多的逆向技巧和思路,如果有人利用本文技术去进行非法商业获取利益带来的法律责任都是操作者自己承担,和本文以及作者没关系,本文涉及到的代码项目可以去 奋飞的朋友们 知识星球自取,欢迎加入知识星球一起学习探讨技术。有问题可以加我wx: fenfei331 讨论下。


关注微信公众号: 奋飞安全,最新技术干货实时推送


相关文章
|
API
4.10 x64dbg 反汇编功能的封装
LyScript 插件提供的反汇编系列函数虽然能够实现基本的反汇编功能,但在实际使用中,可能会遇到一些更为复杂的需求,此时就需要根据自身需要进行二次开发,以实现更加高级的功能。本章将继续深入探索反汇编功能,并将介绍如何实现反汇编代码的检索、获取上下一条代码等功能。这些功能对于分析和调试代码都非常有用,因此是书中重要的内容之一。在本章的学习过程中,读者不仅可以掌握反汇编的基础知识和技巧,还能够了解如何进行插件的开发和调试,这对于提高读者的技能和能力也非常有帮助。
112 0
4.10 x64dbg 反汇编功能的封装
|
6月前
|
Android开发
Xposed模块 -- Hook函数参数
Xposed模块 -- Hook函数参数
95 0
|
存储 编译器 C语言
STM32的启动过程 — startup_xxxx.s文件解析(MDK和GCC双环境)
无论是是何种MCU,从简单的51,MSP430,到ARM9,ARM11,A7 都必须有启动文件,对于MCU来说,他是如何找到并执行main函数的,就需要用到“启动文件”,本文就来说说 STM32 的启动过程。
1165 1
STM32的启动过程 — startup_xxxx.s文件解析(MDK和GCC双环境)
|
存储 编译器 C++
[√]leak-tracer源码使用到的函数
[√]leak-tracer源码使用到的函数
64 0
|
算法 Java 数据安全/隐私保护
frida hook native层巧解Android逆向题
frida hook native层巧解Android逆向题
|
编译器 开发工具 Windows
x64dbg 2022 最新版编译方法
x64dbg 调试器的源码编译很麻烦,网络上的编译方法均为老版本,对于新版本来说编译过程中会出现各种错误,编译的坑可以说是一个接着一个,本人通过研究总结出了一套编译拳法可以完美编译输出,不过话说回来x64dbg这种使用两个编译器开发的方式以及调用太多的第三方项目想要完全编译其实也是非常困难的,如下笔记只提供编译x64dbg本体,并确保其能够正常运行。
194 0
x64dbg 2022 最新版编译方法
【UVM源码学习】uvm_recorder
【UVM源码学习】uvm_recorder
269 0
【UVM源码学习】uvm_recorder
ALSA驱动源码之devm_snd_soc_register_component源码分析
ALSA驱动源码之devm_snd_soc_register_component源码分析
|
JSON 算法 安全
Frida + AndroidAsync 实现 RPC
Frida + AndroidAsync 实现 RPC
Frida + AndroidAsync 实现 RPC