-
-
Notifications
You must be signed in to change notification settings - Fork 201
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
31 changed files
with
9,863 additions
and
214 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.