首页
社区
课程
招聘
[原创]【Frida 实战】如何拦截 sub_xxxx 这种函数
2020-5-31 22:01 32177

[原创]【Frida 实战】如何拦截 sub_xxxx 这种函数

2020-5-31 22:01
32177

本文是 Frida 实战系列教程的第三篇,讲解如何拦截 sub_xxxx 这种函数,以及如何替换和调用原始函数。
第一篇:https://www.ioshacker.net/thread-360-1-1.html
第二篇:https://bbs.pediy.com/thread-259424.htm

 

在 IDA 里看到的 sub_xxxx 这种类型的函数是因为没有符号,所以 IDA 解析时就显示 sub_xxxx,其中 xxxx 是 IDA 读取到的函数地址。在第二篇教程中,我们初步学习了拦截器(Interceptor)的使用,知道了怎么拦截函数,但是如果是自定义函数,去掉符号信息可能就没有名称,我们该怎么拦截呢?下面我们来实际操作,自定义一个静态函数,名称是 add,功能就是将第一个参数和第二个参数相加,返回相加的结果,然后调用 add 函数时第一个参数和第二个参数都传入 1,执行之后会打印出 “1 + 1 = 2",具体代码如下:

static int add(int num1, int num2){

    int sum = num1 + num2;
    return sum;
}

int sum = add(1, 1);
NSLog(@"1 + 1 = %d", sum);

编译成功之后,执行下面的命令即可去掉符号。这时使用 IDA 查看可执行文件只会显示 sub_xxxx 这类名称,没有函数的真实名称。

strip -x .../Products/Debug-iphoneos/CrackMe.app/CrackMe

接下来开始编写 frida 脚本,get_func_addr 是我们定义的一个函数用来获取函数地址的,需要传入两个参数,第一个参数是模块的名称,第二个参数是在 IDA 里看到的函数地址。查看这个地址的方法,我一般是在 IDA 里将基址设为 0,然后定位到目标函数,此时的地址就是函数地址。得到地址之后就可以使用 Interceptor.attach() 拦截函数。

function get_func_addr(module, offset) {

   var base_addr = Module.findBaseAddress(module);
   console.log("base_addr: " + base_addr);

   console.log(hexdump(ptr(base_addr), {
            length: 16,
            header: true,
            ansi: true
        }))

   var func_addr = base_addr.add(offset);
   if (Process.arch == 'arm')
      return func_addr.add(1);  //如果是32位地址+1
   else
      return func_addr;
}

var func_addr = get_func_addr('CrackMe', 0x6684);
console.log('func_addr: ' + func_addr);

console.log(hexdump(ptr(func_addr), {
            length: 16,
            header: true,
            ansi: true
        }))

Interceptor.attach(ptr(func_addr), {
   onEnter: function(args) {

      console.log("onEnter");
      var num1 = args[0];
      var num2 = args[1];

      console.log("num1: " + num1);
      console.log("num2: " + num2);

   },
   onLeave: function(retval) {

      console.log("onLeave");
      retval.replace(3);  //返回值替换成3
   }
});

执行 frida 附加进程并加载 test.js 脚本

frida -U -l test.js CrackMe

有时候目标函数是在进程启动后马上就触发,使用附加的方式可能来不及注入脚本,函数就已经触发了。这种情况可以先退出进程,使用 -f 参数直接启动进程,可以在启动时马上注入脚本。需要注意的是进程名称填写的是包名。

frida -U -l test.js -f net.ioshacker.CrackMe

此时进入交互模式,然后输入 %resume,让进程继续运行,不然过一会进程会被强制退出,如果不想每次都输入 %resume,就添加 --no-pause 参数,这样在运行进程时不会暂停。

frida -U -l test2.js -f net.ioshacker.CrackMe --no-pause

脚本执行后会打印出基址、基址内存中的数据、add 函数地址、add 函数内存中的数据,信息如下:

base_addr: 0x100a14000
            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
100a14000  cf fa ed fe 0c 00 00 01 00 00 00 00 02 00 00 00  ................
func_addr: 0x100a1a684
            0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
