Compare commits

..
52 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås fd5a274680 subprojects: Prepare for release 2024-07-08 14:27:06 +02:00
Ole André Vadla Ravnås d3f949a51c subprojects: Bump outdated 2024-07-08 14:25:06 +02:00
Ole André Vadla Ravnås 6d3ea5f589 fruity: Clean up OS-specific bits 2024-07-08 14:24:29 +02:00
Ole André Vadla Ravnås 3f6b41dc8c fruity: Handle libusb failing to initialize
Which may happen due to insufficient privileges, stripped down
environments such as containers, etc.
2024-07-08 10:26:46 +02:00
Ole André Vadla Ravnås 2c23afacfd subprojects: Prepare for release 2024-07-06 00:07:28 +02:00
Ole André Vadla Ravnås 51bf048d5b subprojects: Bump outdated 2024-07-06 00:06:32 +02:00
Ole André Vadla RavnåsandHåvard Sørbø be6a2b305c socket: Fix Host header logic in negotiate_connection()
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-06 00:04:35 +02:00
Ole André Vadla Ravnås 5625a08fd9 device: Fix property getters for id and name
When Device is created with a custom value for either.

Due to typos the getters ended up recursing until stack overflow.
2024-07-05 23:35:47 +02:00
Ole André Vadla Ravnås 684739a1e6 subprojects: Prepare for release 2024-07-05 15:32:45 +02:00
Ole André Vadla RavnåsandHåvard Sørbø e0c36e6826 fruity: Retry RSD port lookup on failure
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 15:31:42 +02:00
Ole André Vadla RavnåsandHåvard Sørbø eeb0c7a50a fruity: Fix gadget upload when no usbmux connection is available
By listening on all interfaces, not just loopback, so we can connect to
it across the tunnel.

Also switch to IPv6 while at it.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 15:22:40 +02:00
Ole André Vadla RavnåsandHåvard Sørbø f2dc84324a fruity: Skip fetching dyld symbols if libSystem is initialized
We only need them for early instrumentation.

