美文网首页
Frida Javascript api #Intercepto

Frida Javascript api #Intercepto

作者: 邓超_码农加点中 | 来源:发表于2020-02-15 18:50 被阅读0次

    原文链接: https://frida.re/docs/javascript-api/#interceptor
    欢迎加入 Frida 交流群: 1049977261

    Interceptor

    • Interceptor.attach(target, callbacks[, data]):
      拦截位于 target 的方法的调用. target 是一个 NativePointer 类型的对象, 指明了您想要拦截的方法的地址.
      请注意, 在 32 位 ARM 上, 对于 ARM 函数, 此地址的最低有效位必须设置为 0, 对于 Thumb 函数, 此地址必须设置为 1.
      如果您通过 Frida 的 API 来获得一个地址, 例如 Module.getExportByName(), 那么 Frida 就会替您处理这个细节.

      callbacks 是一个包含一个或多个以下方法的对象:

      • onEnter: function (args):
        在原方法被调用前触发.
        args 是一个可以用来读写原方法参数的 NativePointer 数组.

      • onLeave: function (retval):
        在原方法返回前触发.
        retval 是一个含有原返回值的 NativePointer 驱动的对象.
        您可以调用 retval.replace(1337) 将返回值替换为整型 1337, 或调用 retval.replace(ptr("0x1234")) 将返回值替换为一个指针.
        请注意, 这个对象在 onLeave 调用完后将被回收, 因此请不要保存并在回调以外的地方使用这个对象. 如果您需要保存它包含的值, 您可以执行一次深度复制, 例如: ptr(retval.toString()).

      如果钩子函数被频繁的调用, onEnteronLeave 有可能是指向由 CModule
      编译的本地 C 方法的 NativePointer. 它们的签名是:

      • void onEnter (GumInvocationContext * ic)

      • void onLeave (GumInvocationContext * ic)

      这种情况下, 第三个可选参数 data 可以是一个 gum_invocation_context_get_listener_function_data() 可接触的 NativePointer 对象.

      您也可以通过传递一个与 onEnter 有相同的签名方法而不是 callbacks 对象来拦截任意指令.
      此时传递给它的 args 参数只会在被拦截的指令位于函数的开头或寄存器/堆栈尚未偏离的指定的点时为您提供合理的值.

      和上面一样, 这个方法也通过给 callbacks 指明一个 NativePointer 对象来调用 C 实现而不是通过 JavaScript 方法实现.

      返回一个可用于 Interceptor#detach() 的监听器对象.

      请注意, 这些方法将伴随着 this 被一起调用, this 是一个可存储任意数据的局部线程对象, 当您需要在 onLeave 中读取 onEnter 里的参数时十分有用.

      例如:

    Interceptor.attach(Module.getExportByName('libc.so', 'read'), {
      onEnter: function (args) {
        this.fileDescriptor = args[0].toInt32();
      },
      onLeave: function (retval) {
        if (retval.toInt32() > 0) {
          /* do something with this.fileDescriptor */
        }
      }
    });
    
    • 另外, 这个对象包含一些实用的属性:

      • returnAddress: NativePointer 类型的返回地址

      • context: 带有键 pcspNativePointer 对象, 分别为 ia32 / x64 / arm 指定 EIP / RIP / PC 和 ESP / RSP / SP. 也可以使用其他处理器特定的键, 例如 eax, rax, r0, x0等.
        您也可以通过分配这些键来更新寄存器值.

      • errno: (UNIX) 当前的 errno 值 (您可以替换它)

      • lastError: (Windows) 当前操作系统的 error 值 (您可以替换它)

      • threadId: 操作系统线程 ID

      • depth: 相对其他调用的调用深度

      例如:

    Interceptor.attach(Module.getExportByName(null, 'read'), {
      onEnter: function (args) {
        console.log('Context information:');
        console.log('Context  : ' + JSON.stringify(this.context));
        console.log('Return   : ' + this.returnAddress);
        console.log('ThreadId : ' + this.threadId);
        console.log('Depth    : ' + this.depth);
        console.log('Errornr  : ' + this.err);
    
        // Save arguments for processing in onLeave.
        this.fd = args[0].toInt32();
        this.buf = args[1];
        this.count = args[2].toInt32();
      },
      onLeave: function (result) {
        console.log('----------')
        // Show argument 1 (buf), saved during onEnter.
        var numBytes = result.toInt32();
        if (numBytes > 0) {
          console.log(hexdump(this.buf, { length: numBytes, ansi: true }));
        }
        console.log('Result   : ' + numBytes);
      }
    })
    
    性能方面的注意事项

    回调对象对性能有显著的影响. 如果您只需要检查参数但不关心返回结果, 或者相反, 请确保您忽略您不需要的回调. 例如, 避免将您的逻辑放到 onEnter 并且让 onLeave 是一个空回调.

    在一台 iPhone 5S 上, 当只提供 onEnter 时, 基础开销是 6 毫秒, 当 onEnteronLeave 一起被提供是则是 11 毫秒.

    同样的, 请注意那些每秒钟调用成千上万次的方法. 因为 send() 是异步的, 发送单个消息的总开销并未针对高频进行优化, 因此, Frida 让您来决定是否需要将多个值分批处理到单个 send() 调用中, 以便获得低延迟还是高吞吐量.

    当钩住热函数时, 可以将 Interceptor 与 CModule 结合以使用在 C 中实现的回调.

    • Interceptor.detachAll(): 分离所有之前附加上的回调.

    • Interceptor.replace(target, replacement[, data]):
      使用 replacement 替换 target 处的方法. 这通常在您需要完全或部分地替换已有方法时很有用.

      您可以使用 NativeCallback 来实现一个 JavaScript 的 replacement.

      如果被替换的方法被频繁的调用, 您可以通过 CModule 在 C 语言中实现 replacement. 您也可以稍后指明第三个可选参数 data, 它应当是一个 gum_invocation_context_get_listener_function_data() 可以接触到的 NativePointer 对象. 您可以使用 gum_interceptor_get_current_invocation() 来获得 GumInvocationContext *.

      请注意, replacement 将持续存活直到 Interceptor#revert 被调用.

      如果您想要链接到原实现, 您可以在您的实现中通过 NativeFunction 同步调用 target, 这将直接调用原实现.

      例如:

    var openPtr = Module.getExportByName('libc.so', 'open');
    var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
    Interceptor.replace(openPtr, new NativeCallback(function (pathPtr, flags) {
      var path = pathPtr.readUtf8String();
      log('Opening "' + path + '"');
      var fd = open(pathPtr, flags);
      log('Got fd: ' + fd);
      return fd;
    }, 'int', ['pointer', 'int']));
    
    • Interceptor.revert(target): 将 target 处的方法还原到之前的实现.

    • Interceptor.flush():
      确保任何待定的更改已提交到内存. 这应该仅在少有的必要的情况中被执行, 例如, 您刚刚 attach()replace() 了一个您即将通过 NativeFunction 调用的方法. 待定的改动将自动地在当前线程即将离开 JavaScript 运行时或 send() 被调用时被齐平, 比如在 RPC 方法中返回, 以及调用 console 中的任意 API.

    相关文章

      网友评论

          本文标题:Frida Javascript api #Intercepto

          本文链接:https://www.haomeiwen.com/subject/lzuyfhtx.html