100a1a684  ff 43 00 d1 e0 0f 00 b9 e1 0b 00 b9 e0 0f 40 b9  .C............@.

为什么要将可执行文件的基址数据和 add 函数内存中数据打印出来呢?这是为了验证我们获取到的地址是否正确,比如基址内存中的数据是 cf fa ed fe,说明是 Mach-O 头部,确实是可执行文件。打印出 add 函数内存中的数据也是为了验证结果,查看函数内存中的数据与 IDA 里看到的十六进制机器码对比是否一致,如果不一致说明获取到的函数地址是错误的,如果一致代表正确。IDA 里看到的十六进制机器码数据如下图所示,图中的原本是显示的函数名是 sub_6684,为了演示方便,我没有去掉符号,所以显示是函数原始名称 add。

 

 

当 add 函数触发时,在调用 add 函数时会打印出 “1 + 1 = 3”,因为返回值被替换成 3。

 

Interceptor.attach() 拦截到相应的函数时不能阻止原始函数的执行,比如有些情况,我们不想执行原始函数,或者是判断参数达到某个条件时才执行原始函数,否则不执行,这种情况可以使用 Interceptor.replace() 来替换原始函数,比如下面这段代码可以替换掉 add 函数, NativeFunction 里有三个 int,分别代表是返回值、第一个参数、第二个参数。

var addPtr = get_func_addr('CrackMe', 0x6684);
var add = new NativeFunction(addPtr, 'int', ['int', 'int']);

// 进行替换
Interceptor.replace(add, new NativeCallback(function(num1, num2) {

    if((num1 == 1) && (num2 == 1)){
        console.log("1+1");
    }
    // 调用原函数
    return add(num1, num2);
    //return add(2, 3); //将参数替换掉
}, 'int', ['int', 'int']));

有些情况我们需要拦截系统的函数,比如要拦截 open 函数,可以使用下面的代码。

var openPtr = Module.getExportByName(null, 'open');
var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);

Interceptor.replace(openPtr, new NativeCallback(function (pathPtr, flags) {
  var path = pathPtr.readUtf8String();
  console.log('Opening "' + path + '"');
  var fd = open(pathPtr, flags);
  console.log('Got fd: ' + fd);
  return fd;
}, 'int', ['pointer', 'int']));

[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2020-6-13 12:41 被暗夜盗魔编辑 ,原因:
收藏
点赞5
打赏
分享
最新回复 (10)
雪    币: 1366
活跃值: (5584)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
supperlitt 2020-6-1 07:58
2
0
可以,省略了,查看map拿地址。
雪    币: 210
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
周小鱼 2020-6-1 15:26
3
0
膜拜大佬!能不能出个ios frida rpc的文章?感谢!
雪    币: 2911
活跃值: (2547)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
小调调 2020-6-1 22:00
4
0
地址+1才能正常hook成功
雪    币: 2520
活跃值: (667)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
暗夜盗魔 1 2020-6-3 02:26
5
0
周小鱼 膜拜大佬!能不能出个ios frida rpc的文章?感谢!
好的,下周会更新
雪    币: 2520
活跃值: (667)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
暗夜盗魔 1 2020-6-3 02:26
6
0
小调调 地址+1才能正常hook成功
你是说32位机器要+1吧?
雪    币: 2911
活跃值: (2547)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
小调调 2020-6-3 10:01
7
0
暗夜盗魔 你是说32位机器要+1吧?
是滴,就是不知道为啥这么干
雪    币: 3539
活跃值: (901)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
smartdon 1 2020-6-8 11:18
8
0
小调调 是滴,就是不知道为啥这么干
准确来说,32位的arm指令hook的时候不用加1,thumb才需要加1,不过大部分都是thumb
雪    币: 248
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
@=llfly 2020-9-11 11:32
9
0
Mark
雪    币: 2686
活跃值: (995)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
luoyanbei 2020-9-17 10:14
10
0
学习了,哈哈
雪    币: 4
活跃值: (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
selectyjc 2022-12-16 15:51
11
0
游客
登录 | 注册 方可回帖
返回