消息传递

本页为社区译文;如有疑义,请以英文原文为准。 英文原文

本教程介绍如何向目标进程发送消息,以及如何从目标进程接收消息。

设置实验环境

创建文件 hello.c:

#include <stdio.h>
#include <unistd.h>

void
f (int n)
{
  printf ("Number: %d\n", n);
}

int
main (int argc,
      char * argv[])
{
  int i = 0;

  printf ("f() is at %p\n", f);

  while (1)
  {
    f (i++);
    sleep (1);
  }
}

使用以下命令编译:

$ gcc -Wall hello.c -o hello

启动程序,并记下 f() 的地址(以下示例中为 0x400544):

f() is at 0x400544
Number: 0
Number: 1
Number: 2
…

从目标进程发送消息

以下脚本演示如何将消息发回 Python 进程。可以发送任何能够序列化为 JSON 的 JavaScript 值。

创建文件 send.py,内容如下:

import frida
import sys

session = frida.attach("hello")
script = session.create_script("send(1337);")
def on_message(message, data):
    print(message)
script.on('message', on_message)
script.load()
sys.stdin.read()

运行此脚本时:

$ python send.py

它应打印以下消息:

{'type': 'send', 'payload': 1337}

这表示 JavaScript 代码 send(1337) 已在 hello 进程内部执行。按 Ctrl-D 终止脚本。

处理 JavaScript 运行时错误

如果 JavaScript 脚本抛出未捕获的异常,该异常会从目标进程传播到 Python 脚本。如果将 send(1337) 替换为 send(a)(一个未定义变量),Python 将收到以下消息:

{'type': 'error', 'description': 'ReferenceError: a is not defined', 'lineNumber': 1}

请注意 type 字段(error 与 send)。

在目标进程中接收消息

可以从 Python 脚本向 JavaScript 脚本发送消息。创建文件 pingpong.py:

import frida
import sys

session = frida.attach("hello")
script = session.create_script("""
    recv('poke', function onMessage(pokeMessage) { send('pokeBack'); });
""")
def on_message(message, data):
    print(message)
script.on('message', on_message)
script.load()
script.post({"type": "poke"})
sys.stdin.read()

运行脚本:

$ python pingpong.py

会产生以下输出:

{'type': 'send', 'payload': 'pokeBack'}
recv() 的工作机制

recv() 方法本身是异步(非阻塞)的。注册的回调(onMessage)只会接收一条消息。要接收下一条消息,必须使用 recv() 重新注册回调。

在目标进程中阻塞接收

可以在 JavaScript 脚本内部等待消息到达(阻塞接收)。创建脚本 rpc.py:

import frida
import sys

session = frida.attach("hello")
script = session.create_script("""
Interceptor.attach(ptr("%s"), {
    onEnter(args) {
        send(args[0].toString());
        const op = recv('input', value => {
            args[0] = ptr(value.payload);
        });
        op.wait();
    }
});
""" % int(sys.argv[1], 16))
def on_message(message, data):
    print(message)
    val = int(message['payload'], 16)
    script.post({'type': 'input', 'payload': str(val * 2)})
script.on('message', on_message)
script.load()
sys.stdin.read()

程序 hello 应处于运行状态,并且应记下它启动时打印的地址(例如 0x400544)。运行:

$ python rpc.py 0x400544

然后观察运行 hello 的终端中发生的变化:

Number: 3
Number: 8
Number: 10
Number: 12
Number: 14
Number: 16
Number: 18
Number: 20
Number: 22
Number: 24
Number: 26
Number: 14

hello 程序应开始输出“加倍”后的值,直到停止 Python 脚本(Ctrl-D)。