mirror of
https://github.com/frida/frida-core.git
synced 2026-11-10 10:01:35 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd5a274680 | ||
|
|
d3f949a51c | ||
|
|
6d3ea5f589 | ||
|
|
3f6b41dc8c | ||
|
|
2c23afacfd | ||
|
|
51bf048d5b | ||
|
|
be6a2b305c | ||
|
|
5625a08fd9 |
No files matched your search
+12
-2
@@ -152,7 +152,14 @@ namespace Frida {
|
||||
var request = new StringBuilder.sized (256);
|
||||
request.append ("GET /ws HTTP/1.1\r\n");
|
||||
string protocol = (transport == TLS) ? "wss" : "ws";
|
||||
var uri = Uri.build (UriFlags.NONE, protocol, null, host, -1, "/ws", null, null);
|
||||
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", make_host_header_value (uri));
|
||||
@@ -221,7 +228,10 @@ namespace Frida {
|
||||
|
||||
private string make_host_header_value (Uri uri) {
|
||||
unowned string host = uri.get_host ();
|
||||
return Hostname.is_ip_address (host) ? @"[$host]" : 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 {
|
||||
|
||||
+1
-1
@@ -559,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']
|
||||
|
||||
+2
-2
@@ -485,7 +485,7 @@ namespace Frida {
|
||||
public string id {
|
||||
get {
|
||||
if (_id != null)
|
||||
return id;
|
||||
return _id;
|
||||
return provider.id;
|
||||
}
|
||||
}
|
||||
@@ -493,7 +493,7 @@ namespace Frida {
|
||||
public string name {
|
||||
get {
|
||||
if (_name != null)
|
||||
return name;
|
||||
return _name;
|
||||
return provider.name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,196 +1,5 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
#if 0
|
||||
public sealed class LinuxTunnelFinder : Object, TunnelFinder {
|
||||
public async Tunnel? find_tunnel (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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 {
|
||||
var netstack = tunnel_connection.tunnel_netstack;
|
||||
var endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: tunnel_connection.remote_address,
|
||||
port: port,
|
||||
scope_id: netstack.scope_id
|
||||
);
|
||||
return yield netstack.open_tcp_connection (endpoint, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxFruitFinder : Object, FruitFinder {
|
||||
public string? udid_from_iface (string ifname) throws Error {
|
||||
var net = "/sys/class/net";
|
||||
@@ -0,0 +1,585 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public sealed class MacOSCoreDeviceBackend : Object, Backend {
|
||||
private Gee.Map<string, MacOSCoreDeviceTransport> transports = new Gee.HashMap<string, MacOSCoreDeviceTransport> ();
|
||||
|
||||
private Promise<bool> all_current_devices_listed = new Promise<bool> ();
|
||||
private Promise<bool> browse_request = new Promise<bool> ();
|
||||
|
||||
private XpcClient? pairingd;
|
||||
private Darwin.GCD.DispatchQueue queue =
|
||||
new Darwin.GCD.DispatchQueue ("re.frida.fruity.remotepairing", Darwin.GCD.DispatchQueueAttr.SERIAL);
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
pairingd = XpcClient.make_for_mach_service ("com.apple.CoreDevice.remotepairingd", queue);
|
||||
pairingd.notify["state"].connect (on_state_changed);
|
||||
pairingd.message.connect (on_message);
|
||||
|
||||
do_browse.begin ();
|
||||
|
||||
try {
|
||||
yield all_current_devices_listed.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private async void do_browse () {
|
||||
try {
|
||||
var r = new PairingdRequest ("RemotePairing.BrowseRequest");
|
||||
r.body.set_bool ("currentDevicesOnly", false);
|
||||
yield pairingd.request (r.message, null);
|
||||
browse_request.resolve (true);
|
||||
} catch (GLib.Error e) {
|
||||
browse_request.reject (e);
|
||||
}
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
}
|
||||
|
||||
private void on_state_changed (Object obj, ParamSpec pspec) {
|
||||
if (pairingd.state == CLOSED && !all_current_devices_listed.future.ready) {
|
||||
all_current_devices_listed.reject (
|
||||
new Error.TRANSPORT ("Connection closed while waiting for initial device list"));
|
||||
}
|
||||
}
|
||||
|
||||
private void on_message (Darwin.Xpc.Object obj) {
|
||||
var reader = new XpcObjectReader (obj);
|
||||
try {
|
||||
reader.read_member ("mangledTypeName");
|
||||
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
|
||||
reader
|
||||
.end_member ()
|
||||
.read_member ("value");
|
||||
if (reader.try_read_member ("deviceFound")) {
|
||||
reader
|
||||
.read_member ("_0")
|
||||
.read_member ("deviceInfo");
|
||||
|
||||
var udid = reader.read_member ("udid").get_string_value ();
|
||||
reader.end_member ();
|
||||
if (transports.has_key (udid))
|
||||
return;
|
||||
|
||||
var device_info = (Darwin.Xpc.Dictionary) reader.current_object;
|
||||
|
||||
var pairing_device = new XpcClient (device_info.create_connection ("endpoint"), queue);
|
||||
|
||||
on_device_found (udid, pairing_device, device_info);
|
||||
} else if (reader.try_read_member ("allCurrentDevicesListed")) {
|
||||
all_current_devices_listed.resolve (true);
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void on_device_found (string udid, XpcClient pairing_device, Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
var transport = new MacOSCoreDeviceTransport (udid, pairing_device, device_info);
|
||||
transport.closed.connect (on_transport_closed);
|
||||
transports[udid] = transport;
|
||||
transport_attached (transport);
|
||||
}
|
||||
|
||||
private void on_transport_closed (MacOSCoreDeviceTransport transport) {
|
||||
transport_detached (transport);
|
||||
transports.unset (transport.udid);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MacOSCoreDeviceTransport : Object, Transport {
|
||||
public signal void closed ();
|
||||
|
||||
public XpcClient pairing_device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public ConnectionType connection_type {
|
||||
get {
|
||||
return _connection_type;
|
||||
}
|
||||
}
|
||||
|
||||
public string udid {
|
||||
get {
|
||||
return _udid;
|
||||
}
|
||||
}
|
||||
|
||||
public string? name {
|
||||
get {
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public UsbmuxDevice? usbmux_device {
|
||||
get {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ConnectionType _connection_type;
|
||||
private string _udid;
|
||||
private string _name;
|
||||
|
||||
private Promise<Tunnel>? tunnel_request;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public MacOSCoreDeviceTransport (string udid, XpcClient pairing_device, Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
Object (pairing_device: pairing_device);
|
||||
_udid = udid;
|
||||
update_device_info (device_info);
|
||||
}
|
||||
|
||||
construct {
|
||||
pairing_device.notify["state"].connect (on_state_changed);
|
||||
pairing_device.message.connect (on_message);
|
||||
}
|
||||
|
||||
private void update_device_info (Darwin.Xpc.Dictionary device_info) throws Error {
|
||||
_connection_type = (device_info.get_value ("untrustedRSDDeviceInfo") != null)
|
||||
? ConnectionType.USB
|
||||
: ConnectionType.NETWORK;
|
||||
|
||||
var reader = new XpcObjectReader (device_info);
|
||||
_name = reader.read_member ("name").get_string_value ();
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
private void on_state_changed (Object obj, ParamSpec pspec) {
|
||||
if (pairing_device.state == CLOSED) {
|
||||
io_cancellable.cancel ();
|
||||
|
||||
closed ();
|
||||
}
|
||||
}
|
||||
|
||||
private void on_message (Darwin.Xpc.Object obj) {
|
||||
var reader = new XpcObjectReader (obj);
|
||||
try {
|
||||
reader.read_member ("mangledTypeName");
|
||||
if (reader.get_string_value () == "RemotePairing.ServiceEvent") {
|
||||
reader
|
||||
.end_member ()
|
||||
.read_member ("value");
|
||||
if (reader.try_read_member ("deviceFound")) {
|
||||
reader
|
||||
.read_member ("_0")
|
||||
.read_member ("deviceInfo");
|
||||
var device_info = (Darwin.Xpc.Dictionary)
|
||||
reader.get_object_value (Darwin.Xpc.Dictionary.TYPE);
|
||||
update_device_info (device_info);
|
||||
} else if (reader.try_read_member ("tunnelUsageAssertionsInvalidated")) {
|
||||
reader.read_member ("assertionIdentifiers");
|
||||
var ids = new Gee.ArrayList<Bytes> ();
|
||||
size_t n = reader.count_elements ();
|
||||
for (size_t i = 0; i != n; i++) {
|
||||
reader.read_element (i);
|
||||
ids.add (new Bytes (reader.get_uuid_value ()));
|
||||
reader.end_element ();
|
||||
}
|
||||
on_tunnel_usage_assertions_invalidated.begin (ids);
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private async void on_tunnel_usage_assertions_invalidated (Gee.List<Bytes> ids) {
|
||||
if (tunnel_request == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
var tunnel = (MacOSTunnel) yield tunnel_request.future.wait_async (io_cancellable);
|
||||
unowned uint8[] tunnel_assertion_id = tunnel.assertion_identifier.get_bytes ();
|
||||
foreach (var id in ids) {
|
||||
if (Memory.cmp (id.get_data (), tunnel_assertion_id, id.get_size ()) == 0) {
|
||||
tunnel_request = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
while (tunnel_request != null) {
|
||||
try {
|
||||
return yield tunnel_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (IOError e) {
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
var tunnel = new MacOSTunnel (pairing_device);
|
||||
yield tunnel.attach (cancellable);
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
|
||||
return tunnel;
|
||||
} catch (GLib.Error e) {
|
||||
tunnel_request.reject (e);
|
||||
tunnel_request = null;
|
||||
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MacOSTunnel : Object, Tunnel {
|
||||
public DiscoveryService discovery {
|
||||
get {
|
||||
return _discovery;
|
||||
}
|
||||
}
|
||||
|
||||
public Darwin.Xpc.Uuid? assertion_identifier {
|
||||
get {
|
||||
return _assertion_identifier;
|
||||
}
|
||||
}
|
||||
|
||||
private XpcClient pairing_device;
|
||||
private Darwin.Xpc.Uuid? _assertion_identifier;
|
||||
private InetAddress? tunnel_device_address;
|
||||
private DiscoveryService? _discovery;
|
||||
|
||||
public MacOSTunnel (XpcClient pairing_device) {
|
||||
this.pairing_device = pairing_device;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.ReleaseAssertionRequest");
|
||||
r.body.set_value ("assertionIdentifier", _assertion_identifier);
|
||||
try {
|
||||
yield pairing_device.request (r.message, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async void attach (Cancellable? cancellable) throws Error, IOError {
|
||||
var r = new PairingdRequest ("RemotePairing.CreateAssertionCommand");
|
||||
r.body.set_int64 ("flags", 0);
|
||||
var response = yield pairing_device.request (r.message, cancellable);
|
||||
|
||||
var reader = new XpcObjectReader (response);
|
||||
reader.read_member ("response");
|
||||
|
||||
_assertion_identifier = (Darwin.Xpc.Uuid) reader
|
||||
.read_member ("assertionIdentifier")
|
||||
.get_object_value (Darwin.Xpc.Uuid.TYPE);
|
||||
reader.end_member ();
|
||||
|
||||
string tunnel_ip_address = reader
|
||||
.read_member ("info")
|
||||
.read_member ("tunnelIPAddress")
|
||||
.get_string_value ();
|
||||
tunnel_device_address = new InetAddress.from_string (tunnel_ip_address);
|
||||
|
||||
_discovery = yield locate_discovery_service (tunnel_device_address, cancellable);
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
SocketConnection connection;
|
||||
try {
|
||||
var service_address = new InetSocketAddress (tunnel_device_address, port);
|
||||
|
||||
var client = new SocketClient ();
|
||||
connection = yield client.connect_async (service_address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private static async DiscoveryService locate_discovery_service (InetAddress tunnel_device_address, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var path_buf = new char[4096];
|
||||
unowned string path = (string) path_buf;
|
||||
|
||||
uint delays[] = { 0, 50, 250 };
|
||||
for (uint attempts = 0; attempts != delays.length; attempts++) {
|
||||
uint delay = delays[attempts];
|
||||
if (delay != 0) {
|
||||
var timeout_source = new TimeoutSource (delay);
|
||||
timeout_source.set_callback (locate_discovery_service.callback);
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (locate_discovery_service.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
timeout_source.destroy ();
|
||||
|
||||
if (cancellable.is_cancelled ())
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var item in XNU.query_active_tcp_connections ()) {
|
||||
if (item.family != IPV6)
|
||||
continue;
|
||||
if (!item.foreign_address.equal (tunnel_device_address))
|
||||
continue;
|
||||
if (Darwin.XNU.proc_pidpath (item.effective_pid, path_buf) <= 0)
|
||||
continue;
|
||||
if (path != "/usr/libexec/remoted")
|
||||
continue;
|
||||
|
||||
try {
|
||||
var connectable = new InetSocketAddress (tunnel_device_address, item.foreign_port);
|
||||
|
||||
var sc = new SocketClient ();
|
||||
SocketConnection connection = yield sc.connect_async (connectable, cancellable);
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return yield DiscoveryService.open (connection, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to detect RSD port");
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PairingdRequest {
|
||||
public Darwin.Xpc.Dictionary message = new Darwin.Xpc.Dictionary ();
|
||||
public Darwin.Xpc.Dictionary body = new Darwin.Xpc.Dictionary ();
|
||||
|
||||
public PairingdRequest (string name) {
|
||||
message.set_string ("mangledTypeName", name);
|
||||
message.set_value ("value", body);
|
||||
}
|
||||
}
|
||||
|
||||
namespace XNU {
|
||||
public PcbList query_active_tcp_connections () {
|
||||
size_t size = 0;
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", null, &size);
|
||||
|
||||
var pcbs = new uint8[size];
|
||||
Darwin.XNU.sysctlbyname ("net.inet.tcp.pcblist_n", pcbs, &size);
|
||||
|
||||
return new PcbList (pcbs);
|
||||
}
|
||||
|
||||
public class PcbList {
|
||||
private uint8[] pcbs;
|
||||
|
||||
internal PcbList (owned uint8[] pcbs) {
|
||||
this.pcbs = (owned) pcbs;
|
||||
}
|
||||
|
||||
public Iterator iterator () {
|
||||
return new Iterator (this);
|
||||
}
|
||||
|
||||
public class Iterator {
|
||||
private PcbList list;
|
||||
private InetItem * cursor;
|
||||
|
||||
internal Iterator (PcbList list) {
|
||||
this.list = list;
|
||||
|
||||
var gen = (Darwin.XNU.InetPcbGeneration *) list.pcbs;
|
||||
cursor = (InetItem *) ((uint8 *) list.pcbs + gen->length);
|
||||
}
|
||||
|
||||
public Item? next_value () {
|
||||
InetPcb * pcb = null;
|
||||
while (true) {
|
||||
if (cursor->length == 24)
|
||||
return null;
|
||||
|
||||
switch (cursor->kind) {
|
||||
case InetItemKind.PCB:
|
||||
pcb = (InetPcb *) cursor;
|
||||
break;
|
||||
case InetItemKind.SOCKET:
|
||||
var item = new Item (*pcb, *((InetSocket *) cursor));
|
||||
advance ();
|
||||
return item;
|
||||
}
|
||||
|
||||
advance ();
|
||||
}
|
||||
}
|
||||
|
||||
private void advance () {
|
||||
uint32 l = cursor->length;
|
||||
if (l % 8 != 0)
|
||||
l += 8 - (l % 8);
|
||||
cursor = (InetItem *) ((uint8 *) cursor + l);
|
||||
}
|
||||
}
|
||||
|
||||
public class Item {
|
||||
public SocketFamily family {
|
||||
get {
|
||||
return ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
? SocketFamily.IPV6
|
||||
: SocketFamily.IPV4;
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress local_address {
|
||||
get {
|
||||
if (cached_local_address == null)
|
||||
cached_local_address = parse_address (pcb.local_address);
|
||||
return cached_local_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 local_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.local_port);
|
||||
}
|
||||
}
|
||||
|
||||
public InetAddress foreign_address {
|
||||
get {
|
||||
if (cached_foreign_address == null)
|
||||
cached_foreign_address = parse_address (pcb.foreign_address);
|
||||
return cached_foreign_address;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 foreign_port {
|
||||
get {
|
||||
return uint16.from_big_endian (pcb.foreign_port);
|
||||
}
|
||||
}
|
||||
|
||||
public int32 effective_pid {
|
||||
get {
|
||||
return sock.effective_pid;
|
||||
}
|
||||
}
|
||||
|
||||
private InetPcb pcb;
|
||||
private InetSocket sock;
|
||||
private InetAddress? cached_local_address;
|
||||
private InetAddress? cached_foreign_address;
|
||||
|
||||
public Item (InetPcb pcb, InetSocket sock) {
|
||||
this.pcb = pcb;
|
||||
this.sock = sock;
|
||||
}
|
||||
|
||||
private InetAddress parse_address (uint8[] bytes) {
|
||||
if ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
return new InetAddress.from_bytes (bytes, IPV6);
|
||||
var addr = (Darwin.XNU.InetAddr4in6 *) bytes;
|
||||
return new InetAddress.from_bytes ((uint8[]) &addr->addr4.s_addr, IPV4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetItem {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
}
|
||||
|
||||
public enum InetItemKind {
|
||||
SOCKET = 0x001,
|
||||
PCB = 0x010,
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetPcb {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 inpp;
|
||||
public uint16 foreign_port;
|
||||
public uint16 local_port;
|
||||
public uint32 per_protocol_pcb_low;
|
||||
public uint32 per_protocol_pcb_high;
|
||||
public uint32 generation_count_low;
|
||||
public uint32 generation_count_high;
|
||||
public int flags;
|
||||
public uint32 flow;
|
||||
public uint8 version_flag;
|
||||
public uint8 ip_ttl;
|
||||
public uint8 ip_protocol;
|
||||
public uint8 padding;
|
||||
public uint8 foreign_address[16];
|
||||
public uint8 local_address[16];
|
||||
public InetDepend4 depend4;
|
||||
public InetDepend6 depend6;
|
||||
public uint32 flowhash;
|
||||
public uint32 flags2;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetSocket {
|
||||
public uint32 length;
|
||||
public uint32 kind;
|
||||
|
||||
public uint64 so;
|
||||
public int16 type;
|
||||
public uint16 options_low;
|
||||
public uint16 options_high;
|
||||
public int16 linger;
|
||||
public int16 state;
|
||||
public uint16 pcb[4];
|
||||
public uint16 protocol_low;
|
||||
public uint16 protocol_high;
|
||||
public uint16 family_low;
|
||||
public uint16 family_high;
|
||||
public int16 qlen;
|
||||
public int16 incqlen;
|
||||
public int16 qlimit;
|
||||
public int16 timeo;
|
||||
public uint16 error;
|
||||
public int32 pgid;
|
||||
public uint32 oobmark;
|
||||
public uint32 uid;
|
||||
public int32 last_pid;
|
||||
public int32 effective_pid;
|
||||
public uint64 gencnt;
|
||||
public uint32 flags;
|
||||
public uint32 flags1;
|
||||
public int32 usecount;
|
||||
public int32 retaincnt;
|
||||
public uint32 filter_flags;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend4 {
|
||||
public uint8 ip_tos;
|
||||
}
|
||||
|
||||
[SimpleType]
|
||||
public struct InetDepend6 {
|
||||
public uint8 hlim;
|
||||
public int checksum;
|
||||
public uint16 interface_index;
|
||||
public int16 hops;
|
||||
}
|
||||
}
|
||||
}
|
||||
+82
-631
@@ -205,10 +205,12 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
var transport = transports.first_match (t => t is TunnelFinder);
|
||||
if (transport == null)
|
||||
return null;
|
||||
return yield ((TunnelFinder) transport).find_tunnel (cancellable);
|
||||
foreach (var transport in transports) {
|
||||
Tunnel? tunnel = yield transport.find_tunnel (cancellable);
|
||||
if (tunnel != null)
|
||||
return tunnel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public async LockdownClient get_lockdown_client (Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -431,14 +433,8 @@ namespace Frida.Fruity {
|
||||
public abstract UsbmuxDevice? usbmux_device {
|
||||
get;
|
||||
}
|
||||
}
|
||||
|
||||
private interface Backend : Object {
|
||||
public signal void transport_attached (Transport transport);
|
||||
public signal void transport_detached (Transport transport);
|
||||
|
||||
public abstract async void start (Cancellable? cancellable) throws IOError;
|
||||
public abstract async void stop (Cancellable? cancellable) throws IOError;
|
||||
public abstract async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
public enum ConnectionType {
|
||||
@@ -446,6 +442,23 @@ namespace Frida.Fruity {
|
||||
NETWORK
|
||||
}
|
||||
|
||||
public interface Tunnel : Object {
|
||||
public abstract DiscoveryService discovery {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async void close (Cancellable? cancellable) throws IOError;
|
||||
public abstract async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
public interface Backend : Object {
|
||||
public signal void transport_attached (Transport transport);
|
||||
public signal void transport_detached (Transport transport);
|
||||
|
||||
public abstract async void start (Cancellable? cancellable) throws IOError;
|
||||
public abstract async void stop (Cancellable? cancellable) throws IOError;
|
||||
}
|
||||
|
||||
private sealed class UsbmuxBackend : Object, Backend {
|
||||
public bool available {
|
||||
get {
|
||||
@@ -681,6 +694,10 @@ namespace Frida.Fruity {
|
||||
public UsbmuxTransport (UsbmuxDevice device) {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PortableCoreDeviceBackend : Object, Backend {
|
||||
@@ -745,7 +762,8 @@ namespace Frida.Fruity {
|
||||
|
||||
io_cancellable.cancel ();
|
||||
|
||||
usb_context.interrupt_event_handler ();
|
||||
if (usb_context != null)
|
||||
usb_context.interrupt_event_handler ();
|
||||
|
||||
usb_worker.join ();
|
||||
usb_worker = null;
|
||||
@@ -787,7 +805,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private void perform_usb_work () {
|
||||
LibUSB.Context.init (out usb_context);
|
||||
if (LibUSB.Context.init (out usb_context) != SUCCESS)
|
||||
return;
|
||||
|
||||
AtomicUint.inc (ref pending_device_arrivals);
|
||||
|
||||
@@ -953,7 +972,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PortableCoreDeviceTransport : Object, Transport, TunnelFinder {
|
||||
private sealed class PortableCoreDeviceTransport : Object, Transport {
|
||||
public UsbDevice usb_device {
|
||||
get;
|
||||
construct;
|
||||
@@ -1315,9 +1334,7 @@ namespace Frida.Fruity {
|
||||
|
||||
public interface FruitFinder : Object {
|
||||
public static FruitFinder make_default () {
|
||||
#if MACOS
|
||||
return new MacOSFruitFinder ();
|
||||
#elif LINUX && !ANDROID
|
||||
#if LINUX && !ANDROID
|
||||
return new LinuxFruitFinder ();
|
||||
#else
|
||||
return new NullFruitFinder ();
|
||||
@@ -1333,619 +1350,53 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
#if MACOS
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MacOSCoreDeviceTransport : Object, Transport, TunnelFinder {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private 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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
internal const string PAIRING_REGTYPE = "_remoted._tcp";
|
||||
internal const string PAIRING_DOMAIN = "local.";
|
||||
|
||||
public interface PairingBrowser : Object {
|
||||
public static PairingBrowser make_default () {
|
||||
#if LINUX && !ANDROID
|
||||
return new LinuxPairingBrowser ();
|
||||
#else
|
||||
return new NullPairingBrowser ();
|
||||
#endif
|
||||
}
|
||||
|
||||
public signal void services_discovered (PairingServiceDetails[] services);
|
||||
}
|
||||
|
||||
public class NullPairingBrowser : Object, PairingBrowser {
|
||||
}
|
||||
|
||||
public interface PairingServiceDetails : Object {
|
||||
public abstract string name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract uint interface_index {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract string interface_name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async Gee.List<PairingServiceHost> resolve (Cancellable? cancellable = null) throws Error, IOError;
|
||||
}
|
||||
|
||||
public interface PairingServiceHost : Object {
|
||||
public abstract string name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract uint16 port {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract Gee.List<string> txt_record {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async Gee.List<InetSocketAddress> resolve (Cancellable? cancellable = null) throws Error, IOError;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,81 +3,6 @@ namespace Frida.Fruity {
|
||||
using OpenSSL;
|
||||
using OpenSSL.Envelope;
|
||||
|
||||
private const string PAIRING_REGTYPE = "_remoted._tcp";
|
||||
private const string PAIRING_DOMAIN = "local.";
|
||||
|
||||
public interface TunnelFinder : Object {
|
||||
public static TunnelFinder make_default () {
|
||||
return new NullTunnelFinder ();
|
||||
}
|
||||
|
||||
public abstract async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
public class NullTunnelFinder : Object, TunnelFinder {
|
||||
public async Tunnel? find_tunnel (Cancellable? cancellable) throws Error, IOError {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Tunnel : Object {
|
||||
public abstract DiscoveryService discovery {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async void close (Cancellable? cancellable) throws IOError;
|
||||
public abstract async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
public interface PairingBrowser : Object {
|
||||
public static PairingBrowser make_default () {
|
||||
#if DARWIN
|
||||
return new DarwinPairingBrowser ();
|
||||
#elif LINUX && !ANDROID
|
||||
return new LinuxPairingBrowser ();
|
||||
#else
|
||||
return new NullPairingBrowser ();
|
||||
#endif
|
||||
}
|
||||
|
||||
public signal void services_discovered (PairingServiceDetails[] services);
|
||||
}
|
||||
|
||||
public class NullPairingBrowser : Object, PairingBrowser {
|
||||
}
|
||||
|
||||
public interface PairingServiceDetails : Object {
|
||||
public abstract string name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract uint interface_index {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract string interface_name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async Gee.List<PairingServiceHost> resolve (Cancellable? cancellable = null) throws Error, IOError;
|
||||
}
|
||||
|
||||
public interface PairingServiceHost : Object {
|
||||
public abstract string name {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract uint16 port {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract Gee.List<string> txt_record {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async Gee.List<InetSocketAddress> resolve (Cancellable? cancellable = null) throws Error, IOError;
|
||||
}
|
||||
|
||||
public class DiscoveryService : Object, AsyncInitable {
|
||||
public IOStream stream {
|
||||
get;
|
||||
|
||||
+6
-12
@@ -498,6 +498,12 @@ if have_fruity_backend
|
||||
'fruity' / 'ncm.vala',
|
||||
]
|
||||
|
||||
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 == 'windows'
|
||||
backend_sources += 'fruity' / 'device-monitor-windows.c'
|
||||
elif host_os_family == 'darwin'
|
||||
@@ -506,15 +512,6 @@ if have_fruity_backend
|
||||
backend_sources += 'fruity' / 'device-monitor-unix.c'
|
||||
endif
|
||||
|
||||
if host_os_family == 'darwin'
|
||||
backend_sources += 'fruity' / 'xpc-darwin.vala'
|
||||
endif
|
||||
if host_os == 'macos'
|
||||
backend_sources += 'fruity' / 'iokit.vala'
|
||||
elif host_os == 'linux'
|
||||
backend_sources += 'fruity' / 'xpc-linux.vala'
|
||||
endif
|
||||
|
||||
backend_deps += openssl_dep
|
||||
backend_vala_args += [
|
||||
'--pkg=libnghttp2',
|
||||
@@ -527,11 +524,8 @@ if have_fruity_backend
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 16.4.0
|
||||
revision = 16.4.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
[CCode (cheader_filename = "dns_sd.h", gir_namespace = "Darwin", gir_version = "1.0")]
|
||||
namespace Darwin.DNSSD {
|
||||
[CCode (cname = "DNSServiceRef", ref_function = "", unref_function = "")]
|
||||
public class DNSService {
|
||||
[CCode (cname = "DNSServiceCreateConnection")]
|
||||
public static ErrorType create_connection (out DNSService service);
|
||||
|
||||
[CCode (cname = "DNSServiceRefDeallocate")]
|
||||
public void deallocate ();
|
||||
|
||||
[CCode (cname = "DNSServiceSetDispatchQueue")]
|
||||
public ErrorType set_dispatch_queue (Darwin.GCD.DispatchQueue queue);
|
||||
|
||||
[CCode (cname = "DNSServiceBrowse")]
|
||||
public static ErrorType browse (ref DNSService sd_ref, Flags flags, uint32 interface_index, string regtype, string? domain,
|
||||
BrowseReply callback);
|
||||
|
||||
[CCode (cname = "DNSServiceResolve")]
|
||||
public static ErrorType resolve (ref DNSService sd_ref, Flags flags, uint32 interface_index, string name, string regtype,
|
||||
string domain, ResolveReply callback);
|
||||
|
||||
[CCode (cname = "DNSServiceGetAddrInfo")]
|
||||
public static ErrorType get_addr_info (ref DNSService sd_ref, Flags flags, uint32 interface_index, Protocol protocol,
|
||||
string hostname, GetAddrInfoReply callback);
|
||||
|
||||
[CCode (cname = "DNSServiceBrowseReply")]
|
||||
public delegate void BrowseReply (DNSService sd_ref, Flags flags, uint32 interface_index, ErrorType error_code,
|
||||
string service_name, string regtype, string reply_domain);
|
||||
|
||||
[CCode (cname = "DNSServiceResolveReply")]
|
||||
public delegate void ResolveReply (DNSService sd_ref, Flags flags, uint32 interface_index, ErrorType error_code,
|
||||
string fullname, string hosttarget, uint16 port, [CCode (array_length_pos = 7.9, array_length_type = "uint16_t")] uint8[] txt_record);
|
||||
|
||||
[CCode (cname = "DNSServiceGetAddrInfoReply")]
|
||||
public delegate void GetAddrInfoReply (DNSService sd_ref, Flags flags, uint32 interface_index, ErrorType error_code,
|
||||
string hostname, void * address, uint32 ttl);
|
||||
|
||||
[CCode (cname = "DNSServiceErrorType", cprefix = "kDNSServiceErr_", has_type_id = false)]
|
||||
public enum ErrorType {
|
||||
NoError,
|
||||
Unknown,
|
||||
NoSuchName,
|
||||
NoMemory,
|
||||
BadParam,
|
||||
BadReference,
|
||||
BadState,
|
||||
BadFlags,
|
||||
Unsupported,
|
||||
NotInitialized,
|
||||
AlreadyRegistered,
|
||||
NameConflict,
|
||||
Invalid,
|
||||
Firewall,
|
||||
Incompatible,
|
||||
BadInterfaceIndex,
|
||||
Refused,
|
||||
NoSuchRecord,
|
||||
NoAuth,
|
||||
NoSuchKey,
|
||||
NATTraversal,
|
||||
DoubleNAT,
|
||||
BadTime,
|
||||
BadSig,
|
||||
BadKey,
|
||||
Transient,
|
||||
ServiceNotRunning,
|
||||
NATPortMappingUnsupported,
|
||||
NATPortMappingDisabled,
|
||||
NoRouter,
|
||||
PollingMode,
|
||||
Timeout,
|
||||
DefunctConnection,
|
||||
PolicyDenied,
|
||||
NotPermitted,
|
||||
}
|
||||
|
||||
[CCode (cname = "DNSServiceFlags", cprefix = "kDNSServiceFlags", has_type_id = false)]
|
||||
[Flags]
|
||||
public enum Flags {
|
||||
MoreComing,
|
||||
QueueRequest,
|
||||
AutoTrigger,
|
||||
Add,
|
||||
Default,
|
||||
NoAutoRename,
|
||||
Shared,
|
||||
Unique,
|
||||
BrowseDomains,
|
||||
RegistrationDomains,
|
||||
LongLivedQuery,
|
||||
AllowRemoteQuery,
|
||||
ForceMulticast,
|
||||
Force,
|
||||
KnownUnique,
|
||||
ReturnIntermediates,
|
||||
ShareConnection,
|
||||
SuppressUnusable,
|
||||
Timeout,
|
||||
IncludeP2P,
|
||||
WakeOnResolve,
|
||||
BackgroundTrafficClass,
|
||||
IncludeAWDL,
|
||||
EnableDNSSEC,
|
||||
Validate,
|
||||
Secure,
|
||||
Insecure,
|
||||
Bogus,
|
||||
Indeterminate,
|
||||
UnicastResponse,
|
||||
ValidateOptional,
|
||||
WakeOnlyService,
|
||||
ThresholdOne,
|
||||
ThresholdFinder,
|
||||
ThresholdReached,
|
||||
PrivateOne,
|
||||
PrivateTwo,
|
||||
PrivateThree,
|
||||
PrivateFour,
|
||||
PrivateFive,
|
||||
[CCode (cname = "kDNSServiceFlagAnsweredFromCache")]
|
||||
AnsweredFromCache,
|
||||
AllowExpiredAnswers,
|
||||
ExpiredAnswer,
|
||||
}
|
||||
|
||||
[CCode (cname = "DNSServiceProtocol", cprefix = "kDNSServiceProtocol_", has_type_id = false)]
|
||||
public enum Protocol {
|
||||
IPv4 = 0x01,
|
||||
IPv6 = 0x02,
|
||||
UDP = 0x10,
|
||||
TCP = 0x20,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
[CCode (cheader_filename = "IOKit/IOKitLib.h", gir_namespace = "Darwin", gir_version = "1.0")]
|
||||
namespace Darwin.IOKit {
|
||||
using CoreFoundation;
|
||||
using Darwin.XNU;
|
||||
|
||||
[CCode (cname = "IOMasterPort")]
|
||||
public KernReturn main_port (MachPort bootstrap_port, out MachPort main_port);
|
||||
|
||||
[CCode (cname = "IOServiceMatching")]
|
||||
public MutableDictionary service_matching (string name);
|
||||
|
||||
[CCode (cname = "IOServiceGetMatchingServices")]
|
||||
public KernReturn get_matching_services (MachPort main_port, owned MutableDictionary matching_dict,
|
||||
out IOIterator iterator);
|
||||
|
||||
[CCode (cname = "kIOEthernetInterfaceClass", cheader_filename = "IOKit/network/IOEthernetInterface.h")]
|
||||
public const string ETHERNET_INTERFACE_CLASS;
|
||||
|
||||
[CCode (cname = "kIOBSDNameKey", cheader_filename = "IOKit/IOBSD.h")]
|
||||
public const string BSD_NAME_KEY;
|
||||
|
||||
[CCode (cname = "kIOServicePlane", cheader_filename = "IOKit/IOKitKeys.h")]
|
||||
public const string IOSERVICE_PLANE;
|
||||
|
||||
[CCode (cname = "io_registry_entry_t", destroy_function = "IOObjectRelease", has_type_id = false)]
|
||||
public struct IORegistryEntry : IOObject {
|
||||
[CCode (cname = "IORegistryEntryCreateCFProperties")]
|
||||
public KernReturn create_cf_properties (out MutableDictionary properties, Allocator? allocator, uint options);
|
||||
|
||||
[CCode (cname = "IORegistryEntryCreateCFProperty")]
|
||||
public CoreFoundation.Type create_cf_property (String key, Allocator? allocator, uint options);
|
||||
|
||||
[CCode (cname = "IORegistryEntryGetParentEntry")]
|
||||
public KernReturn get_parent_entry (string plane, out IOObject parent);
|
||||
}
|
||||
|
||||
[CCode (cname = "io_iterator_t", destroy_function = "IOObjectRelease")]
|
||||
public struct IOIterator : MachPort {
|
||||
[CCode (cname = "IOIteratorReset")]
|
||||
public void reset ();
|
||||
|
||||
[CCode (cname = "IOIteratorIsValid")]
|
||||
public bool is_valid ();
|
||||
|
||||
[CCode (cname = "IOIteratorNext")]
|
||||
public IOObject next ();
|
||||
}
|
||||
|
||||
[CCode (cname = "io_service_t", destroy_function = "IOObjectRelease", has_type_id = false)]
|
||||
public struct IOService : IOObject {
|
||||
}
|
||||
|
||||
[CCode (cname = "io_object_t", destroy_function = "IOObjectRelease", has_type_id = false)]
|
||||
public struct IOObject : MachPort {
|
||||
[CCode (cname = "IO_OBJECT_NULL")]
|
||||
public const IOObject NULL;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
[CCode (cheader_filename = "net/if.h", lower_case_cprefix = "", gir_namespace = "Darwin", gir_version = "1.0")]
|
||||
namespace Darwin.Net {
|
||||
public const int IFNAMSIZ;
|
||||
|
||||
public unowned string? if_indextoname (uint ifindex, [CCode (array_length = false)] char[] ifname);
|
||||
}
|
||||
Reference in new issue
Block a user