mirror of
https://github.com/frida/frida-core.git
synced 2026-11-08 17:12:49 -10:00
Compare commits
No files matched your search
@@ -0,0 +1,34 @@
|
||||
namespace Frida {
|
||||
public class CFArray<T> {
|
||||
private CoreFoundation.Array handle;
|
||||
|
||||
public static CFArray<T> wrap<T> (void * handle) {
|
||||
return new CFArray<T> ((CoreFoundation.Array) handle);
|
||||
}
|
||||
|
||||
private CFArray (CoreFoundation.Array handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public Iterator<T> iterator () {
|
||||
return new Iterator<T> (handle);
|
||||
}
|
||||
|
||||
public class Iterator<T> {
|
||||
public CoreFoundation.Array handle;
|
||||
private CoreFoundation.Index i = 0;
|
||||
private CoreFoundation.Index length;
|
||||
|
||||
internal Iterator (CoreFoundation.Array arr) {
|
||||
handle = arr;
|
||||
length = arr.length;
|
||||
}
|
||||
|
||||
public unowned T? next_value () {
|
||||
if (i == length)
|
||||
return null;
|
||||
return handle[i++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,10 @@ if host_os == 'macos'
|
||||
]
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
base_sources += 'corefoundation.vala'
|
||||
endif
|
||||
|
||||
extra_deps = []
|
||||
if host_os_family != 'windows'
|
||||
extra_vala_args += '--pkg=gio-unix-2.0'
|
||||
@@ -33,6 +37,7 @@ if nice_dep.found()
|
||||
endif
|
||||
if host_os_family == 'darwin'
|
||||
base_vala_args += [
|
||||
'--pkg=corefoundation',
|
||||
'--pkg=darwin-gcd',
|
||||
'--pkg=darwin-xpc',
|
||||
]
|
||||
|
||||
+2
-15
@@ -774,25 +774,12 @@ namespace Frida {
|
||||
protected extern void _handle_transport_packet (uint8[] data);
|
||||
|
||||
protected int _emit_transport_packet (uint8[] data) {
|
||||
var v = OutputVector ();
|
||||
v.buffer = data;
|
||||
v.size = data.length;
|
||||
|
||||
OutputVector[] vectors = { v };
|
||||
|
||||
var m = OutputMessage ();
|
||||
m.vectors = vectors;
|
||||
m.num_vectors = vectors.length;
|
||||
|
||||
OutputMessage[] messages = { m };
|
||||
|
||||
try {
|
||||
transport_socket.send_messages (messages, 0, 0, io_cancellable);
|
||||
Udp.send (data, transport_socket, io_cancellable);
|
||||
return 0;
|
||||
} catch (GLib.Error e) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected void _on_sctp_socket_events_changed () {
|
||||
|
||||
+175
-55
@@ -44,6 +44,59 @@ namespace Frida {
|
||||
public extern void enable_nodelay (Socket socket);
|
||||
}
|
||||
|
||||
namespace Udp {
|
||||
public size_t recv (uint8[] data, DatagramBased source, Cancellable? cancellable, out InetSocketAddress remote_address)
|
||||
throws Error, IOError {
|
||||
var v = InputVector ();
|
||||
v.buffer = data;
|
||||
v.size = data.length;
|
||||
|
||||
InputVector[] vectors = { v };
|
||||
|
||||
var m = InputMessage ();
|
||||
remote_address = null;
|
||||
m.address = &remote_address;
|
||||
m.vectors = vectors;
|
||||
m.num_vectors = vectors.length;
|
||||
|
||||
InputMessage[] messages = { m };
|
||||
|
||||
try {
|
||||
source.receive_messages (messages, 0, 0, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
return messages[0].bytes_received;
|
||||
}
|
||||
|
||||
public void send (uint8[] data, DatagramBased sink, Cancellable? cancellable) throws Error, IOError {
|
||||
send_to (data, null, sink, cancellable);
|
||||
}
|
||||
|
||||
public void send_to (uint8[] data, InetSocketAddress? dest_addr, DatagramBased sink, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var v = OutputVector ();
|
||||
v.buffer = data;
|
||||
v.size = data.length;
|
||||
|
||||
OutputVector[] vectors = { v };
|
||||
|
||||
var m = OutputMessage ();
|
||||
m.address = dest_addr;
|
||||
m.vectors = vectors;
|
||||
m.num_vectors = vectors.length;
|
||||
|
||||
OutputMessage[] messages = { m };
|
||||
|
||||
try {
|
||||
sink.send_messages (messages, 0, 0, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class EndpointParameters : Object {
|
||||
public string? address {
|
||||
get;
|
||||
@@ -99,11 +152,17 @@ namespace Frida {
|
||||
var request = new StringBuilder.sized (256);
|
||||
request.append ("GET /ws HTTP/1.1\r\n");
|
||||
string protocol = (transport == TLS) ? "wss" : "ws";
|
||||
string canonical_host = canonicalize_host (host);
|
||||
string uri = protocol + "://" + canonical_host + "/ws";
|
||||
var msg = new Soup.Message ("GET", uri);
|
||||
NetworkAddress addr;
|
||||
try {
|
||||
addr = NetworkAddress.parse (host, 0);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
}
|
||||
uint16 port = addr.get_port ();
|
||||
var uri = Uri.build (UriFlags.NONE, protocol, null, addr.get_hostname (), (port != 0) ? port : -1, "/ws", null, null);
|
||||
var msg = new Soup.Message.from_uri ("GET", uri);
|
||||
Soup.websocket_client_prepare_handshake (msg, origin, null, null);
|
||||
msg.request_headers.replace ("Host", canonical_host);
|
||||
msg.request_headers.replace ("Host", make_host_header_value (uri));
|
||||
msg.request_headers.replace ("User-Agent", "Frida/" + _version_string ());
|
||||
msg.request_headers.foreach ((name, val) => {
|
||||
request.append (name + ": " + val + "\r\n");
|
||||
@@ -167,13 +226,12 @@ namespace Frida {
|
||||
return connection;
|
||||
}
|
||||
|
||||
private string canonicalize_host (string raw_host) {
|
||||
if (raw_host.has_suffix (":80") || raw_host.has_suffix (":443")) {
|
||||
string[] tokens = raw_host.split (":", 2);
|
||||
return tokens[0];
|
||||
}
|
||||
|
||||
return raw_host;
|
||||
private string make_host_header_value (Uri uri) {
|
||||
unowned string host = uri.get_host ();
|
||||
if (!Hostname.is_ip_address (host))
|
||||
return host;
|
||||
var inet_addr = new InetAddress.from_string (host);
|
||||
return (inet_addr.get_family () == IPV6) ? @"[$host]" : host;
|
||||
}
|
||||
|
||||
public class WebService : Object {
|
||||
@@ -195,14 +253,20 @@ namespace Frida {
|
||||
default = FAIL;
|
||||
}
|
||||
|
||||
public DynamicInterfaceObserver? dynamic_interface_observer {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public SocketAddress? listen_address {
|
||||
get {
|
||||
return _listen_address;
|
||||
}
|
||||
}
|
||||
|
||||
private Soup.Server? server;
|
||||
private Soup.Server? main_server;
|
||||
private SocketAddress? _listen_address;
|
||||
private Gee.Map<string, Soup.Server> dynamic_interface_servers = new Gee.HashMap<string, Soup.Server> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
@@ -210,11 +274,12 @@ namespace Frida {
|
||||
private MainContext? dbus_context;
|
||||
|
||||
public WebService (EndpointParameters endpoint_params, WebServiceFlavor flavor,
|
||||
PortConflictBehavior on_port_conflict = FAIL) {
|
||||
PortConflictBehavior on_port_conflict = FAIL, DynamicInterfaceObserver? dynif_observer = null) {
|
||||
Object (
|
||||
endpoint_params: endpoint_params,
|
||||
flavor: flavor,
|
||||
on_port_conflict: on_port_conflict
|
||||
on_port_conflict: on_port_conflict,
|
||||
dynamic_interface_observer: dynif_observer
|
||||
);
|
||||
}
|
||||
|
||||
@@ -250,23 +315,11 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async SocketAddress do_start (Cancellable? cancellable) throws Error, IOError {
|
||||
server = (Soup.Server) Object.new (typeof (Soup.Server),
|
||||
"tls-certificate", endpoint_params.certificate);
|
||||
|
||||
server.add_websocket_handler ("/ws", endpoint_params.origin, null, on_websocket_opened);
|
||||
|
||||
if (endpoint_params.asset_root != null)
|
||||
server.add_handler (null, on_asset_request);
|
||||
|
||||
main_server = create_server ();
|
||||
SocketAddress? first_effective_address = null;
|
||||
SocketConnectable connectable = (flavor == CONTROL)
|
||||
? parse_control_address (endpoint_params.address, endpoint_params.port)
|
||||
: parse_cluster_address (endpoint_params.address, endpoint_params.port);
|
||||
|
||||
Soup.ServerListenOptions listen_options = (endpoint_params.certificate != null)
|
||||
? Soup.ServerListenOptions.HTTPS
|
||||
: 0;
|
||||
|
||||
SocketAddress? first_effective_address = null;
|
||||
var enumerator = connectable.enumerate ();
|
||||
while (true) {
|
||||
SocketAddress? address;
|
||||
@@ -281,29 +334,10 @@ namespace Frida {
|
||||
SocketAddress? effective_address = null;
|
||||
InetSocketAddress? inet_address = address as InetSocketAddress;
|
||||
if (inet_address != null) {
|
||||
uint16 start_port = inet_address.get_port ();
|
||||
uint16 candidate_port = start_port;
|
||||
do {
|
||||
try {
|
||||
server.listen (inet_address, listen_options);
|
||||
effective_address = inet_address;
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.ADDRESS_IN_USE && on_port_conflict == PICK_NEXT) {
|
||||
candidate_port++;
|
||||
if (candidate_port == start_port)
|
||||
throw new Error.ADDRESS_IN_USE ("Unable to bind to any port");
|
||||
if (candidate_port == 0)
|
||||
candidate_port = 1024;
|
||||
inet_address = new InetSocketAddress (inet_address.get_address (),
|
||||
candidate_port);
|
||||
} else {
|
||||
throw_listen_error (e);
|
||||
}
|
||||
}
|
||||
} while (effective_address == null);
|
||||
effective_address = listen_on_inet_address (inet_address, main_server);
|
||||
} else {
|
||||
try {
|
||||
server.listen (address, listen_options);
|
||||
main_server.listen (address, compute_listen_options ());
|
||||
effective_address = address;
|
||||
} catch (GLib.Error e) {
|
||||
throw_listen_error (e);
|
||||
@@ -317,9 +351,85 @@ namespace Frida {
|
||||
if (first_effective_address == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to resolve listening address");
|
||||
|
||||
if (dynamic_interface_observer != null) {
|
||||
dynamic_interface_observer.interface_attached.connect (on_dynamic_interface_attached);
|
||||
dynamic_interface_observer.interface_detached.connect (on_dynamic_interface_detached);
|
||||
dynamic_interface_observer.start ();
|
||||
}
|
||||
|
||||
return first_effective_address;
|
||||
}
|
||||
|
||||
private void on_dynamic_interface_attached (string name, InetAddress ip) {
|
||||
uint16 port = endpoint_params.port;
|
||||
if (port == 0)
|
||||
port = (flavor == CONTROL) ? DEFAULT_CONTROL_PORT : DEFAULT_CLUSTER_PORT;
|
||||
|
||||
var server = create_server ();
|
||||
try {
|
||||
listen_on_inet_address (new InetSocketAddress (ip, port), server);
|
||||
} catch (Error e) {
|
||||
return;
|
||||
}
|
||||
dynamic_interface_servers[name] = server;
|
||||
}
|
||||
|
||||
private void on_dynamic_interface_detached (string name, InetAddress ip) {
|
||||
Soup.Server server;
|
||||
if (dynamic_interface_servers.unset (name, out server))
|
||||
destroy_server (server);
|
||||
}
|
||||
|
||||
private Soup.Server create_server () {
|
||||
var server = (Soup.Server) Object.new (typeof (Soup.Server),
|
||||
"tls-certificate", endpoint_params.certificate);
|
||||
|
||||
server.add_websocket_handler ("/ws", endpoint_params.origin, null, on_websocket_opened);
|
||||
|
||||
if (endpoint_params.asset_root != null)
|
||||
server.add_handler (null, on_asset_request);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
private void destroy_server (Soup.Server server) {
|
||||
if (endpoint_params.asset_root != null)
|
||||
server.remove_handler ("/");
|
||||
server.remove_handler ("/ws");
|
||||
|
||||
server.disconnect ();
|
||||
}
|
||||
|
||||
private Soup.ServerListenOptions compute_listen_options () {
|
||||
return (endpoint_params.certificate != null)
|
||||
? Soup.ServerListenOptions.HTTPS
|
||||
: 0;
|
||||
}
|
||||
|
||||
private InetSocketAddress listen_on_inet_address (InetSocketAddress address, Soup.Server server) throws Error {
|
||||
InetSocketAddress candidate_address = address;
|
||||
uint16 start_port = address.get_port ();
|
||||
uint16 candidate_port = start_port;
|
||||
do {
|
||||
try {
|
||||
server.listen (candidate_address, compute_listen_options ());
|
||||
return candidate_address;
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.ADDRESS_IN_USE && on_port_conflict == PICK_NEXT) {
|
||||
candidate_port++;
|
||||
if (candidate_port == start_port)
|
||||
throw new Error.ADDRESS_IN_USE ("Unable to bind to any port");
|
||||
if (candidate_port == 0)
|
||||
candidate_port = 1024;
|
||||
candidate_address = new InetSocketAddress (candidate_address.get_address (),
|
||||
candidate_port);
|
||||
} else {
|
||||
throw_listen_error (e);
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private static void throw_listen_error (GLib.Error e) throws Error {
|
||||
if (e is IOError.ADDRESS_IN_USE)
|
||||
@@ -341,14 +451,17 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void do_stop () {
|
||||
if (server == null)
|
||||
return;
|
||||
foreach (var server in dynamic_interface_servers.values)
|
||||
destroy_server (server);
|
||||
dynamic_interface_servers.clear ();
|
||||
|
||||
if (endpoint_params.asset_root != null)
|
||||
server.remove_handler ("/");
|
||||
server.remove_handler ("/ws");
|
||||
if (dynamic_interface_observer != null) {
|
||||
dynamic_interface_observer.interface_attached.disconnect (on_dynamic_interface_attached);
|
||||
dynamic_interface_observer.interface_detached.disconnect (on_dynamic_interface_detached);
|
||||
}
|
||||
|
||||
server.disconnect ();
|
||||
if (main_server != null)
|
||||
destroy_server (main_server);
|
||||
}
|
||||
|
||||
private void on_websocket_opened (Soup.Server server, Soup.ServerMessage msg, string path,
|
||||
@@ -662,6 +775,13 @@ namespace Frida {
|
||||
PICK_NEXT
|
||||
}
|
||||
|
||||
public interface DynamicInterfaceObserver : Object {
|
||||
public signal void interface_attached (string name, InetAddress ip);
|
||||
public signal void interface_detached (string name, InetAddress ip);
|
||||
|
||||
public abstract void start ();
|
||||
}
|
||||
|
||||
public extern static unowned string _version_string ();
|
||||
|
||||
private class WebConnection : IOStream {
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace Frida {
|
||||
public abstract unowned ObjectBuilder add_uint64_value (uint64 val);
|
||||
public abstract unowned ObjectBuilder add_data_value (Bytes val);
|
||||
public abstract unowned ObjectBuilder add_string_value (string val);
|
||||
public abstract unowned ObjectBuilder add_uuid_value (string val);
|
||||
public abstract unowned ObjectBuilder add_raw_value (Bytes val);
|
||||
|
||||
public abstract Bytes build ();
|
||||
@@ -239,11 +238,6 @@ namespace Frida {
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_uuid_value (string val) {
|
||||
builder.add_string_value (val);
|
||||
return this;
|
||||
}
|
||||
|
||||
public unowned ObjectBuilder add_raw_value (Bytes val) {
|
||||
string uuid = Uuid.string_random ();
|
||||
builder.add_string_value (uuid);
|
||||
|
||||
+18
-3
@@ -75,6 +75,10 @@ namespace Frida {
|
||||
return reply;
|
||||
}
|
||||
|
||||
public void post (Darwin.Xpc.Object message) throws Error, IOError {
|
||||
connection.send_message (message);
|
||||
}
|
||||
|
||||
private void change_state (State new_state) {
|
||||
_state = new_state;
|
||||
notify_property ("state");
|
||||
@@ -85,9 +89,16 @@ namespace Frida {
|
||||
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 ();
|
||||
switch (state) {
|
||||
case OPEN:
|
||||
change_state (CLOSED);
|
||||
break;
|
||||
case CLOSING:
|
||||
change_state (CLOSED);
|
||||
unref ();
|
||||
break;
|
||||
case CLOSED:
|
||||
assert_not_reached ();
|
||||
}
|
||||
} else {
|
||||
message (obj);
|
||||
@@ -188,6 +199,10 @@ namespace Frida {
|
||||
return peek_scope ().get_object<Darwin.Xpc.String> (Darwin.Xpc.String.TYPE).get_string_ptr ();
|
||||
}
|
||||
|
||||
public unowned uint8[] get_uuid_value () throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.Uuid> (Darwin.Xpc.Uuid.TYPE).get_bytes ()[:16];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -1497,7 +1497,8 @@ namespace Frida.Gadget {
|
||||
var endpoint_params = new EndpointParameters (interaction.address, interaction.port,
|
||||
parse_certificate (interaction.certificate, location), interaction.origin, auth_service, asset_root);
|
||||
|
||||
service = new WebService (endpoint_params, CONTROL, interaction.on_port_conflict);
|
||||
service = new WebService (endpoint_params, WebServiceFlavor.CONTROL, interaction.on_port_conflict,
|
||||
new TunnelInterfaceObserver ());
|
||||
service.incoming.connect (on_incoming_connection);
|
||||
yield service.start (io_cancellable);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ raw_gadget = shared_library('frida-gadget-raw', gadget_sources,
|
||||
link_depends: extra_link_depends,
|
||||
vs_module_defs: 'frida-gadget.def',
|
||||
override_options: ['b_asneeded=true'],
|
||||
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep, gumjs_dep, base_dep, tls_provider_dep, payload_dep] + platform_deps,
|
||||
dependencies: [gio_dep, gee_dep, json_glib_dep, gum_dep, gumjs_dep, base_dep, netif_dep, tls_provider_dep, payload_dep] + platform_deps,
|
||||
)
|
||||
|
||||
operations = [
|
||||
|
||||
@@ -2,6 +2,7 @@ if host_os == 'android'
|
||||
subdir('selinux')
|
||||
endif
|
||||
subdir('base')
|
||||
subdir('netif')
|
||||
subdir('pipe')
|
||||
subdir('payload')
|
||||
if have_local_backend
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
netif_sources = [
|
||||
'tunnel-interface-observer.vala',
|
||||
]
|
||||
|
||||
extra_vala_args = []
|
||||
extra_link_args = []
|
||||
|
||||
if host_os in ['ios', 'tvos']
|
||||
extra_vala_args += [
|
||||
'--pkg=corefoundation',
|
||||
'--pkg=darwin-gcd',
|
||||
'--pkg=darwin-systemconfiguration',
|
||||
]
|
||||
extra_link_args += '-Wl,-framework,SystemConfiguration'
|
||||
endif
|
||||
|
||||
netif = static_library('frida-netif', netif_sources,
|
||||
c_args: frida_component_cflags,
|
||||
vala_args: [base_vala_args, extra_vala_args],
|
||||
dependencies: [base_dep, gee_dep],
|
||||
)
|
||||
|
||||
netif_dep = declare_dependency(
|
||||
include_directories: include_directories('.'),
|
||||
link_args: extra_link_args,
|
||||
link_with: netif,
|
||||
dependencies: base_dep,
|
||||
)
|
||||
@@ -0,0 +1,92 @@
|
||||
public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
|
||||
#if IOS || TVOS
|
||||
private Gee.Map<string, InetAddress> interfaces = new Gee.HashMap<string, InetAddress> ();
|
||||
|
||||
private Darwin.SystemConfiguration.DynamicStore? store;
|
||||
private Darwin.GCD.DispatchQueue event_queue =
|
||||
new Darwin.GCD.DispatchQueue ("re.frida.endpoint-enumerator", Darwin.GCD.DispatchQueueAttr.SERIAL);
|
||||
|
||||
private MainContext? main_context;
|
||||
|
||||
public override void dispose () {
|
||||
if (store != null) {
|
||||
store.set_dispatch_queue (null);
|
||||
store = null;
|
||||
ref ();
|
||||
event_queue.dispatch_async (do_dispose);
|
||||
}
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
private void do_dispose () {
|
||||
unref ();
|
||||
}
|
||||
|
||||
public void start () {
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
Darwin.SystemConfiguration.DynamicStoreContext context = { 0, };
|
||||
context.info = this;
|
||||
store = new Darwin.SystemConfiguration.DynamicStore (null, CoreFoundation.String.make ("Frida"),
|
||||
on_interfaces_changed_wrapper, context);
|
||||
var pattern = CoreFoundation.String.make ("State:/Network/Interface/utun.*/IPv6");
|
||||
var patterns = new CoreFoundation.Array (null, ((CoreFoundation.Type[]) &pattern)[:1]);
|
||||
store.set_notification_keys (null, patterns);
|
||||
store.set_dispatch_queue (event_queue);
|
||||
handle_interface_changes (store.copy_key_list (pattern));
|
||||
}
|
||||
|
||||
private static void on_interfaces_changed_wrapper (Darwin.SystemConfiguration.DynamicStore store,
|
||||
CoreFoundation.Array changed_keys, void * info) {
|
||||
unowned TunnelInterfaceObserver enumerator = (TunnelInterfaceObserver) info;
|
||||
enumerator.on_interfaces_changed (changed_keys);
|
||||
}
|
||||
|
||||
private void on_interfaces_changed (CoreFoundation.Array changed_keys) {
|
||||
schedule_on_frida_thread (() => {
|
||||
if (store != null)
|
||||
handle_interface_changes (changed_keys);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private void handle_interface_changes (CoreFoundation.Array changed_keys) {
|
||||
var addresses_str = CoreFoundation.String.make ("Addresses");
|
||||
|
||||
foreach (var key in CFArray.wrap<CoreFoundation.String> (changed_keys)) {
|
||||
string name = key.to_string ().split ("/")[3];
|
||||
|
||||
InetAddress? address = null;
|
||||
var val = (CoreFoundation.Dictionary) store.copy_value (key);
|
||||
if (val != null) {
|
||||
foreach (var raw_address in CFArray.wrap<CoreFoundation.String> (val[addresses_str])) {
|
||||
var str = raw_address.to_string ();
|
||||
bool is_reserved_ipv6_range = str.has_prefix ("fc") || str.has_prefix ("fd");
|
||||
bool is_tunnel = is_reserved_ipv6_range && str.has_suffix ("::1");
|
||||
if (is_tunnel) {
|
||||
address = new InetAddress.from_string (str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (address != null && !interfaces.has_key (name)) {
|
||||
interfaces[name] = address;
|
||||
interface_attached (name, address);
|
||||
}
|
||||
} else {
|
||||
if (interfaces.unset (name, out address))
|
||||
interface_detached (name, address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void schedule_on_frida_thread (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (main_context);
|
||||
}
|
||||
#else
|
||||
public void start () {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "pipe-glue.h"
|
||||
|
||||
#include "frida-tvos.h"
|
||||
|
||||
#include "pipe-glue.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
+68
-12
@@ -92,6 +92,12 @@ else
|
||||
host_arch = host_machine.cpu_family()
|
||||
host_abi = host_arch
|
||||
endif
|
||||
if cc.sizeof('void *') == 8
|
||||
host_cpu_mode = '64'
|
||||
else
|
||||
host_cpu_mode = '32'
|
||||
endif
|
||||
host_docks_mobile_devices = host_os in ['windows', 'macos', 'linux', 'freebsd'] and host_cpu_mode == '64'
|
||||
gumjs_archs = {
|
||||
'x86': 'ia32',
|
||||
'x86_64': 'x64',
|
||||
@@ -156,12 +162,6 @@ if host_os_family == 'darwin'
|
||||
endif
|
||||
endif
|
||||
|
||||
if cc.sizeof('void *') == 8
|
||||
host_cpu_mode = '64'
|
||||
else
|
||||
host_cpu_mode = '32'
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin' and host_arch == 'arm64'
|
||||
host_is_modern = host_abi == 'arm64e'
|
||||
else
|
||||
@@ -348,14 +348,27 @@ else
|
||||
endif
|
||||
|
||||
have_local_backend = get_option('local_backend').allowed()
|
||||
have_fruity_backend = get_option('fruity_backend').allowed()
|
||||
have_droidy_backend = get_option('droidy_backend').allowed()
|
||||
have_fruity_backend = get_option('fruity_backend') \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
.allowed()
|
||||
have_droidy_backend = get_option('droidy_backend') \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
.allowed()
|
||||
have_socket_backend = get_option('socket_backend').allowed()
|
||||
have_barebone_backend = quickjs_dep.found()
|
||||
have_barebone_backend = get_option('barebone_backend') \
|
||||
.disable_if(not quickjs_dep.found()) \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
.allowed()
|
||||
have_compiler_backend = get_option('compiler_backend') \
|
||||
.disable_if(not have_local_backend, error_message: 'compiler backend requires the local backend for script execution') \
|
||||
.disable_auto_if(host_os == 'watchos') \
|
||||
.allowed()
|
||||
if get_option('compiler_snapshot').auto()
|
||||
# Generating our snapshot with V8's CPU simulator takes a very long time.
|
||||
generate_compiler_snapshot = meson.can_run_host_binaries()
|
||||
else
|
||||
generate_compiler_snapshot = get_option('compiler_snapshot').enabled()
|
||||
endif
|
||||
|
||||
build_gadget = get_option('gadget') \
|
||||
.disable_auto_if(meson.is_subproject()) \
|
||||
@@ -450,10 +463,28 @@ openssl_dep = dependency('openssl',
|
||||
)
|
||||
|
||||
if have_fruity_backend
|
||||
foreach dep : ['libnghttp2', 'libngtcp2', 'libngtcp2_crypto_quictls', 'lwip']
|
||||
foreach dep : ['libnghttp2', 'libngtcp2', 'libngtcp2_crypto_quictls', 'libusb-1.0']
|
||||
backend_deps_private += dependency(dep)
|
||||
backend_reqs_private += dep
|
||||
endforeach
|
||||
|
||||
lwip_options = [
|
||||
'ipv4=disabled',
|
||||
'ipv6=enabled',
|
||||
'dns=disabled',
|
||||
'arp=disabled',
|
||||
'ethernet=enabled',
|
||||
'tcp_mss=1360',
|
||||
'tcp_snd_buf=65535',
|
||||
'tcp_wnd=65535',
|
||||
]
|
||||
if optimize_for_prod
|
||||
lwip_options += 'lwip_debug=disabled'
|
||||
else
|
||||
lwip_options += 'lwip_debug=enabled'
|
||||
endif
|
||||
backend_deps_private += dependency('lwip', default_options: lwip_options)
|
||||
backend_reqs_private += 'lwip'
|
||||
endif
|
||||
|
||||
if quickjs_dep.found()
|
||||
@@ -528,7 +559,7 @@ if host_os_family == 'darwin'
|
||||
backend_libs_private += ['-Wl,-framework,Foundation', '-lbsm']
|
||||
endif
|
||||
if host_os == 'macos'
|
||||
backend_libs_private += ['-Wl,-framework,AppKit', '-Wl,-framework,IOKit']
|
||||
backend_libs_private += ['-Wl,-framework,AppKit']
|
||||
endif
|
||||
if host_os in ['ios', 'tvos']
|
||||
backend_libs_private += ['-Wl,-framework,CoreGraphics', '-Wl,-framework,UIKit']
|
||||
@@ -655,8 +686,9 @@ if cc.get_id() == 'gcc' and cc.compiles('#if __GNUC__ >= 5\n#error NOTNEEDED\n#e
|
||||
add_project_arguments('-w', language: 'c')
|
||||
endif
|
||||
|
||||
vapidir = meson.current_source_dir() / 'vapi'
|
||||
vala_flags += [
|
||||
'--vapidir=' + meson.current_source_dir() / 'vapi',
|
||||
'--vapidir=' + vapidir,
|
||||
'--pkg', 'config',
|
||||
]
|
||||
|
||||
@@ -669,11 +701,13 @@ if host_abi != host_arch
|
||||
vala_flags += ['--define=' + host_abi.to_upper()]
|
||||
endif
|
||||
add_project_arguments(vala_flags, language: 'vala')
|
||||
add_project_arguments('-I' + vapidir, language: 'c')
|
||||
|
||||
native_vala_flags = [
|
||||
'--vapidir=' + meson.current_source_dir() / 'vapi',
|
||||
]
|
||||
add_project_arguments(native_vala_flags, language: 'vala', native: true)
|
||||
add_project_arguments('-I' + vapidir, language: 'c', native: true)
|
||||
|
||||
subdir('compat')
|
||||
subdir('tools')
|
||||
@@ -692,9 +726,31 @@ if build_tests
|
||||
subdir('tests')
|
||||
endif
|
||||
|
||||
summary(
|
||||
{
|
||||
'local': have_local_backend,
|
||||
'fruity': have_fruity_backend,
|
||||
'droidy': have_droidy_backend,
|
||||
'socket': have_socket_backend,
|
||||
'barebone': have_barebone_backend,
|
||||
'compiler': have_compiler_backend,
|
||||
},
|
||||
section: 'Backends',
|
||||
bool_yn: true,
|
||||
)
|
||||
summary(
|
||||
{
|
||||
'compat': compat_summary,
|
||||
'assets': get_option('assets'),
|
||||
'mapper': have_mapper,
|
||||
'connectivity': tls_provider_dep.found(),
|
||||
'compiler_snapshot': generate_compiler_snapshot,
|
||||
'gadget': build_gadget,
|
||||
'server': build_server,
|
||||
'portal': build_portal,
|
||||
'inject': build_inject,
|
||||
'tests': build_tests,
|
||||
},
|
||||
section: 'Features',
|
||||
bool_yn: true,
|
||||
)
|
||||
+1
-1
Submodule releng updated: b0e46f7b69...54cc29651c.
+4
-2
@@ -327,8 +327,10 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
|
||||
if stripped_line == "}":
|
||||
ignoring = False
|
||||
else:
|
||||
if stripped_line.startswith("public abstract") or stripped_line.startswith("public class Promise") \
|
||||
or stripped_line.startswith("public interface Future"):
|
||||
if stripped_line.startswith("public abstract") \
|
||||
or stripped_line.startswith("public class Promise") \
|
||||
or stripped_line.startswith("public interface Future") \
|
||||
or stripped_line.startswith("public class CF"):
|
||||
ignoring = True
|
||||
elif stripped_line.startswith("public enum") or stripped_line.startswith("public errordomain"):
|
||||
name = re.match(r"^public (?:enum|errordomain) (\w+) ", stripped_line).group(1)
|
||||
|
||||
@@ -3,13 +3,7 @@ compiler_sources = files(
|
||||
)
|
||||
|
||||
if have_compiler_backend
|
||||
if get_option('compiler_snapshot').auto()
|
||||
# Generating our snapshot with V8's CPU simulator takes a very long time.
|
||||
use_snapshot = meson.can_run_host_binaries()
|
||||
else
|
||||
use_snapshot = get_option('compiler_snapshot').enabled()
|
||||
endif
|
||||
compiler_mksnapshot = use_snapshot ? v8_mksnapshot : ''
|
||||
compiler_mksnapshot = generate_compiler_snapshot ? v8_mksnapshot : ''
|
||||
|
||||
compiler_agent = custom_target('frida-compiler-agent',
|
||||
input: [
|
||||
|
||||
@@ -89,7 +89,8 @@ namespace Frida {
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
host_session.uninjected.connect (notify_uninjected);
|
||||
|
||||
service = new WebService (endpoint_params, CONTROL);
|
||||
service = new WebService (endpoint_params, WebServiceFlavor.CONTROL, PortConflictBehavior.FAIL,
|
||||
new TunnelInterfaceObserver ());
|
||||
service.incoming.connect (on_server_connection);
|
||||
|
||||
broker_service.incoming.connect (on_broker_service_connection);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
devkit_outputs = [
|
||||
'frida-core.gir',
|
||||
'frida-core.h',
|
||||
static_lib_prefix + 'frida-core' + static_lib_suffix,
|
||||
'frida-core-example.c',
|
||||
|
||||
+33
-30
@@ -242,7 +242,7 @@ namespace Frida {
|
||||
opts["keepalive_interval"] = interval;
|
||||
}
|
||||
|
||||
var device = new Device (this, id, name, HostSessionProviderKind.REMOTE, socket_device.provider, raw_options);
|
||||
var device = new Device (this, socket_device.provider, id, name, raw_options);
|
||||
devices.add (device);
|
||||
added (device);
|
||||
changed ();
|
||||
@@ -358,7 +358,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void on_provider_available (HostSessionProvider provider) {
|
||||
var device = new Device (this, provider.id, provider.name, provider.kind, provider);
|
||||
var device = new Device (this, provider);
|
||||
devices.add (device);
|
||||
|
||||
foreach (var observer in on_device_added.to_array ())
|
||||
@@ -483,13 +483,19 @@ namespace Frida {
|
||||
public signal void lost ();
|
||||
|
||||
public string id {
|
||||
get;
|
||||
construct;
|
||||
get {
|
||||
if (_id != null)
|
||||
return _id;
|
||||
return provider.id;
|
||||
}
|
||||
}
|
||||
|
||||
public string name {
|
||||
get;
|
||||
construct;
|
||||
get {
|
||||
if (_name != null)
|
||||
return _name;
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
@@ -498,8 +504,18 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public DeviceType dtype {
|
||||
get;
|
||||
construct;
|
||||
get {
|
||||
switch (provider.kind) {
|
||||
case HostSessionProviderKind.LOCAL:
|
||||
return DeviceType.LOCAL;
|
||||
case HostSessionProviderKind.REMOTE:
|
||||
return DeviceType.REMOTE;
|
||||
case HostSessionProviderKind.USB:
|
||||
return DeviceType.USB;
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Bus bus {
|
||||
@@ -518,7 +534,8 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public delegate bool ProcessPredicate (Process process);
|
||||
private string? _id;
|
||||
private string? _name;
|
||||
|
||||
private HostSessionOptions? host_session_options;
|
||||
private Promise<HostSession>? host_session_request;
|
||||
@@ -533,32 +550,18 @@ namespace Frida {
|
||||
private Gee.Set<Service> services = new Gee.HashSet<Service> ();
|
||||
private Bus _bus;
|
||||
|
||||
internal Device (DeviceManager? manager, string id, string name, HostSessionProviderKind kind, HostSessionProvider provider,
|
||||
HostSessionOptions? options = null) {
|
||||
DeviceType dtype;
|
||||
switch (kind) {
|
||||
case HostSessionProviderKind.LOCAL:
|
||||
dtype = DeviceType.LOCAL;
|
||||
break;
|
||||
case HostSessionProviderKind.REMOTE:
|
||||
dtype = DeviceType.REMOTE;
|
||||
break;
|
||||
case HostSessionProviderKind.USB:
|
||||
dtype = DeviceType.USB;
|
||||
break;
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
public delegate bool ProcessPredicate (Process process);
|
||||
|
||||
internal Device (DeviceManager? manager, HostSessionProvider provider, string? id = null, string? name = null,
|
||||
HostSessionOptions? options = null) {
|
||||
Object (
|
||||
id: id,
|
||||
name: name,
|
||||
icon: provider.icon,
|
||||
dtype: dtype,
|
||||
manager: manager,
|
||||
provider: provider,
|
||||
manager: manager
|
||||
icon: provider.icon
|
||||
);
|
||||
|
||||
_id = id;
|
||||
_name = name;
|
||||
host_session_options = options;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ static const FridaFruityModel fruity_models[] =
|
||||
};
|
||||
|
||||
void
|
||||
_frida_fruity_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon, GError ** error)
|
||||
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
GError ** error)
|
||||
{
|
||||
const FridaFruityModel * model;
|
||||
guint i;
|
||||
@@ -1,188 +1,5 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public sealed class LinuxTunnelFinder : Object, TunnelFinder {
|
||||
public async Tunnel? find (string udid, Cancellable? cancellable) throws Error, IOError {
|
||||
NcmPeer? peer = yield locate_ncm_peer (udid, cancellable);
|
||||
if (peer == null)
|
||||
return null;
|
||||
|
||||
var bootstrap_disco = yield DiscoveryService.open (
|
||||
yield peer.open_tcp_connection (58783, cancellable), cancellable);
|
||||
|
||||
var tunnel_service = bootstrap_disco.get_service ("com.apple.internal.dt.coredevice.untrusted.tunnelservice");
|
||||
var pairing_transport = new XpcPairingTransport (yield peer.open_tcp_connection (tunnel_service.port, cancellable));
|
||||
var pairing_service = yield PairingService.open (pairing_transport, cancellable);
|
||||
|
||||
TunnelConnection tc = yield pairing_service.open_tunnel (peer.address, cancellable);
|
||||
|
||||
var disco = yield DiscoveryService.open (yield tc.open_connection (tc.remote_rsd_port, cancellable), cancellable);
|
||||
|
||||
return new LinuxTunnel (tc, disco);
|
||||
}
|
||||
|
||||
private static async NcmPeer? locate_ncm_peer (string udid, Cancellable? cancellable) throws Error, IOError {
|
||||
var device_ifaddrs = new Gee.ArrayList<unowned Linux.Network.IfAddrs> ();
|
||||
var fruit_finder = FruitFinder.make_default ();
|
||||
string raw_udid = udid.replace ("-", "");
|
||||
Linux.Network.IfAddrs ifaddrs;
|
||||
Linux.Network.getifaddrs (out ifaddrs);
|
||||
for (unowned Linux.Network.IfAddrs candidate = ifaddrs; candidate != null; candidate = candidate.ifa_next) {
|
||||
if (candidate.ifa_addr.sa_family != Posix.AF_INET6)
|
||||
continue;
|
||||
|
||||
string? candidate_udid = fruit_finder.udid_from_iface (candidate.ifa_name);
|
||||
if (candidate_udid != raw_udid)
|
||||
continue;
|
||||
|
||||
device_ifaddrs.add (candidate);
|
||||
}
|
||||
if (device_ifaddrs.is_empty)
|
||||
return null;
|
||||
|
||||
var sockets = new Gee.ArrayList<Socket> ();
|
||||
var sources = new Gee.ArrayList<Source> ();
|
||||
var readable_sockets = new Gee.ArrayQueue<Socket> ();
|
||||
bool timed_out = false;
|
||||
bool waiting = false;
|
||||
|
||||
var remote_address = new InetSocketAddress.from_string ("ff02::fb", 5353);
|
||||
var remoted_mdns_request = make_remoted_mdns_request ();
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
foreach (unowned Linux.Network.IfAddrs ifaddr in device_ifaddrs) {
|
||||
try {
|
||||
var sock = new Socket (IPV6, DATAGRAM, UDP);
|
||||
sock.bind (SocketAddress.from_native ((void *) ifaddr.ifa_addr, sizeof (Posix.SockAddrIn6)), false);
|
||||
sock.send_to (remote_address, remoted_mdns_request.get_data (), cancellable);
|
||||
|
||||
var source = sock.create_source (IN, cancellable);
|
||||
source.set_callback (() => {
|
||||
readable_sockets.offer (sock);
|
||||
if (waiting)
|
||||
locate_ncm_peer.callback ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
|
||||
sockets.add (sock);
|
||||
sources.add (source);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
var timeout_source = new TimeoutSource.seconds (2);
|
||||
timeout_source.set_callback (() => {
|
||||
timed_out = true;
|
||||
if (waiting)
|
||||
locate_ncm_peer.callback ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
timeout_source.attach (main_context);
|
||||
sources.add (timeout_source);
|
||||
|
||||
Socket? sock;
|
||||
while ((sock = readable_sockets.poll ()) == null && !timed_out) {
|
||||
waiting = true;
|
||||
yield;
|
||||
waiting = false;
|
||||
}
|
||||
|
||||
foreach (var source in sources)
|
||||
source.destroy ();
|
||||
|
||||
if (sock == null && timed_out)
|
||||
throw new Error.TIMED_OUT ("Unexpectedly timed out while waiting for mDNS reply");
|
||||
|
||||
InetSocketAddress sender;
|
||||
try {
|
||||
SocketAddress raw_sender;
|
||||
var response_buf = new uint8[2048];
|
||||
sock.receive_from (out raw_sender, response_buf, cancellable);
|
||||
sender = (InetSocketAddress) raw_sender;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
return new NcmPeer ("%s%%%u".printf (sender.address.to_string (), sender.scope_id));
|
||||
}
|
||||
|
||||
private static Bytes make_remoted_mdns_request () {
|
||||
uint16 transaction_id = 0;
|
||||
uint16 flags = 0;
|
||||
uint16 num_questions = 1;
|
||||
uint16 answer_rrs = 0;
|
||||
uint16 authority_rrs = 0;
|
||||
uint16 additional_rrs = 0;
|
||||
string components[] = { "_remoted", "_tcp", "local" };
|
||||
uint16 record_type = 12;
|
||||
uint16 dns_class = 1;
|
||||
return new BufferBuilder (BIG_ENDIAN)
|
||||
.append_uint16 (transaction_id)
|
||||
.append_uint16 (flags)
|
||||
.append_uint16 (num_questions)
|
||||
.append_uint16 (answer_rrs)
|
||||
.append_uint16 (authority_rrs)
|
||||
.append_uint16 (additional_rrs)
|
||||
.append_uint8 ((uint8) components[0].length)
|
||||
.append_string (components[0], StringTerminator.NONE)
|
||||
.append_uint8 ((uint8) components[1].length)
|
||||
.append_string (components[1], StringTerminator.NONE)
|
||||
.append_uint8 ((uint8) components[2].length)
|
||||
.append_string (components[2], StringTerminator.NUL)
|
||||
.append_uint16 (record_type)
|
||||
.append_uint16 (dns_class)
|
||||
.build ();
|
||||
}
|
||||
|
||||
private class NcmPeer {
|
||||
public string address;
|
||||
|
||||
public class NcmPeer (string address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
SocketConnection connection;
|
||||
try {
|
||||
NetworkAddress service_address = NetworkAddress.parse (address, port);
|
||||
|
||||
var client = new SocketClient ();
|
||||
connection = yield client.connect_async (service_address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class LinuxTunnel : Object, Tunnel {
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelConnection tunnel_connection;
|
||||
private DiscoveryService _discovery;
|
||||
|
||||
public LinuxTunnel (TunnelConnection conn, DiscoveryService disco) {
|
||||
tunnel_connection = conn;
|
||||
_discovery = disco;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
tunnel_connection.cancel ();
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
return yield tunnel_connection.open_connection (port, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxFruitFinder : Object, FruitFinder {
|
||||
public string? udid_from_iface (string ifname) throws Error {
|
||||
var net = "/sys/class/net";
|
||||
@@ -0,0 +1,585 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public sealed class MacOSCoreDeviceBackend : Object, Backend {
|
||||
private Gee.Map<string, MacOSCoreDeviceTransport> transports = new Gee.HashMap<string, MacOSCoreDeviceTransport> ();
|
||||
|
||||
private Promise<bool> all_current_devices_listed = new Promise<bool> ();
|
||||
private Promise<bool> browse_request = new Promise<bool> ();
|
||||
|
||||
private XpcClient? pairingd;
|
||||
private Darwin.GCD.DispatchQueue queue =
|
||||
new Darwin.GCD.DispatchQueue ("re.frida.fruity.remotepairing", Darwin.GCD.DispatchQueueAttr.SERIAL);
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
pairingd = XpcClient.make_for_mach_service ("com.apple.CoreDevice.remotepairingd", queue);
|
||||
pairingd.notify["state"].connect (on_state_changed);
|
||||
pairingd.message.connect (on_message);
|
||||
|
||||
do_browse.begin ();
|
||||
|
||||
try {
|
||||
yield all_current_devices_listed.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private async void do_browse () {
|
||||
try {
|
||||
var r = new PairingdRequest ("RemotePairing.BrowseRequest");
|
||||
r.body.set_bool ("currentDevicesOnly", false);
|
||||
yield pairingd.request (r.message, null);
|
||||
browse_request.resolve (true);
|
||||
} catch (GLib.Error e) {
|
||||
browse_request.reject (e);
|
||||
}
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
}
|
||||
|
||||
private void on_state_changed (Object obj, ParamSpec pspec) {
|
||||
if (pairingd.state == CLOSED && !all_current_devices_listed.future.ready) {
|
||||
all_current_devices_listed.reject (
|
||||
new Error.TRANSPORT ("Connection closed while waiting for initial device list"));
|
||||
}
|
||||
}
|
||||
|
||||
private void on_message (Darwin.Xpc.Object obj) {
|
||||
var reader = new XpcObjectReader (obj);
|
||||
try {
|
||||
reader.read_member ("mangledTypeName");
|
||||
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
|
||||
reader
|
||||
.end_member ()
|
||||
.read_member ("value");
|
||||
if (reader.try_read_member ("deviceFound")) {
|
||||
reader
|
||||
.read_member ("_0")
|
||||
.read_member ("deviceInfo");
|
||||
|
||||
var udid = reader.read_member ("udid").get_string_value ();
|
||||
reader.end_member ();
|
||||
if (transports.has_key (udid))
|
||||
return;
|
||||
|
||||
var device_info = (Darwin.Xpc.Dictionary) reader.current_object;
|
||||
|
||||
var pairing_device = new XpcClient (device_info.create_connection ("endpoint"), queue);
|
||||
|
||||
on_device_found (udid, pairing_device, device_info);
|
||||
} else if (reader.try_read_member ("allCurrentDevicesListed")) {
|
||||
all_current_devices_listed.resolve (true);
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void on_device_found (string udid, XpcClient pairing_device, Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
var transport = new MacOSCoreDeviceTransport (udid, pairing_device, device_info);
|
||||
transport.closed.connect (on_transport_closed);
|
||||
transports[udid] = transport;
|
||||
transport_attached (transport);
|
||||
}
|
||||
|
||||
private void on_transport_closed (MacOSCoreDeviceTransport transport) {
|
||||
transport_detached (transport);
|
||||
transports.unset (transport.udid);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MacOSCoreDeviceTransport : Object, Transport {
|
||||
public signal void closed ();
|
||||
|
||||
public XpcClient pairing_device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public ConnectionType connection_type {
|
||||
get {
|
||||
return _connection_type;
|
||||
}
|
||||
}
|
||||
|
||||
public string udid {
|
||||
get {
|
||||
return _udid;
|
||||
}
|
||||
}
|
||||
|
||||
public string? name {
|
||||
get {
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public UsbmuxDevice? usbmux_device {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ConnectionType _connection_type;
|
||||
private string _udid;
|
||||
private string _name;
|
||||
|
||||
private Promise<Tunnel>? tunnel_request;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public MacOSCoreDeviceTransport (string udid, XpcClient pairing_device, Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
Object (pairing_device: pairing_device);
|
||||
_udid = udid;
|
||||
update_device_info (device_info);
|
||||
}
|
||||
|
||||
construct {
|
||||
pairing_device.notify["state"].connect (on_state_changed);
|
||||
pairing_device.message.connect (on_message);
|
||||
}
|
||||
|
||||
private void update_device_info (Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
_connection_type = (device_info.get_value ("untrustedRSDDeviceInfo") != null)
|
||||
? ConnectionType.USB
|
||||
: ConnectionType.NETWORK;
|
||||
|
||||
var reader = new XpcObjectReader (device_info);
|
||||
_name = reader.read_member ("name").get_string_value ();
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
private void on_state_changed (Object obj, ParamSpec pspec) {
|
||||
if (pairing_device.state == CLOSED) {
|
||||
io_cancellable.cancel ();
|
||||
|
||||
closed ();
|
||||
}
|
||||
}
|
||||
|
||||
private void on_message (Darwin.Xpc.Object obj) {
|
||||
var reader = new XpcObjectReader (obj);
|
||||
try {
|
||||
reader.read_member ("mangledTypeName");
|
||||
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
|
||||
reader
|
||||
.end_member ()
|
||||
.read_member ("value");
|
||||
if (reader.try_read_member ("deviceFound")) {
|
||||
reader
|
||||
.read_member ("_0")
|
||||
.read_member ("deviceInfo");
|
||||
var device_info = (Darwin.Xpc.Dictionary)
|
||||
reader.get_object_value (Darwin.Xpc.Dictionary.TYPE);
|
||||
update_device_info (device_info);
|
||||
} else if (reader.try_read_member ("tunnelUsageAssertionsInvalidated")) {
|
||||
reader.read_member ("assertionIdentifiers");
|
||||
var ids = new Gee.ArrayList<Bytes> ();
|
||||
size_t n = reader.count_elements ();
|
||||
for (size_t i = 0; i != n; i++) {
|
||||
reader.read_element (i);
|
||||
ids.add (new Bytes (reader.get_uuid_value ()));
|
||||
reader.end_element ();
|
||||
}
|
||||
on_tunnel_usage_assertions_invalidated.begin (ids);
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private async void on_tunnel_usage_assertions_invalidated (Gee.List<Bytes> ids) {
|
||||
if (tunnel_request == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
var tunnel = (MacOSTunnel) yield tunnel_request.future.wait_async (io_cancellable);
|
||||
unowned uint8[] tunnel_assertion_id = tunnel.assertion_identifier.get_bytes ();
|
||||
foreach (var id in ids) {
|
||||
if (Memory.cmp (id.get_data (), tunnel_assertion_id, id.get_size ()) == 0) {
|
||||
tunnel_request = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
while (tunnel_request != null) {
|
||||
try {
|
||||
return yield tunnel_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (IOError e) {
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
var tunnel = new MacOSTunnel (pairing_device);
|
||||
yield tunnel.attach (cancellable);
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
|
||||
return tunnel;
|
||||
} catch (GLib.Error e) {
|
||||
tunnel_request.reject (e);
|
||||
tunnel_request = null;
|
||||
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MacOSTunnel : Object, Tunnel {
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery;
|
||||
}
|
||||
}
|
||||
|
||||
public Darwin.Xpc.Uuid? assertion_identifier {
|
||||
get {
|
||||
return _assertion_identifier;
|
||||
}
|
||||
}
|
||||
|
||||
private XpcClient pairing_device;
|
||||
private Darwin.Xpc.Uuid? _assertion_identifier;
|
||||
private InetAddress? tunnel_device_address;
|
||||
private DiscoveryService? _discovery;
|
||||
|
||||
public MacOSTunnel (XpcClient pairing_device) {
|
||||
this.pairing_device = pairing_device;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.ReleaseAssertionRequest");
|
||||
r.body.set_value ("assertionIdentifier", _assertion_identifier);
|
||||
try {
|
||||
yield pairing_device.request (r.message, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async void attach (Cancellable? cancellable) throws Error, IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.CreateAssertionCommand");
|
||||
r.body.set_int64 ("flags", 0);
|
||||
var response = yield pairing_device.request (r.message, cancellable);
|
||||
|
||||
var reader = new XpcObjectReader (response);
|
||||
reader.read_member ("response");
|
||||
|
||||
_assertion_identifier = (Darwin.Xpc.Uuid) reader
|
||||
.read_member ("assertionIdentifier")
|
||||
.get_object_value (Darwin.Xpc.Uuid.TYPE);
|
||||
reader.end_member ();
|
||||
|
||||
string tunnel_ip_address = reader
|
||||
.read_member ("info")
|
||||
.read_member ("tunnelIPAddress")
|
||||
.get_string_value ();
|
||||
tunnel_device_address = new InetAddress.from_string (tunnel_ip_address);
|
||||
|
||||
_discovery = yield locate_discovery_service (tunnel_device_address, cancellable);
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
SocketConnection connection;
|
||||
try {
|
||||
var service_address = new InetSocketAddress (tunnel_device_address, port);
|
||||
|
||||
var client = new SocketClient ();
|
||||
connection = yield client.connect_async (service_address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private static async DiscoveryService locate_discovery_service (InetAddress tunnel_device_address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var path_buf = new char[4096];
|
||||
unowned string path = (string) path_buf;
|
||||
|
||||
uint delays[] = { 0, 50, 250 };
|
||||
for (uint attempts = 0; attempts != delays.length; attempts++) {
|
||||
uint delay = delays[attempts];
|
||||
if (delay != 0) {
|
||||
var timeout_source = new TimeoutSource (delay);
|
||||
timeout_source.set_callback (locate_discovery_service.callback);
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (locate_discovery_service.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
timeout_source.destroy ();
|
||||
|
||||
if (cancellable.is_cancelled ())
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var item in XNU.query_active_tcp_connections ()) {
|
||||
if (item.family != IPV6)
|
||||
continue;
|
||||
if (!item.foreign_address.equal (tunnel_device_address))
|
||||
continue;
|
||||
if (Darwin.XNU.proc_pidpath (item.effective_pid, path_buf) <= 0)
|
||||
continue;
|
||||
if (path != "/usr/libexec/remoted")
|
||||
continue;
|
||||
|
||||
try {
|
||||
var connectable = new InetSocketAddress (tunnel_device_address, item.foreign_port);
|
||||
|
||||
var sc = new SocketClient ();
|
||||
SocketConnection connection = yield sc.connect_async (connectable, cancellable);
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return yield DiscoveryService.open (connection, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect RSD port");
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PairingdRequest {
|
||||
public Darwin.Xpc.Dictionary message = new Darwin.Xpc.Dictionary ();
|
||||
public Darwin.Xpc.Dictionary body = new Darwin.Xpc.Dictionary ();
|
||||
|
||||
public PairingdRequest (string name) {
|
||||
message.set_string ("mangledTypeName", name);
|
||||
message.set_value ("value", body);
|
||||
}
|
||||
}
|
||||
|
||||
namespace XNU {
|
||||
public PcbList query_active_tcp_connections () {
|
||||
size_t size = 0;
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", null, &size);
|
||||
|
||||
var pcbs = new uint8[size];
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", pcbs, &size);
|
||||
|
||||
return new PcbList (pcbs);
|
||||
}
|
||||
|
||||
public class PcbList {
|
||||
private uint8[] pcbs;
|
||||
|
||||
internal PcbList (owned uint8[] pcbs) {
|
||||
this.pcbs = (owned) pcbs;
|
||||
}
|
||||
|
||||
public Iterator iterator () {
|
||||
return new Iterator (this);
|
||||
}
|
||||
|
||||
public class Iterator {
|
||||
private PcbList list;
|
||||
private InetItem * cursor;
|
||||
|
||||
internal Iterator (PcbList list) {
|
||||
this.list = list;
|
||||
|
||||
var gen = (Darwin.XNU.InetPcbGeneration *) list.pcbs;
|
||||
cursor = (InetItem *) ((uint8 *) list.pcbs + gen->length);
|
||||
}
|
||||
|
||||
public Item? next_value () {
|
||||
InetPcb * pcb = null;
|
||||
while (true) {
|
||||
if (cursor->length == 24)
|
||||
return null;
|
||||
|
||||
switch (cursor->kind) {
|
||||
case InetItemKind.PCB:
|
||||
pcb = (InetPcb *) cursor;
|
||||
break;
|
||||
case InetItemKind.SOCKET:
|
||||
var item = new Item (*pcb, *((InetSocket *) cursor));
|
||||
advance ();
|
||||
return item;
|
||||
}
|
||||
|
||||
advance ();
|
||||
}
|
||||
}
|
||||
|
||||
private void advance () {
|
||||
uint32 l = cursor->length;
|
||||
if (l % 8 != 0)
|
||||
l += 8 - (l % 8);
|
||||
cursor = (InetItem *) ((uint8 *) cursor + l);
|
||||
}
|
||||
}
|
||||
|
||||
public class Item {
|
||||
public SocketFamily family {
|
||||
get {
|
||||
return ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
? SocketFamily.IPV6
|
||||
: SocketFamily.IPV4;
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress local_address {
|
||||
get {
|
||||
if (cached_local_address == null)
|
||||
cached_local_address = parse_address (pcb.local_address);
|
||||
return cached_local_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 local_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.local_port);
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress foreign_address {
|
||||
get {
|
||||
if (cached_foreign_address == null)
|
||||
cached_foreign_address = parse_address (pcb.foreign_address);
|
||||
return cached_foreign_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 foreign_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.foreign_port);
|
||||
}
|
||||
}
|
||||
|
||||
public int32 effective_pid {
|
||||
get {
|
||||
return sock.effective_pid;
|
||||
}
|
||||
}
|
||||
|
||||
private InetPcb pcb;
|
||||
private InetSocket sock;
|
||||
private InetAddress? cached_local_address;
|
||||
private InetAddress? cached_foreign_address;
|
||||
|
||||
public Item (InetPcb pcb, InetSocket sock) {
|
||||
this.pcb = pcb;
|
||||
this.sock = sock;
|
||||
}
|
||||
|
||||
private InetAddress parse_address (uint8[] bytes) {
|
||||
if ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
return new InetAddress.from_bytes (bytes, IPV6);
|
||||
var addr = (Darwin.XNU.InetAddr4in6 *) bytes;
|
||||
return new InetAddress.from_bytes ((uint8[]) &addr->addr4.s_addr, IPV4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetItem {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
}
|
||||
|
||||
public enum InetItemKind {
|
||||
SOCKET = 0x001,
|
||||
PCB = 0x010,
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetPcb {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 inpp;
|
||||
public uint16 foreign_port;
|
||||
public uint16 local_port;
|
||||
public uint32 per_protocol_pcb_low;
|
||||
public uint32 per_protocol_pcb_high;
|
||||
public uint32 generation_count_low;
|
||||
public uint32 generation_count_high;
|
||||
public int flags;
|
||||
public uint32 flow;
|
||||
public uint8 version_flag;
|
||||
public uint8 ip_ttl;
|
||||
public uint8 ip_protocol;
|
||||
public uint8 padding;
|
||||
public uint8 foreign_address[16];
|
||||
public uint8 local_address[16];
|
||||
public InetDepend4 depend4;
|
||||
public InetDepend6 depend6;
|
||||
public uint32 flowhash;
|
||||
public uint32 flags2;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetSocket {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 so;
|
||||
public int16 type;
|
||||
public uint16 options_low;
|
||||
public uint16 options_high;
|
||||
public int16 linger;
|
||||
public int16 state;
|
||||
public uint16 pcb[4];
|
||||
public uint16 protocol_low;
|
||||
public uint16 protocol_high;
|
||||
public uint16 family_low;
|
||||
public uint16 family_high;
|
||||
public int16 qlen;
|
||||
public int16 incqlen;
|
||||
public int16 qlimit;
|
||||
public int16 timeo;
|
||||
public uint16 error;
|
||||
public int32 pgid;
|
||||
public uint32 oobmark;
|
||||
public uint32 uid;
|
||||
public int32 last_pid;
|
||||
public int32 effective_pid;
|
||||
public uint64 gencnt;
|
||||
public uint32 flags;
|
||||
public uint32 flags1;
|
||||
public int32 usecount;
|
||||
public int32 retaincnt;
|
||||
public uint32 filter_flags;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend4 {
|
||||
public uint8 ip_tos;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend6 {
|
||||
public uint8 hlim;
|
||||
public int checksum;
|
||||
public uint16 interface_index;
|
||||
public int16 hops;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "frida-base.h"
|
||||
|
||||
void
|
||||
_frida_fruity_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
GError ** error)
|
||||
{
|
||||
*name = g_strdup ("iOS Device");
|
||||
@@ -71,7 +71,7 @@ static gpointer frida_read_registry_value (HKEY key, WCHAR * value_name, DWORD e
|
||||
static GUID GUID_APPLE_USB = { 0xF0B32BE3, 0x6678, 0x4879, { 0x92, 0x30, 0x0E4, 0x38, 0x45, 0xD8, 0x05, 0xEE } };
|
||||
|
||||
void
|
||||
_frida_fruity_host_session_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
_frida_fruity_usbmux_backend_extract_details_for_device (gint product_id, const char * udid, char ** name, GVariant ** icon,
|
||||
GError ** error)
|
||||
{
|
||||
gboolean result = FALSE;
|
||||
File diff suppressed because it is too large.
Load diff
+165
-504
@@ -1,129 +1,24 @@
|
||||
namespace Frida {
|
||||
public class FruityHostSessionBackend : Object, HostSessionBackend {
|
||||
private Fruity.UsbmuxClient control_client;
|
||||
|
||||
private Gee.HashSet<uint> devices = new Gee.HashSet<uint> ();
|
||||
private Gee.HashMap<uint, FruityHostSessionProvider> providers = new Gee.HashMap<uint, FruityHostSessionProvider> ();
|
||||
|
||||
private Promise<bool> start_request;
|
||||
private Cancellable start_cancellable;
|
||||
private SourceFunc on_start_completed;
|
||||
private Fruity.DeviceMonitor device_monitor = new Fruity.DeviceMonitor ();
|
||||
private Gee.Map<Fruity.Device, FruityHostSessionProvider> providers =
|
||||
new Gee.HashMap<Fruity.Device, FruityHostSessionProvider> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
construct {
|
||||
device_monitor.device_attached.connect (on_device_attached);
|
||||
device_monitor.device_detached.connect (on_device_detached);
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
start_request = new Promise<bool> ();
|
||||
start_cancellable = new Cancellable ();
|
||||
on_start_completed = start.callback;
|
||||
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var timeout_source = new TimeoutSource (500);
|
||||
timeout_source.set_callback (start.callback);
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (start.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
do_start.begin ();
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
timeout_source.destroy ();
|
||||
on_start_completed = null;
|
||||
}
|
||||
|
||||
private async void do_start () {
|
||||
bool success = yield try_start_control_connection ();
|
||||
if (success) {
|
||||
/* Perform a dummy-request to flush out any pending device attach notifications. */
|
||||
try {
|
||||
yield control_client.connect_to_port (Fruity.DeviceId (uint.MAX), 0, start_cancellable);
|
||||
assert_not_reached ();
|
||||
} catch (GLib.Error expected_error) {
|
||||
if (expected_error.code == IOError.CONNECTION_CLOSED) {
|
||||
/* Deal with usbmuxd closing the connection when receiving commands in the wrong state. */
|
||||
control_client.close.begin (null);
|
||||
|
||||
success = yield try_start_control_connection ();
|
||||
if (success) {
|
||||
Fruity.UsbmuxClient flush_client = null;
|
||||
try {
|
||||
flush_client = yield Fruity.UsbmuxClient.open (start_cancellable);
|
||||
try {
|
||||
yield flush_client.connect_to_port (
|
||||
Fruity.DeviceId (uint.MAX), 0,
|
||||
start_cancellable);
|
||||
assert_not_reached ();
|
||||
} catch (GLib.Error expected_error) {
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (flush_client != null)
|
||||
flush_client.close.begin (null);
|
||||
|
||||
if (!success && control_client != null) {
|
||||
control_client.close.begin (null);
|
||||
control_client = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_request.resolve (success);
|
||||
|
||||
if (on_start_completed != null)
|
||||
on_start_completed ();
|
||||
}
|
||||
|
||||
private async bool try_start_control_connection () {
|
||||
bool success = true;
|
||||
|
||||
try {
|
||||
control_client = yield Fruity.UsbmuxClient.open (start_cancellable);
|
||||
|
||||
control_client.device_attached.connect ((details) => {
|
||||
add_device.begin (details);
|
||||
});
|
||||
control_client.device_detached.connect ((id) => {
|
||||
remove_device (id);
|
||||
});
|
||||
|
||||
yield control_client.enable_listen_mode (start_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success && control_client != null) {
|
||||
control_client.close.begin (null);
|
||||
control_client = null;
|
||||
}
|
||||
|
||||
return success;
|
||||
yield device_monitor.start (cancellable);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
start_cancellable.cancel ();
|
||||
|
||||
try {
|
||||
yield start_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
if (control_client != null) {
|
||||
yield control_client.close (cancellable);
|
||||
control_client = null;
|
||||
}
|
||||
|
||||
io_cancellable.cancel ();
|
||||
|
||||
devices.clear ();
|
||||
yield device_monitor.stop (cancellable);
|
||||
|
||||
foreach (var provider in providers.values) {
|
||||
provider_unavailable (provider);
|
||||
@@ -132,148 +27,64 @@ namespace Frida {
|
||||
providers.clear ();
|
||||
}
|
||||
|
||||
private async void add_device (Fruity.DeviceDetails details) {
|
||||
var id = details.id;
|
||||
var raw_id = id.raw_value;
|
||||
if (devices.contains (raw_id))
|
||||
return;
|
||||
devices.add (raw_id);
|
||||
|
||||
string? name = null;
|
||||
Variant? icon = null;
|
||||
|
||||
if (details.connection_type == USB) {
|
||||
bool got_details = false;
|
||||
for (int i = 1; !got_details && devices.contains (raw_id); i++) {
|
||||
try {
|
||||
_extract_details_for_device (details.product_id.raw_value, details.udid.raw_value,
|
||||
out name, out icon);
|
||||
got_details = true;
|
||||
} catch (Error e) {
|
||||
if (i != 20) {
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var delay_source = new TimeoutSource.seconds (1);
|
||||
delay_source.set_callback (add_device.callback);
|
||||
delay_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (io_cancellable);
|
||||
cancel_source.set_callback (add_device.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
delay_source.destroy ();
|
||||
|
||||
if (io_cancellable.is_cancelled ())
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!devices.contains (raw_id))
|
||||
return;
|
||||
if (!got_details) {
|
||||
remove_device (id);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
name = "iOS Device [%s]".printf (details.network_address.address.to_string ());
|
||||
}
|
||||
|
||||
var provider = new FruityHostSessionProvider (name, icon, details);
|
||||
providers[raw_id] = provider;
|
||||
|
||||
private void on_device_attached (Fruity.Device device) {
|
||||
var provider = new FruityHostSessionProvider (device);
|
||||
providers[device] = provider;
|
||||
provider_available (provider);
|
||||
}
|
||||
|
||||
private void remove_device (Fruity.DeviceId id) {
|
||||
var raw_id = id.raw_value;
|
||||
if (!devices.contains (raw_id))
|
||||
return;
|
||||
devices.remove (raw_id);
|
||||
|
||||
private void on_device_detached (Fruity.Device device) {
|
||||
FruityHostSessionProvider provider;
|
||||
if (providers.unset (raw_id, out provider)) {
|
||||
if (providers.unset (device, out provider)) {
|
||||
provider_unavailable (provider);
|
||||
provider.close.begin (io_cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public extern static void _extract_details_for_device (int product_id, string udid, out string name,
|
||||
out Variant? icon) throws Error;
|
||||
}
|
||||
|
||||
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, HostServiceProvider,
|
||||
FruityLockdownProvider, Pairable {
|
||||
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, HostServiceProvider, Pairable {
|
||||
public Fruity.Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public string id {
|
||||
get { return device_details.udid.raw_value; }
|
||||
get { return device.udid; }
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return device_name; }
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return device_icon; }
|
||||
get { return device.icon; }
|
||||
}
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
get {
|
||||
return (device_details.connection_type == USB)
|
||||
return (device.connection_type == USB)
|
||||
? HostSessionProviderKind.USB
|
||||
: HostSessionProviderKind.REMOTE;
|
||||
}
|
||||
}
|
||||
|
||||
public string device_name {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Variant? device_icon {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Fruity.DeviceDetails device_details {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private FruityHostSession? host_session;
|
||||
private Promise<Fruity.Tunnel?>? tunnel_request;
|
||||
private string _name;
|
||||
|
||||
public FruityHostSessionProvider (string name, Variant? icon, Fruity.DeviceDetails details) {
|
||||
Object (
|
||||
device_name: name,
|
||||
device_icon: icon,
|
||||
device_details: details
|
||||
);
|
||||
public FruityHostSessionProvider (Fruity.Device device) {
|
||||
Object (device: device);
|
||||
_name = device.name;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
yield Fruity.DTXConnection.close_all (this, cancellable);
|
||||
|
||||
if (tunnel_request != null) {
|
||||
Fruity.Tunnel? tunnel = null;
|
||||
try {
|
||||
tunnel = yield find_tunnel (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
|
||||
if (tunnel != null)
|
||||
yield tunnel.close (cancellable);
|
||||
}
|
||||
yield Fruity.DTXConnection.close_all (device, cancellable);
|
||||
}
|
||||
|
||||
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
|
||||
host_session = new FruityHostSession (this, this);
|
||||
host_session = new FruityHostSession (device);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
@@ -302,128 +113,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async IOStream open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
if (address.has_prefix ("tcp:")) {
|
||||
ulong raw_port;
|
||||
if (!ulong.try_parse (address.substring (4), out raw_port) || raw_port == 0 || raw_port > uint16.MAX)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid TCP port");
|
||||
uint16 port = (uint16) raw_port;
|
||||
|
||||
if (device_details.connection_type == USB) {
|
||||
Fruity.UsbmuxClient client = null;
|
||||
try {
|
||||
client = yield Fruity.UsbmuxClient.open (cancellable);
|
||||
|
||||
yield client.connect_to_port (device_details.id, port, cancellable);
|
||||
|
||||
return client.connection;
|
||||
} catch (GLib.Error e) {
|
||||
if (client != null)
|
||||
client.close.begin ();
|
||||
|
||||
if (e is Fruity.UsbmuxError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
InetSocketAddress device_address = device_details.network_address;
|
||||
var target_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: device_address.address,
|
||||
port: port,
|
||||
flowinfo: device_address.flowinfo,
|
||||
scope_id: device_address.scope_id
|
||||
);
|
||||
|
||||
var client = new SocketClient ();
|
||||
var connection = yield client.connect_async (target_address, cancellable);
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (address.has_prefix ("lockdown:")) {
|
||||
string service_name = address.substring (9);
|
||||
|
||||
if (service_name.length != 0) {
|
||||
var client = yield get_lockdown_client (cancellable);
|
||||
return yield open_lockdown_service (service_name, client, cancellable);
|
||||
} else {
|
||||
try {
|
||||
var client = yield Fruity.LockdownClient.open (device_details, cancellable);
|
||||
yield client.start_session (cancellable);
|
||||
return client.stream;
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported channel address");
|
||||
}
|
||||
|
||||
public async IOStream open_lockdown_service (string service_name, Fruity.LockdownClient client, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var tunnel = yield find_tunnel (cancellable);
|
||||
if (tunnel != null) {
|
||||
Fruity.ServiceInfo? service_info = null;
|
||||
bool needs_checkin = false;
|
||||
try {
|
||||
service_info = tunnel.discovery.get_service (service_name);
|
||||
} catch (Error e) {
|
||||
if (!(e is Error.NOT_SUPPORTED))
|
||||
throw e;
|
||||
}
|
||||
if (service_info == null) {
|
||||
service_info = tunnel.discovery.get_service (service_name + ".shim.remote");
|
||||
needs_checkin = true;
|
||||
}
|
||||
|
||||
var stream = yield tunnel.open_tcp_connection (service_info.port, cancellable);
|
||||
|
||||
if (needs_checkin) {
|
||||
var service = new Fruity.PlistServiceClient (stream);
|
||||
|
||||
var checkin = new Fruity.Plist ();
|
||||
checkin.set_string ("Request", "RSDCheckin");
|
||||
checkin.set_string ("Label", "Xcode");
|
||||
checkin.set_string ("ProtocolVersion", "2");
|
||||
|
||||
try {
|
||||
yield service.query (checkin, cancellable);
|
||||
|
||||
var result = yield service.read_message (cancellable);
|
||||
if (result.has ("Error")) {
|
||||
var error_type = result.get_string ("Error");
|
||||
if (error_type == "ServiceProhibited")
|
||||
throw new Error.PERMISSION_DENIED ("Service prohibited");
|
||||
throw new Error.NOT_SUPPORTED ("%s", error_type);
|
||||
}
|
||||
} catch (Fruity.PlistServiceError e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
} catch (Frida.Fruity.PlistError e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
try {
|
||||
return yield client.start_service (service_name, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is Fruity.LockdownError.INVALID_SERVICE)
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
return yield device.open_channel (address, cancellable);
|
||||
}
|
||||
|
||||
public async Service open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -432,20 +122,19 @@ namespace Frida {
|
||||
unowned string service_name = tokens[1];
|
||||
|
||||
if (protocol == "plist") {
|
||||
var client = yield get_lockdown_client (cancellable);
|
||||
var stream = yield open_lockdown_service (service_name, client, cancellable);
|
||||
var stream = yield device.open_lockdown_service (service_name, cancellable);
|
||||
|
||||
return new PlistService (stream);
|
||||
}
|
||||
|
||||
if (protocol == "dtx") {
|
||||
var connection = yield Fruity.DTXConnection.obtain (this, cancellable);
|
||||
var connection = yield Fruity.DTXConnection.obtain (device, cancellable);
|
||||
|
||||
return new DTXService (service_name, connection);
|
||||
}
|
||||
|
||||
if (protocol == "xpc") {
|
||||
var tunnel = yield find_tunnel (cancellable);
|
||||
var tunnel = yield device.find_tunnel (cancellable);
|
||||
if (tunnel == null)
|
||||
throw new Error.NOT_SUPPORTED ("RemoteXPC not supported by device");
|
||||
|
||||
@@ -458,58 +147,9 @@ namespace Frida {
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported service address");
|
||||
}
|
||||
|
||||
private async Fruity.LockdownClient get_lockdown_client (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var client = yield Fruity.LockdownClient.open (device_details, cancellable);
|
||||
yield client.start_session (cancellable);
|
||||
return client;
|
||||
} catch (Frida.Fruity.LockdownError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Fruity.Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
while (tunnel_request != null) {
|
||||
try {
|
||||
return yield tunnel_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (IOError e) {
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
tunnel_request = new Promise<Fruity.Tunnel> ();
|
||||
|
||||
try {
|
||||
bool supported_by_os = true;
|
||||
var lockdown = yield get_lockdown_client (cancellable);
|
||||
var response = yield lockdown.get_value (null, null, cancellable);
|
||||
Fruity.PlistDict properties = response.get_dict ("Value");
|
||||
if (properties.get_string ("ProductName") == "iPhone OS") {
|
||||
uint ios_major_version = uint.parse (properties.get_string ("ProductVersion").split (".")[0]);
|
||||
supported_by_os = ios_major_version >= 17;
|
||||
}
|
||||
|
||||
Fruity.Tunnel? tunnel = null;
|
||||
if (supported_by_os)
|
||||
tunnel = yield Fruity.TunnelFinder.make_default ().find (device_details.udid.raw_value, cancellable);
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
|
||||
return tunnel;
|
||||
} catch (GLib.Error e) {
|
||||
var api_error = new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
|
||||
tunnel_request.reject (api_error);
|
||||
tunnel_request = null;
|
||||
|
||||
throw_api_error (api_error);
|
||||
}
|
||||
}
|
||||
|
||||
private async void unpair (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var client = yield Fruity.LockdownClient.open (device_details, cancellable);
|
||||
var client = yield device.get_lockdown_client (cancellable);
|
||||
yield client.unpair (cancellable);
|
||||
} catch (Fruity.LockdownError e) {
|
||||
if (e is Fruity.LockdownError.NOT_PAIRED)
|
||||
@@ -519,19 +159,8 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public interface FruityLockdownProvider : Object {
|
||||
public abstract async Fruity.LockdownClient get_lockdown_client (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async IOStream open_lockdown_service (string service_name, Fruity.LockdownClient client,
|
||||
Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
public class FruityHostSession : Object, HostSession {
|
||||
public weak HostChannelProvider channel_provider {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public weak FruityLockdownProvider lockdown_provider {
|
||||
public Fruity.Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
@@ -563,11 +192,8 @@ namespace Frida {
|
||||
DEBUGSERVER_ENDPOINT_LEGACY,
|
||||
};
|
||||
|
||||
public FruityHostSession (HostChannelProvider channel_provider, FruityLockdownProvider lockdown_provider) {
|
||||
Object (
|
||||
channel_provider: channel_provider,
|
||||
lockdown_provider: lockdown_provider
|
||||
);
|
||||
public FruityHostSession (Fruity.Device device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
@@ -615,7 +241,7 @@ namespace Frida {
|
||||
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var lockdown = yield device.get_lockdown_client (cancellable);
|
||||
var response = yield lockdown.get_value (null, null, cancellable);
|
||||
Fruity.PlistDict properties = response.get_dict ("Value");
|
||||
|
||||
@@ -623,12 +249,19 @@ namespace Frida {
|
||||
os["id"] = "ios";
|
||||
os["name"] = properties.get_string ("ProductName");
|
||||
os["version"] = properties.get_string ("ProductVersion");
|
||||
os["build"] = properties.get_string ("BuildVersion");
|
||||
parameters["os"] = os;
|
||||
|
||||
parameters["platform"] = "darwin";
|
||||
|
||||
parameters["arch"] = properties.get_string ("CPUArchitecture").has_prefix ("arm64") ? "arm64" : "arm";
|
||||
|
||||
var hardware = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
hardware["product"] = properties.get_string ("ProductType");
|
||||
hardware["platform"] = properties.get_string ("HardwarePlatform");
|
||||
hardware["model"] = properties.get_string ("HardwareModel");
|
||||
parameters["hardware"] = hardware;
|
||||
|
||||
parameters["access"] = "jailed";
|
||||
|
||||
parameters["name"] = properties.get_string ("DeviceName");
|
||||
@@ -726,15 +359,10 @@ namespace Frida {
|
||||
add_process_metadata (info.parameters, process);
|
||||
|
||||
if (scope == FULL) {
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (lockdown, cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (device, cancellable);
|
||||
|
||||
Bytes png = yield springboard.get_icon_png_data (identifier);
|
||||
add_app_icons (info.parameters, png);
|
||||
} catch (Fruity.SpringboardServicesError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
Bytes png = yield springboard.get_icon_png_data (identifier);
|
||||
add_app_icons (info.parameters, png);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,18 +393,13 @@ namespace Frida {
|
||||
|
||||
Gee.Map<string, Bytes>? icons = null;
|
||||
if (scope == FULL) {
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (lockdown, cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (device, cancellable);
|
||||
|
||||
var app_ids = new Gee.ArrayList<string> ();
|
||||
foreach (var app in apps)
|
||||
app_ids.add (app.identifier);
|
||||
var app_ids = new Gee.ArrayList<string> ();
|
||||
foreach (var app in apps)
|
||||
app_ids.add (app.identifier);
|
||||
|
||||
icons = yield springboard.get_icon_png_data_batch (app_ids.to_array (), cancellable);
|
||||
} catch (Fruity.SpringboardServicesError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
icons = yield springboard.get_icon_png_data_batch (app_ids.to_array (), cancellable);
|
||||
}
|
||||
|
||||
Gee.List<Fruity.DeviceInfoService.ProcessInfo> processes =
|
||||
@@ -881,19 +504,14 @@ namespace Frida {
|
||||
if (scope == FULL) {
|
||||
icon_by_pid = new Gee.HashMap<uint, Bytes> ();
|
||||
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
try {
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (lockdown, cancellable);
|
||||
var springboard = yield Fruity.SpringboardServicesClient.open (device, cancellable);
|
||||
|
||||
var pngs = yield springboard.get_icon_png_data_batch (app_ids.to_array (), cancellable);
|
||||
var pngs = yield springboard.get_icon_png_data_batch (app_ids.to_array (), cancellable);
|
||||
|
||||
int i = 0;
|
||||
foreach (string app_id in app_ids) {
|
||||
icon_by_pid[app_pids[i]] = pngs[app_id];
|
||||
i++;
|
||||
}
|
||||
} catch (Fruity.SpringboardServicesError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
int i = 0;
|
||||
foreach (string app_id in app_ids) {
|
||||
icon_by_pid[app_pids[i]] = pngs[app_id];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -948,32 +566,25 @@ namespace Frida {
|
||||
|
||||
private async void fetch_apps (Promise<Gee.List<Fruity.ApplicationDetails>> promise, Cancellable? cancellable) {
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var installation_proxy = yield Fruity.InstallationProxyClient.open (lockdown, cancellable);
|
||||
var installation_proxy = yield Fruity.InstallationProxyClient.open (device, cancellable);
|
||||
|
||||
var apps = yield installation_proxy.browse (cancellable);
|
||||
|
||||
promise.resolve (apps);
|
||||
} catch (Error e) {
|
||||
} catch (GLib.Error e) {
|
||||
promise.reject (e);
|
||||
} catch (IOError e) {
|
||||
promise.reject (e);
|
||||
} catch (Fruity.InstallationProxyError e) {
|
||||
promise.reject (new Error.NOT_SUPPORTED ("%s", e.message));
|
||||
}
|
||||
}
|
||||
|
||||
private async void fetch_processes (Promise<Gee.List<Fruity.DeviceInfoService.ProcessInfo>> promise,
|
||||
Cancellable? cancellable) {
|
||||
try {
|
||||
var device_info = yield Fruity.DeviceInfoService.open (channel_provider, cancellable);
|
||||
var device_info = yield Fruity.DeviceInfoService.open (device, cancellable);
|
||||
|
||||
var processes = yield device_info.enumerate_processes (cancellable);
|
||||
|
||||
promise.resolve (processes);
|
||||
} catch (Error e) {
|
||||
promise.reject (e);
|
||||
} catch (IOError e) {
|
||||
} catch (GLib.Error e) {
|
||||
promise.reject (e);
|
||||
}
|
||||
}
|
||||
@@ -1156,46 +767,40 @@ namespace Frida {
|
||||
gadget_path = gadget_value.get_string ();
|
||||
}
|
||||
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var installation_proxy = yield Fruity.InstallationProxyClient.open (device, cancellable);
|
||||
|
||||
var installation_proxy = yield Fruity.InstallationProxyClient.open (lockdown, cancellable);
|
||||
var query = new Fruity.PlistDict ();
|
||||
var ids = new Fruity.PlistArray ();
|
||||
ids.add_string (program);
|
||||
query.set_array ("BundleIDs", ids);
|
||||
|
||||
var query = new Fruity.PlistDict ();
|
||||
var ids = new Fruity.PlistArray ();
|
||||
ids.add_string (program);
|
||||
query.set_array ("BundleIDs", ids);
|
||||
var matches = yield installation_proxy.lookup (query, cancellable);
|
||||
var app = matches[program];
|
||||
if (app == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to find app with bundle identifier “%s”", program);
|
||||
|
||||
var matches = yield installation_proxy.lookup (query, cancellable);
|
||||
var app = matches[program];
|
||||
if (app == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Unable to find app with bundle identifier “%s”", program);
|
||||
|
||||
string[] argv = { app.path };
|
||||
if (options.has_argv) {
|
||||
var provided_argv = options.argv;
|
||||
var length = provided_argv.length;
|
||||
for (int i = 1; i < length; i++)
|
||||
argv += provided_argv[i];
|
||||
}
|
||||
|
||||
var lldb = yield start_lldb_service (lockdown, cancellable);
|
||||
var process = yield lldb.launch (argv, launch_options, cancellable);
|
||||
if (process.observed_state == ALREADY_RUNNING) {
|
||||
yield lldb.kill (cancellable);
|
||||
yield lldb.close (cancellable);
|
||||
|
||||
lldb = yield start_lldb_service (lockdown, cancellable);
|
||||
process = yield lldb.launch (argv, launch_options, cancellable);
|
||||
}
|
||||
|
||||
var session = new LLDBSession (lldb, process, gadget_path, channel_provider);
|
||||
add_lldb_session (session);
|
||||
|
||||
return process.pid;
|
||||
} catch (Fruity.InstallationProxyError e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
string[] argv = { app.path };
|
||||
if (options.has_argv) {
|
||||
var provided_argv = options.argv;
|
||||
var length = provided_argv.length;
|
||||
for (int i = 1; i < length; i++)
|
||||
argv += provided_argv[i];
|
||||
}
|
||||
|
||||
var lldb = yield start_lldb_service (cancellable);
|
||||
var process = yield lldb.launch (argv, launch_options, cancellable);
|
||||
if (process.observed_state == ALREADY_RUNNING) {
|
||||
yield lldb.kill (cancellable);
|
||||
yield lldb.close (cancellable);
|
||||
|
||||
lldb = yield start_lldb_service (cancellable);
|
||||
process = yield lldb.launch (argv, launch_options, cancellable);
|
||||
}
|
||||
|
||||
var session = new LLDBSession (lldb, process, gadget_path, device);
|
||||
add_lldb_session (session);
|
||||
|
||||
return process.pid;
|
||||
}
|
||||
|
||||
public async void input (uint pid, uint8[] data, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1241,15 +846,14 @@ namespace Frida {
|
||||
}
|
||||
|
||||
try {
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var lldb = yield start_lldb_service (lockdown, cancellable);
|
||||
var lldb = yield start_lldb_service (cancellable);
|
||||
var process = yield lldb.attach_by_pid (pid, cancellable);
|
||||
|
||||
lldb_session = new LLDBSession (lldb, process, null, channel_provider);
|
||||
lldb_session = new LLDBSession (lldb, process, null, device);
|
||||
yield lldb_session.kill (cancellable);
|
||||
yield lldb_session.close (cancellable);
|
||||
} catch (Error e) {
|
||||
var process_control = yield Fruity.ProcessControlService.open (channel_provider, cancellable);
|
||||
var process_control = yield Fruity.ProcessControlService.open (device, cancellable);
|
||||
yield process_control.kill (pid, cancellable);
|
||||
}
|
||||
}
|
||||
@@ -1276,13 +880,12 @@ namespace Frida {
|
||||
if (pid == 0)
|
||||
throw new Error.NOT_SUPPORTED ("The Frida system session is not available on jailed iOS");
|
||||
|
||||
var lockdown = yield lockdown_provider.get_lockdown_client (cancellable);
|
||||
var lldb = yield start_lldb_service (lockdown, cancellable);
|
||||
var lldb = yield start_lldb_service (cancellable);
|
||||
var process = yield lldb.attach_by_pid (pid, cancellable);
|
||||
|
||||
string? gadget_path = null;
|
||||
|
||||
lldb_session = new LLDBSession (lldb, process, gadget_path, channel_provider);
|
||||
lldb_session = new LLDBSession (lldb, process, gadget_path, device);
|
||||
add_lldb_session (lldb_session);
|
||||
|
||||
var gadget_details = yield lldb_session.query_gadget_details (cancellable);
|
||||
@@ -1294,11 +897,10 @@ namespace Frida {
|
||||
throw new Error.INVALID_OPERATION ("Only meant to be implemented by services");
|
||||
}
|
||||
|
||||
private async LLDB.Client start_lldb_service (Fruity.LockdownClient lockdown, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
private async LLDB.Client start_lldb_service (Cancellable? cancellable) throws Error, IOError {
|
||||
foreach (unowned string endpoint in DEBUGSERVER_ENDPOINT_CANDIDATES) {
|
||||
try {
|
||||
var lldb_stream = yield lockdown_provider.open_lockdown_service (endpoint, lockdown, cancellable);
|
||||
var lldb_stream = yield device.open_lockdown_service (endpoint, cancellable);
|
||||
return yield LLDB.Client.open (lldb_stream, cancellable);
|
||||
} catch (Error e) {
|
||||
if (!(e is Error.NOT_SUPPORTED))
|
||||
@@ -1313,7 +915,7 @@ namespace Frida {
|
||||
private async AgentSessionId attach_via_gadget (uint pid, HashTable<string, Variant> options,
|
||||
Fruity.Injector.GadgetDetails gadget_details, Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var stream = yield channel_provider.open_channel (
|
||||
var stream = yield device.open_channel (
|
||||
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (gadget_details.port),
|
||||
cancellable);
|
||||
|
||||
@@ -1366,8 +968,8 @@ namespace Frida {
|
||||
var transport_broker = server.transport_broker;
|
||||
if (transport_broker != null) {
|
||||
try {
|
||||
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id,
|
||||
channel_provider, cancellable);
|
||||
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, device,
|
||||
cancellable);
|
||||
} catch (Error e) {
|
||||
if (e is Error.NOT_SUPPORTED)
|
||||
server.transport_broker = null;
|
||||
@@ -1480,7 +1082,7 @@ namespace Frida {
|
||||
|
||||
DBusConnection? connection = null;
|
||||
try {
|
||||
var stream = yield channel_provider.open_channel (
|
||||
var stream = yield device.open_channel (
|
||||
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT),
|
||||
cancellable);
|
||||
|
||||
@@ -2474,16 +2076,75 @@ namespace Frida {
|
||||
}
|
||||
|
||||
if (val.is_of_type (new VariantType ("av"))) {
|
||||
builder.begin_array ();
|
||||
add_vararray_values (val, builder);
|
||||
builder.end_array ();
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
case TUPLE:
|
||||
if (val.n_children () != 2) {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid type annotation: %s",
|
||||
(string) val.get_type ().peek_string ());
|
||||
}
|
||||
|
||||
var type = val.get_child_value (0);
|
||||
if (!type.is_of_type (VariantType.STRING)) {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid type annotation: %s",
|
||||
(string) val.get_type ().peek_string ());
|
||||
}
|
||||
unowned string type_str = type.get_string ();
|
||||
|
||||
add_variant_value_of_type (val.get_child_value (1), type_str, builder);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported type: %s", (string) val.get_type ().peek_string ());
|
||||
}
|
||||
|
||||
private static void add_variant_value_of_type (Variant val, string type, Fruity.XpcBodyBuilder builder) throws Error {
|
||||
switch (type) {
|
||||
case "bool":
|
||||
check_type (val, VariantType.BOOLEAN);
|
||||
builder.add_bool_value (val.get_boolean ());
|
||||
break;
|
||||
case "int64":
|
||||
check_type (val, VariantType.INT64);
|
||||
builder.add_int64_value (val.get_int64 ());
|
||||
break;
|
||||
case "uint64":
|
||||
check_type (val, VariantType.UINT64);
|
||||
builder.add_uint64_value (val.get_uint64 ());
|
||||
break;
|
||||
case "data":
|
||||
check_type (val, new VariantType ("ay"));
|
||||
builder.add_data_value (val.get_data_as_bytes ());
|
||||
break;
|
||||
case "string":
|
||||
check_type (val, VariantType.STRING);
|
||||
builder.add_string_value (val.get_string ());
|
||||
break;
|
||||
case "uuid":
|
||||
check_type (val, new VariantType ("ay"));
|
||||
if (val.get_size () != 16)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid UUID");
|
||||
unowned uint8[] data = (uint8[]) val.get_data ();
|
||||
builder.add_uuid_value (data[:16]);
|
||||
break;
|
||||
default:
|
||||
throw new Error.INVALID_ARGUMENT ("Unsupported type: %s", type);
|
||||
}
|
||||
}
|
||||
|
||||
private static void check_type (Variant v, VariantType t) throws Error {
|
||||
if (!v.is_of_type (t)) {
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid %s: %s",
|
||||
(string) t.peek_string (),
|
||||
(string) v.get_type ().peek_string ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,7 @@ frida_fetch_dyld_symbols (char * output_buffer, const void * dyld_load_address)
|
||||
const char * name = strings + sym->n_un.n_strx;
|
||||
|
||||
if (frida_str_contains (name, "libdyld_initialize") ||
|
||||
frida_str_contains (name, "notifyObjCInit") ||
|
||||
frida_str_contains (name, "restartWithDyldInCache") ||
|
||||
frida_str_equals (name, "_gProcessInfo") ||
|
||||
frida_str_equals (name, "__ZN5dyld412gProcessInfoE") ||
|
||||
|
||||
@@ -5,11 +5,15 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
|
||||
{
|
||||
uint8_t error_code;
|
||||
int fd;
|
||||
struct sockaddr_in addr;
|
||||
struct sockaddr_in6 addr = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = IN6ADDR_ANY_INIT,
|
||||
.sin6_port = 0,
|
||||
};
|
||||
socklen_t addr_len;
|
||||
int res;
|
||||
|
||||
fd = api->socket (AF_INET, SOCK_STREAM, 0);
|
||||
fd = api->socket (AF_INET6, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
goto socket_failed;
|
||||
|
||||
@@ -17,10 +21,6 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
|
||||
if (res == -1)
|
||||
goto setsockopt_failed;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
|
||||
addr.sin_port = 0;
|
||||
|
||||
addr_len = sizeof (addr);
|
||||
|
||||
res = api->bind (fd, (const struct sockaddr *) &addr, addr_len);
|
||||
@@ -35,7 +35,7 @@ frida_listen (int rx_buffer_size, const FridaUploadApi * api)
|
||||
if (res == -1)
|
||||
goto listen_failed;
|
||||
|
||||
return ((uint64_t) fd << 16) | ntohs (addr.sin_port);
|
||||
return ((uint64_t) fd << 16) | ntohs (addr.sin6_port);
|
||||
|
||||
socket_failed:
|
||||
{
|
||||
|
||||
+109
-89
@@ -64,7 +64,6 @@ namespace Frida.Fruity.Injector {
|
||||
|
||||
private LLDB.AppleDyldFields? dyld_fields;
|
||||
private uint64 dyld_base;
|
||||
private Gee.Map<string, uint64?>? dyld_symbols;
|
||||
|
||||
private bool libsystem_initialized;
|
||||
|
||||
@@ -160,7 +159,6 @@ namespace Frida.Fruity.Injector {
|
||||
return true;
|
||||
}, cancellable);
|
||||
}
|
||||
dyld_symbols = yield fetch_dyld_symbols (cancellable);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -629,22 +627,24 @@ namespace Frida.Fruity.Injector {
|
||||
|
||||
/* Compiled from helpers/upload-listener.c */
|
||||
private const uint8[] UPLOAD_LISTENER_CODE = {
|
||||
0xff, 0x43, 0x01, 0xd1, 0xf6, 0x57, 0x02, 0xa9, 0xf4, 0x4f, 0x03, 0xa9, 0xfd, 0x7b, 0x04, 0xa9, 0xfd, 0x03, 0x01,
|
||||
0x91, 0xf3, 0x03, 0x01, 0xaa, 0xe0, 0x1f, 0x00, 0xb9, 0x28, 0x00, 0x40, 0xf9, 0x40, 0x00, 0x80, 0x52, 0x21, 0x00,
|
||||
0x80, 0x52, 0x02, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x80, 0x05, 0x00, 0x54, 0xf4,
|
||||
0x03, 0x00, 0xaa, 0x68, 0x06, 0x40, 0xf9, 0xe3, 0x73, 0x00, 0x91, 0xe1, 0xff, 0x9f, 0x52, 0x42, 0x00, 0x82, 0x52,
|
||||
0x84, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xa0, 0x04, 0x00, 0x54, 0x48, 0x00, 0x80,
|
||||
0x52, 0xe8, 0x27, 0x00, 0x39, 0xe8, 0x0f, 0x80, 0x52, 0x08, 0x20, 0xa0, 0x72, 0xe8, 0x0f, 0x00, 0xb9, 0xff, 0x17,
|
||||
0x00, 0x79, 0x08, 0x02, 0x80, 0x52, 0xe8, 0x07, 0x00, 0xb9, 0x68, 0x0a, 0x40, 0xf9, 0xe1, 0x23, 0x00, 0x91, 0xe0,
|
||||
0x03, 0x14, 0xaa, 0x02, 0x02, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x00, 0x03, 0x00, 0x54,
|
||||
0x68, 0x12, 0x40, 0xf9, 0xe1, 0x23, 0x00, 0x91, 0xe2, 0x13, 0x00, 0x91, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f,
|
||||
0xff, 0x83, 0x01, 0xd1, 0xf6, 0x57, 0x03, 0xa9, 0xf4, 0x4f, 0x04, 0xa9, 0xfd, 0x7b, 0x05, 0xa9, 0xfd, 0x43, 0x01,
|
||||
0x91, 0xf3, 0x03, 0x01, 0xaa, 0x08, 0x00, 0x00, 0x90, 0x08, 0xb1, 0x04, 0x91, 0xa0, 0xc3, 0x1d, 0xb8, 0x00, 0x01,
|
||||
0xc0, 0x3d, 0xe0, 0x07, 0x80, 0x3d, 0x00, 0xc1, 0xc0, 0x3c, 0xe0, 0xc3, 0x81, 0x3c, 0x28, 0x00, 0x40, 0xf9, 0xc0,
|
||||
0x03, 0x80, 0x52, 0x21, 0x00, 0x80, 0x52, 0x02, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31,
|
||||
0xc0, 0x04, 0x00, 0x54, 0xf4, 0x03, 0x00, 0xaa, 0x68, 0x06, 0x40, 0xf9, 0xa3, 0x93, 0x00, 0xd1, 0xe1, 0xff, 0x9f,
|
||||
0x52, 0x42, 0x00, 0x82, 0x52, 0x84, 0x00, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xe0, 0x03,
|
||||
0x00, 0x54, 0x88, 0x03, 0x80, 0x52, 0xe8, 0x0f, 0x00, 0xb9, 0x68, 0x0a, 0x40, 0xf9, 0xe1, 0x43, 0x00, 0x91, 0xe0,
|
||||
0x03, 0x14, 0xaa, 0x82, 0x03, 0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0x00, 0x03, 0x00, 0x54,
|
||||
0x68, 0x12, 0x40, 0xf9, 0xe1, 0x43, 0x00, 0x91, 0xe2, 0x33, 0x00, 0x91, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f,
|
||||
0xd6, 0x1f, 0x04, 0x00, 0x31, 0x60, 0x02, 0x00, 0x54, 0x68, 0x0e, 0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x21, 0x00,
|
||||
0x80, 0x52, 0x00, 0x01, 0x3f, 0xd6, 0x1f, 0x04, 0x00, 0x31, 0xe0, 0x01, 0x00, 0x54, 0x88, 0x7e, 0x40, 0x93, 0xe9,
|
||||
0x17, 0x40, 0x79, 0x29, 0x09, 0xc0, 0x5a, 0x35, 0x7d, 0x10, 0x53, 0x15, 0xbd, 0x70, 0xb3, 0x0d, 0x00, 0x00, 0x14,
|
||||
0x27, 0x40, 0x79, 0x29, 0x09, 0xc0, 0x5a, 0x35, 0x7d, 0x10, 0x53, 0x15, 0xbd, 0x70, 0xb3, 0x0d, 0x00, 0x00, 0x14,
|
||||
0x15, 0x20, 0xe0, 0xd2, 0x0b, 0x00, 0x00, 0x14, 0x15, 0x40, 0xe0, 0xd2, 0x06, 0x00, 0x00, 0x14, 0x15, 0x60, 0xe0,
|
||||
0xd2, 0x04, 0x00, 0x00, 0x14, 0x15, 0x80, 0xe0, 0xd2, 0x02, 0x00, 0x00, 0x14, 0x15, 0xa0, 0xe0, 0xd2, 0x68, 0x42,
|
||||
0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0xe0, 0x03, 0x15, 0xaa, 0xfd, 0x7b, 0x44, 0xa9, 0xf4,
|
||||
0x4f, 0x43, 0xa9, 0xf6, 0x57, 0x42, 0xa9, 0xff, 0x43, 0x01, 0x91, 0xc0, 0x03, 0x5f, 0xd6
|
||||
0x40, 0xf9, 0xe0, 0x03, 0x14, 0xaa, 0x00, 0x01, 0x3f, 0xd6, 0xe0, 0x03, 0x15, 0xaa, 0xfd, 0x7b, 0x45, 0xa9, 0xf4,
|
||||
0x4f, 0x44, 0xa9, 0xf6, 0x57, 0x43, 0xa9, 0xff, 0x83, 0x01, 0x91, 0xc0, 0x03, 0x5f, 0xd6, 0x00, 0x1e, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
/* Compiled from helpers/upload-receiver.c */
|
||||
@@ -810,7 +810,7 @@ namespace Frida.Fruity.Injector {
|
||||
return total;
|
||||
}
|
||||
|
||||
private uint64 resolve_dyld_symbol (string name, string nick) throws Error {
|
||||
private static uint64 resolve_dyld_symbol (string name, string nick, Gee.Map<string, uint64?> dyld_symbols) throws Error {
|
||||
uint64? val = dyld_symbols[name];
|
||||
if (val == null)
|
||||
throw new Error.UNSUPPORTED ("Unsupported iOS version (%s not found)", nick);
|
||||
@@ -821,13 +821,15 @@ namespace Frida.Fruity.Injector {
|
||||
if (libsystem_initialized)
|
||||
return;
|
||||
|
||||
var dyld_symbols = yield fetch_dyld_symbols (cancellable);
|
||||
|
||||
yield restore_main_thread_state (cancellable);
|
||||
|
||||
uint64? libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE"];
|
||||
if (libdyld_initialize != null)
|
||||
yield ensure_libsystem_initialized_for_dyld_v4_and_above (libdyld_initialize, cancellable);
|
||||
yield ensure_libsystem_initialized_for_dyld_v4_and_above (libdyld_initialize, dyld_symbols, cancellable);
|
||||
else
|
||||
yield ensure_libsystem_initialized_for_dyld_v3_and_below (cancellable);
|
||||
yield ensure_libsystem_initialized_for_dyld_v3_and_below (dyld_symbols, cancellable);
|
||||
|
||||
yield save_main_thread_state (cancellable);
|
||||
|
||||
@@ -836,17 +838,21 @@ namespace Frida.Fruity.Injector {
|
||||
}
|
||||
|
||||
private async void ensure_libsystem_initialized_for_dyld_v4_and_above (uint64 libdyld_initialize,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
Gee.Map<string, uint64?> dyld_symbols, Cancellable? cancellable) throws GLib.Error {
|
||||
uint64? process_info_ptr = dyld_symbols["__ZL12sProcessInfo"];
|
||||
if (process_info_ptr == null)
|
||||
process_info_ptr = dyld_symbols["__ZN5dyld412gProcessInfoE"];
|
||||
if (process_info_ptr == null)
|
||||
process_info_ptr = dyld_symbols["_gProcessInfo"];
|
||||
|
||||
GDB.Breakpoint init_breakpoint = yield lldb.add_breakpoint (SOFT, libdyld_initialize, 4, cancellable);
|
||||
uint64? notify_objc_init = dyld_symbols["__ZN5dyld412RuntimeState14notifyObjCInitEPKNS_6LoaderE"];
|
||||
uint64 initializer = (notify_objc_init != null) ? notify_objc_init : libdyld_initialize;
|
||||
GDB.Breakpoint init_breakpoint = yield lldb.add_breakpoint (SOFT, initializer, 4, cancellable);
|
||||
|
||||
GDB.Breakpoint? restart_breakpoint = null;
|
||||
uint64? restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv"];
|
||||
uint64? restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPK15DyldSharedCachePv"];
|
||||
if (restart_with_dyld_in_cache == null)
|
||||
restart_with_dyld_in_cache = dyld_symbols["__ZN5dyld422restartWithDyldInCacheEPKNS_10KernelArgsEPKN5dyld39MachOFileEPv"];
|
||||
if (restart_with_dyld_in_cache != null)
|
||||
restart_breakpoint = yield lldb.add_breakpoint (SOFT, restart_with_dyld_in_cache, 4, cancellable);
|
||||
|
||||
@@ -856,12 +862,18 @@ namespace Frida.Fruity.Injector {
|
||||
if (hit_breakpoint == null)
|
||||
throw new Error.UNSUPPORTED ("Unexpected exception");
|
||||
|
||||
if (restart_breakpoint != null)
|
||||
yield restart_breakpoint.remove (cancellable);
|
||||
yield init_breakpoint.remove (cancellable);
|
||||
|
||||
if (hit_breakpoint == restart_breakpoint) {
|
||||
if (process_info_ptr == null)
|
||||
throw new Error.UNSUPPORTED ("Missing gProcessInfo");
|
||||
uint64 process_info = yield lldb.read_pointer (process_info_ptr, cancellable);
|
||||
uint64 dyld_image_load_address = yield lldb.read_pointer (process_info + 0x20, cancellable);
|
||||
|
||||
initializer = dyld_image_load_address + (initializer - dyld_base);
|
||||
|
||||
var rebased_symbols = new Gee.HashMap<string, uint64?> ();
|
||||
foreach (var e in dyld_symbols.entries)
|
||||
rebased_symbols[e.key] = dyld_image_load_address + (e.value - dyld_base);
|
||||
@@ -869,13 +881,12 @@ namespace Frida.Fruity.Injector {
|
||||
dyld_base = dyld_image_load_address;
|
||||
dyld_symbols = rebased_symbols;
|
||||
|
||||
yield restart_breakpoint.remove (cancellable);
|
||||
yield init_breakpoint.remove (cancellable);
|
||||
uint64 real_libdyld_initialize = dyld_symbols["__ZN5dyld44APIs19_libdyld_initializeEPKNS_16LibSystemHelpersE"];
|
||||
init_breakpoint = yield lldb.add_breakpoint (SOFT, real_libdyld_initialize, 4, cancellable);
|
||||
init_breakpoint = yield lldb.add_breakpoint (SOFT, initializer, 4, cancellable);
|
||||
|
||||
exception = (LLDB.Exception) yield lldb.continue_until_exception (cancellable);
|
||||
|
||||
yield init_breakpoint.remove (cancellable);
|
||||
|
||||
hit_breakpoint = exception.breakpoint;
|
||||
if (hit_breakpoint == null)
|
||||
throw new Error.UNSUPPORTED ("Unexpected exception");
|
||||
@@ -884,7 +895,10 @@ namespace Frida.Fruity.Injector {
|
||||
}
|
||||
|
||||
assert (hit_breakpoint == init_breakpoint);
|
||||
yield init_breakpoint.remove (cancellable);
|
||||
|
||||
bool done_because_we_hit_notify_objc_init = initializer != libdyld_initialize;
|
||||
if (done_because_we_hit_notify_objc_init)
|
||||
return;
|
||||
|
||||
uint64 dyld_size = dyld_symbols["dyld_size"];
|
||||
uint64 frame_here = exception.context["fp"];
|
||||
@@ -913,7 +927,8 @@ namespace Frida.Fruity.Injector {
|
||||
yield caller_breakpoint.remove (cancellable);
|
||||
}
|
||||
|
||||
private async void ensure_libsystem_initialized_for_dyld_v3_and_below (Cancellable? cancellable) throws GLib.Error {
|
||||
private async void ensure_libsystem_initialized_for_dyld_v3_and_below (Gee.Map<string, uint64?> dyld_symbols,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
GDB.Breakpoint? modern_breakpoint = null;
|
||||
uint64 launch_with_closure = 0;
|
||||
|
||||
@@ -933,7 +948,8 @@ namespace Frida.Fruity.Injector {
|
||||
modern_breakpoint = yield lldb.add_breakpoint (SOFT, run_initializers_call, 4, cancellable);
|
||||
}
|
||||
|
||||
uint64 initialize_main_executable = resolve_dyld_symbol ("__ZN4dyld24initializeMainExecutableEv", "initializeMainExecutable");
|
||||
uint64 initialize_main_executable = resolve_dyld_symbol ("__ZN4dyld24initializeMainExecutableEv",
|
||||
"initializeMainExecutable", dyld_symbols);
|
||||
GDB.Breakpoint legacy_breakpoint = yield lldb.add_breakpoint (SOFT, initialize_main_executable, 4, cancellable);
|
||||
|
||||
var exception = yield lldb.continue_until_exception (cancellable);
|
||||
@@ -948,7 +964,7 @@ namespace Frida.Fruity.Injector {
|
||||
yield modern_breakpoint.remove (cancellable);
|
||||
|
||||
if (hit_breakpoint == legacy_breakpoint)
|
||||
yield initialize_libsystem_from_legacy_codepath (cancellable);
|
||||
yield initialize_libsystem_from_legacy_codepath (dyld_symbols, cancellable);
|
||||
else
|
||||
assert (hit_breakpoint == modern_breakpoint);
|
||||
}
|
||||
@@ -989,7 +1005,8 @@ namespace Frida.Fruity.Injector {
|
||||
throw new Error.UNSUPPORTED ("Unable to probe dyld v3 internals; please file a bug");
|
||||
}
|
||||
|
||||
private async void initialize_libsystem_from_legacy_codepath (Cancellable? cancellable) throws GLib.Error {
|
||||
private async void initialize_libsystem_from_legacy_codepath (Gee.Map<string, uint64?> dyld_symbols,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
uint64 code = jit_page;
|
||||
|
||||
var code_builder_pac = lldb.make_buffer_builder ();
|
||||
@@ -1055,8 +1072,8 @@ namespace Frida.Fruity.Injector {
|
||||
code_builder.write_pointer (error_buf_literal_offset, helpers + error_buffer_offset);
|
||||
yield lldb.write_byte_array (code, code_builder.build (), cancellable);
|
||||
|
||||
uint64 register_thread_helpers =
|
||||
resolve_dyld_symbol ("__ZL21registerThreadHelpersPKN4dyld16LibSystemHelpersE", "registerThreadHelpers");
|
||||
uint64 register_thread_helpers = resolve_dyld_symbol ("__ZL21registerThreadHelpersPKN4dyld16LibSystemHelpersE",
|
||||
"registerThreadHelpers", dyld_symbols);
|
||||
yield invoke_remote_function (register_thread_helpers, {
|
||||
helpers
|
||||
}, null, cancellable);
|
||||
@@ -1075,8 +1092,8 @@ namespace Frida.Fruity.Injector {
|
||||
ExceptionHandler? strcmp_handler = null;
|
||||
|
||||
var dlopen = dyld_symbols.has_key ("_dlopen_internal")
|
||||
? resolve_dyld_symbol ("_dlopen_internal", "dlopen")
|
||||
: resolve_dyld_symbol ("_dlopen", "dlopen");
|
||||
? resolve_dyld_symbol ("_dlopen_internal", "dlopen", dyld_symbols)
|
||||
: resolve_dyld_symbol ("_dlopen", "dlopen", dyld_symbols);
|
||||
yield invoke_remote_function (dlopen, {
|
||||
libsystem_string,
|
||||
9,
|
||||
@@ -1184,68 +1201,71 @@ namespace Frida.Fruity.Injector {
|
||||
0xff, 0x03, 0x02, 0xd1, 0xfc, 0x6f, 0x02, 0xa9, 0xfa, 0x67, 0x03, 0xa9, 0xf8, 0x5f, 0x04, 0xa9, 0xf6, 0x57, 0x05,
|
||||
0xa9, 0xf4, 0x4f, 0x06, 0xa9, 0xfd, 0x7b, 0x07, 0xa9, 0xfd, 0xc3, 0x01, 0x91, 0xf4, 0x03, 0x01, 0xaa, 0xe0, 0x07,
|
||||
0x00, 0xf9, 0x13, 0x00, 0x80, 0xd2, 0x19, 0x00, 0x80, 0xd2, 0x35, 0x80, 0x00, 0x91, 0x3a, 0x10, 0x40, 0xb9, 0x16,
|
||||
0x00, 0x00, 0x90, 0xd6, 0xb2, 0x12, 0x91, 0x17, 0x00, 0x00, 0x90, 0xf7, 0xce, 0x12, 0x91, 0x1a, 0x04, 0x00, 0x34,
|
||||
0x00, 0x00, 0x90, 0xd6, 0x3e, 0x13, 0x91, 0x17, 0x00, 0x00, 0x90, 0xf7, 0x5a, 0x13, 0x91, 0x1a, 0x04, 0x00, 0x34,
|
||||
0xa8, 0x02, 0x40, 0xb9, 0x1f, 0x09, 0x00, 0x71, 0xa0, 0x01, 0x00, 0x54, 0x1f, 0x2d, 0x00, 0x71, 0xa0, 0x01, 0x00,
|
||||
0x54, 0x1f, 0x65, 0x00, 0x71, 0x81, 0x01, 0x00, 0x54, 0xb8, 0x22, 0x00, 0x91, 0xe0, 0x03, 0x18, 0xaa, 0xe1, 0x03,
|
||||
0x16, 0xaa, 0xa5, 0x00, 0x00, 0x94, 0x80, 0x01, 0x00, 0x34, 0xb3, 0xa2, 0x41, 0xa9, 0xe8, 0x0b, 0x00, 0xf9, 0x04,
|
||||
0x16, 0xaa, 0xaa, 0x00, 0x00, 0x94, 0x80, 0x01, 0x00, 0x34, 0xb3, 0xa2, 0x41, 0xa9, 0xe8, 0x0b, 0x00, 0xf9, 0x04,
|
||||
0x00, 0x00, 0x14, 0xfc, 0x03, 0x15, 0xaa, 0x02, 0x00, 0x00, 0x14, 0xfb, 0x03, 0x15, 0xaa, 0xa8, 0x06, 0x40, 0xb9,
|
||||
0xb5, 0x02, 0x08, 0x8b, 0x5a, 0x07, 0x00, 0x51, 0x7a, 0xfd, 0xff, 0x35, 0x09, 0x00, 0x00, 0x14, 0xe0, 0x03, 0x18,
|
||||
0xaa, 0xe1, 0x03, 0x17, 0xaa, 0x96, 0x00, 0x00, 0x94, 0x00, 0xff, 0xff, 0x34, 0xa8, 0x0e, 0x40, 0xf9, 0xa9, 0x16,
|
||||
0xaa, 0xe1, 0x03, 0x17, 0xaa, 0x9b, 0x00, 0x00, 0x94, 0x00, 0xff, 0xff, 0x34, 0xa8, 0x0e, 0x40, 0xf9, 0xa9, 0x16,
|
||||
0x40, 0xf9, 0x19, 0x01, 0x09, 0xcb, 0xf4, 0xff, 0xff, 0x17, 0x88, 0x02, 0x13, 0xcb, 0x28, 0x03, 0x08, 0x8b, 0x89,
|
||||
0x0b, 0x40, 0xb9, 0x13, 0x01, 0x09, 0x8b, 0x89, 0x13, 0x40, 0xb9, 0x19, 0x01, 0x09, 0x8b, 0xe8, 0x07, 0x40, 0xf9,
|
||||
0xe8, 0x0f, 0x00, 0xf9, 0x16, 0x00, 0x00, 0x90, 0xd6, 0x22, 0x0f, 0x91, 0x35, 0x01, 0x80, 0x52, 0x78, 0x0b, 0x40,
|
||||
0xb9, 0x68, 0x0f, 0x40, 0xb9, 0x1f, 0x03, 0x08, 0x6b, 0x20, 0x0a, 0x00, 0x54, 0x77, 0x52, 0x38, 0x8b, 0xe8, 0x02,
|
||||
0x40, 0xb9, 0x3c, 0x03, 0x08, 0x8b, 0xe0, 0x03, 0x1c, 0xaa, 0xe1, 0x03, 0x16, 0xaa, 0x65, 0x00, 0x00, 0x94, 0x00,
|
||||
0x07, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x6c, 0x0f, 0x91, 0x60, 0x00, 0x00, 0x94,
|
||||
0x60, 0x06, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xc8, 0x0f, 0x91, 0x72, 0x00, 0x00,
|
||||
0x94, 0xc0, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x00, 0x10, 0x91, 0x6d, 0x00,
|
||||
0x00, 0x94, 0x20, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x68, 0x10, 0x91, 0x68,
|
||||
0x00, 0x00, 0x94, 0x80, 0x04, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xb4, 0x10, 0x91,
|
||||
0x4c, 0x00, 0x00, 0x94, 0xe0, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xfc, 0x10,
|
||||
0x91, 0x47, 0x00, 0x00, 0x94, 0x40, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x60,
|
||||
0x11, 0x91, 0x42, 0x00, 0x00, 0x94, 0xa0, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21,
|
||||
0xb8, 0x11, 0x91, 0x5c, 0x00, 0x00, 0x94, 0x00, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90,
|
||||
0x21, 0xd8, 0x11, 0x91, 0x57, 0x00, 0x00, 0x94, 0x60, 0x01, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00,
|
||||
0x90, 0x21, 0xf8, 0x11, 0x91, 0x33, 0x00, 0x00, 0x94, 0xc0, 0x00, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00,
|
||||
0x00, 0x90, 0x21, 0x44, 0x12, 0x91, 0x2e, 0x00, 0x00, 0x94, 0x20, 0x02, 0x00, 0x34, 0xba, 0x00, 0x00, 0x34, 0xe8,
|
||||
0x0f, 0x40, 0xf9, 0x49, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe8, 0x06, 0x40, 0xf9,
|
||||
0x81, 0x02, 0x08, 0x8b, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0x15, 0x15, 0x00,
|
||||
0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0xe1, 0x03, 0x1c, 0xaa, 0x5d, 0x00, 0x00, 0x94, 0x5a, 0x07,
|
||||
0x00, 0x11, 0x18, 0x07, 0x00, 0x11, 0xae, 0xff, 0xff, 0x17, 0xe1, 0x23, 0x41, 0xa9, 0x49, 0x01, 0x80, 0x52, 0x09,
|
||||
0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0x3d, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9,
|
||||
0x29, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0x01, 0x00, 0x00, 0x90, 0x21, 0x88, 0x12,
|
||||
0x91, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0xe9, 0x07, 0x40, 0xf9, 0x00, 0x01,
|
||||
0x09, 0xcb, 0x1f, 0x01, 0x00, 0x39, 0xfd, 0x7b, 0x47, 0xa9, 0xf4, 0x4f, 0x46, 0xa9, 0xf6, 0x57, 0x45, 0xa9, 0xf8,
|
||||
0x5f, 0x44, 0xa9, 0xfa, 0x67, 0x43, 0xa9, 0xfc, 0x6f, 0x42, 0xa9, 0xff, 0x03, 0x02, 0x91, 0xc0, 0x03, 0x5f, 0xd6,
|
||||
0xf6, 0x57, 0xbd, 0xa9, 0xf4, 0x4f, 0x01, 0xa9, 0xfd, 0x7b, 0x02, 0xa9, 0xfd, 0x83, 0x00, 0x91, 0xf3, 0x03, 0x01,
|
||||
0xaa, 0xf4, 0x03, 0x00, 0xaa, 0x35, 0x00, 0x40, 0x39, 0x96, 0x02, 0x40, 0x39, 0x36, 0x01, 0x00, 0x34, 0xdf, 0x02,
|
||||
0x15, 0x6b, 0xa1, 0x00, 0x00, 0x54, 0xe0, 0x03, 0x14, 0xaa, 0xe1, 0x03, 0x13, 0xaa, 0x12, 0x00, 0x00, 0x94, 0x60,
|
||||
0x00, 0x00, 0x37, 0x94, 0x06, 0x00, 0x91, 0xf7, 0xff, 0xff, 0x17, 0xdf, 0x02, 0x00, 0x71, 0xe0, 0x07, 0x9f, 0x1a,
|
||||
0xfd, 0x7b, 0x42, 0xa9, 0xf4, 0x4f, 0x41, 0xa9, 0xf6, 0x57, 0xc3, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x14, 0x40,
|
||||
0x38, 0x29, 0x14, 0x40, 0x38, 0x1f, 0x01, 0x00, 0x71, 0x00, 0x11, 0x49, 0x7a, 0x80, 0xff, 0xff, 0x54, 0x1f, 0x01,
|
||||
0x09, 0x6b, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x28, 0x00, 0x40, 0x39, 0xa8, 0x00, 0x00, 0x34, 0x21,
|
||||
0x04, 0x00, 0x91, 0x09, 0x14, 0x40, 0x38, 0x3f, 0x01, 0x08, 0x6b, 0x60, 0xff, 0xff, 0x54, 0x1f, 0x01, 0x00, 0x71,
|
||||
0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x09, 0x00, 0x80, 0x52, 0x08, 0x00, 0x40, 0xf9, 0x8a, 0x07, 0x80,
|
||||
0x52, 0x0b, 0x00, 0x00, 0x90, 0x6b, 0x01, 0x13, 0x91, 0x5f, 0x11, 0x00, 0x31, 0x80, 0x01, 0x00, 0x54, 0x2c, 0x24,
|
||||
0xca, 0x9a, 0x8c, 0x0d, 0x40, 0x92, 0x2d, 0x01, 0x00, 0x12, 0x9f, 0x01, 0x00, 0x71, 0xa0, 0x19, 0x40, 0x7a, 0x29,
|
||||
0x15, 0x9f, 0x1a, 0x69, 0x00, 0x00, 0x36, 0x6c, 0x69, 0x6c, 0x38, 0x0c, 0x15, 0x00, 0x38, 0x4a, 0x11, 0x00, 0x51,
|
||||
0xf4, 0xff, 0xff, 0x17, 0x69, 0x00, 0x00, 0x37, 0x09, 0x06, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0x08, 0x00, 0x00,
|
||||
0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x00, 0x40, 0xf9, 0x29, 0x00, 0x40, 0x39, 0x89, 0x00, 0x00, 0x34, 0x21, 0x04,
|
||||
0x00, 0x91, 0x09, 0x15, 0x00, 0x38, 0xfc, 0xff, 0xff, 0x17, 0x08, 0x00, 0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x6c,
|
||||
0x69, 0x62, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x00, 0x72,
|
||||
0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x57, 0x69, 0x74, 0x68, 0x44, 0x79, 0x6c, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x63,
|
||||
0x68, 0x65, 0x00, 0x5f, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x5f,
|
||||
0x5a, 0x4e, 0x35, 0x64, 0x79, 0x6c, 0x64, 0x34, 0x31, 0x32, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x45, 0x00, 0x5f, 0x5f, 0x5a, 0x4c, 0x31, 0x32, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x00, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x57, 0x69, 0x74, 0x68, 0x43, 0x6c, 0x6f, 0x73,
|
||||
0x75, 0x72, 0x65, 0x00, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4d, 0x61, 0x69, 0x6e, 0x45,
|
||||
0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54,
|
||||
0x68, 0x72, 0x65, 0x61, 0x64, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x73, 0x00, 0x5f, 0x64, 0x6c, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x00, 0x5f, 0x73, 0x74, 0x72, 0x63, 0x6d, 0x70, 0x00, 0x64, 0x6f, 0x4d, 0x6f, 0x64, 0x49, 0x6e, 0x69, 0x74,
|
||||
0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x6f, 0x47, 0x65, 0x74, 0x44, 0x4f, 0x46, 0x53,
|
||||
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x5f,
|
||||
0x5f, 0x54, 0x45, 0x58, 0x54, 0x00, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
|
||||
0xe8, 0x0f, 0x00, 0xf9, 0x16, 0x00, 0x00, 0x90, 0xd6, 0x72, 0x0f, 0x91, 0x35, 0x01, 0x80, 0x52, 0x78, 0x0b, 0x40,
|
||||
0xb9, 0x68, 0x0f, 0x40, 0xb9, 0x1f, 0x03, 0x08, 0x6b, 0xc0, 0x0a, 0x00, 0x54, 0x77, 0x52, 0x38, 0x8b, 0xe8, 0x02,
|
||||
0x40, 0xb9, 0x3c, 0x03, 0x08, 0x8b, 0xe0, 0x03, 0x1c, 0xaa, 0xe1, 0x03, 0x16, 0xaa, 0x6a, 0x00, 0x00, 0x94, 0xa0,
|
||||
0x07, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xbc, 0x0f, 0x91, 0x65, 0x00, 0x00, 0x94,
|
||||
0x00, 0x07, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xf8, 0x0f, 0x91, 0x60, 0x00, 0x00,
|
||||
0x94, 0x60, 0x06, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x54, 0x10, 0x91, 0x72, 0x00,
|
||||
0x00, 0x94, 0xc0, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x8c, 0x10, 0x91, 0x6d,
|
||||
0x00, 0x00, 0x94, 0x20, 0x05, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0xf4, 0x10, 0x91,
|
||||
0x68, 0x00, 0x00, 0x94, 0x80, 0x04, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x40, 0x11,
|
||||
0x91, 0x4c, 0x00, 0x00, 0x94, 0xe0, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21, 0x88,
|
||||
0x11, 0x91, 0x47, 0x00, 0x00, 0x94, 0x40, 0x03, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90, 0x21,
|
||||
0xec, 0x11, 0x91, 0x42, 0x00, 0x00, 0x94, 0xa0, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00, 0x90,
|
||||
0x21, 0x44, 0x12, 0x91, 0x5c, 0x00, 0x00, 0x94, 0x00, 0x02, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00, 0x00,
|
||||
0x90, 0x21, 0x64, 0x12, 0x91, 0x57, 0x00, 0x00, 0x94, 0x60, 0x01, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01, 0x00,
|
||||
0x00, 0x90, 0x21, 0x84, 0x12, 0x91, 0x33, 0x00, 0x00, 0x94, 0xc0, 0x00, 0x00, 0x37, 0xe0, 0x03, 0x1c, 0xaa, 0x01,
|
||||
0x00, 0x00, 0x90, 0x21, 0xd0, 0x12, 0x91, 0x2e, 0x00, 0x00, 0x94, 0x20, 0x02, 0x00, 0x34, 0xba, 0x00, 0x00, 0x34,
|
||||
0xe8, 0x0f, 0x40, 0xf9, 0x49, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe8, 0x06, 0x40,
|
||||
0xf9, 0x81, 0x02, 0x08, 0x8b, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0x15, 0x15,
|
||||
0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0xe1, 0x03, 0x1c, 0xaa, 0x5d, 0x00, 0x00, 0x94, 0x5a,
|
||||
0x07, 0x00, 0x11, 0x18, 0x07, 0x00, 0x11, 0xa9, 0xff, 0xff, 0x17, 0xe1, 0x23, 0x41, 0xa9, 0x49, 0x01, 0x80, 0x52,
|
||||
0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0xe0, 0x63, 0x00, 0x91, 0x3d, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40,
|
||||
0xf9, 0x29, 0x01, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0xe8, 0x0f, 0x00, 0xf9, 0x01, 0x00, 0x00, 0x90, 0x21, 0x14,
|
||||
0x13, 0x91, 0xe0, 0x63, 0x00, 0x91, 0x4c, 0x00, 0x00, 0x94, 0xe8, 0x0f, 0x40, 0xf9, 0xe9, 0x07, 0x40, 0xf9, 0x00,
|
||||
0x01, 0x09, 0xcb, 0x1f, 0x01, 0x00, 0x39, 0xfd, 0x7b, 0x47, 0xa9, 0xf4, 0x4f, 0x46, 0xa9, 0xf6, 0x57, 0x45, 0xa9,
|
||||
0xf8, 0x5f, 0x44, 0xa9, 0xfa, 0x67, 0x43, 0xa9, 0xfc, 0x6f, 0x42, 0xa9, 0xff, 0x03, 0x02, 0x91, 0xc0, 0x03, 0x5f,
|
||||
0xd6, 0xf6, 0x57, 0xbd, 0xa9, 0xf4, 0x4f, 0x01, 0xa9, 0xfd, 0x7b, 0x02, 0xa9, 0xfd, 0x83, 0x00, 0x91, 0xf3, 0x03,
|
||||
0x01, 0xaa, 0xf4, 0x03, 0x00, 0xaa, 0x35, 0x00, 0x40, 0x39, 0x96, 0x02, 0x40, 0x39, 0x36, 0x01, 0x00, 0x34, 0xdf,
|
||||
0x02, 0x15, 0x6b, 0xa1, 0x00, 0x00, 0x54, 0xe0, 0x03, 0x14, 0xaa, 0xe1, 0x03, 0x13, 0xaa, 0x12, 0x00, 0x00, 0x94,
|
||||
0x60, 0x00, 0x00, 0x37, 0x94, 0x06, 0x00, 0x91, 0xf7, 0xff, 0xff, 0x17, 0xdf, 0x02, 0x00, 0x71, 0xe0, 0x07, 0x9f,
|
||||
0x1a, 0xfd, 0x7b, 0x42, 0xa9, 0xf4, 0x4f, 0x41, 0xa9, 0xf6, 0x57, 0xc3, 0xa8, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x14,
|
||||
0x40, 0x38, 0x29, 0x14, 0x40, 0x38, 0x1f, 0x01, 0x00, 0x71, 0x00, 0x11, 0x49, 0x7a, 0x80, 0xff, 0xff, 0x54, 0x1f,
|
||||
0x01, 0x09, 0x6b, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x28, 0x00, 0x40, 0x39, 0xa8, 0x00, 0x00, 0x34,
|
||||
0x21, 0x04, 0x00, 0x91, 0x09, 0x14, 0x40, 0x38, 0x3f, 0x01, 0x08, 0x6b, 0x60, 0xff, 0xff, 0x54, 0x1f, 0x01, 0x00,
|
||||
0x71, 0xe0, 0x17, 0x9f, 0x1a, 0xc0, 0x03, 0x5f, 0xd6, 0x09, 0x00, 0x80, 0x52, 0x08, 0x00, 0x40, 0xf9, 0x8a, 0x07,
|
||||
0x80, 0x52, 0x0b, 0x00, 0x00, 0x90, 0x6b, 0xc1, 0x13, 0x91, 0x5f, 0x11, 0x00, 0x31, 0x80, 0x01, 0x00, 0x54, 0x2c,
|
||||
0x24, 0xca, 0x9a, 0x8c, 0x0d, 0x40, 0x92, 0x2d, 0x01, 0x00, 0x12, 0x9f, 0x01, 0x00, 0x71, 0xa0, 0x19, 0x40, 0x7a,
|
||||
0x29, 0x15, 0x9f, 0x1a, 0x69, 0x00, 0x00, 0x36, 0x6c, 0x69, 0x6c, 0x38, 0x0c, 0x15, 0x00, 0x38, 0x4a, 0x11, 0x00,
|
||||
0x51, 0xf4, 0xff, 0xff, 0x17, 0x69, 0x00, 0x00, 0x37, 0x09, 0x06, 0x80, 0x52, 0x09, 0x15, 0x00, 0x38, 0x08, 0x00,
|
||||
0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6, 0x08, 0x00, 0x40, 0xf9, 0x29, 0x00, 0x40, 0x39, 0x89, 0x00, 0x00, 0x34, 0x21,
|
||||
0x04, 0x00, 0x91, 0x09, 0x15, 0x00, 0x38, 0xfc, 0xff, 0xff, 0x17, 0x08, 0x00, 0x00, 0xf9, 0xc0, 0x03, 0x5f, 0xd6,
|
||||
0x6c, 0x69, 0x62, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x00,
|
||||
0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4f, 0x62, 0x6a, 0x43, 0x49, 0x6e, 0x69, 0x74, 0x00, 0x72, 0x65, 0x73, 0x74,
|
||||
0x61, 0x72, 0x74, 0x57, 0x69, 0x74, 0x68, 0x44, 0x79, 0x6c, 0x64, 0x49, 0x6e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x00,
|
||||
0x5f, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f, 0x00, 0x5f, 0x5f, 0x5a, 0x4e, 0x35,
|
||||
0x64, 0x79, 0x6c, 0x64, 0x34, 0x31, 0x32, 0x67, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x45, 0x00, 0x5f, 0x5f, 0x5a, 0x4c, 0x31, 0x32, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x00, 0x6c, 0x61, 0x75, 0x6e, 0x63, 0x68, 0x57, 0x69, 0x74, 0x68, 0x43, 0x6c, 0x6f, 0x73, 0x75, 0x72, 0x65,
|
||||
0x00, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x4d, 0x61, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63,
|
||||
0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x68, 0x72, 0x65,
|
||||
0x61, 0x64, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x73, 0x00, 0x5f, 0x64, 0x6c, 0x6f, 0x70, 0x65, 0x6e, 0x00, 0x5f,
|
||||
0x73, 0x74, 0x72, 0x63, 0x6d, 0x70, 0x00, 0x64, 0x6f, 0x4d, 0x6f, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x75, 0x6e,
|
||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x6f, 0x47, 0x65, 0x74, 0x44, 0x4f, 0x46, 0x53, 0x65, 0x63, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x73, 0x00, 0x64, 0x79, 0x6c, 0x64, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x5f, 0x5f, 0x54, 0x45,
|
||||
0x58, 0x54, 0x00, 0x5f, 0x5f, 0x4c, 0x49, 0x4e, 0x4b, 0x45, 0x44, 0x49, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
||||
0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66
|
||||
};
|
||||
|
||||
private async SymbolSet resolve_symbols (SymbolQuery query, Cancellable? cancellable) throws GLib.Error {
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class InstallationProxyClient : Object, AsyncInitable {
|
||||
public LockdownClient lockdown {
|
||||
public Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private PlistServiceClient service;
|
||||
|
||||
private InstallationProxyClient (LockdownClient lockdown) {
|
||||
Object (lockdown: lockdown);
|
||||
private InstallationProxyClient (Device device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
public static async InstallationProxyClient open (LockdownClient lockdown, Cancellable? cancellable = null) throws InstallationProxyError, IOError {
|
||||
var client = new InstallationProxyClient (lockdown);
|
||||
public static async InstallationProxyClient open (Device device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var client = new InstallationProxyClient (device);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_local_error (e);
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws InstallationProxyError, IOError {
|
||||
try {
|
||||
var stream = yield lockdown.start_service ("com.apple.mobile.installation_proxy", cancellable);
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
var stream = yield device.open_lockdown_service ("com.apple.mobile.installation_proxy", cancellable);
|
||||
|
||||
service = new PlistServiceClient (stream);
|
||||
} catch (LockdownError e) {
|
||||
throw new InstallationProxyError.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
service = new PlistServiceClient (stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -40,8 +36,7 @@ namespace Frida.Fruity {
|
||||
yield service.close (cancellable);
|
||||
}
|
||||
|
||||
public async Gee.ArrayList<ApplicationDetails> browse (Cancellable? cancellable = null)
|
||||
throws InstallationProxyError, IOError {
|
||||
public async Gee.ArrayList<ApplicationDetails> browse (Cancellable? cancellable = null) throws Error, IOError {
|
||||
try {
|
||||
var result = new Gee.ArrayList<ApplicationDetails> ();
|
||||
|
||||
@@ -75,7 +70,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public async Gee.HashMap<string, ApplicationDetails> lookup (PlistDict query, Cancellable? cancellable = null)
|
||||
throws InstallationProxyError, IOError {
|
||||
throws Error, IOError {
|
||||
try {
|
||||
var result = new Gee.HashMap<string, ApplicationDetails> ();
|
||||
|
||||
@@ -189,28 +184,13 @@ namespace Frida.Fruity {
|
||||
return new ApplicationDetails (identifier, name, version, build, path, containers, debuggable);
|
||||
}
|
||||
|
||||
private static void throw_local_error (GLib.Error e) throws InstallationProxyError, IOError {
|
||||
if (e is InstallationProxyError)
|
||||
throw (InstallationProxyError) e;
|
||||
|
||||
if (e is IOError)
|
||||
throw (IOError) e;
|
||||
|
||||
assert_not_reached ();
|
||||
private static Error error_from_service (PlistServiceError e) {
|
||||
return new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
private static InstallationProxyError error_from_service (PlistServiceError e) {
|
||||
return new InstallationProxyError.PROTOCOL ("%s", e.message);
|
||||
private static Error error_from_plist (PlistError e) {
|
||||
return new Error.PROTOCOL ("Unexpected response: %s", e.message);
|
||||
}
|
||||
|
||||
private static InstallationProxyError error_from_plist (PlistError e) {
|
||||
return new InstallationProxyError.PROTOCOL ("Unexpected response: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public errordomain InstallationProxyError {
|
||||
INVALID_ARGUMENT,
|
||||
PROTOCOL
|
||||
}
|
||||
|
||||
public class ApplicationDetails : Object {
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
namespace Frida {
|
||||
using CoreFoundation;
|
||||
using Darwin.XNU;
|
||||
|
||||
internal class IORegistry {
|
||||
private MachPort main_port;
|
||||
|
||||
public static IORegistry open () throws Error {
|
||||
MachPort port;
|
||||
kern_check (Darwin.IOKit.main_port (MachPort.NULL, out port));
|
||||
return new IORegistry (port);
|
||||
}
|
||||
|
||||
private IORegistry (MachPort main_port) {
|
||||
this.main_port = main_port;
|
||||
}
|
||||
|
||||
public IOIterator<IORegistryEntry> matching_services (owned MutableDictionary matching_dict) throws Error {
|
||||
Darwin.IOKit.IOIterator h;
|
||||
kern_check (Darwin.IOKit.get_matching_services (main_port, matching_dict, out h));
|
||||
return new IOIterator<IORegistryEntry> ((owned) h);
|
||||
}
|
||||
}
|
||||
|
||||
internal class IORegistryEntry : GLib.Object {
|
||||
public IOObject io_object {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
internal IORegistryEntry (IOObject obj) {
|
||||
GLib.Object (io_object: obj);
|
||||
}
|
||||
|
||||
public MutableDictionary get_properties () throws Error {
|
||||
MutableDictionary properties;
|
||||
kern_check (unwrap ().create_cf_properties (out properties, null, 0));
|
||||
return properties;
|
||||
}
|
||||
|
||||
public string? get_string_property (string key) {
|
||||
var v = (String) unwrap ().create_cf_property (String.make (key), null, 0);
|
||||
return (v != null) ? v.to_string () : null;
|
||||
}
|
||||
|
||||
public IORegistryEntry parent (string plane) throws Error {
|
||||
Darwin.IOKit.IOObject h;
|
||||
kern_check (unwrap ().get_parent_entry (plane, out h));
|
||||
return new IORegistryEntry (new IOObject ((owned) h));
|
||||
}
|
||||
|
||||
private unowned Darwin.IOKit.IORegistryEntry unwrap () {
|
||||
return (Darwin.IOKit.IORegistryEntry) io_object.handle;
|
||||
}
|
||||
}
|
||||
|
||||
internal class IOIterator<T> {
|
||||
private Darwin.IOKit.IOIterator handle;
|
||||
|
||||
internal IOIterator (owned Darwin.IOKit.IOIterator handle) {
|
||||
this.handle = (owned) handle;
|
||||
}
|
||||
|
||||
public IOIterator<T> iterator () {
|
||||
return this;
|
||||
}
|
||||
|
||||
public T? next_value () {
|
||||
Darwin.IOKit.IOObject h = handle.next ();
|
||||
if (h == Darwin.IOKit.IOObject.NULL)
|
||||
return null;
|
||||
return GLib.Object.new (typeof (T), io_object: new IOObject ((owned) h));
|
||||
}
|
||||
}
|
||||
|
||||
internal class IOObject {
|
||||
internal Darwin.IOKit.IOObject handle;
|
||||
|
||||
internal IOObject (owned Darwin.IOKit.IOObject handle) {
|
||||
this.handle = (owned) handle;
|
||||
}
|
||||
}
|
||||
|
||||
private void kern_check (KernReturn result) throws Error {
|
||||
if (result != KernReturn.SUCCESS)
|
||||
throw new Error.NOT_SUPPORTED ("%s", mach_error_string (result));
|
||||
}
|
||||
}
|
||||
+26
-41
@@ -1,18 +1,14 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class LockdownClient : Object, AsyncInitable {
|
||||
public class LockdownClient : Object {
|
||||
public signal void closed ();
|
||||
|
||||
public DeviceDetails device_details {
|
||||
public PlistServiceClient service {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public IOStream stream {
|
||||
get { return service.stream; }
|
||||
}
|
||||
|
||||
private PlistServiceClient service;
|
||||
private UsbmuxDevice? usbmux_device;
|
||||
private Plist? pair_record;
|
||||
private string? host_id;
|
||||
private string? system_buid;
|
||||
@@ -22,29 +18,20 @@ namespace Frida.Fruity {
|
||||
|
||||
private const uint16 LOCKDOWN_PORT = 62078;
|
||||
|
||||
private LockdownClient (DeviceDetails device_details) {
|
||||
Object (device_details: device_details);
|
||||
public LockdownClient (IOStream stream) {
|
||||
Object (service: new PlistServiceClient (stream));
|
||||
}
|
||||
|
||||
public static async LockdownClient open (DeviceDetails device_details, Cancellable? cancellable = null)
|
||||
construct {
|
||||
service.closed.connect (on_service_closed);
|
||||
}
|
||||
|
||||
public static async LockdownClient open (UsbmuxDevice device, Cancellable? cancellable = null)
|
||||
throws LockdownError, IOError {
|
||||
var client = new LockdownClient (device_details);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_local_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws LockdownError, IOError {
|
||||
var device = device_details;
|
||||
|
||||
try {
|
||||
var usbmux = yield UsbmuxClient.open (cancellable);
|
||||
|
||||
Plist pair_record;
|
||||
try {
|
||||
pair_record = yield usbmux.read_pair_record (device.udid, cancellable);
|
||||
} catch (UsbmuxError e) {
|
||||
@@ -52,6 +39,10 @@ namespace Frida.Fruity {
|
||||
throw new LockdownError.NOT_PAIRED ("Not paired");
|
||||
throw e;
|
||||
}
|
||||
|
||||
string? host_id = null;
|
||||
string? system_buid = null;
|
||||
TlsCertificate? tls_certificate = null;
|
||||
try {
|
||||
host_id = pair_record.get_string ("HostID");
|
||||
system_buid = pair_record.get_string ("SystemBUID");
|
||||
@@ -64,15 +55,19 @@ namespace Frida.Fruity {
|
||||
|
||||
yield usbmux.connect_to_port (device.id, LOCKDOWN_PORT, cancellable);
|
||||
|
||||
service = new PlistServiceClient (usbmux.connection);
|
||||
service.closed.connect (on_service_closed);
|
||||
var client = new LockdownClient (usbmux.connection);
|
||||
client.usbmux_device = device;
|
||||
client.pair_record = pair_record;
|
||||
client.host_id = host_id;
|
||||
client.system_buid = system_buid;
|
||||
client.tls_certificate = tls_certificate;
|
||||
|
||||
yield query_type (cancellable);
|
||||
yield client.query_type (cancellable);
|
||||
|
||||
return client;
|
||||
} catch (UsbmuxError e) {
|
||||
throw new LockdownError.UNSUPPORTED ("%s", e.message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable = null) throws IOError {
|
||||
@@ -182,7 +177,7 @@ namespace Frida.Fruity {
|
||||
bool enable_encryption = response.has ("EnableServiceSSL") && response.get_boolean ("EnableServiceSSL");
|
||||
|
||||
var client = yield UsbmuxClient.open (cancellable);
|
||||
yield client.connect_to_port (device_details.id, (uint16) response.get_integer ("Port"), cancellable);
|
||||
yield client.connect_to_port (usbmux_device.id, (uint16) response.get_integer ("Port"), cancellable);
|
||||
|
||||
SocketConnection raw_connection = client.connection;
|
||||
IOStream stream = raw_connection;
|
||||
@@ -242,7 +237,7 @@ namespace Frida.Fruity {
|
||||
|
||||
try {
|
||||
var usbmux = yield UsbmuxClient.open (cancellable);
|
||||
yield usbmux.delete_pair_record (device_details.udid, cancellable);
|
||||
yield usbmux.delete_pair_record (usbmux_device.udid, cancellable);
|
||||
} catch (UsbmuxError e) {
|
||||
if (!(e is UsbmuxError.INVALID_ARGUMENT))
|
||||
throw new LockdownError.PROTOCOL ("%s", e.message);
|
||||
@@ -269,16 +264,6 @@ namespace Frida.Fruity {
|
||||
return request;
|
||||
}
|
||||
|
||||
private static void throw_local_error (GLib.Error e) throws LockdownError, IOError {
|
||||
if (e is LockdownError)
|
||||
throw (LockdownError) e;
|
||||
|
||||
if (e is IOError)
|
||||
throw (IOError) e;
|
||||
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
private static LockdownError error_from_service (PlistServiceError e) {
|
||||
return new LockdownError.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
internal sealed class UsbNcmDriver : Object, AsyncInitable {
|
||||
public UsbDevice device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public VirtualNetworkStack netstack {
|
||||
get {
|
||||
return _netstack;
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress? remote_ipv6_address {
|
||||
get {
|
||||
return _remote_ipv6_address;
|
||||
}
|
||||
}
|
||||
|
||||
private uint8 data_iface;
|
||||
private int data_altsetting;
|
||||
private uint8 rx_address;
|
||||
private uint8 tx_address;
|
||||
|
||||
private VirtualNetworkStack? _netstack;
|
||||
private uint16 next_outgoing_sequence = 1;
|
||||
|
||||
private InetAddress? _remote_ipv6_address;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
private enum UsbDescriptorType {
|
||||
INTERFACE = 0x04,
|
||||
}
|
||||
|
||||
private enum UsbCommSubclass {
|
||||
NCM = 0x0d,
|
||||
}
|
||||
|
||||
private enum UsbDataSubclass {
|
||||
UNDEFINED = 0x00,
|
||||
}
|
||||
|
||||
private enum UsbCdcDescriptorSubtype {
|
||||
ETHERNET = 0x0f,
|
||||
}
|
||||
|
||||
private enum EtherType {
|
||||
IPV6 = 0x86dd,
|
||||
}
|
||||
|
||||
private enum IPV6NextHeader {
|
||||
UDP = 0x11,
|
||||
}
|
||||
|
||||
public static async UsbNcmDriver open (UsbDevice device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var driver = new UsbNcmDriver (device);
|
||||
|
||||
try {
|
||||
yield driver.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return driver;
|
||||
}
|
||||
|
||||
private UsbNcmDriver (UsbDevice device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
unowned LibUSB.DeviceHandle handle = device.handle;
|
||||
|
||||
LibUSB.ConfigDescriptor config;
|
||||
Usb.check (device.raw_device.get_active_config_descriptor (out config),
|
||||
"Failed to get active USB config descriptor");
|
||||
|
||||
bool found_cdc_header = false;
|
||||
bool found_data_interface = false;
|
||||
uint8 mac_address_index = 0;
|
||||
foreach (var iface in config.@interface) {
|
||||
foreach (var setting in iface.altsetting) {
|
||||
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
|
||||
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
|
||||
try {
|
||||
parse_cdc_header (setting.extra, out mac_address_index);
|
||||
found_cdc_header = true;
|
||||
} catch (Error e) {
|
||||
break;
|
||||
}
|
||||
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
|
||||
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
|
||||
setting.endpoint.length == 2) {
|
||||
found_data_interface = true;
|
||||
|
||||
data_iface = setting.bInterfaceNumber;
|
||||
data_altsetting = setting.bAlternateSetting;
|
||||
|
||||
foreach (var ep in setting.endpoint) {
|
||||
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) == LibUSB.EndpointDirection.IN)
|
||||
rx_address = ep.bEndpointAddress;
|
||||
else
|
||||
tx_address = ep.bEndpointAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found_cdc_header || !found_data_interface)
|
||||
throw_user_error ("No USB CDC-NCM interface found");
|
||||
|
||||
uint8 mac_address[6];
|
||||
string mac_address_str = yield device.read_string_descriptor (mac_address_index, device.default_language_id,
|
||||
cancellable);
|
||||
if (mac_address_str.length != 12)
|
||||
throw new Error.PROTOCOL ("Invalid MAC address");
|
||||
for (uint i = 0; i != 6; i++) {
|
||||
uint v;
|
||||
mac_address_str.substring (i * 2, 2).scanf ("%02X", out v);
|
||||
mac_address[i] = (uint8) v;
|
||||
}
|
||||
|
||||
handle.detach_kernel_driver (data_iface);
|
||||
try {
|
||||
Usb.check (handle.claim_interface (data_iface), "Failed to claim USB interface");
|
||||
} catch (Error e) {
|
||||
throw_user_error (@"Unable to claim USB CDC-NCM interface ($(e.message))");
|
||||
}
|
||||
Usb.check (handle.set_interface_alt_setting (data_iface, data_altsetting),
|
||||
"Failed to set USB interface alt setting");
|
||||
|
||||
_netstack = new VirtualNetworkStack (new Bytes (mac_address), null, 1500);
|
||||
_netstack.outgoing_datagram.connect (on_netif_outgoing_datagram);
|
||||
|
||||
process_incoming_datagrams.begin ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private void throw_user_error (string message) throws Error {
|
||||
#if WINDOWS
|
||||
throw new Error.NOT_SUPPORTED ("%s; use https://zadig.akeo.ie to switch from Apple's official driver onto " +
|
||||
"Microsoft's WinUSB driver, so libusb can access it", message);
|
||||
#else
|
||||
throw new Error.NOT_SUPPORTED ("%s", message);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void close () {
|
||||
io_cancellable.cancel ();
|
||||
_netstack.stop ();
|
||||
}
|
||||
|
||||
private async void process_incoming_datagrams () {
|
||||
var data = new uint8[64 * 1024];
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
size_t n = yield device.bulk_transfer (rx_address, data, uint.MAX, io_cancellable);
|
||||
handle_ncm_frame (data[:n]);
|
||||
} catch (GLib.Error e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle_ncm_frame (uint8[] data) throws GLib.Error {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_data (data));
|
||||
input.byte_order = LITTLE_ENDIAN;
|
||||
|
||||
uint8 raw_signature[4 + 1];
|
||||
unowned string signature = (string) raw_signature;
|
||||
size_t bytes_read;
|
||||
|
||||
input.read_all (raw_signature[:4], out bytes_read);
|
||||
if (signature != "NCMH")
|
||||
throw new Error.PROTOCOL ("Invalid NTH16 signature");
|
||||
input.skip (6);
|
||||
var ndp_index = input.read_uint16 ();
|
||||
|
||||
do {
|
||||
input.seek (ndp_index, SET);
|
||||
input.read_all (raw_signature[:4], out bytes_read);
|
||||
if (signature != "NCM0")
|
||||
throw new Error.PROTOCOL ("Invalid NDP16 signature");
|
||||
input.skip (2);
|
||||
var next_ndp_index = input.read_uint16 ();
|
||||
|
||||
while (true) {
|
||||
var datagram_index = input.read_uint16 ();
|
||||
var datagram_length = input.read_uint16 ();
|
||||
if (datagram_index == 0 || datagram_length == 0)
|
||||
break;
|
||||
|
||||
int64 previous_offset = input.tell ();
|
||||
input.seek (datagram_index, SET);
|
||||
var datagram_buf = new uint8[datagram_length];
|
||||
input.read_all (datagram_buf, out bytes_read);
|
||||
input.seek (previous_offset, SET);
|
||||
|
||||
var datagram = new Bytes.take ((owned) datagram_buf);
|
||||
|
||||
if (_remote_ipv6_address == null) {
|
||||
_remote_ipv6_address = try_infer_remote_address_from_datagram (datagram);
|
||||
if (_remote_ipv6_address != null)
|
||||
notify_property ("remote-ipv6-address");
|
||||
}
|
||||
|
||||
_netstack.handle_incoming_datagram (datagram);
|
||||
}
|
||||
|
||||
ndp_index = next_ndp_index;
|
||||
} while (ndp_index != 0);
|
||||
}
|
||||
|
||||
private async void on_netif_outgoing_datagram (Bytes datagram) {
|
||||
uint16 transfer_header_length = 12;
|
||||
uint16 ndp_header_length = 16;
|
||||
uint16 alignment_padding_length = 2;
|
||||
|
||||
uint16 datagram_start_index = transfer_header_length + ndp_header_length + alignment_padding_length;
|
||||
uint16 datagram_length = (uint16) datagram.length;
|
||||
|
||||
uint16 sentinel_start_index = 0;
|
||||
uint16 sentinel_size = 0;
|
||||
|
||||
uint16 sequence = next_outgoing_sequence++;
|
||||
uint16 block_length = datagram_start_index + datagram_length;
|
||||
uint16 ndp_index = transfer_header_length;
|
||||
uint16 next_ndp_index = 0;
|
||||
|
||||
uint16 alignment_padding_value = 0;
|
||||
|
||||
var frame = new BufferBuilder (LITTLE_ENDIAN)
|
||||
.append_string ("NCMH", StringTerminator.NONE)
|
||||
.append_uint16 (transfer_header_length)
|
||||
.append_uint16 (sequence)
|
||||
.append_uint16 (block_length)
|
||||
.append_uint16 (ndp_index)
|
||||
.append_string ("NCM0", StringTerminator.NONE)
|
||||
.append_uint16 (ndp_header_length)
|
||||
.append_uint16 (next_ndp_index)
|
||||
.append_uint16 (datagram_start_index)
|
||||
.append_uint16 (datagram_length)
|
||||
.append_uint16 (sentinel_start_index)
|
||||
.append_uint16 (sentinel_size)
|
||||
.append_uint16 (alignment_padding_value)
|
||||
.append_bytes (datagram)
|
||||
.build ();
|
||||
|
||||
try {
|
||||
yield device.bulk_transfer (tx_address, frame.get_data (), uint.MAX, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private static void parse_cdc_header (uint8[] header, out uint8 mac_address_index) throws Error {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_data (header));
|
||||
input.set_byte_order (LITTLE_ENDIAN);
|
||||
|
||||
try {
|
||||
for (int offset = 0; offset != header.length;) {
|
||||
uint8 length = input.read_byte ();
|
||||
if (length < 3)
|
||||
throw new Error.PROTOCOL ("Invalid descriptor length");
|
||||
|
||||
uint8 descriptor_type = input.read_byte ();
|
||||
if (descriptor_type != (LibUSB.RequestType.CLASS | UsbDescriptorType.INTERFACE))
|
||||
throw new Error.PROTOCOL ("Invalid descriptor type");
|
||||
|
||||
uint8 descriptor_subtype = input.read_byte ();
|
||||
if (descriptor_subtype == UsbCdcDescriptorSubtype.ETHERNET) {
|
||||
mac_address_index = input.read_byte ();
|
||||
return;
|
||||
}
|
||||
|
||||
input.skip (length - 3);
|
||||
offset += length;
|
||||
}
|
||||
} catch (IOError e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
throw new Error.PROTOCOL ("CDC Ethernet descriptor not found");
|
||||
}
|
||||
|
||||
private static InetAddress? try_infer_remote_address_from_datagram (Bytes datagram) {
|
||||
if (datagram.get_size () < 0x3e)
|
||||
return null;
|
||||
|
||||
var buf = new Buffer (datagram, BIG_ENDIAN);
|
||||
|
||||
var ethertype = (EtherType) buf.read_uint16 (12);
|
||||
if (ethertype != IPV6)
|
||||
return null;
|
||||
|
||||
var next_header = (IPV6NextHeader) buf.read_uint8 (20);
|
||||
if (next_header != UDP)
|
||||
return null;
|
||||
|
||||
return new InetAddress.from_bytes (datagram[22:22 + 16].get_data (), IPV6);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,38 +1,33 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class SpringboardServicesClient : Object, AsyncInitable {
|
||||
public LockdownClient lockdown {
|
||||
public Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private PlistServiceClient service;
|
||||
|
||||
private SpringboardServicesClient (LockdownClient lockdown) {
|
||||
Object (lockdown: lockdown);
|
||||
private SpringboardServicesClient (Device device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
public static async SpringboardServicesClient open (LockdownClient lockdown,
|
||||
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
|
||||
var client = new SpringboardServicesClient (lockdown);
|
||||
public static async SpringboardServicesClient open (Device device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var client = new SpringboardServicesClient (device);
|
||||
|
||||
try {
|
||||
yield client.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_local_error (e);
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws SpringboardServicesError, IOError {
|
||||
try {
|
||||
var stream = yield lockdown.start_service ("com.apple.springboardservices", cancellable);
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
var stream = yield device.open_lockdown_service ("com.apple.springboardservices", cancellable);
|
||||
|
||||
service = new PlistServiceClient (stream);
|
||||
} catch (LockdownError e) {
|
||||
throw new SpringboardServicesError.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
service = new PlistServiceClient (stream);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -41,15 +36,14 @@ namespace Frida.Fruity {
|
||||
yield service.close (cancellable);
|
||||
}
|
||||
|
||||
public async Bytes get_icon_png_data (string bundle_id,
|
||||
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
|
||||
public async Bytes get_icon_png_data (string bundle_id, Cancellable? cancellable = null) throws Error, IOError {
|
||||
try {
|
||||
var request = make_request ("getIconPNGData");
|
||||
request.set_string ("bundleId", bundle_id);
|
||||
|
||||
var response = yield service.query (request, cancellable);
|
||||
if (response.has ("Error"))
|
||||
throw new SpringboardServicesError.INVALID_ARGUMENT ("%s", response.get_string ("Error"));
|
||||
throw new Error.INVALID_ARGUMENT ("%s", response.get_string ("Error"));
|
||||
|
||||
return response.get_bytes ("pngData");
|
||||
} catch (PlistServiceError e) {
|
||||
@@ -59,8 +53,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async Gee.HashMap<string, Bytes> get_icon_png_data_batch (string[] bundle_ids,
|
||||
Cancellable? cancellable = null) throws SpringboardServicesError, IOError {
|
||||
public async Gee.HashMap<string, Bytes> get_icon_png_data_batch (string[] bundle_ids, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
try {
|
||||
foreach (unowned string bundle_id in bundle_ids) {
|
||||
var request = make_request ("getIconPNGData");
|
||||
@@ -73,7 +67,7 @@ namespace Frida.Fruity {
|
||||
do {
|
||||
foreach (Plist response in yield service.read_messages (0, cancellable)) {
|
||||
if (response.has ("Error")) {
|
||||
throw new SpringboardServicesError.INVALID_ARGUMENT ("%s",
|
||||
throw new Error.INVALID_ARGUMENT ("%s",
|
||||
response.get_string ("Error"));
|
||||
}
|
||||
|
||||
@@ -98,27 +92,12 @@ namespace Frida.Fruity {
|
||||
return request;
|
||||
}
|
||||
|
||||
private static void throw_local_error (GLib.Error e) throws SpringboardServicesError, IOError {
|
||||
if (e is SpringboardServicesError)
|
||||
throw (SpringboardServicesError) e;
|
||||
|
||||
if (e is IOError)
|
||||
throw (IOError) e;
|
||||
|
||||
assert_not_reached ();
|
||||
private static Error error_from_service (PlistServiceError e) {
|
||||
return new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
|
||||
private static SpringboardServicesError error_from_service (PlistServiceError e) {
|
||||
return new SpringboardServicesError.PROTOCOL ("%s", e.message);
|
||||
private static Error error_from_plist (PlistError e) {
|
||||
return new Error.PROTOCOL ("Unexpected response: %s", e.message);
|
||||
}
|
||||
|
||||
private static SpringboardServicesError error_from_plist (PlistError e) {
|
||||
return new SpringboardServicesError.PROTOCOL ("Unexpected response: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public errordomain SpringboardServicesError {
|
||||
INVALID_ARGUMENT,
|
||||
PROTOCOL
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
internal sealed class UsbDevice : Object, AsyncInitable {
|
||||
public LibUSB.Device? raw_device {
|
||||
get {
|
||||
return _raw_device;
|
||||
}
|
||||
}
|
||||
|
||||
public LibUSB.DeviceHandle? handle {
|
||||
get {
|
||||
return _handle;
|
||||
}
|
||||
}
|
||||
|
||||
public string udid {
|
||||
get {
|
||||
return _udid;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 default_language_id {
|
||||
get {
|
||||
return _default_language_id;
|
||||
}
|
||||
}
|
||||
|
||||
private LibUSB.Device? _raw_device;
|
||||
private LibUSB.DeviceHandle? _handle;
|
||||
private string _udid;
|
||||
private uint16 _default_language_id;
|
||||
|
||||
private enum AppleSpecificRequest {
|
||||
GET_MODE = 0x45,
|
||||
SET_MODE = 0x52,
|
||||
}
|
||||
|
||||
private const string MODE_INITIAL_UNTETHERED = "3:3:3:0"; // => 5:3:3:0
|
||||
private const string MODE_INITIAL_TETHERED = "4:4:3:4"; // => 5:4:3:4
|
||||
|
||||
public static async UsbDevice open (LibUSB.Device raw_device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var device = new UsbDevice (raw_device);
|
||||
|
||||
try {
|
||||
yield device.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
private UsbDevice (LibUSB.Device raw_device) {
|
||||
Object ();
|
||||
_raw_device = raw_device;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
Usb.check (_raw_device.open (out _handle), "Failed to open USB device");
|
||||
|
||||
Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable);
|
||||
if (language_ids_response.get_size () < sizeof (uint16))
|
||||
throw new Error.PROTOCOL ("Invalid language IDs response");
|
||||
Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN);
|
||||
_default_language_id = language_ids.read_uint16 (0);
|
||||
|
||||
var dev_desc = LibUSB.DeviceDescriptor (_raw_device);
|
||||
string serial_number = yield read_string_descriptor (dev_desc.iSerialNumber, _default_language_id, cancellable);
|
||||
_udid = udid_from_serial_number (serial_number);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void close () {
|
||||
_handle = null;
|
||||
_raw_device = null;
|
||||
}
|
||||
|
||||
public async bool maybe_modeswitch (Cancellable? cancellable) throws Error, IOError {
|
||||
var response = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
|
||||
AppleSpecificRequest.GET_MODE,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
1000,
|
||||
cancellable);
|
||||
string mode = parse_mode (response);
|
||||
bool is_initial_mode = mode == MODE_INITIAL_UNTETHERED || mode == MODE_INITIAL_TETHERED;
|
||||
if (!is_initial_mode)
|
||||
return false;
|
||||
|
||||
response = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
|
||||
AppleSpecificRequest.SET_MODE,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
1000,
|
||||
cancellable);
|
||||
if (response.get_size () != 1 || response[0] != 0x00)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string parse_mode (Bytes mode) throws Error {
|
||||
if (mode.get_size () != 4)
|
||||
throw new Error.PROTOCOL ("Invalid mode response");
|
||||
unowned uint8[] m = mode.get_data ();
|
||||
return "%u:%u:%u:%u".printf (m[0], m[1], m[2], m[3]);
|
||||
}
|
||||
|
||||
private static string udid_from_serial_number (string serial) {
|
||||
if (serial.length == 24)
|
||||
return serial.substring (0, 8) + "-" + serial.substring (8);
|
||||
return serial;
|
||||
}
|
||||
|
||||
public async string read_string_descriptor (uint8 index, uint16 language_id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var response = yield read_string_descriptor_bytes (index, language_id, cancellable);
|
||||
try {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
|
||||
input.byte_order = LITTLE_ENDIAN;
|
||||
|
||||
size_t size = response.get_size ();
|
||||
if (size % sizeof (unichar2) != 0)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor");
|
||||
size_t n = size / sizeof (unichar2);
|
||||
var chars = new unichar2[n];
|
||||
for (size_t i = 0; i != n; i++)
|
||||
chars[i] = input.read_uint16 ();
|
||||
|
||||
unowned string16 str = (string16) chars;
|
||||
return str.to_utf8 ((long) n);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Bytes read_string_descriptor_bytes (uint8 index, uint16 language_id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var response = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.STANDARD | LibUSB.EndpointDirection.IN,
|
||||
LibUSB.StandardRequest.GET_DESCRIPTOR,
|
||||
(LibUSB.DescriptorType.STRING << 8) | index,
|
||||
language_id,
|
||||
1024,
|
||||
1000,
|
||||
cancellable);
|
||||
try {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
|
||||
input.byte_order = LITTLE_ENDIAN;
|
||||
|
||||
uint8 length = input.read_byte ();
|
||||
if (length < 2)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor length");
|
||||
|
||||
uint8 type = input.read_byte ();
|
||||
if (type != LibUSB.DescriptorType.STRING)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor type");
|
||||
|
||||
size_t remainder = response.get_size () - 2;
|
||||
length -= 2;
|
||||
if (length > remainder)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor length");
|
||||
|
||||
return response[2:2 + length];
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Bytes control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint16 length,
|
||||
uint timeout, Cancellable? cancellable) throws Error, IOError {
|
||||
var transfer = new LibUSB.Transfer ();
|
||||
var ready_closure = new TransferReadyClosure (control_transfer.callback);
|
||||
|
||||
var buffer = new uint8[sizeof (LibUSB.ControlSetup) + length];
|
||||
LibUSB.Transfer.fill_control_setup (buffer, request_type, request, val, index, length);
|
||||
transfer.fill_control_transfer (_handle, buffer, on_transfer_ready, ready_closure, timeout);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
transfer.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
try {
|
||||
Usb.check (transfer.submit (), "Failed to submit control transfer");
|
||||
yield;
|
||||
} finally {
|
||||
cancel_source.destroy ();
|
||||
}
|
||||
|
||||
Usb.check_transfer (transfer.status, "Control transfer failed");
|
||||
|
||||
return new Bytes (((uint8[]) transfer.control_get_data ())[:transfer.actual_length]);
|
||||
}
|
||||
|
||||
public async size_t bulk_transfer (uint8 endpoint, uint8[] buffer, uint timeout, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var transfer = new LibUSB.Transfer ();
|
||||
var ready_closure = new TransferReadyClosure (bulk_transfer.callback);
|
||||
|
||||
transfer.fill_bulk_transfer (_handle, endpoint, buffer, on_transfer_ready, ready_closure, timeout);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
transfer.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (MainContext.get_thread_default ());
|
||||
|
||||
try {
|
||||
Usb.check (transfer.submit (), "Failed to submit bulk transfer");
|
||||
yield;
|
||||
} finally {
|
||||
cancel_source.destroy ();
|
||||
}
|
||||
|
||||
Usb.check_transfer (transfer.status, "Bulk transfer failed");
|
||||
|
||||
return transfer.actual_length;
|
||||
}
|
||||
|
||||
private static void on_transfer_ready (LibUSB.Transfer transfer) {
|
||||
TransferReadyClosure * closure = transfer.user_data;
|
||||
closure->schedule ();
|
||||
}
|
||||
|
||||
private class TransferReadyClosure {
|
||||
private SourceFunc? handler;
|
||||
private MainContext main_context;
|
||||
|
||||
public TransferReadyClosure (owned SourceFunc handler) {
|
||||
this.handler = (owned) handler;
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
}
|
||||
|
||||
public void schedule () {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
handler ();
|
||||
handler = null;
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Usb {
|
||||
internal static void check (LibUSB.Error error, string prefix) throws Error {
|
||||
if (error >= LibUSB.Error.SUCCESS)
|
||||
return;
|
||||
|
||||
string message = @"$prefix: $(error.get_description ())";
|
||||
|
||||
switch (error) {
|
||||
case ACCESS:
|
||||
throw new Error.PERMISSION_DENIED ("%s", message);
|
||||
case NOT_FOUND:
|
||||
throw new Error.INVALID_OPERATION ("%s", message);
|
||||
case TIMEOUT:
|
||||
throw new Error.TIMED_OUT ("%s", message);
|
||||
default:
|
||||
throw new Error.TRANSPORT ("%s", message);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void check_transfer (LibUSB.TransferStatus status, string prefix) throws Error, IOError {
|
||||
if (status == COMPLETED)
|
||||
return;
|
||||
|
||||
string message = @"$prefix: $(status.to_string ())";
|
||||
|
||||
switch (status) {
|
||||
case TIMED_OUT:
|
||||
throw new Error.TIMED_OUT ("%s", message);
|
||||
case CANCELLED:
|
||||
throw new IOError.CANCELLED ("%s", message);
|
||||
default:
|
||||
throw new Error.TRANSPORT ("%s", message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-65
@@ -1,13 +1,16 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class UsbmuxClient : Object, AsyncInitable {
|
||||
public signal void device_attached (DeviceDetails details);
|
||||
public signal void device_detached (DeviceId id);
|
||||
public signal void device_attached (UsbmuxDevice device);
|
||||
public signal void device_detached (UsbmuxDevice device);
|
||||
|
||||
public SocketConnection? connection {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private Gee.Map<uint, UsbmuxDevice> devices = new Gee.HashMap<uint, UsbmuxDevice> ();
|
||||
|
||||
private InputStream? input;
|
||||
private OutputStream? output;
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
@@ -132,11 +135,12 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async void connect_to_port (DeviceId device_id, uint16 port, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
public async void connect_to_port (uint device_id, uint16 port, Cancellable? cancellable = null)
|
||||
throws UsbmuxError, IOError {
|
||||
assert (is_processing_messages);
|
||||
|
||||
var request = create_request ("Connect");
|
||||
request.set_integer ("DeviceID", device_id.raw_value);
|
||||
request.set_integer ("DeviceID", device_id);
|
||||
request.set_integer ("PortNumber", port.to_big_endian ());
|
||||
|
||||
var response = yield query (request, MODE_SWITCH, cancellable);
|
||||
@@ -160,9 +164,9 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async Plist read_pair_record (Udid udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
public async Plist read_pair_record (string udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
var request = create_request ("ReadPairRecord");
|
||||
request.set_string ("PairRecordID", udid.raw_value);
|
||||
request.set_string ("PairRecordID", udid);
|
||||
|
||||
var response = yield query (request, REGULAR, cancellable);
|
||||
try {
|
||||
@@ -175,9 +179,9 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public async void delete_pair_record (Udid udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
public async void delete_pair_record (string udid, Cancellable? cancellable = null) throws UsbmuxError, IOError {
|
||||
var request = create_request ("DeletePairRecord");
|
||||
request.set_string ("PairRecordID", udid.raw_value);
|
||||
request.set_string ("PairRecordID", udid);
|
||||
|
||||
var response = yield query (request, REGULAR, cancellable);
|
||||
check_pair_response (response, "DeletePairRecord");
|
||||
@@ -276,28 +280,26 @@ namespace Frida.Fruity {
|
||||
if (props.has ("ConnectionType") && props.get_string ("ConnectionType") == "Network")
|
||||
connection_type = NETWORK;
|
||||
|
||||
var device_id = DeviceId ((uint) body.get_integer ("DeviceID"));
|
||||
var device_id = (uint) body.get_integer ("DeviceID");
|
||||
|
||||
ProductId product_id;
|
||||
if (connection_type == USB)
|
||||
product_id = ProductId ((int) props.get_integer ("ProductID"));
|
||||
else
|
||||
product_id = ProductId (-1);
|
||||
int product_id = (connection_type == USB) ? (int) props.get_integer ("ProductID") : -1;
|
||||
|
||||
string raw_udid = props.get_string ("SerialNumber");
|
||||
if (raw_udid.length == 24)
|
||||
raw_udid = raw_udid[:8] + "-" + raw_udid[8:];
|
||||
var udid = Udid (raw_udid);
|
||||
string udid = props.get_string ("SerialNumber");
|
||||
if (udid.length == 24)
|
||||
udid = udid[:8] + "-" + udid[8:];
|
||||
|
||||
InetSocketAddress? network_address = null;
|
||||
if (connection_type == NETWORK)
|
||||
network_address = parse_network_address (props.get_bytes ("NetworkAddress"));
|
||||
|
||||
var details =
|
||||
new DeviceDetails (connection_type, device_id, product_id, udid, network_address);
|
||||
device_attached (details);
|
||||
var device =
|
||||
new UsbmuxDevice (connection_type, device_id, product_id, udid, network_address);
|
||||
devices[device_id] = device;
|
||||
device_attached (device);
|
||||
} else if (message_type == "Detached") {
|
||||
device_detached (DeviceId ((uint) body.get_integer ("DeviceID")));
|
||||
UsbmuxDevice? device;
|
||||
if (devices.unset ((uint) body.get_integer ("DeviceID"), out device))
|
||||
device_detached (device);
|
||||
} else {
|
||||
throw new UsbmuxError.PROTOCOL ("Unexpected message type: %s", message_type);
|
||||
}
|
||||
@@ -546,23 +548,23 @@ namespace Frida.Fruity {
|
||||
PROTOCOL
|
||||
}
|
||||
|
||||
public class DeviceDetails : Object {
|
||||
public class UsbmuxDevice : Object {
|
||||
public ConnectionType connection_type {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public DeviceId id {
|
||||
public uint id {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public ProductId product_id {
|
||||
public int product_id {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Udid udid {
|
||||
public string udid {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
@@ -572,7 +574,7 @@ namespace Frida.Fruity {
|
||||
construct;
|
||||
}
|
||||
|
||||
public DeviceDetails (ConnectionType connection_type, DeviceId id, ProductId product_id, Udid udid,
|
||||
public UsbmuxDevice (ConnectionType connection_type, uint id, int product_id, string udid,
|
||||
InetSocketAddress? network_address) {
|
||||
Object (
|
||||
connection_type: connection_type,
|
||||
@@ -583,42 +585,4 @@ namespace Frida.Fruity {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ConnectionType {
|
||||
USB,
|
||||
NETWORK
|
||||
}
|
||||
|
||||
public struct DeviceId {
|
||||
public uint raw_value {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public DeviceId (uint raw_value) {
|
||||
this.raw_value = raw_value;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ProductId {
|
||||
public int raw_value {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ProductId (int raw_value) {
|
||||
this.raw_value = raw_value;
|
||||
}
|
||||
}
|
||||
|
||||
public struct Udid {
|
||||
public string raw_value {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Udid (string raw_value) {
|
||||
this.raw_value = raw_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,298 +0,0 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
using Darwin.GCD;
|
||||
using Darwin.DNSSD;
|
||||
using Darwin.Net;
|
||||
|
||||
public class DarwinPairingBrowser : Object, PairingBrowser {
|
||||
private MainContext main_context;
|
||||
private DispatchQueue dispatch_queue = new DispatchQueue ("re.frida.fruity.browser-queue", DispatchQueueAttr.SERIAL);
|
||||
|
||||
private DNSService dns_connection;
|
||||
private DNSService browse_session;
|
||||
private TaskQueue task_queue;
|
||||
|
||||
private Gee.List<PairingServiceDetails> current_batch = new Gee.ArrayList<PairingServiceDetails> ();
|
||||
|
||||
construct {
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
dispatch_queue.dispatch_async (() => {
|
||||
DNSService.create_connection (out dns_connection);
|
||||
dns_connection.set_dispatch_queue (dispatch_queue);
|
||||
|
||||
DNSService session = dns_connection;
|
||||
DNSService.browse (ref session, PrivateFive | ShareConnection, 0, PAIRING_REGTYPE, PAIRING_DOMAIN,
|
||||
on_browse_reply);
|
||||
browse_session = session;
|
||||
|
||||
task_queue = new TaskQueue (this);
|
||||
});
|
||||
}
|
||||
|
||||
~DarwinPairingBrowser () {
|
||||
dispatch_queue.dispatch_sync (() => {
|
||||
browse_session.deallocate ();
|
||||
dns_connection.deallocate ();
|
||||
});
|
||||
}
|
||||
|
||||
private void on_browse_reply (DNSService sd_ref, DNSService.Flags flags, uint32 interface_index,
|
||||
DNSService.ErrorType error_code, string service_name, string regtype, string reply_domain) {
|
||||
if (error_code != NoError)
|
||||
return;
|
||||
|
||||
var interface_name_buf = new char[IFNAMSIZ];
|
||||
unowned string interface_name = if_indextoname (interface_index, interface_name_buf);
|
||||
|
||||
var service = new DarwinPairingServiceDetails (service_name, interface_index, interface_name, task_queue);
|
||||
current_batch.add (service);
|
||||
|
||||
if ((flags & DNSService.Flags.MoreComing) != 0)
|
||||
return;
|
||||
|
||||
var services = current_batch;
|
||||
current_batch = new Gee.ArrayList<PairingServiceDetails> ();
|
||||
|
||||
schedule_on_frida_thread (() => {
|
||||
services_discovered (services.to_array ());
|
||||
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);
|
||||
}
|
||||
|
||||
private class TaskQueue : Object, DNSServiceProvider {
|
||||
private weak DarwinPairingBrowser parent;
|
||||
|
||||
private DNSServiceTask? current = null;
|
||||
private Gee.Deque<DNSServiceTask> pending = new Gee.ArrayQueue<DNSServiceTask> ();
|
||||
|
||||
public TaskQueue (DarwinPairingBrowser parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
private async T with_dns_service<T> (DNSServiceTask task, Cancellable? cancellable) throws Error, IOError {
|
||||
var promise = new Promise<Object> ();
|
||||
task.set_data ("promise", promise);
|
||||
pending.offer_tail (task);
|
||||
|
||||
maybe_start_next ();
|
||||
|
||||
return (T) yield promise.future.wait_async (cancellable);
|
||||
}
|
||||
|
||||
private void maybe_start_next () {
|
||||
if (current != null)
|
||||
return;
|
||||
|
||||
DNSServiceTask? task = pending.poll_head ();
|
||||
if (task == null)
|
||||
return;
|
||||
current = task;
|
||||
|
||||
parent.dispatch_queue.dispatch_async (() => {
|
||||
current.dns_connection = parent.dns_connection;
|
||||
current.on_complete = on_complete;
|
||||
current.start ();
|
||||
});
|
||||
}
|
||||
|
||||
private void on_complete (Object? result, Error? error) {
|
||||
parent.schedule_on_frida_thread (() => {
|
||||
Promise<Object> promise = current.steal_data ("promise");
|
||||
|
||||
if (error != null)
|
||||
promise.reject (error);
|
||||
else
|
||||
promise.resolve (result);
|
||||
|
||||
current = null;
|
||||
maybe_start_next ();
|
||||
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface DNSServiceProvider : Object {
|
||||
public abstract async T with_dns_service<T> (DNSServiceTask task, Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
private abstract class DNSServiceTask : Object {
|
||||
internal DNSService? dns_connection;
|
||||
internal CompleteFunc? on_complete;
|
||||
|
||||
protected DNSService? session;
|
||||
|
||||
public delegate void CompleteFunc (Object? result, Error? error);
|
||||
|
||||
public abstract void start ();
|
||||
|
||||
protected void complete (Object? result, Error? error) {
|
||||
if (session != null && session != dns_connection) {
|
||||
session.deallocate ();
|
||||
session = null;
|
||||
}
|
||||
|
||||
on_complete (result, error);
|
||||
on_complete = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class DarwinPairingServiceDetails : Object, PairingServiceDetails {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public uint interface_index {
|
||||
get { return _interface_index; }
|
||||
}
|
||||
|
||||
public string interface_name {
|
||||
get { return _interface_name; }
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private uint _interface_index;
|
||||
private string _interface_name;
|
||||
|
||||
private DNSServiceProvider dns;
|
||||
|
||||
internal DarwinPairingServiceDetails (string name, uint interface_index, string interface_name, DNSServiceProvider dns) {
|
||||
_name = name;
|
||||
_interface_index = interface_index;
|
||||
_interface_name = interface_name;
|
||||
|
||||
this.dns = dns;
|
||||
}
|
||||
|
||||
public async Gee.List<PairingServiceHost> resolve (Cancellable? cancellable) throws Error, IOError {
|
||||
var task = new ResolveTask (this);
|
||||
return yield dns.with_dns_service (task, cancellable);
|
||||
}
|
||||
|
||||
private class ResolveTask : DNSServiceTask {
|
||||
private weak DarwinPairingServiceDetails parent;
|
||||
|
||||
private Gee.List<PairingServiceHost> hosts = new Gee.ArrayList<PairingServiceHost> ();
|
||||
|
||||
public ResolveTask (DarwinPairingServiceDetails parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public override void start () {
|
||||
session = dns_connection;
|
||||
DNSService.resolve (ref session, PrivateFive | ShareConnection, parent.interface_index, parent.name,
|
||||
PAIRING_REGTYPE, PAIRING_DOMAIN, on_resolve_reply);
|
||||
}
|
||||
|
||||
private void on_resolve_reply (DNSService sd_ref, DNSService.Flags flags, uint32 interface_index,
|
||||
DNSService.ErrorType error_code, string fullname, string hosttarget, uint16 port,
|
||||
uint8[] raw_txt_record) {
|
||||
if (error_code != NoError) {
|
||||
complete (null,
|
||||
new Error.TRANSPORT ("Unable to resolve service '%s' on interface %s",
|
||||
parent.name, parent.interface_name));
|
||||
return;
|
||||
}
|
||||
|
||||
var txt_record = new Gee.ArrayList<string> ();
|
||||
size_t cursor = 0;
|
||||
size_t remaining = raw_txt_record.length;
|
||||
while (remaining != 0) {
|
||||
size_t len = raw_txt_record[cursor];
|
||||
cursor++;
|
||||
remaining--;
|
||||
if (len > remaining)
|
||||
break;
|
||||
|
||||
unowned string raw_val = (string) raw_txt_record[cursor:cursor + len];
|
||||
string val = raw_val.make_valid ((ssize_t) len);
|
||||
txt_record.add (val);
|
||||
|
||||
cursor += len;
|
||||
remaining -= len;
|
||||
}
|
||||
|
||||
hosts.add (new DarwinPairingServiceHost (parent, hosttarget, uint16.from_big_endian (port), txt_record,
|
||||
parent.dns));
|
||||
|
||||
if ((flags & DNSService.Flags.MoreComing) == 0)
|
||||
complete (hosts, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DarwinPairingServiceHost : Object, PairingServiceHost {
|
||||
public string name {
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public uint16 port {
|
||||
get { return _port; }
|
||||
}
|
||||
|
||||
public Gee.List<string> txt_record {
|
||||
get { return _txt_record; }
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private uint16 _port;
|
||||
private Gee.List<string> _txt_record;
|
||||
|
||||
private PairingServiceDetails service;
|
||||
private DNSServiceProvider dns;
|
||||
|
||||
internal DarwinPairingServiceHost (PairingServiceDetails service, string name, uint16 port, Gee.List<string> txt_record,
|
||||
DNSServiceProvider dns) {
|
||||
_name = name;
|
||||
_port = port;
|
||||
_txt_record = txt_record;
|
||||
|
||||
this.service = service;
|
||||
this.dns = dns;
|
||||
}
|
||||
|
||||
public async Gee.List<InetSocketAddress> resolve (Cancellable? cancellable) throws Error, IOError {
|
||||
var task = new ResolveTask (this);
|
||||
return yield dns.with_dns_service (task, cancellable);
|
||||
}
|
||||
|
||||
private class ResolveTask : DNSServiceTask {
|
||||
private weak DarwinPairingServiceHost parent;
|
||||
|
||||
private Gee.List<InetSocketAddress> addresses = new Gee.ArrayList<InetSocketAddress> ();
|
||||
|
||||
public ResolveTask (DarwinPairingServiceHost parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public override void start () {
|
||||
session = dns_connection;
|
||||
DNSService.get_addr_info (ref session, PrivateFive | ShareConnection, parent.service.interface_index, IPv6,
|
||||
parent.name, on_info_reply);
|
||||
}
|
||||
|
||||
private void on_info_reply (DNSService sd_ref, DNSService.Flags flags, uint32 interface_index,
|
||||
DNSService.ErrorType error_code, string hostname, void * address, uint32 ttl) {
|
||||
if (error_code != NoError) {
|
||||
complete (null,
|
||||
new Error.TRANSPORT ("Unable to resolve host '%s' on interface %s",
|
||||
parent.name, parent.service.interface_name));
|
||||
return;
|
||||
}
|
||||
|
||||
addresses.add ((InetSocketAddress) SocketAddress.from_native (address, sizeof (Posix.SockAddrIn6)));
|
||||
|
||||
if ((flags & DNSService.Flags.MoreComing) == 0)
|
||||
complete (addresses, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,420 +0,0 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
using Darwin.GCD;
|
||||
using Darwin.IOKit;
|
||||
|
||||
public sealed class MacOSTunnelFinder : Object, TunnelFinder {
|
||||
public async Tunnel? find (string udid, Cancellable? cancellable) throws Error, IOError {
|
||||
var main_context = MainContext.ref_thread_default ();
|
||||
var event_queue = new DispatchQueue ("re.frida.fruity.tunnel", DispatchQueueAttr.SERIAL);
|
||||
|
||||
Darwin.Xpc.Dictionary? device_info = null;
|
||||
bool all_listed = false;
|
||||
bool waiting = false;
|
||||
|
||||
var pairingd = XpcClient.make_for_mach_service ("com.apple.CoreDevice.remotepairingd", event_queue);
|
||||
pairingd.notify["state"].connect ((obj, pspec) => {
|
||||
if (waiting)
|
||||
find.callback ();
|
||||
});
|
||||
pairingd.message.connect (obj => {
|
||||
var reader = new XpcObjectReader (obj);
|
||||
|
||||
try {
|
||||
reader.read_member ("mangledTypeName");
|
||||
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
|
||||
reader
|
||||
.end_member ()
|
||||
.read_member ("value");
|
||||
if (reader.try_read_member ("deviceFound")) {
|
||||
reader
|
||||
.read_member ("_0")
|
||||
.read_member ("deviceInfo")
|
||||
.read_member ("udid");
|
||||
if (reader.get_string_value () == udid) {
|
||||
reader.end_member ();
|
||||
device_info = (Darwin.Xpc.Dictionary) reader.current_object;
|
||||
}
|
||||
} else if (reader.has_member ("allCurrentDevicesListed")) {
|
||||
all_listed = true;
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
if (waiting)
|
||||
find.callback ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
});
|
||||
|
||||
var r = new PairingdRequest ("RemotePairing.BrowseRequest");
|
||||
r.body.set_bool ("currentDevicesOnly", true);
|
||||
yield pairingd.request (r.message, cancellable);
|
||||
|
||||
while (!all_listed && pairingd.state == OPEN) {
|
||||
waiting = true;
|
||||
yield;
|
||||
waiting = false;
|
||||
}
|
||||
|
||||
if (device_info == null) {
|
||||
if (all_listed)
|
||||
throw new Error.INVALID_ARGUMENT ("Device not found");
|
||||
else
|
||||
throw new Error.NOT_SUPPORTED ("Unexpectedly lost connection to remotepairingd");
|
||||
}
|
||||
|
||||
var pairing_device = new XpcClient (device_info.create_connection ("endpoint"), event_queue);
|
||||
var tunnel = new MacOSTunnel (pairing_device);
|
||||
yield tunnel.attach (cancellable);
|
||||
return tunnel;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MacOSTunnel : Object, Tunnel {
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery;
|
||||
}
|
||||
}
|
||||
|
||||
private XpcClient pairing_device;
|
||||
private Darwin.Xpc.Object? assertion_identifier;
|
||||
private InetAddress? tunnel_device_address;
|
||||
private DiscoveryService? _discovery;
|
||||
|
||||
public MacOSTunnel (XpcClient pairing_device) {
|
||||
this.pairing_device = pairing_device;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.ReleaseAssertionRequest");
|
||||
r.body.set_value ("assertionIdentifier", assertion_identifier);
|
||||
try {
|
||||
yield pairing_device.request (r.message, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async void attach (Cancellable? cancellable) throws Error, IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.CreateAssertionCommand");
|
||||
r.body.set_int64 ("flags", 0);
|
||||
var response = yield pairing_device.request (r.message, cancellable);
|
||||
|
||||
var reader = new XpcObjectReader (response);
|
||||
reader.read_member ("response");
|
||||
|
||||
assertion_identifier = reader
|
||||
.read_member ("assertionIdentifier")
|
||||
.get_object_value (Darwin.Xpc.Uuid.TYPE);
|
||||
reader.end_member ();
|
||||
|
||||
string tunnel_ip_address = reader
|
||||
.read_member ("info")
|
||||
.read_member ("tunnelIPAddress")
|
||||
.get_string_value ();
|
||||
tunnel_device_address = new InetAddress.from_string (tunnel_ip_address);
|
||||
|
||||
_discovery = yield locate_discovery_service (tunnel_device_address, cancellable);
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
SocketConnection connection;
|
||||
try {
|
||||
var service_address = new InetSocketAddress (tunnel_device_address, port);
|
||||
|
||||
var client = new SocketClient ();
|
||||
connection = yield client.connect_async (service_address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private static async DiscoveryService locate_discovery_service (InetAddress tunnel_device_address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var path_buf = new char[4096];
|
||||
unowned string path = (string) path_buf;
|
||||
|
||||
foreach (var item in XNU.query_active_tcp_connections ()) {
|
||||
if (item.family != IPV6)
|
||||
continue;
|
||||
if (!item.foreign_address.equal (tunnel_device_address))
|
||||
continue;
|
||||
if (Darwin.XNU.proc_pidpath (item.effective_pid, path_buf) <= 0)
|
||||
continue;
|
||||
if (path != "/usr/libexec/remoted")
|
||||
continue;
|
||||
|
||||
try {
|
||||
var connectable = new InetSocketAddress (tunnel_device_address, item.foreign_port);
|
||||
|
||||
var sc = new SocketClient ();
|
||||
SocketConnection connection = yield sc.connect_async (connectable, cancellable);
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return yield DiscoveryService.open (connection, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect RSD port");
|
||||
}
|
||||
}
|
||||
|
||||
private class PairingdRequest {
|
||||
public Darwin.Xpc.Dictionary message = new Darwin.Xpc.Dictionary ();
|
||||
public Darwin.Xpc.Dictionary body = new Darwin.Xpc.Dictionary ();
|
||||
|
||||
public PairingdRequest (string name) {
|
||||
message.set_string ("mangledTypeName", name);
|
||||
message.set_value ("value", body);
|
||||
}
|
||||
}
|
||||
|
||||
public class MacOSFruitFinder : Object, FruitFinder {
|
||||
public string? udid_from_iface (string ifname) throws Error {
|
||||
var matching_dict = Darwin.IOKit.service_matching (Darwin.IOKit.ETHERNET_INTERFACE_CLASS);
|
||||
if (matching_dict == null)
|
||||
return null;
|
||||
matching_dict.add (CoreFoundation.String.make (Darwin.IOKit.BSD_NAME_KEY), CoreFoundation.String.make (ifname));
|
||||
|
||||
var registry = IORegistry.open ();
|
||||
foreach (var service in registry.matching_services (matching_dict)) {
|
||||
var usb_serial = find_idevice (service.parent (Darwin.IOKit.IOSERVICE_PLANE));
|
||||
if (usb_serial != null)
|
||||
return usb_serial;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string? find_idevice (IORegistryEntry service) throws Error {
|
||||
if (service.get_string_property ("CFBundleIdentifier") == "com.apple.driver.usb.cdc.ncm")
|
||||
return find_idevice (service.parent (Darwin.IOKit.IOSERVICE_PLANE));
|
||||
|
||||
var props = service.get_properties ();
|
||||
|
||||
var prod = props.get_string_value ("USB Product Name");
|
||||
if (prod != "iPhone" && prod != "iPad")
|
||||
return null;
|
||||
|
||||
return props.get_string_value ("USB Serial Number");
|
||||
}
|
||||
}
|
||||
|
||||
namespace XNU {
|
||||
public PcbList query_active_tcp_connections () {
|
||||
size_t size = 0;
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", null, &size);
|
||||
|
||||
var pcbs = new uint8[size];
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", pcbs, &size);
|
||||
|
||||
return new PcbList (pcbs);
|
||||
}
|
||||
|
||||
public class PcbList {
|
||||
private uint8[] pcbs;
|
||||
|
||||
internal PcbList (owned uint8[] pcbs) {
|
||||
this.pcbs = (owned) pcbs;
|
||||
}
|
||||
|
||||
public Iterator iterator () {
|
||||
return new Iterator (this);
|
||||
}
|
||||
|
||||
public class Iterator {
|
||||
private PcbList list;
|
||||
private InetItem * cursor;
|
||||
|
||||
internal Iterator (PcbList list) {
|
||||
this.list = list;
|
||||
|
||||
var gen = (Darwin.XNU.InetPcbGeneration *) list.pcbs;
|
||||
cursor = (InetItem *) ((uint8 *) list.pcbs + gen->length);
|
||||
}
|
||||
|
||||
public Item? next_value () {
|
||||
InetPcb * pcb = null;
|
||||
while (true) {
|
||||
if (cursor->length == 24)
|
||||
return null;
|
||||
|
||||
switch (cursor->kind) {
|
||||
case InetItemKind.PCB:
|
||||
pcb = (InetPcb *) cursor;
|
||||
break;
|
||||
case InetItemKind.SOCKET:
|
||||
var item = new Item (*pcb, *((InetSocket *) cursor));
|
||||
advance ();
|
||||
return item;
|
||||
}
|
||||
|
||||
advance ();
|
||||
}
|
||||
}
|
||||
|
||||
private void advance () {
|
||||
uint32 l = cursor->length;
|
||||
if (l % 8 != 0)
|
||||
l += 8 - (l % 8);
|
||||
cursor = (InetItem *) ((uint8 *) cursor + l);
|
||||
}
|
||||
}
|
||||
|
||||
public class Item {
|
||||
public SocketFamily family {
|
||||
get {
|
||||
return ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
? SocketFamily.IPV6
|
||||
: SocketFamily.IPV4;
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress local_address {
|
||||
get {
|
||||
if (cached_local_address == null)
|
||||
cached_local_address = parse_address (pcb.local_address);
|
||||
return cached_local_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 local_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.local_port);
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress foreign_address {
|
||||
get {
|
||||
if (cached_foreign_address == null)
|
||||
cached_foreign_address = parse_address (pcb.foreign_address);
|
||||
return cached_foreign_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 foreign_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.foreign_port);
|
||||
}
|
||||
}
|
||||
|
||||
public int32 effective_pid {
|
||||
get {
|
||||
return sock.effective_pid;
|
||||
}
|
||||
}
|
||||
|
||||
private InetPcb pcb;
|
||||
private InetSocket sock;
|
||||
private InetAddress? cached_local_address;
|
||||
private InetAddress? cached_foreign_address;
|
||||
|
||||
public Item (InetPcb pcb, InetSocket sock) {
|
||||
this.pcb = pcb;
|
||||
this.sock = sock;
|
||||
}
|
||||
|
||||
private InetAddress parse_address (uint8[] bytes) {
|
||||
if ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
return new InetAddress.from_bytes (bytes, IPV6);
|
||||
var addr = (Darwin.XNU.InetAddr4in6 *) bytes;
|
||||
return new InetAddress.from_bytes ((uint8[]) &addr->addr4.s_addr, IPV4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetItem {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
}
|
||||
|
||||
public enum InetItemKind {
|
||||
SOCKET = 0x001,
|
||||
PCB = 0x010,
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetPcb {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 inpp;
|
||||
public uint16 foreign_port;
|
||||
public uint16 local_port;
|
||||
public uint32 per_protocol_pcb_low;
|
||||
public uint32 per_protocol_pcb_high;
|
||||
public uint32 generation_count_low;
|
||||
public uint32 generation_count_high;
|
||||
public int flags;
|
||||
public uint32 flow;
|
||||
public uint8 version_flag;
|
||||
public uint8 ip_ttl;
|
||||
public uint8 ip_protocol;
|
||||
public uint8 padding;
|
||||
public uint8 foreign_address[16];
|
||||
public uint8 local_address[16];
|
||||
public InetDepend4 depend4;
|
||||
public InetDepend6 depend6;
|
||||
public uint32 flowhash;
|
||||
public uint32 flags2;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetSocket {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 so;
|
||||
public int16 type;
|
||||
public uint16 options_low;
|
||||
public uint16 options_high;
|
||||
public int16 linger;
|
||||
public int16 state;
|
||||
public uint16 pcb[4];
|
||||
public uint16 protocol_low;
|
||||
public uint16 protocol_high;
|
||||
public uint16 family_low;
|
||||
public uint16 family_high;
|
||||
public int16 qlen;
|
||||
public int16 incqlen;
|
||||
public int16 qlimit;
|
||||
public int16 timeo;
|
||||
public uint16 error;
|
||||
public int32 pgid;
|
||||
public uint32 oobmark;
|
||||
public uint32 uid;
|
||||
public int32 last_pid;
|
||||
public int32 effective_pid;
|
||||
public uint64 gencnt;
|
||||
public uint32 flags;
|
||||
public uint32 flags1;
|
||||
public int32 usecount;
|
||||
public int32 retaincnt;
|
||||
public uint32 filter_flags;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend4 {
|
||||
public uint8 ip_tos;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend6 {
|
||||
public uint8 hlim;
|
||||
public int checksum;
|
||||
public uint16 interface_index;
|
||||
public int16 hops;
|
||||
}
|
||||
}
|
||||
}
|
||||
+79
-774
File diff suppressed because it is too large.
Load diff
+16
-20
@@ -481,6 +481,7 @@ endif
|
||||
if have_fruity_backend
|
||||
backend_sources += [
|
||||
'fruity' / 'fruity-host-session.vala',
|
||||
'fruity' / 'device-monitor.vala',
|
||||
'fruity' / 'xpc.vala',
|
||||
'fruity' / 'dtx.vala',
|
||||
'fruity' / 'lockdown.vala',
|
||||
@@ -492,26 +493,23 @@ if have_fruity_backend
|
||||
'fruity' / 'keyed-archive.vala',
|
||||
'fruity' / 'plist.vala',
|
||||
'fruity' / 'plist-service.vala',
|
||||
'fruity' / 'network-stack.vala',
|
||||
'fruity' / 'usb.vala',
|
||||
'fruity' / 'ncm.vala',
|
||||
]
|
||||
|
||||
if host_os_family == 'windows'
|
||||
backend_sources += 'fruity' / 'fruity-host-session-windows.c'
|
||||
elif host_os_family == 'darwin'
|
||||
backend_sources += 'fruity' / 'fruity-host-session-darwin.m'
|
||||
else
|
||||
backend_sources += 'fruity' / 'fruity-host-session-unix.c'
|
||||
if host_os == 'macos'
|
||||
backend_sources += 'fruity' / 'device-monitor-macos.vala'
|
||||
elif host_os == 'linux'
|
||||
backend_sources += 'fruity' / 'device-monitor-linux.vala'
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
backend_sources += 'fruity' / 'xpc-darwin.vala'
|
||||
endif
|
||||
if host_os == 'macos'
|
||||
backend_sources += [
|
||||
'fruity' / 'xpc-macos.vala',
|
||||
'fruity' / 'iokit.vala',
|
||||
]
|
||||
elif host_os == 'linux'
|
||||
backend_sources += 'fruity' / 'xpc-linux.vala'
|
||||
if host_os_family == 'windows'
|
||||
backend_sources += 'fruity' / 'device-monitor-windows.c'
|
||||
elif host_os_family == 'darwin'
|
||||
backend_sources += 'fruity' / 'device-monitor-darwin.m'
|
||||
else
|
||||
backend_sources += 'fruity' / 'device-monitor-unix.c'
|
||||
endif
|
||||
|
||||
backend_deps += openssl_dep
|
||||
@@ -519,17 +517,15 @@ if have_fruity_backend
|
||||
'--pkg=libnghttp2',
|
||||
'--pkg=libngtcp2',
|
||||
'--pkg=libngtcp2_crypto_quictls',
|
||||
'--pkg=libusb-1.0',
|
||||
'--pkg=lwip',
|
||||
'--pkg=openssl',
|
||||
]
|
||||
if host_os_family == 'darwin'
|
||||
backend_vala_args += [
|
||||
'--pkg=darwin-xnu',
|
||||
'--pkg=darwin-iokit',
|
||||
'--pkg=darwin-xpc',
|
||||
'--pkg=darwin-gcd',
|
||||
'--pkg=darwin-net',
|
||||
'--pkg=darwin-dns-sd',
|
||||
'--pkg=corefoundation',
|
||||
]
|
||||
endif
|
||||
@@ -567,7 +563,7 @@ subdir('compiler')
|
||||
core_sources = base_sources + backend_sources + compiler_sources
|
||||
|
||||
internal_libs = [base, pipe] + backend_libs
|
||||
internal_deps = [base_dep, pipe_dep]
|
||||
internal_deps = [base_dep, netif_dep, pipe_dep]
|
||||
|
||||
if host_os_family == 'windows'
|
||||
gio_platform_vala_args = ['--pkg=gio-windows-2.0']
|
||||
|
||||
@@ -61,8 +61,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
construct {
|
||||
_device = new Device (null, "portal", "Portal", HostSessionProviderKind.LOCAL,
|
||||
new PortalHostSessionProvider (this));
|
||||
_device = new Device (null, new PortalHostSessionProvider (this));
|
||||
|
||||
cluster_service = new WebService (cluster_params, CLUSTER);
|
||||
cluster_service.incoming.connect (on_incoming_cluster_connection);
|
||||
@@ -930,9 +929,8 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return _name; }
|
||||
get { return "Portal"; }
|
||||
}
|
||||
private string _name = "Portal";
|
||||
|
||||
public Variant? icon {
|
||||
get { return _icon; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 16.3.2
|
||||
revision = 16.4.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/libusb.git
|
||||
revision = be4d8ec1012f35ff6ce9474ed8d7c3ec4182ac55
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
dependency_names = libusb-1.0
|
||||
|
||||
+152
-31
@@ -61,8 +61,13 @@ namespace Frida.HostSessionTest {
|
||||
h.run ();
|
||||
});
|
||||
|
||||
GLib.Test.add_func ("/HostSession/Fruity/Manual/xpc", () => {
|
||||
var h = new Harness ((h) => Fruity.Manual.xpc.begin (h as Harness));
|
||||
GLib.Test.add_func ("/HostSession/Fruity/Manual/Xpc/list", () => {
|
||||
var h = new Harness ((h) => Fruity.Manual.Xpc.list.begin (h as Harness));
|
||||
h.run ();
|
||||
});
|
||||
|
||||
GLib.Test.add_func ("/HostSession/Fruity/Manual/Xpc/launch", () => {
|
||||
var h = new Harness ((h) => Fruity.Manual.Xpc.launch.begin (h as Harness));
|
||||
h.run ();
|
||||
});
|
||||
#endif
|
||||
@@ -3632,6 +3637,9 @@ namespace Frida.HostSessionTest {
|
||||
|
||||
namespace Manual {
|
||||
|
||||
private const string DEVICE_ID = "<device-id>";
|
||||
private const string APP_ID = "<app-id>";
|
||||
|
||||
private static async void lockdown (Harness h) {
|
||||
if (!GLib.Test.slow ()) {
|
||||
stdout.printf ("<skipping, run in slow mode with iOS device connected> ");
|
||||
@@ -3641,15 +3649,13 @@ namespace Frida.HostSessionTest {
|
||||
|
||||
h.disable_timeout ();
|
||||
|
||||
var device_id = "<device-id>";
|
||||
var app_id = "<app-id>";
|
||||
string? target_name = null;
|
||||
uint target_pid = 0;
|
||||
|
||||
var device_manager = new DeviceManager ();
|
||||
|
||||
try {
|
||||
var device = yield device_manager.get_device_by_id (device_id);
|
||||
var device = yield device_manager.get_device_by_id (DEVICE_ID);
|
||||
|
||||
device.output.connect ((pid, fd, data) => {
|
||||
var chars = data.get_data ();
|
||||
@@ -3669,9 +3675,21 @@ namespace Frida.HostSessionTest {
|
||||
|
||||
var timer = new Timer ();
|
||||
|
||||
printerr ("device.query_system_parameters()");
|
||||
timer.reset ();
|
||||
var p = yield device.query_system_parameters ();
|
||||
printerr (" => got parameters, took %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
var iter = HashTableIter<string, Variant> (p);
|
||||
string k;
|
||||
Variant v;
|
||||
while (iter.next (out k, out v))
|
||||
printerr ("%s: %s\n", k, v.print (false));
|
||||
|
||||
printerr ("device.enumerate_applications()");
|
||||
timer.reset ();
|
||||
var apps = yield device.enumerate_applications ();
|
||||
var opts = new ApplicationQueryOptions ();
|
||||
opts.scope = FULL;
|
||||
var apps = yield device.enumerate_applications (opts);
|
||||
printerr (" => got %d apps, took %u ms\n", apps.size (), (uint) (timer.elapsed () * 1000.0));
|
||||
if (GLib.Test.verbose ()) {
|
||||
var length = apps.size ();
|
||||
@@ -3694,7 +3712,7 @@ namespace Frida.HostSessionTest {
|
||||
} else {
|
||||
printerr ("device.spawn()");
|
||||
timer.reset ();
|
||||
pid = yield device.spawn (app_id);
|
||||
pid = yield device.spawn (APP_ID);
|
||||
printerr (" => pid=%u, took %u ms\n", pid, (uint) (timer.elapsed () * 1000.0));
|
||||
}
|
||||
|
||||
@@ -3744,42 +3762,145 @@ namespace Frida.HostSessionTest {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
}
|
||||
|
||||
try {
|
||||
yield device_manager.close ();
|
||||
} catch (IOError e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
h.done ();
|
||||
}
|
||||
|
||||
private static async void xpc (Harness h) {
|
||||
if (!GLib.Test.slow ()) {
|
||||
stdout.printf ("<skipping, run in slow mode with iOS device connected> ");
|
||||
namespace Xpc {
|
||||
|
||||
private static async void list (Harness h) {
|
||||
if (!GLib.Test.slow ()) {
|
||||
stdout.printf ("<skipping, run in slow mode with iOS device connected> ");
|
||||
h.done ();
|
||||
return;
|
||||
}
|
||||
|
||||
var device_manager = new DeviceManager ();
|
||||
|
||||
try {
|
||||
var timer = new Timer ();
|
||||
var device = yield device_manager.get_device_by_id (DEVICE_ID);
|
||||
printerr ("[*] Got device in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
|
||||
timer.reset ();
|
||||
var appservice = yield device.open_service ("xpc:com.apple.coredevice.appservice");
|
||||
printerr ("[*] Opened service in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
|
||||
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.featureIdentifier"] = "com.apple.coredevice.feature.listprocesses";
|
||||
parameters["CoreDevice.action"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.input"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
timer.reset ();
|
||||
var response = yield appservice.request (parameters);
|
||||
printerr ("[*] Made request in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
printerr ("Got response: %s\n", response.print (true));
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
yield;
|
||||
}
|
||||
|
||||
h.done ();
|
||||
return;
|
||||
}
|
||||
|
||||
var device_id = "<device-id>";
|
||||
private static async void launch (Harness h) {
|
||||
if (!GLib.Test.slow ()) {
|
||||
stdout.printf ("<skipping, run in slow mode with iOS device connected> ");
|
||||
h.done ();
|
||||
return;
|
||||
}
|
||||
|
||||
var device_manager = new DeviceManager ();
|
||||
var device_manager = new DeviceManager ();
|
||||
|
||||
try {
|
||||
var timer = new Timer ();
|
||||
var device = yield device_manager.get_device_by_id (device_id);
|
||||
printerr ("[*] Got device in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
try {
|
||||
var timer = new Timer ();
|
||||
var device = yield device_manager.get_device_by_id (DEVICE_ID);
|
||||
printerr ("[*] Got device in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
|
||||
timer.reset ();
|
||||
var appservice = yield device.open_service ("xpc:com.apple.coredevice.appservice");
|
||||
printerr ("[*] Opened service in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
timer.reset ();
|
||||
var appservice = yield device.open_service ("xpc:com.apple.coredevice.appservice");
|
||||
printerr ("[*] Opened service in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
|
||||
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.featureIdentifier"] = "com.apple.coredevice.feature.listprocesses";
|
||||
parameters["CoreDevice.action"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.input"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
timer.reset ();
|
||||
var response = yield appservice.request (parameters);
|
||||
printerr ("[*] Made request in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
printerr ("Got response: %s\n", response.print (true));
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
var stdio_stream = yield device.open_channel ("tcp:com.apple.coredevice.openstdiosocket");
|
||||
uint8 stdio_uuid[16];
|
||||
size_t n;
|
||||
yield stdio_stream.get_input_stream ().read_all_async (stdio_uuid, Priority.DEFAULT, null, out n);
|
||||
|
||||
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
parameters["CoreDevice.featureIdentifier"] = "com.apple.coredevice.feature.launchapplication";
|
||||
parameters["CoreDevice.action"] = new HashTable<string, Variant> (str_hash, str_equal);
|
||||
|
||||
var standard_input = Variant.new_from_data<void> (new VariantType ("ay"), stdio_uuid, true);
|
||||
var standard_output = standard_input;
|
||||
var standard_error = standard_output;
|
||||
var input = new Variant.parsed (@"""{
|
||||
'applicationSpecifier': <{
|
||||
'bundleIdentifier': <{
|
||||
'_0': <'$APP_ID'>
|
||||
}>
|
||||
}>,
|
||||
'options': <{
|
||||
'arguments': <@av []>,
|
||||
'environmentVariables': <@a{sv} {}>,
|
||||
'standardIOUsesPseudoterminals': <true>,
|
||||
'startStopped': <false>,
|
||||
'terminateExisting': <true>,
|
||||
'user': <{
|
||||
'active': <true>
|
||||
}>,
|
||||
'platformSpecificOptions': <
|
||||
b'<?xml version="1.0" encoding="UTF-8"?><plist version="1.0"><dict/></plist>'
|
||||
>
|
||||
}>,
|
||||
'standardIOIdentifiers': <{
|
||||
'standardInput': <('uuid', %@ay)>,
|
||||
'standardOutput': <('uuid', %@ay)>,
|
||||
'standardError': <('uuid', %@ay)>
|
||||
}>
|
||||
}""",
|
||||
standard_input,
|
||||
standard_output,
|
||||
standard_error);
|
||||
printerr ("input: %s\n", input.print (true));
|
||||
parameters["CoreDevice.input"] = input;
|
||||
timer.reset ();
|
||||
var response = yield appservice.request (parameters);
|
||||
printerr ("[*] Made request in %u ms\n", (uint) (timer.elapsed () * 1000.0));
|
||||
printerr ("Got response: %s\n", response.print (true));
|
||||
|
||||
process_stdio.begin (stdio_stream);
|
||||
|
||||
// Wait forever...
|
||||
h.disable_timeout ();
|
||||
yield;
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
yield;
|
||||
}
|
||||
|
||||
h.done ();
|
||||
}
|
||||
|
||||
private async void process_stdio (IOStream stream) {
|
||||
try {
|
||||
var input = new DataInputStream (stream.get_input_stream ());
|
||||
while (true) {
|
||||
var line = yield input.read_line_async ();
|
||||
if (line == null) {
|
||||
printerr ("process_stdio: EOF\n");
|
||||
break;
|
||||
}
|
||||
printerr ("process_stdio got line: %s\n", line);
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("process_stdio: %s\n", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
h.done ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,49 @@
|
||||
[CCode (cheader_filename = "CoreFoundation/CFDictionary.h", gir_namespace = "Darwin", gir_version = "1.0")]
|
||||
[CCode (cheader_filename = "CoreFoundation/CoreFoundation.h", cprefix = "CF", gir_namespace = "CoreFoundation", gir_version = "1.0")]
|
||||
namespace CoreFoundation {
|
||||
[Compact]
|
||||
[CCode (cname = "struct __CFDictionary", ref_function = "CFRetain", unref_function = "CFRelease")]
|
||||
[CCode (cname = "struct __CFArray")]
|
||||
public class Array : Type {
|
||||
[CCode (cname = "CFArrayCreate")]
|
||||
public Array (Allocator? allocator, [CCode (array_length_type = "CFIndex")] Type[] values,
|
||||
ArrayCallBacks callbacks = ArrayCallBacks.DEFAULT);
|
||||
|
||||
public Index length {
|
||||
[CCode (cname = "CFArrayGetCount")]
|
||||
get;
|
||||
}
|
||||
|
||||
[CCode (cname = "CFArrayGetValueAtIndex")]
|
||||
public void * @get (Index i);
|
||||
}
|
||||
|
||||
public struct ArrayCallBacks {
|
||||
[CCode (cname = "kCFTypeArrayCallBacks")]
|
||||
public static ArrayCallBacks DEFAULT;
|
||||
|
||||
public Index version;
|
||||
public ArrayRetainCallBack retain;
|
||||
public ArrayReleaseCallBack release;
|
||||
public ArrayCopyDescriptionCallBack copyDescription;
|
||||
public ArrayEqualCallBack equal;
|
||||
}
|
||||
|
||||
public delegate void * ArrayRetainCallBack (Allocator allocator, void * value);
|
||||
public delegate void ArrayReleaseCallBack (Allocator allocator, void * value);
|
||||
public delegate String ArrayCopyDescriptionCallBack (void * value);
|
||||
public delegate bool ArrayEqualCallBack (void * value1, void * value2);
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "struct __CFArray")]
|
||||
public class MutableArray : Array {
|
||||
[CCode (cname = "CFArrayRemoveAllValues")]
|
||||
public void clear ();
|
||||
|
||||
[CCode (cname = "CFArrayAppendValue")]
|
||||
public void add (void * value);
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "struct __CFDictionary")]
|
||||
public class Dictionary : Type {
|
||||
[CCode (cname = "CFDictionaryGetCount")]
|
||||
public Index count ();
|
||||
@@ -15,7 +57,7 @@ namespace CoreFoundation {
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cname = "struct __CFDictionary", ref_function = "CFRetain", unref_function = "CFRelease")]
|
||||
[CCode (cname = "struct __CFDictionary")]
|
||||
public class MutableDictionary : Dictionary {
|
||||
[CCode (cname = "CFDictionaryRemoveAllValues")]
|
||||
public void clear ();
|
||||
@@ -27,9 +69,8 @@ namespace CoreFoundation {
|
||||
public void remove (void * key);
|
||||
}
|
||||
|
||||
// TODO: Inherit CFTypeRef? CFStringRef vs __CFString
|
||||
[Compact]
|
||||
[CCode (cname = "const struct __CFString", ref_function = "CFRetain", unref_function = "CFRelease")]
|
||||
[CCode (cname = "const struct __CFString")]
|
||||
public class String : Type {
|
||||
public Index length {
|
||||
get { return _length (); }
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
[CCode (cheader_filename = "dns_sd.h", gir_namespace = "Darwin", gir_version = "1.0")]
|
||||
namespace Darwin.DNSSD {
|
||||
[CCode (cname = "DNSServiceRef", ref_function = "", unref_function = "")]
|
||||
public class DNSService {
|
||||
[CCode (cname = "DNSServiceCreateConnection")]
|
||||
public static ErrorType create_connection (out DNSService service);
|
||||
|
||||
[CCode (cname = "DNSServiceRefDeallocate")]
|
||||
public void deallocate ();
|
||||
|
||||
[CCode (cname = "DNSServiceSetDispatchQueue")]
|
||||
public ErrorType set_dispatch_queue (Darwin.GCD.DispatchQueue queue);
|
||||
|
||||
[CCode (cname = "DNSServiceBrowse")]
|
||||
public static ErrorType browse (ref DNSService sd_ref, Flags flags, uint32 interface_index, string regtype, string? domain,
|
||||
BrowseReply callback);
|
||||
|
||||
[CCode (cname = "DNSServiceResolve")]
|
||||
public static ErrorType resolve (ref DNSService sd_ref, Flags flags, uint32 interface_index, string name, string regtype,
|
||||
string domain, ResolveReply callback);
|
||||
|
||||
[CCode (cname = "DNSServiceGetAddrInfo")]
|
||||
public static ErrorType get_addr_info (ref DNSService sd_ref, Flags flags, uint32 interface_index, Protocol protocol,
|
||||
string hostname, GetAddrInfoReply callback);
|
||||
|
||||
[CCode (cname = "DNSServiceBrowseReply")]
|
||||
public delegate void BrowseReply (DNSService sd_ref, Flags flags, uint32 interface_index, ErrorType error_code,
|
||||
string service_name, string regtype, string reply_domain);
|
||||
|
||||
[CCode (cname = "DNSServiceResolveReply")]
|
||||
public delegate void ResolveReply (DNSService sd_ref, Flags flags, uint32 interface_index, ErrorType error_code,
|
||||
string fullname, string hosttarget, uint16 port, [CCode (array_length_pos = 7.9, array_length_type = "uint16_t")] uint8[] txt_record);
|
||||
|
||||
[CCode (cname = "DNSServiceGetAddrInfoReply")]
|
||||
public delegate void GetAddrInfoReply (DNSService sd_ref, Flags flags, uint32 interface_index, ErrorType error_code,
|
||||
string hostname, void * address, uint32 ttl);
|
||||
|
||||
[CCode (cname = "DNSServiceErrorType", cprefix = "kDNSServiceErr_", has_type_id = false)]
|
||||
public enum ErrorType {
|
||||
NoError,
|
||||
Unknown,
|
||||
NoSuchName,
|
||||
NoMemory,
|
||||
BadParam,
|
||||
BadReference,
|
||||
BadState,
|
||||
BadFlags,
|
||||
Unsupported,
|
||||
NotInitialized,
|
||||
AlreadyRegistered,
|
||||
NameConflict,
|
||||
Invalid,
|
||||
Firewall,
|
||||
Incompatible,
|
||||
BadInterfaceIndex,
|
||||
Refused,
|
||||
NoSuchRecord,
|
||||
NoAuth,
|
||||
NoSuchKey,
|
||||
NATTraversal,
|
||||
DoubleNAT,
|
||||
BadTime,
|
||||
BadSig,
|
||||
BadKey,
|
||||
Transient,
|
||||
ServiceNotRunning,
|
||||
NATPortMappingUnsupported,
|
||||
NATPortMappingDisabled,
|
||||
NoRouter,
|
||||
PollingMode,
|
||||
Timeout,
|
||||
DefunctConnection,
|
||||
PolicyDenied,
|
||||
NotPermitted,
|
||||
}
|
||||
|
||||
[CCode (cname = "DNSServiceFlags", cprefix = "kDNSServiceFlags", has_type_id = false)]
|
||||
[Flags]
|
||||
public enum Flags {
|
||||
MoreComing,
|
||||
QueueRequest,
|
||||
AutoTrigger,
|
||||
Add,
|
||||
Default,
|
||||
NoAutoRename,
|
||||
Shared,
|
||||
Unique,
|
||||
BrowseDomains,
|
||||
RegistrationDomains,
|
||||
LongLivedQuery,
|
||||
AllowRemoteQuery,
|
||||
ForceMulticast,
|
||||
Force,
|
||||
KnownUnique,
|
||||
ReturnIntermediates,
|
||||
ShareConnection,
|
||||
SuppressUnusable,
|
||||
Timeout,
|
||||
IncludeP2P,
|
||||
WakeOnResolve,
|
||||
BackgroundTrafficClass,
|
||||
IncludeAWDL,
|
||||
EnableDNSSEC,
|
||||
Validate,
|
||||
Secure,
|
||||
Insecure,
|
||||
Bogus,
|
||||
Indeterminate,
|
||||
UnicastResponse,
|
||||
ValidateOptional,
|
||||
WakeOnlyService,
|
||||
ThresholdOne,
|
||||
ThresholdFinder,
|
||||
ThresholdReached,
|
||||
PrivateOne,
|
||||
PrivateTwo,
|
||||
PrivateThree,
|
||||
PrivateFour,
|
||||
PrivateFive,
|
||||
[CCode (cname = "kDNSServiceFlagAnsweredFromCache")]
|
||||
AnsweredFromCache,
|
||||
AllowExpiredAnswers,
|
||||
ExpiredAnswer,
|
||||
}
|
||||
|
||||
[CCode (cname = "DNSServiceProtocol", cprefix = "kDNSServiceProtocol_", has_type_id = false)]
|
||||
public enum Protocol {
|
||||
IPv4 = 0x01,
|
||||
IPv6 = 0x02,
|
||||
UDP = 0x10,
|
||||
TCP = 0x20,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
[CCode (cheader_filename = "IOKit/IOKitLib.h", gir_namespace = "Darwin", gir_version = "1.0")]
|
||||
namespace Darwin.IOKit {
|
||||
using CoreFoundation;
|
||||
using Darwin.XNU;
|
||||
|
||||
[CCode (cname = "IOMasterPort")]
|
||||
public KernReturn main_port (MachPort bootstrap_port, out MachPort main_port);
|
||||
|
||||
[CCode (cname = "IOServiceMatching")]
|
||||
public MutableDictionary service_matching (string name);
|
||||
|
||||
[CCode (cname = "IOServiceGetMatchingServices")]
|
||||
public KernReturn get_matching_services (MachPort main_port, owned MutableDictionary matching_dict,
|
||||
out IOIterator iterator);
|
||||
|
||||
[CCode (cname = "kIOEthernetInterfaceClass", cheader_filename = "IOKit/network/IOEthernetInterface.h")]
|
||||
public const string ETHERNET_INTERFACE_CLASS;
|
||||
|
||||
[CCode (cname = "kIOBSDNameKey", cheader_filename = "IOKit/IOBSD.h")]
|
||||
public const string BSD_NAME_KEY;
|
||||
|
||||
[CCode (cname = "kIOServicePlane", cheader_filename = "IOKit/IOKitKeys.h")]
|
||||
public const string IOSERVICE_PLANE;
|
||||
|
||||
[CCode (cname = "io_registry_entry_t", destroy_function = "IOObjectRelease", has_type_id = false)]
|
||||
public struct IORegistryEntry : IOObject {
|
||||
[CCode (cname = "IORegistryEntryCreateCFProperties")]
|
||||
public KernReturn create_cf_properties (out MutableDictionary properties, Allocator? allocator, uint options);
|
||||
|
||||
[CCode (cname = "IORegistryEntryCreateCFProperty")]
|
||||
public CoreFoundation.Type create_cf_property (String key, Allocator? allocator, uint options);
|
||||
|
||||
[CCode (cname = "IORegistryEntryGetParentEntry")]
|
||||
public KernReturn get_parent_entry (string plane, out IOObject parent);
|
||||
}
|
||||
|
||||
[CCode (cname = "io_iterator_t", destroy_function = "IOObjectRelease")]
|
||||
public struct IOIterator : MachPort {
|
||||
[CCode (cname = "IOIteratorReset")]
|
||||
public void reset ();
|
||||
|
||||
[CCode (cname = "IOIteratorIsValid")]
|
||||
public bool is_valid ();
|
||||
|
||||
[CCode (cname = "IOIteratorNext")]
|
||||
public IOObject next ();
|
||||
}
|
||||
|
||||
[CCode (cname = "io_service_t", destroy_function = "IOObjectRelease", has_type_id = false)]
|
||||
public struct IOService : IOObject {
|
||||
}
|
||||
|
||||
[CCode (cname = "io_object_t", destroy_function = "IOObjectRelease", has_type_id = false)]
|
||||
public struct IOObject : MachPort {
|
||||
[CCode (cname = "IO_OBJECT_NULL")]
|
||||
public const IOObject NULL;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
[CCode (cheader_filename = "net/if.h", lower_case_cprefix = "", gir_namespace = "Darwin", gir_version = "1.0")]
|
||||
namespace Darwin.Net {
|
||||
public const int IFNAMSIZ;
|
||||
|
||||
public unowned string? if_indextoname (uint ifindex, [CCode (array_length = false)] char[] ifname);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef __DARWIN_SYSTEMCONFIGURATION_H__
|
||||
#define __DARWIN_SYSTEMCONFIGURATION_H__
|
||||
|
||||
#ifndef HAVE_MACOS
|
||||
# include <os/availability.h>
|
||||
# undef API_UNAVAILABLE
|
||||
# define API_UNAVAILABLE(...)
|
||||
#endif
|
||||
|
||||
#include <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
[CCode (cheader_filename = "darwin-systemconfiguration.h", cprefix = "SC", gir_namespace = "Darwin", gir_version = "1.0")]
|
||||
namespace Darwin.SystemConfiguration {
|
||||
[Compact]
|
||||
[CCode (cname = "struct __SCDynamicStore")]
|
||||
public class DynamicStore : CoreFoundation.Type {
|
||||
[CCode (cname = "SCDynamicStoreCreate")]
|
||||
public DynamicStore (CoreFoundation.Allocator? allocator, CoreFoundation.String name, DynamicStoreCallBack callout,
|
||||
DynamicStoreContext context);
|
||||
|
||||
[CCode (cname = "SCDynamicStoreSetNotificationKeys")]
|
||||
public bool set_notification_keys (CoreFoundation.Array? keys, CoreFoundation.Array? patterns);
|
||||
|
||||
[CCode (cname = "SCDynamicStoreSetDispatchQueue")]
|
||||
public bool set_dispatch_queue (Darwin.GCD.DispatchQueue? queue);
|
||||
|
||||
[CCode (cname = "SCDynamicStoreCopyKeyList")]
|
||||
public CoreFoundation.Array copy_key_list (CoreFoundation.String pattern);
|
||||
|
||||
[CCode (cname = "SCDynamicStoreCopyValue")]
|
||||
public CoreFoundation.Type? copy_value (CoreFoundation.String key);
|
||||
|
||||
[CCode (cname = "SCDynamicStoreCopyMultiple")]
|
||||
public CoreFoundation.Dictionary copy_multiple (CoreFoundation.Array? keys, CoreFoundation.Array? patterns = null);
|
||||
}
|
||||
|
||||
[CCode (has_target = false)]
|
||||
public delegate void DynamicStoreCallBack (DynamicStore store, CoreFoundation.Array changed_keys, void * info);
|
||||
|
||||
public struct DynamicStoreContext {
|
||||
public CoreFoundation.Index version;
|
||||
public void * info;
|
||||
public RetainFunc? retain;
|
||||
public ReleaseFunc? release;
|
||||
public CopyDescriptionFunc? copyDescription;
|
||||
}
|
||||
|
||||
[CCode (has_target = false)]
|
||||
public delegate void * RetainFunc (void * info);
|
||||
[CCode (has_target = false)]
|
||||
public delegate void ReleaseFunc (void * info);
|
||||
[CCode (has_target = false)]
|
||||
public delegate CoreFoundation.String CopyDescriptionFunc (void * info);
|
||||
}
|
||||
@@ -24,19 +24,35 @@ namespace LWIP {
|
||||
public void set_up ();
|
||||
public void set_down ();
|
||||
|
||||
public void set_link_up ();
|
||||
public void set_link_down ();
|
||||
|
||||
public void ip6_addr_set (int8 addr_idx, IP6Address address);
|
||||
public void create_ip6_linklocal_address (bool from_mac_48bit);
|
||||
public ErrorCode add_ip6_address (IP6Address address, int8 * chosen_index = null);
|
||||
public void ip6_addr_set_state (int8 addr_index, IP6AddressState state);
|
||||
|
||||
[CCode (cname = "netif_input")]
|
||||
public static ErrorCode default_input_handler (PacketBuffer pbuf, NetworkInterface netif);
|
||||
|
||||
public IP6Address ip6_addr[IPV6_NUM_ADDRESSES];
|
||||
|
||||
public NetworkInterfaceInputFunc input;
|
||||
public NetworkInterfaceLinkOutputFunc linkoutput;
|
||||
public NetworkInterfaceOutputIP6Func output_ip6;
|
||||
|
||||
public void * state;
|
||||
|
||||
public uint16 mtu;
|
||||
|
||||
public uint8 hwaddr[MAX_HWADDR_LEN];
|
||||
public uint8 hwaddr_len;
|
||||
|
||||
public NetworkInterfaceFlags flags;
|
||||
|
||||
public uint8 num;
|
||||
|
||||
public const uint8 MAX_HWADDR_LEN;
|
||||
}
|
||||
|
||||
[CCode (cname = "netif_init_fn", has_target = false)]
|
||||
@@ -45,13 +61,46 @@ namespace LWIP {
|
||||
[CCode (cname = "netif_input_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceInputFunc (PacketBuffer pbuf, NetworkInterface netif);
|
||||
|
||||
[CCode (cname = "netif_linkoutput_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceLinkOutputFunc (NetworkInterface netif, PacketBuffer pbuf);
|
||||
|
||||
[CCode (cname = "netif_output_ip6_fn", has_target = false)]
|
||||
public delegate ErrorCode NetworkInterfaceOutputIP6Func (NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
|
||||
[Flags]
|
||||
[CCode (cheader_filename = "lwip/netif.h", cprefix = "NETIF_FLAG_")]
|
||||
public enum NetworkInterfaceFlags {
|
||||
UP,
|
||||
BROADCAST,
|
||||
LINK_UP,
|
||||
ETHARP,
|
||||
ETHERNET,
|
||||
IGMP,
|
||||
MLD6,
|
||||
}
|
||||
|
||||
[CCode (cheader_filename = "lwip/prot/ethernet.h", lower_case_cprefix = "ETH_")]
|
||||
namespace Ethernet {
|
||||
public const uint8 HWADDR_LEN;
|
||||
|
||||
namespace IPv6 {
|
||||
[CCode (cheader_filename = "lwip/ethip6.h", cname = "ethip6_output")]
|
||||
public ErrorCode output (NetworkInterface netif, PacketBuffer pbuf, IP6Address address);
|
||||
}
|
||||
}
|
||||
|
||||
public const uint IPV6_NUM_ADDRESSES;
|
||||
|
||||
[CCode (cheader_filename = "lwip/ip6_addr.h", cname = "ip6_addr_t", cprefix = "ip6_addr_")]
|
||||
public struct IP6Address {
|
||||
[CCode (cname = "ip6addr_aton")]
|
||||
public static IP6Address parse (string str);
|
||||
|
||||
[CCode (cname = "ip6addr_ntoa_r")]
|
||||
public unowned string? to_string (char[] buf);
|
||||
|
||||
public uint32 addr[4];
|
||||
public uint8 zone;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
@@ -79,6 +128,18 @@ namespace LWIP {
|
||||
ANY,
|
||||
}
|
||||
|
||||
[CCode (cheader_filename = "lwip/mld6.h", cname = "struct netif", cprefix = "netif_")]
|
||||
namespace IP6MulticastListenerDiscovery {
|
||||
[CCode (cname = "mld6_joingroup")]
|
||||
public ErrorCode join_group (IP6Address src_addr, IP6Address group_addr);
|
||||
[CCode (cname = "mld6_joingroup_netif")]
|
||||
public ErrorCode join_group_netif (ref NetworkInterface netif, IP6Address group_addr);
|
||||
[CCode (cname = "mld6_leavegroup")]
|
||||
public ErrorCode leave_group (IP6Address src_addr, IP6Address group_addr);
|
||||
[CCode (cname = "mld6_leavegroup_netif")]
|
||||
public ErrorCode leave_group_netif (ref NetworkInterface netif, IP6Address group_addr);
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cheader_filename = "lwip/pbuf.h", cname = "struct pbuf", cprefix = "pbuf_")]
|
||||
public class PacketBuffer {
|
||||
@@ -168,6 +229,43 @@ namespace LWIP {
|
||||
}
|
||||
}
|
||||
|
||||
[Compact]
|
||||
[CCode (cheader_filename = "lwip/udp.h", cname = "struct udp_pcb", cprefix = "udp_")]
|
||||
public class UdpPcb {
|
||||
[CCode (cname = "udp_new_ip_type")]
|
||||
public static unowned UdpPcb make (IPAddressType type);
|
||||
|
||||
public void remove ();
|
||||
|
||||
[CCode (cname = "udp_recv")]
|
||||
public void set_recv_callback (RecvFunc f);
|
||||
|
||||
public ErrorCode bind (IP6Address? ipaddr = null, uint16 port = 0);
|
||||
public void bind_netif (NetworkInterface? netif);
|
||||
|
||||
public ErrorCode connect (IP6Address ipaddr, uint16 port);
|
||||
|
||||
public ErrorCode send (PacketBuffer pbuf);
|
||||
public ErrorCode sendto (PacketBuffer pbuf, IP6Address dst_ip, uint16 dst_port);
|
||||
|
||||
[CCode (cname = "tcp_recv_fn", instance_pos = 0)]
|
||||
public delegate void RecvFunc (UdpPcb pcb, owned PacketBuffer? pbuf, IP6Address addr, uint16 port);
|
||||
|
||||
public IP6Address local_ip;
|
||||
public IP6Address remote_ip;
|
||||
public uint8 netif_idx;
|
||||
public uint8 so_options;
|
||||
public uint8 tos;
|
||||
public uint8 ttl;
|
||||
|
||||
public uint8 flags;
|
||||
public uint16 local_port;
|
||||
public uint16 remote_port;
|
||||
|
||||
public uint8 mcast_ifindex;
|
||||
public uint8 mcast_ttl;
|
||||
}
|
||||
|
||||
[CCode (cheader_filename = "lwip/err.h", cname = "err_t", cprefix = "ERR_", lower_case_cprefix = "err_", has_type_id = false)]
|
||||
public enum ErrorCode {
|
||||
OK,
|
||||
|
||||
Reference in new issue
Block a user