Skip to content

Commit

Permalink
fruity: Add RemoteXPC support
Browse files Browse the repository at this point in the history
Kudos to @doronz88 for figuring out most of the protocol bits that we
needed.

Co-authored-by: Håvard Sørbø <[email protected]>
  • Loading branch information
oleavr and hsorbo committed May 30, 2024
1 parent 6158b87 commit a9bbd74
Show file tree
Hide file tree
Showing 31 changed files with 9,859 additions and 214 deletions.
51 changes: 51 additions & 0 deletions lib/base/frida-darwin.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "frida-darwin.h"

gpointer
_frida_dispatch_retain (gpointer object)
{
dispatch_retain (object);
return object;
}

void
_frida_xpc_connection_set_event_handler (xpc_connection_t connection, FridaXpcHandler handler, gpointer user_data)
{
xpc_connection_set_event_handler (connection, ^(xpc_object_t object)
{
handler (object, user_data);
});
}

void
_frida_xpc_connection_send_message_with_reply (xpc_connection_t connection, xpc_object_t message, dispatch_queue_t replyq,
FridaXpcHandler handler, gpointer user_data, GDestroyNotify notify)
{
xpc_connection_send_message_with_reply (connection, message, replyq, ^(xpc_object_t object)
{
handler (object, user_data);
if (notify != NULL)
notify (user_data);
});
}

gchar *
_frida_xpc_object_to_string (xpc_object_t object)
{
gchar * result;
char * str;

str = xpc_copy_description (object);
result = g_strdup (str);
free (str);

return result;
}

gboolean
_frida_xpc_dictionary_apply (xpc_object_t dict, FridaXpcDictionaryApplier applier, gpointer user_data)
{
return xpc_dictionary_apply (dict, ^bool (const char * key, xpc_object_t val)
{
return applier (key, val, user_data);
});
}
22 changes: 22 additions & 0 deletions lib/base/frida-darwin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef __FRIDA_DARWIN_H__
#define __FRIDA_DARWIN_H__

#ifdef HAVE_MACOS

#include <glib.h>
#include <xpc/xpc.h>

typedef void (* FridaXpcHandler) (xpc_object_t object, gpointer user_data);
typedef gboolean (* FridaXpcDictionaryApplier) (const gchar * key, xpc_object_t val, gpointer user_data);

gpointer _frida_dispatch_retain (gpointer object);

void _frida_xpc_connection_set_event_handler (xpc_connection_t connection, FridaXpcHandler handler, gpointer user_data);
void _frida_xpc_connection_send_message_with_reply (xpc_connection_t connection, xpc_object_t message, dispatch_queue_t replyq,
FridaXpcHandler handler, gpointer user_data, GDestroyNotify notify);
gchar * _frida_xpc_object_to_string (xpc_object_t object);
gboolean _frida_xpc_dictionary_apply (xpc_object_t dict, FridaXpcDictionaryApplier applier, gpointer user_data);

#endif

#endif
14 changes: 14 additions & 0 deletions lib/base/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ base_sources = [
]

extra_vala_args = []

if host_os == 'macos'
base_sources += [
'xpc.vala',
'frida-darwin.c',
]
endif

extra_deps = []
if host_os_family != 'windows'
extra_vala_args += '--pkg=gio-unix-2.0'
Expand All @@ -23,6 +31,12 @@ base_vala_args = [gum_vala_args, '--pkg=gio-2.0', '--pkg=json-glib-1.0']
if nice_dep.found()
base_vala_args += '--pkg=nice'
endif
if host_os_family == 'darwin'
base_vala_args += [
'--pkg=darwin-gcd',
'--pkg=darwin-xpc',
]
endif

