Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to support binary data on stdin and stdout #798

Merged
merged 4 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions bindings/gumjs/runtime/message-dispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ function MessageDispatcher() {
function handleMessage(rawMessage, data) {
const message = JSON.parse(rawMessage);
if (message instanceof Array && message[0] === 'frida:rpc') {
handleRpcMessage(message[1], message[2], message.slice(3));
handleRpcMessage(message[1], message[2], message.slice(3), data);
} else {
messages.push([message, data]);
dispatchMessages();
}
}

function handleRpcMessage(id, operation, params) {
function handleRpcMessage(id, operation, params, data) {
const exports = rpc.exports;

if (operation === 'call') {
Expand All @@ -46,7 +46,7 @@ function MessageDispatcher() {
}

try {
const result = exports[method].apply(exports, args);
const result = exports[method].call(exports, ...args, data);
if (typeof result === 'object' && result !== null &&
typeof result.then === 'function') {
result
Expand All @@ -67,13 +67,15 @@ function MessageDispatcher() {
}
}

function reply(id, type, result, params) {
params = params || [];

if (result instanceof ArrayBuffer)
send(['frida:rpc', id, type, {}].concat(params), result);
else
send(['frida:rpc', id, type, result].concat(params));
function reply(id, type, result, params = []) {
if (Array.isArray(result) && result.length === 2 && result[1] instanceof ArrayBuffer) {
const [value, data] = result;
send(['frida:rpc', id, type, value, ...params], data);
} else if (result instanceof ArrayBuffer) {
send(['frida:rpc', id, type, undefined, ...params], result);
} else {
send(['frida:rpc', id, type, result, ...params]);
}
}

function dispatchMessages() {
Expand Down
141 changes: 97 additions & 44 deletions tests/gumjs/script.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Copyright (C) 2021 Abdelrahman Eid <[email protected]>
* Copyright (C) 2023 Grant Douglas <[email protected]>
* Copyright (C) 2024 Hillel Pinto <[email protected]>
* Copyright (C) 2024 Håvard Sørbø <[email protected]>
*
* Licence: wxWindows Library Licence, Version 3.1
*/
Expand All @@ -27,7 +28,6 @@ TESTLIST_BEGIN (script)
TESTENTRY (recv_wait_in_an_application_thread_should_throw_on_unload)
TESTENTRY (recv_wait_in_our_js_thread_should_throw_on_unload)
TESTENTRY (recv_wait_should_not_leak)
TESTENTRY (rpc_can_be_performed)
TESTENTRY (message_can_be_logged)
TESTENTRY (thread_can_be_forced_to_sleep)
TESTENTRY (thread_backtrace_can_be_captured_with_limit)
Expand All @@ -44,6 +44,19 @@ TESTLIST_BEGIN (script)
TESTENTRY (crash_on_thread_holding_js_lock_should_not_deadlock)
#endif

TESTGROUP_BEGIN ("RPC")
TESTENTRY (method_can_be_called_sync)
TESTENTRY (method_can_be_called_async)
TESTENTRY (method_can_throw_sync)
TESTENTRY (method_can_throw_async)
TESTENTRY (method_can_return_null)
TESTENTRY (method_can_receive_binary_data)
TESTENTRY (method_can_return_binary_data)
TESTENTRY (method_can_return_value_and_binary_data)
TESTENTRY (method_list_can_be_queried)
TESTENTRY (calling_inexistent_method_should_throw_error)
TESTGROUP_END ()

TESTGROUP_BEGIN ("WeakRef")
TESTENTRY (weak_ref_api_should_be_supported)
TESTENTRY (weak_callback_is_triggered_on_gc)
Expand Down Expand Up @@ -5992,60 +6005,100 @@ TESTCASE (array_buffer_can_be_created)
EXPECT_NO_MESSAGES ();
}

TESTCASE (rpc_can_be_performed)
TESTCASE (method_can_be_called_sync)
{
COMPILE_AND_LOAD_SCRIPT ("rpc.exports.add = (a, b) => a + b;");
POST_MESSAGE ("[\"frida:rpc\",42,\"call\",\"add\",[1,2]]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",42,\"ok\",3]");
}

TESTCASE (method_can_be_called_async)
{
COMPILE_AND_LOAD_SCRIPT ("rpc.exports.add = async (a, b) => a + b;");
POST_MESSAGE ("[\"frida:rpc\",42,\"call\",\"add\",[1,2]]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",42,\"ok\",3]");
}

TESTCASE (method_can_throw_sync)
{
COMPILE_AND_LOAD_SCRIPT (
"rpc.exports.foo = (a, b) => {"
"const result = a + b;"
"if (result >= 0)"
"return result;"
"else "
"throw new Error('no');"
"};"
"rpc.exports.bar = (a, b) => {"
"return new Promise((resolve, reject) => {"
"const result = a + b;"
"if (result >= 0)"
"resolve(result);"
"else "
"reject(new Error('nope'));"
"});"
"};"
"rpc.exports.badger = () => {"
"const buf = Memory.allocUtf8String(\"Yo\");"
"return buf.readByteArray(2);"
"};"
"rpc.exports.returnNull = () => {"
"return null;"
"};");
EXPECT_NO_MESSAGES ();
"rpc.exports.raise = () => { throw new Error('no'); }");
POST_MESSAGE ("[\"frida:rpc\",42,\"call\",\"raise\",[]]");
EXPECT_SEND_MESSAGE_WITH_PREFIX ("[\"frida:rpc\",42,\"error\",\"no\",");
}

POST_MESSAGE ("[\"frida:rpc\",1,\"list\"]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",1,\"ok\","
"[\"foo\",\"bar\",\"badger\",\"returnNull\"]]");
TESTCASE (method_can_throw_async)
{
COMPILE_AND_LOAD_SCRIPT (
"rpc.exports.raise = async () => { throw new Error('no'); }");
POST_MESSAGE ("[\"frida:rpc\",42,\"call\",\"raise\",[]]");
EXPECT_SEND_MESSAGE_WITH_PREFIX ("[\"frida:rpc\",42,\"error\",\"no\",");
}

POST_MESSAGE ("[\"frida:rpc\",2,\"call\",\"foo\",[1,2]]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",2,\"ok\",3]");
TESTCASE (method_can_return_null)
{
COMPILE_AND_LOAD_SCRIPT ("rpc.exports.returnNull = () => null;");
POST_MESSAGE ("[\"frida:rpc\",42,\"call\",\"returnNull\",[]]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",42,\"ok\",null]");
}

POST_MESSAGE ("[\"frida:rpc\",3,\"call\",\"foo\",[1,-2]]");
EXPECT_SEND_MESSAGE_WITH_PREFIX ("[\"frida:rpc\",3,\"error\",\"no\",");
TESTCASE (method_can_receive_binary_data)
{
const guint8 data_to_send[2] = { 0x13, 0x37 };
GBytes * bytes;

POST_MESSAGE ("[\"frida:rpc\",4,\"call\",\"bar\",[3,4]]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",4,\"ok\",7]");
COMPILE_AND_LOAD_SCRIPT (
"rpc.exports.eat = (str, data) => {"
"send(str, data);"
"}");

POST_MESSAGE ("[\"frida:rpc\",5,\"call\",\"bar\",[3,-4]]");
EXPECT_SEND_MESSAGE_WITH_PREFIX ("[\"frida:rpc\",5,\"error\",\"nope\",");
bytes = g_bytes_new_static (data_to_send, sizeof (data_to_send));
gum_script_post (fixture->script,
"[\"frida:rpc\",42,\"call\",\"eat\",[\"yoghurt\"]]", bytes);
g_bytes_unref (bytes);

POST_MESSAGE ("[\"frida:rpc\",6,\"call\",\"baz\",[]]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",6,\"error\","
"\"unable to find method 'baz'\"]");
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA ("\"yoghurt\"", "13 37");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",42,\"ok\",null]");
}

POST_MESSAGE ("[\"frida:rpc\",7,\"call\",\"badger\",[]]");
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA ("[\"frida:rpc\",7,\"ok\",{}]",
TESTCASE (method_can_return_binary_data)
{
COMPILE_AND_LOAD_SCRIPT (
"rpc.exports.read = () => {"
"const buf = Memory.allocUtf8String(\"Yo\");"
"return buf.readByteArray(2);"
"};");
POST_MESSAGE ("[\"frida:rpc\",42,\"call\",\"read\",[]]");
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA ("[\"frida:rpc\",42,\"ok\",null]",
"59 6f");
}

POST_MESSAGE ("[\"frida:rpc\",8,\"call\",\"returnNull\",[]]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",8,\"ok\",null]");
TESTCASE (method_can_return_value_and_binary_data)
{
COMPILE_AND_LOAD_SCRIPT (
"rpc.exports.read = () => {"
"const buf = Memory.allocUtf8String(\"Yo\");"
"return [{meta: 'data'}, buf.readByteArray(2)];"
"};");
POST_MESSAGE ("[\"frida:rpc\",42,\"call\",\"read\",[]]");
EXPECT_SEND_MESSAGE_WITH_PAYLOAD_AND_DATA (
"[\"frida:rpc\",42,\"ok\",{\"meta\":\"data\"}]",
"59 6f");
}

TESTCASE (method_list_can_be_queried)
{
COMPILE_AND_LOAD_SCRIPT ("rpc.exports = { a() {}, b() {}, c() {} };");
POST_MESSAGE ("[\"frida:rpc\",42,\"list\"]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",42,\"ok\",[\"a\",\"b\",\"c\"]]");
}

TESTCASE (calling_inexistent_method_should_throw_error)
{
COMPILE_AND_LOAD_SCRIPT ("");
POST_MESSAGE ("[\"frida:rpc\",42,\"call\",\"banana\",[]]");
EXPECT_SEND_MESSAGE_WITH ("[\"frida:rpc\",42,\"error\","
"\"unable to find method 'banana'\"]");
}

TESTCASE (message_can_be_sent)
Expand Down
Loading