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部分的指令,重写成为跳转指令

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

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

jmp.jpg

重写成为跳转指令

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

oldcode.png

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

跑一下结果

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

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

modify.png

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

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

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

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

1// Hook 逻辑,这里只打印 参数 2hookSyscall(address, new NativeCallback(function (dirfd, pathname, mode, flags) { 3 let path = pathname.readCString(); 4 5 log('Called openat hook'); 6 log('- R0: ' + dirfd); 7 log('- R1: ' + path); 8 log('- R2: ' + mode); 9 log('- R3: ' + flags); 10 11 return 0; 12}, 'int', ['int', 'pointer', 'int', 'int'])); 13 14 15...... 16 17 18// 创建一个新的代码空间,放我们自己的代码 19let frida = Memory.alloc(Process.pageSize); 20 21// 开始写程序了 22writer = new ArmWriter(code, { pc: frida }); 23 24// 执行备份的指令 25writer.putBytes(instructions); 26 27// 寄存器入栈,这里把r0也入栈了 28// FF 5F 2D E9 STMFD SP!, {R0-R12,LR} 寄存器入栈 29writer.putInstruction(0xE92D5FFF); 30// 00 A0 0F E1 MRS R10, CPSR 31// 00 04 2D E9 STMFD SP!, {R10} // 状态寄存器入栈 32writer.putInstruction(0xE10FA000); 33writer.putInstruction(0xE92D0400); 34 35// instructions.size = 20 + 5条指令 36// 修改lr寄存器,保障执行我们自己的逻辑之后还能回来继续向下执行。 37writer.putLdrRegAddress("lr",frida.add(20 + 5*4)); 38writer.putBImm(callback); 39 40// 00 04 BD E8 LDMFD SP!, {R10} // 状态寄存器出栈 41// 0A F0 29 E1 MSR CPSR_cf, R10 42writer.putInstruction(0xE8BD0400); 43writer.putInstruction(0xE129F00A); 44 45// FF 5F BD E8 LDMFD SP!, {R0-R12,LR} 寄存器出栈 46writer.putInstruction(0xE8BD5FFF); 47 48// 我回来了 0x93762204 49writer.putBranchAddress(retAddress); 50writer.flush();

再跑一下,

1Called openat hook 2- R0: 86 3- R1: /proc/self/maps 4- R2: 0 5- R3: 0

三、总结

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

frida 脚本采用 typescript project, 调试和编译脚本的时候需要参照 https://github.com/oleavr/frida-agent-example

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

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

我们的实现只是把参数和结果打印出来了,在我们自己的 NativeCallback 中并不能方便的去修改这些入参和返回值。这个就给大家留作业了。 参照 https://github.com/zhuotong/Android_InlineHook 的原理,那就想怎么玩就怎么玩。

ffshow.jpeg

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

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

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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

PhoneGap设置Icon

参考:http://cordova.apache.org/docs/en/latest/config\_ref/images.html通过config.xml中的<icon标签来设置Icon<iconsrc"res/ios/icon.png"platform"ios"width"57"height"57"densi