base = static_library('frida-base-' + api_version, base_sources,
c_args: frida_component_cflags,
Expand Down
236 changes: 236 additions & 0 deletions lib/base/xpc.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
namespace Frida {
public class XpcClient : Object {
public signal void message (Darwin.Xpc.Object obj);

public State state {
get {
return _state;
}
}

public Darwin.Xpc.Connection connection {
get;
construct;
}

public Darwin.GCD.DispatchQueue queue {
get;
construct;
}

private State _state;
private string? close_reason;
private MainContext main_context;

public enum State {
OPEN,
CLOSING,
CLOSED,
}

public static XpcClient make_for_mach_service (string name, Darwin.GCD.DispatchQueue queue) {
return new XpcClient (Darwin.Xpc.Connection.create_mach_service (name, queue, 0), queue);
}

public XpcClient (Darwin.Xpc.Connection connection, Darwin.GCD.DispatchQueue queue) {
Object (connection: connection, queue: queue);
}

construct {
main_context = MainContext.ref_thread_default ();

connection.set_event_handler (on_event);
connection.activate ();
}

public override void dispose () {
if (close_reason != null) {
change_state (CLOSED);
} else {
change_state (CLOSING);
this.ref ();
connection.cancel ();
}

base.dispose ();
}

public async Darwin.Xpc.Object request (Darwin.Xpc.Object message, Cancellable? cancellable) throws Error, IOError {
Darwin.Xpc.Object? reply = null;
connection.send_message_with_reply (message, queue, r => {
schedule_on_frida_thread (() => {
reply = r;
request.callback ();
return Source.REMOVE;
});
});

yield;

if (reply.type == Darwin.Xpc.Error.TYPE) {
var e = (Darwin.Xpc.Error) reply;
throw new Error.NOT_SUPPORTED ("%s", e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION));
}

return reply;
}

private void change_state (State new_state) {
_state = new_state;
notify_property ("state");
}

private void on_event (Darwin.Xpc.Object obj) {
schedule_on_frida_thread (() => {
if (obj.type == Darwin.Xpc.Error.TYPE) {
var e = (Darwin.Xpc.Error) obj;
close_reason = e.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
if (state == CLOSING) {
change_state (CLOSED);
unref ();
}
} else {
message (obj);
}
return Source.REMOVE;
});
}

private void schedule_on_frida_thread (owned SourceFunc function) {
var source = new IdleSource ();
source.set_callback ((owned) function);
source.attach (main_context);
}
}

public class XpcObjectReader {
public Darwin.Xpc.Object root_object {
get {
return scopes.peek_head ().object;
}
}

public Darwin.Xpc.Object current_object {
get {
return scopes.peek_tail ().object;
}
}

private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();

public XpcObjectReader (Darwin.Xpc.Object obj) {
push_scope (obj);
}

public bool has_member (string name) throws Error {
return peek_scope ().get_dictionary ().get_value (name) != null;
}

public bool try_read_member (string name) throws Error {
var scope = peek_scope ();
var dict = scope.get_dictionary ();
var val = dict.get_value (name);
if (val == null)
return false;

push_scope (val);

return true;
}

public unowned XpcObjectReader read_member (string name) throws Error {
var scope = peek_scope ();
var dict = scope.get_dictionary ();
var val = dict.get_value (name);
if (val == null)
throw new Error.PROTOCOL ("Key '%s' not found in dictionary: %s", name, scope.object.to_string ());

push_scope (val);

return this;
}

public unowned XpcObjectReader end_member () {
pop_scope ();

return this;
}

public size_t count_elements () throws Error {
return peek_scope ().get_array ().count;
}

public unowned XpcObjectReader read_element (size_t index) throws Error {
push_scope (peek_scope ().get_array ().get_value (index));

return this;
}

public unowned XpcObjectReader end_element () throws Error {
pop_scope ();

return this;
}

public bool get_bool_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.Bool> (Darwin.Xpc.Bool.TYPE).get_value ();
}

public int64 get_int64_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.Int64> (Darwin.Xpc.Int64.TYPE).get_value ();
}

public uint64 get_uint64_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.UInt64> (Darwin.Xpc.UInt64.TYPE).get_value ();
}

public unowned string get_string_value () throws Error {
return peek_scope ().get_object<Darwin.Xpc.String> (Darwin.Xpc.String.TYPE).get_string_ptr ();
}

public unowned string get_error_description () throws Error {
var error = peek_scope ().get_object<Darwin.Xpc.Error> (Darwin.Xpc.Error.TYPE);
return error.get_string (Darwin.Xpc.Error.KEY_DESCRIPTION);
}

public unowned Darwin.Xpc.Object get_object_value (Darwin.Xpc.Type expected_type) throws Error {
return peek_scope ().get_object<Darwin.Xpc.Object> (expected_type);
}

private void push_scope (Darwin.Xpc.Object obj) {
scopes.offer_tail (new Scope (obj));
}

private Scope peek_scope () {
return scopes.peek_tail ();
}

private Scope pop_scope () {
return scopes.poll_tail ();
}

private class Scope {
public Darwin.Xpc.Object object;
private unowned Darwin.Xpc.Type type;

public Scope (Darwin.Xpc.Object obj) {
object = obj;
type = obj.type;
}

public unowned T get_object<T> (Darwin.Xpc.Type expected_type) throws Error {
if (type != expected_type)
throw new Error.PROTOCOL ("Expected type '%s', got '%s'", expected_type.name, type.name);
return object;
}

public unowned Darwin.Xpc.Array get_array () throws Error {
return get_object<Darwin.Xpc.Array> (Darwin.Xpc.Array.TYPE);
}

public unowned Darwin.Xpc.Dictionary get_dictionary () throws Error {
return get_object<Darwin.Xpc.Dictionary> (Darwin.Xpc.Dictionary.TYPE);
}
}
}
}
Loading

0 comments on commit a9bbd74

Please sign in to comment.