This means we can attach to a running app not spawned by Frida, even on
latest iOS versions where dyld restarts.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 14:22:26 +02:00
Ole André Vadla RavnåsandHåvard Sørbø b9d07f0bfb fruity: Wire up MacOSCoreDeviceTransport event handling
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 13:55:34 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 8b54716bfc fruity: Ensure provider name doesn't mutate
In case of a transport departing and taking the string storage with it.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 13:51:59 +02:00
Ole André Vadla Ravnås 68bfde7ae0 xpc-object-reader: Add get_uuid_value() 2024-07-05 13:50:57 +02:00
Ole André Vadla RavnåsandHåvard Sørbø bcdcc090b6 device: Refactor construction and property wiring
We can compute some of the properties from the provider.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 13:50:19 +02:00
Ole André Vadla Ravnås 2a1a80abd9 fruity: Tweak ordering of system parameters 2024-07-05 10:44:44 +02:00
ea6307d60b fruity: Add os.build and hardware to system parameters (#534)
Co-authored-by: Ole André Vadla Ravnås <[email protected]>
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-05 10:10:17 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 2bf45ececd fruity: Await ObjC runtime initialization on iOS >= 18
By making use of notifyObjCInit() if available.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-04 15:20:51 +02:00
Ole André Vadla RavnåsandHåvard Sørbø d6b502eb3e fruity: Add support for dyld restart on iOS >= 18
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-04 14:56:51 +02:00
Ole André Vadla Ravnås 0de45afe2f meson: Auto-disable external device backends for 32-bit host machines 2024-07-04 13:49:25 +02:00
Ole André Vadla Ravnås 7262e55468 macos: Replace logging with TODOs in the CoreDevice transport 2024-07-04 13:36:01 +02:00
Ole André Vadla RavnåsandHåvard Sørbø def974818f socket: Support IPv6 addresses in negotiate_connection()
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-04 12:51:58 +02:00
Ole André Vadla Ravnås d5312d288f pipe: Fix tvOS build regression
We need to make sure we include the Mach headers before CoreFoundation
does, so our version availability overrides are used.
2024-07-04 11:16:06 +02:00
Ole André Vadla Ravnås 7e9fb1e1c6 subprojects: Bump outdated 2024-07-03 23:20:05 +02:00
Ole André Vadla RavnåsandHåvard Sørbø e415a2ec83 fruity: Fix the macOS CoreDevice connection type logic
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 14:20:35 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 248127623f tests: Add manual XPC application launching test
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:45:09 +02:00
Ole André Vadla RavnåsandHåvard Sørbø f246b4e17d xpc-service: Support type-annotating request parameters
Represented using a `(type, value)` tuple.

This means uint64 and UUID values can be passed in.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:42:48 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 7c4633358b fruity: Improve open_channel() to support tcp:service-name
Useful for e.g. `com.apple.coredevice.openstdiosocket`.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:35:08 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 85d063ab9b fruity: Revive HostChannelProvider implementation
Removed by accident in 08e5d37348.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:33:25 +02:00
Ole André Vadla RavnåsandHåvard Sørbø a6650efecb xpc: Make add_uuid_value() expect a raw UUID
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:31:54 +02:00
Ole André Vadla RavnåsandHåvard Sørbø d382734bca value: Remove unused UUID functionality
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 13:30:29 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 08e5d37348 fruity: Rework to support userspace NCM
To support systems without a compatible NCM driver, such as contemporary
Linux kernels, Windows, etc.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-03 11:20:16 +02:00
Ole André Vadla Ravnås 6317446c37 xpc-client: Add post() 2024-07-03 11:14:41 +02:00
Ole André Vadla Ravnås 7048e851b0 xpc-client: Handle closure in open state 2024-07-03 11:14:41 +02:00
Ole André Vadla Ravnås 7d22da8d3d meson: Summarize backends and more of the features 2024-07-03 11:14:41 +02:00
Ole André Vadla Ravnås 1f164564ef meson: Auto-disable external device backends on non-desktop 2024-07-03 11:14:41 +02:00
Ole André Vadla Ravnås 47a2e6728b submodules: Bump releng 2024-07-03 11:14:36 +02:00
Ole André Vadla Ravnås b19333420c web-service: Listen on dynamic interfaces last
So that specifying e.g. 0.0.0.0 takes precedence and won't cause start()
to fail.
2024-07-03 10:47:09 +02:00
Ole André Vadla Ravnås 29ec9f0c98 tunnel-interface-observer: Fix confusing variable name
The tunnel isn't only for development tools.
2024-07-03 10:43:02 +02:00
Ole André Vadla Ravnås cbcb7f37bf gadget: Integrate TunnelInterfaceObserver
So we listen on Apple's CoreDevice tunnel network interfaces on iOS and
tvOS.
2024-07-02 22:50:09 +02:00
Ole André Vadla Ravnås f53230bd7a control-service: Integrate TunnelInterfaceObserver
So we listen on Apple's CoreDevice tunnel network interfaces on iOS and
tvOS.
2024-07-02 22:50:09 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 39486a5331 netif: Add TunnelInterfaceObserver for iOS and tvOS
For observing Apple's CoreDevice tunnel network interfaces. Intended for
ControlService (frida-server) and frida-gadget to be able to listen on
such interfaces as they arrive and depart.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 22:50:05 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 67f6895e13 base: Add an iterable CFArray wrapper
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 22:26:15 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 1b80ce0779 vapi: Improve the CoreFoundation bindings
Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 22:26:15 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 9bd3c3974f web-service: Support dynamic network interfaces
By providing a DynamicInterfaceObserver with the desired behavior.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 22:26:12 +02:00
Ole André Vadla RavnåsandHåvard Sørbø 055fe8744a socket: Remove the partial NECP integration
It's not a good fit for keeping track of network interfaces.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-07-02 14:00:35 +02:00
Ole André Vadla RavnåsandHåvard Sørbø a0f7df8d0e socket: Sketch a partial NECP integration
In pursuit of notifications when network interfaces arrive and depart.

Co-authored-by: Håvard Sørbø <[email protected]>
2024-06-28 14:14:01 +02:00
Ole André Vadla Ravnås 4b5fe35b00 devkit: Also install frida-core.gir 2024-06-07 09:54:32 +02:00
Håvard Sørbø 9330f02ca5 xpc-service: Fix handling of request() with arrays (#532)
Where we forgot to call begin/end to actually generate an array, and
would end up generating an invalid XPC object.
2024-06-06 22:46:35 +02:00
Ole André Vadla Ravnås 5d23227efc subprojects: Prepare for release 2024-06-04 14:14:40 +02:00
Ole André Vadla Ravnås 300a55b75c subprojects: Bump outdated 2024-06-04 14:12:37 +02:00
52 changed files with 5119 additions and 2941 deletions

No files matched your search

+34
View File
@@ -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++];
}
}
}
}
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
-6
View File
@@ -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
View File
@@ -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);
+2 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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 = [
+1
View File
@@ -2,6 +2,7 @@ if host_os == 'android'
subdir('selinux')
endif
subdir('base')
subdir('netif')
subdir('pipe')
subdir('payload')
if have_local_backend
+28
View File
@@ -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,
)
+92
View File
@@ -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
}
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+1 -7
View File
@@ -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: [
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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";
+585
View File
@@ -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
View File
@@ -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 ());
}
}
}
}
+1
View File
@@ -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") ||
+7 -7
View File
@@ -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
View File
@@ -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 {
+15 -35
View File
@@ -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 {
-88
View File
@@ -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
View File
@@ -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);
}
+306
View File
@@ -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
+18 -39
View File
@@ -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
}
}
+290
View File
@@ -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
View File
@@ -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;
}
}
}
-298
View File
@@ -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);
}
}
}
}
-420
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+16 -20
View File
@@ -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']
+2 -4
View File
@@ -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 -1
View File
@@ -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]
+8
View File
@@ -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
View File
@@ -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 ();
}
}
+46 -5
View File
@@ -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 (); }
-134
View File
@@ -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,
}
}
}
-58
View File
@@ -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;
}
}
-6
View File
@@ -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);
}
+12
View File
@@ -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
+43
View File
@@ -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);
}
+98
View File
@@ -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,