如今不少App不讲武德了,为了防止 openat 、read、kill 等等底层函数被hook,干脆就直接经过syscall的方式来作系统调用,致使没法hook。git
应对这种状况有两种方案:github
咱们今天采用第二种方法,用frida来实现typescript
. 备份SWI/SVC部分的指令,重写成为跳转指令安全
. 跳转到咱们新的代码空间,把以前备份的指令执行一下。而后执行咱们本身的逻辑。 (打印参数之类的)微信
. 跳回原程序空间,继续往下跑markdown
此次hook使用 frida ArmWriter 来实现,用 putBranchAddress 函数写个跳转指令,须要花20个字节,咱们先看看修改以前的状况。函数
// 咱们定位的锚是 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);
复制代码
跑一下结果oop
==== 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。对比一下。学习
新的代码空间地址是 0xa9b83000。 从咱们修改的指令来看,它会跳转到 0xa9b83000, 木有问题。spa
而后返回的地址是 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… 的原理,那就想怎么玩就怎么玩。
往往剖开本身写过的代码,里面都应有血流出来。
TIP: 本文的目的只有一个就是学习更多的逆向技巧和思路,若是有人利用本文技术去进行非法商业获取利益带来的法律责任都是操做者本身承担,和本文以及做者不要紧,本文涉及到的代码项目能够去 奋飞的朋友们 知识星球自取,欢迎加入知识星球一块儿学习探讨技术。有问题能够加我wx: fenfei331 讨论下。
关注微信公众号: 奋飞安全,最新技术干货实时推送