mirror of
https://github.com/frida/frida-core.git
synced 2026-11-10 10:01:35 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e75fc9b84 | ||
|
|
1b6ca8fe46 | ||
|
|
9e35e245b1 | ||
|
|
4a8c0bb13b | ||
|
|
7c39f1783a | ||
|
|
194f025a98 | ||
|
|
e6b2a6d6dd | ||
|
|
e66b93b647 | ||
|
|
4a887eaed5 | ||
|
|
180075a7a0 | ||
|
|
20ebf7fa15 | ||
|
|
1018aca28d | ||
|
|
93870b9f66 |
No files matched your search
@@ -0,0 +1,240 @@
|
||||
namespace Frida {
|
||||
public class ChannelRegistry : Object {
|
||||
public signal void channel_closed (ChannelId id);
|
||||
|
||||
private Gee.Map<ChannelId?, weak IOStream> channels =
|
||||
new Gee.HashMap<ChannelId?, weak IOStream> (ChannelId.hash, ChannelId.equal);
|
||||
|
||||
~ChannelRegistry () {
|
||||
clear ();
|
||||
}
|
||||
|
||||
public void clear () {
|
||||
foreach (IOStream stream in channels.values.to_array ()) {
|
||||
var channel_stream = stream as ChannelStream;
|
||||
if (channel_stream != null)
|
||||
channel_stream.abandon ();
|
||||
}
|
||||
channels.clear ();
|
||||
}
|
||||
|
||||
public void register (ChannelId id, IOStream stream) {
|
||||
stream.set_data ("channel-registry-entry", new Entry (this, id));
|
||||
lock (channels)
|
||||
channels[id] = stream;
|
||||
}
|
||||
|
||||
public IOStream link (ChannelId id) throws Error {
|
||||
IOStream? stream;
|
||||
lock (channels)
|
||||
stream = channels[id];
|
||||
if (stream == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid channel ID");
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void unlink (ChannelId id) {
|
||||
lock (channels) {
|
||||
if (!channels.unset (id))
|
||||
return;
|
||||
}
|
||||
|
||||
channel_closed (id);
|
||||
}
|
||||
|
||||
private class Entry {
|
||||
public ChannelRegistry parent;
|
||||
public ChannelId id;
|
||||
|
||||
public Entry (ChannelRegistry parent, ChannelId id) {
|
||||
this.parent = parent;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
~Entry () {
|
||||
parent.unlink (id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ChannelEndpoint : Object, Channel {
|
||||
private IOStream? stream;
|
||||
|
||||
private ByteArray write_queue = new ByteArray ();
|
||||
private bool writing = false;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public ChannelEndpoint (IOStream stream) {
|
||||
this.stream = stream;
|
||||
|
||||
read_loop.begin ();
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws Error, IOError {
|
||||
io_cancellable.cancel ();
|
||||
}
|
||||
|
||||
public async void input (uint8[] data, Cancellable? cancellable) throws Error, IOError {
|
||||
if (io_cancellable.is_cancelled ())
|
||||
throw new Error.INVALID_OPERATION ("Channel is closed");
|
||||
|
||||
write_queue.append (data);
|
||||
|
||||
if (!writing) {
|
||||
writing = true;
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
process_write_queue.begin ();
|
||||
return false;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
}
|
||||
|
||||
private async void read_loop () {
|
||||
var input = stream.get_input_stream ();
|
||||
var buffer = new uint8[64 * 1024];
|
||||
|
||||
while (true) {
|
||||
ssize_t n;
|
||||
try {
|
||||
n = yield input.read_async (buffer, Priority.DEFAULT, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
break;
|
||||
}
|
||||
|
||||
output (buffer[:n]);
|
||||
}
|
||||
|
||||
stream.close_async.begin ();
|
||||
stream = null;
|
||||
}
|
||||
|
||||
private async void process_write_queue () {
|
||||
if (stream != null) {
|
||||
var output = stream.get_output_stream ();
|
||||
|
||||
while (write_queue.len > 0) {
|
||||
uint8[] batch = write_queue.steal ();
|
||||
|
||||
size_t bytes_written;
|
||||
try {
|
||||
yield output.write_all_async (batch, Priority.DEFAULT, io_cancellable, out bytes_written);
|
||||
} catch (GLib.Error e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public class ChannelStream : VirtualStream {
|
||||
public Channel channel {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private ByteArray recv_queue = new ByteArray ();
|
||||
private ByteArray send_queue = new ByteArray ();
|
||||
|
||||
public ChannelStream (Channel channel) {
|
||||
Object (channel: channel);
|
||||
}
|
||||
|
||||
construct {
|
||||
channel.output.connect (on_output);
|
||||
}
|
||||
|
||||
public void abandon () {
|
||||
with_state_lock (() => {
|
||||
state = CLOSED;
|
||||
update_pending_io ();
|
||||
});
|
||||
}
|
||||
|
||||
protected override IOCondition query_events () {
|
||||
IOCondition new_events = 0;
|
||||
|
||||
if (recv_queue.len != 0 || state == CLOSED)
|
||||
new_events |= IN;
|
||||
|
||||
if (state == OPEN)
|
||||
new_events |= OUT;
|
||||
|
||||
return new_events;
|
||||
}
|
||||
|
||||
protected override void handle_close () {
|
||||
channel.close.begin (null);
|
||||
}
|
||||
|
||||
public override ssize_t read (uint8[] buffer) throws IOError {
|
||||
ssize_t n = 0;
|
||||
with_state_lock (() => {
|
||||
n = ssize_t.min (recv_queue.len, buffer.length);
|
||||
if (n > 0) {
|
||||
Memory.copy (buffer, recv_queue.data, n);
|
||||
recv_queue.remove_range (0, (uint) n);
|
||||
|
||||
update_pending_io ();
|
||||
} else {
|
||||
if (state == OPEN)
|
||||
n = -1;
|
||||
}
|
||||
});
|
||||
|
||||
if (n == -1)
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public override ssize_t write (uint8[] buffer) throws IOError {
|
||||
with_state_lock (() => {
|
||||
send_queue.append (buffer);
|
||||
});
|
||||
|
||||
if (main_context.is_owner ()) {
|
||||
process_send_queue ();
|
||||
} else {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
process_send_queue ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
}
|
||||
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
private void process_send_queue () {
|
||||
uint8[]? chunk = null;
|
||||
with_state_lock (() => {
|
||||
size_t n = send_queue.len;
|
||||
if (n == 0)
|
||||
return;
|
||||
chunk = send_queue.data[0:n];
|
||||
send_queue.remove_range (0, (uint) n);
|
||||
});
|
||||
if (chunk == null)
|
||||
return;
|
||||
|
||||
channel.input.begin (chunk, io_cancellable);
|
||||
}
|
||||
|
||||
private void on_output (uint8[] data) {
|
||||
with_state_lock (() => {
|
||||
if (data.length != 0)
|
||||
recv_queue.append (data);
|
||||
else
|
||||
state = CLOSED;
|
||||
update_pending_io ();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
base_sources = [
|
||||
'session.vala',
|
||||
'session-glue.c',
|
||||
'channel.vala',
|
||||
'service.vala',
|
||||
'stream.vala',
|
||||
'rpc.vala',
|
||||
'p2p.vala',
|
||||
'p2p-glue.c',
|
||||
|
||||
+34
-232
@@ -452,6 +452,8 @@ namespace Frida {
|
||||
if ((_pending_io & c) != 0)
|
||||
source.set_ready_time (0);
|
||||
}
|
||||
|
||||
notify_property ("pending-io");
|
||||
}
|
||||
|
||||
private void prepare_for_io (int64 timeout, Cancellable? cancellable, out int64 deadline) throws IOError {
|
||||
@@ -563,7 +565,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class SctpConnection : IOStream {
|
||||
public class SctpConnection : VirtualStream {
|
||||
public DatagramBased transport_socket {
|
||||
get;
|
||||
construct;
|
||||
@@ -584,55 +586,14 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public State state {
|
||||
get {
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
public override InputStream input_stream {
|
||||
get {
|
||||
return _input_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override OutputStream output_stream {
|
||||
get {
|
||||
return _output_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public IOCondition pending_io {
|
||||
get {
|
||||
lock (state)
|
||||
return sctp_events;
|
||||
}
|
||||
}
|
||||
|
||||
private State _state = CREATED;
|
||||
private SctpInputStream _input_stream;
|
||||
private SctpOutputStream _output_stream;
|
||||
|
||||
private DatagramBasedSource transport_source;
|
||||
private uint8[] transport_buffer = new uint8[65536];
|
||||
|
||||
private void * sctp_socket;
|
||||
private IOCondition sctp_events = 0;
|
||||
private SctpTimerSource sctp_source;
|
||||
private uint16 stream_id;
|
||||
private ByteArray dcep_message = new ByteArray ();
|
||||
|
||||
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public enum State {
|
||||
CREATED,
|
||||
OPENING,
|
||||
OPENED,
|
||||
CLOSED
|
||||
}
|
||||
|
||||
public SctpConnection (DatagramBased transport_socket, PeerSetup setup, uint16 port, size_t max_message_size) {
|
||||
Object (
|
||||
transport_socket: transport_socket,
|
||||
@@ -649,14 +610,9 @@ namespace Frida {
|
||||
protected extern static void _initialize_sctp_backend ();
|
||||
|
||||
construct {
|
||||
_input_stream = new SctpInputStream (this);
|
||||
_output_stream = new SctpOutputStream (this);
|
||||
|
||||
sctp_socket = _create_sctp_socket ();
|
||||
_connect_sctp_socket (sctp_socket, port);
|
||||
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
transport_source = transport_socket.create_source (IOCondition.IN, io_cancellable);
|
||||
transport_source.set_callback (on_transport_socket_readable);
|
||||
transport_source.attach (main_context);
|
||||
@@ -669,17 +625,22 @@ namespace Frida {
|
||||
|
||||
protected extern void _connect_sctp_socket (void * sock, uint16 port);
|
||||
|
||||
public override bool close (GLib.Cancellable? cancellable) throws IOError {
|
||||
do_close ();
|
||||
return true;
|
||||
protected override VirtualStream.State query_initial_state () {
|
||||
return CREATED;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, GLib.Cancellable? cancellable) throws IOError {
|
||||
do_close ();
|
||||
return true;
|
||||
protected override IOCondition query_events () {
|
||||
return _query_sctp_socket_events (sctp_socket);
|
||||
}
|
||||
|
||||
private void do_close () {
|
||||
protected override void update_pending_io () {
|
||||
base.update_pending_io ();
|
||||
sctp_source.invalidate ();
|
||||
}
|
||||
|
||||
protected extern static IOCondition _query_sctp_socket_events (void * sock);
|
||||
|
||||
protected override void handle_close () {
|
||||
sctp_source.destroy ();
|
||||
transport_source.destroy ();
|
||||
|
||||
@@ -689,13 +650,17 @@ namespace Frida {
|
||||
|
||||
public extern static void _close (void * sock);
|
||||
|
||||
public void shutdown (SctpShutdownType type) throws IOError {
|
||||
_shutdown (sctp_socket, type);
|
||||
public override void shutdown_read () throws IOError {
|
||||
_shutdown (sctp_socket, READ);
|
||||
}
|
||||
|
||||
public override void shutdown_write () throws IOError {
|
||||
_shutdown (sctp_socket, WRITE);
|
||||
}
|
||||
|
||||
public extern static void _shutdown (void * sock, SctpShutdownType type) throws IOError;
|
||||
|
||||
public ssize_t recv (uint8[] buffer) throws IOError {
|
||||
public override ssize_t read (uint8[] buffer) throws IOError {
|
||||
ssize_t n = -1;
|
||||
|
||||
try {
|
||||
@@ -711,18 +676,18 @@ namespace Frida {
|
||||
handle_dcep_message (stream_id, dcep_message.steal ());
|
||||
}
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
} else if (protocol_id == NONE || _state != OPENED) {
|
||||
} else if (protocol_id == NONE || state != OPEN) {
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
}
|
||||
} finally {
|
||||
update_sctp_events ();
|
||||
update_pending_io ();
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public ssize_t send (uint8[] buffer) throws IOError {
|
||||
if (_state != OPENED)
|
||||
public override ssize_t write (uint8[] buffer) throws IOError {
|
||||
if (state != OPEN)
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
|
||||
ssize_t n = ssize_t.min (buffer.length, (ssize_t) max_message_size);
|
||||
@@ -730,20 +695,10 @@ namespace Frida {
|
||||
try {
|
||||
return _send (sctp_socket, stream_id, WEBRTC_BINARY, buffer[0:n]);
|
||||
} finally {
|
||||
update_sctp_events ();
|
||||
update_pending_io ();
|
||||
}
|
||||
}
|
||||
|
||||
public void register_source (Source source, IOCondition condition) {
|
||||
lock (state)
|
||||
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
|
||||
}
|
||||
|
||||
public void unregister_source (Source source) {
|
||||
lock (state)
|
||||
sources.unset (source);
|
||||
}
|
||||
|
||||
private bool on_transport_socket_readable (DatagramBased datagram_based, IOCondition condition) {
|
||||
var v = InputVector ();
|
||||
v.buffer = transport_buffer;
|
||||
@@ -783,9 +738,9 @@ namespace Frida {
|
||||
}
|
||||
|
||||
protected void _on_sctp_socket_events_changed () {
|
||||
update_sctp_events ();
|
||||
update_pending_io ();
|
||||
|
||||
if (_state == CREATED && setup == ACTIVE && (sctp_events & IOCondition.OUT) != 0) {
|
||||
if (state == CREATED && setup == ACTIVE && (pending_io & IOCondition.OUT) != 0) {
|
||||
stream_id = 1;
|
||||
|
||||
uint8[] open_message = {
|
||||
@@ -799,31 +754,12 @@ namespace Frida {
|
||||
};
|
||||
try {
|
||||
_send (sctp_socket, stream_id, WEBRTC_DCEP, open_message);
|
||||
_state = OPENING;
|
||||
state = OPENING;
|
||||
} catch (IOError e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void update_sctp_events () {
|
||||
IOCondition new_events = _query_sctp_socket_events (sctp_socket);
|
||||
|
||||
lock (state) {
|
||||
sctp_events = new_events;
|
||||
|
||||
foreach (var entry in sources.entries) {
|
||||
unowned Source source = entry.key;
|
||||
IOCondition c = entry.value;
|
||||
if ((new_events & c) != 0)
|
||||
source.set_ready_time (0);
|
||||
}
|
||||
|
||||
sctp_source.invalidate ();
|
||||
}
|
||||
}
|
||||
|
||||
protected extern static IOCondition _query_sctp_socket_events (void * sock);
|
||||
|
||||
protected extern static ssize_t _recv (void * sock, uint8[] buffer, out uint16 stream_id,
|
||||
out PayloadProtocolId protocol_id, out SctpMessageFlags message_flags) throws IOError;
|
||||
|
||||
@@ -835,7 +771,7 @@ namespace Frida {
|
||||
|
||||
switch (type) {
|
||||
case DATA_CHANNEL_OPEN: {
|
||||
if (_state != CREATED || setup == ACTIVE)
|
||||
if (state != CREATED || setup == ACTIVE)
|
||||
return;
|
||||
|
||||
this.stream_id = stream_id;
|
||||
@@ -843,15 +779,15 @@ namespace Frida {
|
||||
uint8[] reply = { DcepMessageType.DATA_CHANNEL_ACK };
|
||||
_send (sctp_socket, stream_id, WEBRTC_DCEP, reply);
|
||||
|
||||
_state = OPENED;
|
||||
state = OPEN;
|
||||
|
||||
break;
|
||||
}
|
||||
case DATA_CHANNEL_ACK:
|
||||
if (_state != OPENING)
|
||||
if (state != OPENING)
|
||||
return;
|
||||
|
||||
_state = OPENED;
|
||||
state = OPEN;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -886,140 +822,6 @@ namespace Frida {
|
||||
DATA_CHANNEL_ACK = 0x02
|
||||
}
|
||||
|
||||
private class SctpInputStream : InputStream, PollableInputStream {
|
||||
public weak SctpConnection connection {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public SctpInputStream (SctpConnection connection) {
|
||||
Object (connection: connection);
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
connection.shutdown (READ);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override ssize_t read (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_readable () {
|
||||
return (connection.pending_io & IOCondition.IN) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new SctpIOSource (connection, IOCondition.IN), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
|
||||
return connection.recv (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private class SctpOutputStream : OutputStream, PollableOutputStream {
|
||||
public weak SctpConnection connection {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public SctpOutputStream (SctpConnection connection) {
|
||||
Object (connection: connection);
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
connection.shutdown (WRITE);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override bool flush (GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool flush_async (int io_priority, GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ssize_t write (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_writable () {
|
||||
return (connection.pending_io & IOCondition.OUT) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new SctpIOSource (connection, IOCondition.OUT), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
|
||||
return connection.send (buffer);
|
||||
}
|
||||
|
||||
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
private class SctpIOSource : Source {
|
||||
public SctpConnection connection;
|
||||
public IOCondition condition;
|
||||
|
||||
public SctpIOSource (SctpConnection connection, IOCondition condition) {
|
||||
this.connection = connection;
|
||||
this.condition = condition;
|
||||
|
||||
connection.register_source (this, condition);
|
||||
}
|
||||
|
||||
~SctpIOSource () {
|
||||
connection.unregister_source (this);
|
||||
}
|
||||
|
||||
protected override bool prepare (out int timeout) {
|
||||
timeout = -1;
|
||||
return (connection.pending_io & condition) != 0;
|
||||
}
|
||||
|
||||
protected override bool check () {
|
||||
return (connection.pending_io & condition) != 0;
|
||||
}
|
||||
|
||||
protected override bool dispatch (SourceFunc? callback) {
|
||||
set_ready_time (-1);
|
||||
|
||||
if (callback == null)
|
||||
return Source.REMOVE;
|
||||
|
||||
return callback ();
|
||||
}
|
||||
|
||||
protected static bool closure_callback (Closure closure) {
|
||||
var return_value = Value (typeof (bool));
|
||||
|
||||
closure.invoke (ref return_value, {});
|
||||
|
||||
return return_value.get_boolean ();
|
||||
}
|
||||
}
|
||||
|
||||
private class SctpTimerSource : Source {
|
||||
private static int64 last_process_time = -1;
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace Frida {
|
||||
public class ServiceSessionRegistry : Object {
|
||||
public signal void session_closed (ServiceSessionId id);
|
||||
|
||||
private Gee.Map<ServiceSessionId?, ServiceSession> sessions =
|
||||
new Gee.HashMap<ServiceSessionId?, ServiceSession> (ServiceSessionId.hash, ServiceSessionId.equal);
|
||||
|
||||
~ServiceSessionRegistry () {
|
||||
clear ();
|
||||
}
|
||||
|
||||
public void clear () {
|
||||
foreach (var session in sessions.values.to_array ())
|
||||
session.close ();
|
||||
sessions.clear ();
|
||||
}
|
||||
|
||||
public void register (ServiceSessionId id, ServiceSession session) {
|
||||
sessions[id] = session;
|
||||
|
||||
ServiceSessionId? boxed_id = id;
|
||||
session.set_data ("service-session-id", boxed_id);
|
||||
|
||||
session.close.connect (on_session_close);
|
||||
}
|
||||
|
||||
public ServiceSession link (ServiceSessionId id) throws Error {
|
||||
var session = sessions[id];
|
||||
if (session == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid session ID");
|
||||
return session;
|
||||
}
|
||||
|
||||
public void unlink (ServiceSessionId id) {
|
||||
ServiceSession? session;
|
||||
if (sessions.unset (id, out session)) {
|
||||
session.close.disconnect (on_session_close);
|
||||
|
||||
session_closed (id);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_session_close (ServiceSession session) {
|
||||
ServiceSessionId? id = session.get_data ("service-session-id");
|
||||
unlink (id);
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
-1
@@ -27,6 +27,10 @@ namespace Frida {
|
||||
public abstract async InjectorPayloadId inject_library_blob (uint pid, uint8[] blob, string entrypoint, string data,
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public abstract async ChannelId open_channel (string address, Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public abstract async ServiceSessionId open_service (string address, Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
public signal void child_added (HostChildInfo info);
|
||||
@@ -34,6 +38,8 @@ namespace Frida {
|
||||
public signal void process_crashed (CrashInfo crash);
|
||||
public signal void output (uint pid, int fd, uint8[] data);
|
||||
public signal void agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash);
|
||||
public signal void channel_closed (ChannelId id);
|
||||
public signal void service_session_closed (ServiceSessionId id);
|
||||
public signal void uninjected (InjectorPayloadId id);
|
||||
}
|
||||
|
||||
@@ -743,6 +749,23 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.Channel16")]
|
||||
public interface Channel : Object {
|
||||
public abstract async void close (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void input (uint8[] data, Cancellable? cancellable) throws GLib.Error;
|
||||
public signal void output (uint8[] data);
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.ServiceSession16")]
|
||||
public interface ServiceSession : Object {
|
||||
public signal void close ();
|
||||
public signal void message (Variant message);
|
||||
|
||||
public abstract async void activate (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void cancel (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async Variant request (Variant parameters, Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.TransportBroker16")]
|
||||
public interface TransportBroker : Object {
|
||||
public abstract async void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port,
|
||||
@@ -875,6 +898,14 @@ namespace Frida {
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_authorized ();
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_authorized ();
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_authorized ();
|
||||
}
|
||||
}
|
||||
|
||||
public class UnauthorizedPortalSession : Object, PortalSession {
|
||||
@@ -1502,6 +1533,46 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChannelId {
|
||||
public string handle;
|
||||
|
||||
public ChannelId (string handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public ChannelId.generate () {
|
||||
this.handle = Uuid.string_random ().replace ("-", "");
|
||||
}
|
||||
|
||||
public static uint hash (ChannelId? id) {
|
||||
return id.handle.hash ();
|
||||
}
|
||||
|
||||
public static bool equal (ChannelId? a, ChannelId? b) {
|
||||
return a.handle == b.handle;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ServiceSessionId {
|
||||
public string handle;
|
||||
|
||||
public ServiceSessionId (string handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public ServiceSessionId.generate () {
|
||||
this.handle = Uuid.string_random ().replace ("-", "");
|
||||
}
|
||||
|
||||
public static uint hash (ServiceSessionId? id) {
|
||||
return id.handle.hash ();
|
||||
}
|
||||
|
||||
public static bool equal (ServiceSessionId? a, ServiceSessionId? b) {
|
||||
return a.handle == b.handle;
|
||||
}
|
||||
}
|
||||
|
||||
public struct AgentScriptId {
|
||||
public uint handle;
|
||||
|
||||
@@ -2048,7 +2119,8 @@ namespace Frida {
|
||||
public const string AGENT_SESSION = "/re/frida/AgentSession";
|
||||
public const string AGENT_CONTROLLER = "/re/frida/AgentController";
|
||||
public const string AGENT_MESSAGE_SINK = "/re/frida/AgentMessageSink";
|
||||
public const string CHILD_SESSION = "/re/frida/ChildSession";
|
||||
public const string CHANNEL = "/re/frida/Channel";
|
||||
public const string SERVICE = "/re/frida/Service";
|
||||
public const string TRANSPORT_BROKER = "/re/frida/TransportBroker";
|
||||
public const string PORTAL_SESSION = "/re/frida/PortalSession";
|
||||
public const string BUS_SESSION = "/re/frida/BusSession";
|
||||
@@ -2061,6 +2133,14 @@ namespace Frida {
|
||||
public static string for_agent_message_sink (AgentSessionId id) {
|
||||
return AGENT_MESSAGE_SINK + "/" + id.handle;
|
||||
}
|
||||
|
||||
public static string for_channel (ChannelId id) {
|
||||
return CHANNEL + "/" + id.handle;
|
||||
}
|
||||
|
||||
public static string for_service_session (ServiceSessionId id) {
|
||||
return SERVICE + "/" + id.handle;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Marshal {
|
||||
|
||||
+41
-233
@@ -870,7 +870,7 @@ namespace Frida {
|
||||
|
||||
public extern static unowned string _version_string ();
|
||||
|
||||
private class WebConnection : IOStream {
|
||||
private class WebConnection : VirtualStream {
|
||||
public signal void websocket_closed ();
|
||||
|
||||
public Soup.WebsocketConnection websocket {
|
||||
@@ -878,37 +878,9 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public override InputStream input_stream {
|
||||
get {
|
||||
return _input_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override OutputStream output_stream {
|
||||
get {
|
||||
return _output_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public IOCondition pending_io {
|
||||
get {
|
||||
lock (state)
|
||||
return _pending_io;
|
||||
}
|
||||
}
|
||||
|
||||
private WebInputStream _input_stream;
|
||||
private WebOutputStream _output_stream;
|
||||
|
||||
private Soup.WebsocketState state;
|
||||
private IOCondition _pending_io;
|
||||
private ByteArray recv_queue = new ByteArray ();
|
||||
private ByteArray send_queue = new ByteArray ();
|
||||
|
||||
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
|
||||
|
||||
private MainContext main_context;
|
||||
|
||||
public WebConnection (Soup.WebsocketConnection websocket) {
|
||||
Object (websocket: websocket);
|
||||
}
|
||||
@@ -916,14 +888,6 @@ namespace Frida {
|
||||
construct {
|
||||
websocket.max_incoming_payload_size = (256 * 1024) + 1; // XXX: There's an off-by-one error in libsoup
|
||||
|
||||
_input_stream = new WebInputStream (this);
|
||||
_output_stream = new WebOutputStream (this);
|
||||
|
||||
state = websocket.state;
|
||||
_pending_io = (state == OPEN) ? IOCondition.OUT : IOCondition.IN;
|
||||
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
websocket.closed.connect (on_closed);
|
||||
websocket.message.connect (on_message);
|
||||
}
|
||||
@@ -933,51 +897,50 @@ namespace Frida {
|
||||
websocket.closed.disconnect (on_closed);
|
||||
}
|
||||
|
||||
public override bool close (GLib.Cancellable? cancellable) throws IOError {
|
||||
_close ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, GLib.Cancellable? cancellable) throws IOError {
|
||||
_close ();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _close () {
|
||||
if (main_context.is_owner ()) {
|
||||
do_close ();
|
||||
} else {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
do_close ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
protected override VirtualStream.State query_initial_state () {
|
||||
switch (websocket.state) {
|
||||
case OPEN:
|
||||
case CLOSING:
|
||||
return OPEN;
|
||||
case CLOSED:
|
||||
return CLOSED;
|
||||
}
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
private void do_close () {
|
||||
protected override IOCondition query_events () {
|
||||
IOCondition new_events = 0;
|
||||
|
||||
if (recv_queue.len > 0 || state != OPEN)
|
||||
new_events |= IN;
|
||||
|
||||
if (state == OPEN)
|
||||
new_events |= OUT;
|
||||
|
||||
return new_events;
|
||||
}
|
||||
|
||||
protected override void handle_close () {
|
||||
if (websocket.state != OPEN)
|
||||
return;
|
||||
|
||||
websocket.close (1000, "Closing");
|
||||
}
|
||||
|
||||
public ssize_t recv (uint8[] buffer) throws IOError {
|
||||
ssize_t n;
|
||||
lock (state) {
|
||||
public override ssize_t read (uint8[] buffer) throws IOError {
|
||||
ssize_t n = 0;
|
||||
with_state_lock (() => {
|
||||
n = ssize_t.min (recv_queue.len, buffer.length);
|
||||
if (n > 0) {
|
||||
Memory.copy (buffer, recv_queue.data, n);
|
||||
recv_queue.remove_range (0, (uint) n);
|
||||
|
||||
recompute_pending_io_unlocked ();
|
||||
update_pending_io ();
|
||||
} else {
|
||||
if (state == OPEN)
|
||||
n = -1;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
if (n == -1)
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
@@ -985,9 +948,10 @@ namespace Frida {
|
||||
return n;
|
||||
}
|
||||
|
||||
public ssize_t send (uint8[] buffer) {
|
||||
lock (state)
|
||||
public override ssize_t write (uint8[] buffer) {
|
||||
with_state_lock (() => {
|
||||
send_queue.append (buffer);
|
||||
});
|
||||
|
||||
if (main_context.is_owner ()) {
|
||||
process_send_queue ();
|
||||
@@ -1011,190 +975,34 @@ namespace Frida {
|
||||
|
||||
while (true) {
|
||||
uint8[]? chunk = null;
|
||||
lock (state) {
|
||||
with_state_lock (() => {
|
||||
size_t n = size_t.min (send_queue.len, max_message_size);
|
||||
if (n == 0)
|
||||
return;
|
||||
chunk = send_queue.data[0:n];
|
||||
send_queue.remove_range (0, (uint) n);
|
||||
}
|
||||
});
|
||||
if (chunk == null)
|
||||
return;
|
||||
|
||||
websocket.send_binary (chunk);
|
||||
}
|
||||
}
|
||||
|
||||
public void register_source (Source source, IOCondition condition) {
|
||||
lock (state)
|
||||
sources[source] = condition;
|
||||
}
|
||||
|
||||
public void unregister_source (Source source) {
|
||||
lock (state)
|
||||
sources.unset (source);
|
||||
}
|
||||
|
||||
private void on_closed () {
|
||||
lock (state) {
|
||||
state = websocket.state;
|
||||
recompute_pending_io_unlocked ();
|
||||
}
|
||||
with_state_lock (() => {
|
||||
state = CLOSED;
|
||||
update_pending_io ();
|
||||
});
|
||||
|
||||
websocket_closed ();
|
||||
}
|
||||
|
||||
private void on_message (int type, Bytes message) {
|
||||
lock (state) {
|
||||
with_state_lock (() => {
|
||||
recv_queue.append (message.get_data ());
|
||||
recompute_pending_io_unlocked ();
|
||||
}
|
||||
}
|
||||
|
||||
private void recompute_pending_io_unlocked () {
|
||||
IOCondition new_io = 0;
|
||||
if (recv_queue.len > 0 || state != OPEN)
|
||||
new_io |= IN;
|
||||
if (state == OPEN)
|
||||
new_io |= OUT;
|
||||
_pending_io = new_io;
|
||||
|
||||
foreach (var entry in sources.entries) {
|
||||
unowned Source source = entry.key;
|
||||
IOCondition c = entry.value;
|
||||
if ((new_io & c) != 0)
|
||||
source.set_ready_time (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class WebInputStream : InputStream, PollableInputStream {
|
||||
public weak WebConnection connection {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public WebInputStream (WebConnection connection) {
|
||||
Object (connection: connection);
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override ssize_t read (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_readable () {
|
||||
return (connection.pending_io & IOCondition.IN) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new WebIOSource (connection, IOCondition.IN), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
|
||||
return connection.recv (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private class WebOutputStream : OutputStream, PollableOutputStream {
|
||||
public weak WebConnection connection {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public WebOutputStream (WebConnection connection) {
|
||||
Object (connection: connection);
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override bool flush (GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool flush_async (int io_priority, GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ssize_t write (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_writable () {
|
||||
return (connection.pending_io & IOCondition.OUT) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new WebIOSource (connection, IOCondition.OUT), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
|
||||
return connection.send (buffer);
|
||||
}
|
||||
|
||||
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
private class WebIOSource : Source {
|
||||
public WebConnection connection;
|
||||
public IOCondition condition;
|
||||
|
||||
public WebIOSource (WebConnection connection, IOCondition condition) {
|
||||
this.connection = connection;
|
||||
this.condition = condition;
|
||||
|
||||
connection.register_source (this, condition);
|
||||
}
|
||||
|
||||
~WebIOSource () {
|
||||
connection.unregister_source (this);
|
||||
}
|
||||
|
||||
protected override bool prepare (out int timeout) {
|
||||
timeout = -1;
|
||||
return (connection.pending_io & condition) != 0;
|
||||
}
|
||||
|
||||
protected override bool check () {
|
||||
return (connection.pending_io & condition) != 0;
|
||||
}
|
||||
|
||||
protected override bool dispatch (SourceFunc? callback) {
|
||||
set_ready_time (-1);
|
||||
|
||||
if (callback == null)
|
||||
return Source.REMOVE;
|
||||
|
||||
return callback ();
|
||||
}
|
||||
|
||||
protected static bool closure_callback (Closure closure) {
|
||||
var return_value = Value (typeof (bool));
|
||||
|
||||
closure.invoke (ref return_value, {});
|
||||
|
||||
return return_value.get_boolean ();
|
||||
update_pending_io ();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
namespace Frida {
|
||||
public abstract class VirtualStream : IOStream {
|
||||
public override InputStream input_stream {
|
||||
get {
|
||||
return _input_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override OutputStream output_stream {
|
||||
get {
|
||||
return _output_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public IOCondition pending_io {
|
||||
get {
|
||||
lock (state)
|
||||
return _pending_io;
|
||||
}
|
||||
}
|
||||
|
||||
protected State state = OPEN;
|
||||
|
||||
private VirtualInputStream _input_stream;
|
||||
private VirtualOutputStream _output_stream;
|
||||
|
||||
private IOCondition _pending_io;
|
||||
|
||||
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
|
||||
|
||||
protected Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
protected MainContext main_context;
|
||||
|
||||
protected enum State {
|
||||
CREATED,
|
||||
OPENING,
|
||||
OPEN,
|
||||
CLOSED
|
||||
}
|
||||
|
||||
protected delegate void WorkFunc ();
|
||||
|
||||
construct {
|
||||
_input_stream = new VirtualInputStream (this);
|
||||
_output_stream = new VirtualOutputStream (this);
|
||||
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
state = query_initial_state ();
|
||||
_pending_io = query_events ();
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
_output_stream.detach ();
|
||||
_input_stream.detach ();
|
||||
|
||||
io_cancellable.cancel ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
protected virtual State query_initial_state () {
|
||||
return OPEN;
|
||||
}
|
||||
|
||||
protected abstract IOCondition query_events ();
|
||||
|
||||
public override bool close (GLib.Cancellable? cancellable) throws IOError {
|
||||
_close ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, GLib.Cancellable? cancellable) throws IOError {
|
||||
_close ();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _close () {
|
||||
if (main_context.is_owner ()) {
|
||||
do_close ();
|
||||
} else {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
do_close ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
}
|
||||
}
|
||||
|
||||
private void do_close () {
|
||||
if (state == CLOSED)
|
||||
return;
|
||||
|
||||
handle_close ();
|
||||
}
|
||||
|
||||
protected abstract void handle_close ();
|
||||
|
||||
public virtual void shutdown_read () throws IOError {
|
||||
}
|
||||
|
||||
public virtual void shutdown_write () throws IOError {
|
||||
}
|
||||
|
||||
public abstract ssize_t read (uint8[] buffer) throws IOError;
|
||||
|
||||
public abstract ssize_t write (uint8[] buffer) throws IOError;
|
||||
|
||||
protected void with_state_lock (WorkFunc func) {
|
||||
lock (state)
|
||||
func ();
|
||||
}
|
||||
|
||||
public void register_source (Source source, IOCondition condition) {
|
||||
lock (state)
|
||||
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
|
||||
}
|
||||
|
||||
public void unregister_source (Source source) {
|
||||
lock (state)
|
||||
sources.unset (source);
|
||||
}
|
||||
|
||||
protected virtual void update_pending_io () {
|
||||
_pending_io = query_events ();
|
||||
|
||||
foreach (var entry in sources.entries) {
|
||||
unowned Source source = entry.key;
|
||||
IOCondition c = entry.value;
|
||||
if ((_pending_io & c) != 0)
|
||||
source.set_ready_time (0);
|
||||
}
|
||||
|
||||
notify_property ("pending-io");
|
||||
}
|
||||
}
|
||||
|
||||
private class VirtualInputStream : InputStream, PollableInputStream {
|
||||
private weak VirtualStream stream;
|
||||
|
||||
public VirtualInputStream (VirtualStream stream) {
|
||||
Object ();
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
internal void detach () {
|
||||
stream = null;
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
if (stream != null)
|
||||
stream.shutdown_read ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override ssize_t read (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
if (stream == null)
|
||||
return 0;
|
||||
|
||||
if (!is_readable ()) {
|
||||
bool done = false;
|
||||
var mutex = Mutex ();
|
||||
var cond = Cond ();
|
||||
|
||||
ulong io_handler = stream.notify["pending-io"].connect ((obj, pspec) => {
|
||||
if (is_readable ()) {
|
||||
mutex.lock ();
|
||||
done = true;
|
||||
cond.signal ();
|
||||
mutex.unlock ();
|
||||
}
|
||||
});
|
||||
ulong cancellation_handler = 0;
|
||||
if (cancellable != null) {
|
||||
cancellation_handler = cancellable.connect (() => {
|
||||
mutex.lock ();
|
||||
done = true;
|
||||
cond.signal ();
|
||||
mutex.unlock ();
|
||||
});
|
||||
}
|
||||
|
||||
if (!is_readable ()) {
|
||||
mutex.lock ();
|
||||
while (!done)
|
||||
cond.wait (mutex);
|
||||
mutex.unlock ();
|
||||
}
|
||||
|
||||
if (cancellation_handler != 0)
|
||||
cancellable.disconnect (cancellation_handler);
|
||||
stream.disconnect (io_handler);
|
||||
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
|
||||
return stream.read (buffer);
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_readable () {
|
||||
if (stream == null)
|
||||
return true;
|
||||
return (stream.pending_io & IOCondition.IN) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new VirtualIOSource (stream, IOCondition.IN), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
|
||||
if (stream == null)
|
||||
return 0;
|
||||
return stream.read (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private class VirtualOutputStream : OutputStream, PollableOutputStream {
|
||||
private weak VirtualStream? stream;
|
||||
|
||||
public VirtualOutputStream (VirtualStream stream) {
|
||||
Object ();
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
internal void detach () {
|
||||
stream = null;
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
if (stream != null)
|
||||
stream.shutdown_write ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override bool flush (GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool flush_async (int io_priority, GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ssize_t write (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
return stream.write (buffer);
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_writable () {
|
||||
if (stream == null)
|
||||
return false;
|
||||
return (stream.pending_io & IOCondition.OUT) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new VirtualIOSource (stream, IOCondition.OUT), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
|
||||
if (stream == null)
|
||||
throw new IOError.CLOSED ("Virtual is closed");
|
||||
return stream.write (buffer);
|
||||
}
|
||||
|
||||
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
private class VirtualIOSource : Source {
|
||||
public VirtualStream? stream;
|
||||
public IOCondition condition;
|
||||
|
||||
public VirtualIOSource (VirtualStream stream, IOCondition condition) {
|
||||
this.stream = stream;
|
||||
this.condition = condition;
|
||||
|
||||
if (stream != null)
|
||||
stream.register_source (this, condition);
|
||||
}
|
||||
|
||||
~VirtualIOSource () {
|
||||
if (stream != null)
|
||||
stream.unregister_source (this);
|
||||
}
|
||||
|
||||
protected override bool prepare (out int timeout) {
|
||||
timeout = -1;
|
||||
return is_ready ();
|
||||
}
|
||||
|
||||
protected override bool check () {
|
||||
return is_ready ();
|
||||
}
|
||||
|
||||
private bool is_ready () {
|
||||
IOCondition pending_io = (stream != null) ? stream.pending_io : IOCondition.IN;
|
||||
return (pending_io & condition) != 0;
|
||||
}
|
||||
|
||||
protected override bool dispatch (SourceFunc? callback) {
|
||||
set_ready_time (-1);
|
||||
|
||||
if (callback == null)
|
||||
return Source.REMOVE;
|
||||
|
||||
return callback ();
|
||||
}
|
||||
|
||||
protected static bool closure_callback (Closure closure) {
|
||||
var return_value = Value (typeof (bool));
|
||||
|
||||
closure.invoke (ref return_value, {});
|
||||
|
||||
return return_value.get_boolean ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1927,6 +1927,14 @@ namespace Frida.Gadget {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to inject libraries when embedded");
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to open channels when embedded");
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to open services when embedded");
|
||||
}
|
||||
|
||||
private void validate_pid (uint pid) throws Error {
|
||||
if (pid != this_process.pid)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to act on other processes when embedded");
|
||||
|
||||
+1
-1
@@ -470,7 +470,7 @@ def function_is_public(name):
|
||||
]
|
||||
|
||||
def parse_vala_object_types(source) -> List[ApiObjectType]:
|
||||
return [ApiObjectType(m.group(2), m.group(1)) for m in re.finditer(r"^\t+public\s+(class|interface)\s+(\w+)\s+", source, re.MULTILINE)]
|
||||
return [ApiObjectType(m.group(3), m.group(2)) for m in re.finditer(r"^\t+public\s+(sealed )?(class|interface)\s+(\w+)\s+", source, re.MULTILINE)]
|
||||
|
||||
def parse_vapi_functions(vapi) -> List[ApiFunction]:
|
||||
return [ApiFunction(m.group(1), m.group(0)) for m in re.finditer(r"^\tpublic static .+ (\w+) \(.+;", vapi, re.MULTILINE)]
|
||||
|
||||
@@ -141,6 +141,29 @@ namespace Frida {
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
|
||||
if (host_session != this.host_session)
|
||||
return;
|
||||
|
||||
this.host_session.unlink_agent_session (id);
|
||||
}
|
||||
|
||||
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
|
||||
}
|
||||
|
||||
public void unlink_channel (HostSession host_session, ChannelId id) {
|
||||
}
|
||||
|
||||
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
|
||||
}
|
||||
|
||||
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
}
|
||||
@@ -260,6 +283,14 @@ namespace Frida {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void unlink_agent_session (AgentSessionId id) {
|
||||
BareboneAgentSession? session = agent_sessions[id];
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
session.message_sink = null;
|
||||
}
|
||||
|
||||
public async InjectorPayloadId inject_library_file (uint pid, string path, string entrypoint, string data,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
@@ -270,6 +301,14 @@ namespace Frida {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
|
||||
}
|
||||
|
||||
private void on_agent_session_closed (BareboneAgentSession session) {
|
||||
AgentSessionId id = session.id;
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace Frida {
|
||||
try {
|
||||
var device = yield manager.get_device_by_type (LOCAL, 0, io_cancellable);
|
||||
var session = yield device.get_host_session (io_cancellable);
|
||||
var agent = new Agent (this, (BaseDBusHostSession) session);
|
||||
var agent = new Agent (this, (LocalHostSession) session);
|
||||
|
||||
load_request.resolve (agent);
|
||||
} catch (GLib.Error e) {
|
||||
@@ -238,7 +238,7 @@ namespace Frida {
|
||||
private Promise<string> source_request = new Promise<string> ();
|
||||
private Promise<Bytes?> snapshot_request = new Promise<Bytes?> ();
|
||||
|
||||
public Agent (Compiler parent, BaseDBusHostSession host_session) {
|
||||
public Agent (Compiler parent, LocalHostSession host_session) {
|
||||
Object (
|
||||
parent: parent,
|
||||
host_session: host_session,
|
||||
|
||||
Generated
+14
-14
@@ -9,7 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@frida/crosspath": "3.0.0",
|
||||
"frida-compile": "16.4.1",
|
||||
"frida-compile": "16.4.2",
|
||||
"frida-fs": "5.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -17,7 +17,7 @@
|
||||
"@rollup/plugin-node-resolve": "^14.1.0",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.4",
|
||||
"@types/frida-gum": "18.8.1",
|
||||
"@types/frida-gum": "18.8.2",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/node": "18.7.23",
|
||||
"glob": "^8.0.3",
|
||||
@@ -468,9 +468,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "18.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.1.tgz",
|
||||
"integrity": "sha512-FhlHORjaw43QbNYIyj5Ji7iYSvk7t5nPiC+GuYDVndqc1bFheeAs88b1odp8HhmpQUrrYVU90wapapKVLlmJHg==",
|
||||
"version": "18.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.2.tgz",
|
||||
"integrity": "sha512-5KH6ITrKJQe1UWrNnbXXx3KXRe/Lx9I1EwkBdC+AMmlo9F5UC0PxdC959aGxhfjXBRIHWxXwXPi3JmeWIbeqeg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -641,9 +641,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/frida-compile": {
|
||||
"version": "16.4.1",
|
||||
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.1.tgz",
|
||||
"integrity": "sha512-xI9HNtUFpHxuKBGXaL6XJAYH1zWTvNJDrHRnz1hp0oS24iPHt6c01Jmm3qljZek3oQWn8HhvfNvrzfgbsKzoBQ==",
|
||||
"version": "16.4.2",
|
||||
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.2.tgz",
|
||||
"integrity": "sha512-wNiFk37JVpCoEsuJF9AHZtccM9Z3KGj3yu0Amsb/DKzKnHCxvl8AlAU5qSfYXfAJtzbNuCKGqNBM8u96KvP28A==",
|
||||
"dependencies": {
|
||||
"@frida/assert": "^3.0.1",
|
||||
"@frida/base64-js": "^2.0.3",
|
||||
@@ -1466,9 +1466,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/frida-gum": {
|
||||
"version": "18.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.1.tgz",
|
||||
"integrity": "sha512-FhlHORjaw43QbNYIyj5Ji7iYSvk7t5nPiC+GuYDVndqc1bFheeAs88b1odp8HhmpQUrrYVU90wapapKVLlmJHg==",
|
||||
"version": "18.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.2.tgz",
|
||||
"integrity": "sha512-5KH6ITrKJQe1UWrNnbXXx3KXRe/Lx9I1EwkBdC+AMmlo9F5UC0PxdC959aGxhfjXBRIHWxXwXPi3JmeWIbeqeg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/glob": {
|
||||
@@ -1597,9 +1597,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"frida-compile": {
|
||||
"version": "16.4.1",
|
||||
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.1.tgz",
|
||||
"integrity": "sha512-xI9HNtUFpHxuKBGXaL6XJAYH1zWTvNJDrHRnz1hp0oS24iPHt6c01Jmm3qljZek3oQWn8HhvfNvrzfgbsKzoBQ==",
|
||||
"version": "16.4.2",
|
||||
"resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.2.tgz",
|
||||
"integrity": "sha512-wNiFk37JVpCoEsuJF9AHZtccM9Z3KGj3yu0Amsb/DKzKnHCxvl8AlAU5qSfYXfAJtzbNuCKGqNBM8u96KvP28A==",
|
||||
"requires": {
|
||||
"@frida/assert": "^3.0.1",
|
||||
"@frida/base64-js": "^2.0.3",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@frida/crosspath": "3.0.0",
|
||||
"frida-compile": "16.4.1",
|
||||
"frida-compile": "16.4.2",
|
||||
"frida-fs": "5.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -17,7 +17,7 @@
|
||||
"@rollup/plugin-node-resolve": "^14.1.0",
|
||||
"@rollup/plugin-replace": "^4.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.4",
|
||||
"@types/frida-gum": "18.8.1",
|
||||
"@types/frida-gum": "18.8.2",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/node": "18.7.23",
|
||||
"glob": "^8.0.3",
|
||||
|
||||
+228
-148
@@ -1,5 +1,4 @@
|
||||
namespace Frida {
|
||||
#if HAVE_LOCAL_BACKEND
|
||||
public class ControlService : Object {
|
||||
public EndpointParameters endpoint_params {
|
||||
get;
|
||||
@@ -12,6 +11,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private HostSession host_session;
|
||||
private HostSessionProvider provider;
|
||||
|
||||
private State state = STOPPED;
|
||||
|
||||
@@ -21,8 +21,12 @@ namespace Frida {
|
||||
|
||||
private Gee.Set<ControlChannel> spawn_gaters = new Gee.HashSet<ControlChannel> ();
|
||||
private Gee.Map<uint, PendingSpawn> pending_spawn = new Gee.HashMap<uint, PendingSpawn> ();
|
||||
private Gee.Map<AgentSessionId?, AgentSessionEntry> sessions =
|
||||
private Gee.Map<AgentSessionId?, AgentSessionEntry> agent_sessions =
|
||||
new Gee.HashMap<AgentSessionId?, AgentSessionEntry> (AgentSessionId.hash, AgentSessionId.equal);
|
||||
private Gee.Map<ChannelId?, ChannelEntry> channels =
|
||||
new Gee.HashMap<ChannelId?, ChannelEntry> (ChannelId.hash, ChannelId.equal);
|
||||
private Gee.Map<ServiceSessionId?, ServiceSessionEntry> service_sessions =
|
||||
new Gee.HashMap<ServiceSessionId?, ServiceSessionEntry> (ServiceSessionId.hash, ServiceSessionId.equal);
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
@@ -35,7 +39,8 @@ namespace Frida {
|
||||
STOPPING
|
||||
}
|
||||
|
||||
public ControlService (EndpointParameters endpoint_params, ControlServiceOptions? options = null) {
|
||||
public ControlService (EndpointParameters endpoint_params, ControlServiceOptions? options = null) throws Error {
|
||||
#if HAVE_LOCAL_BACKEND
|
||||
ControlServiceOptions opts = (options != null) ? options : new ControlServiceOptions ();
|
||||
|
||||
HostSession session;
|
||||
@@ -63,7 +68,10 @@ namespace Frida {
|
||||
options: opts
|
||||
);
|
||||
|
||||
assign_session (session);
|
||||
assign_session (session, new PrecreatedLocalHostSessionProvider ((LocalHostSession) session));
|
||||
#else
|
||||
throw new Error.NOT_SUPPORTED ("Local backend not available");
|
||||
#endif
|
||||
}
|
||||
|
||||
public async ControlService.with_device (Device device, EndpointParameters endpoint_params,
|
||||
@@ -77,17 +85,7 @@ namespace Frida {
|
||||
options: opts
|
||||
);
|
||||
|
||||
assign_session (session);
|
||||
}
|
||||
|
||||
internal ControlService.with_host_session (HostSession host_session, EndpointParameters endpoint_params,
|
||||
ControlServiceOptions? options = null) {
|
||||
Object (
|
||||
endpoint_params: endpoint_params,
|
||||
options: (options != null) ? options : new ControlServiceOptions ()
|
||||
);
|
||||
|
||||
assign_session (host_session);
|
||||
assign_session (session, device.provider);
|
||||
}
|
||||
|
||||
construct {
|
||||
@@ -99,7 +97,7 @@ namespace Frida {
|
||||
main_handler = new ConnectionHandler (this, null);
|
||||
}
|
||||
|
||||
private void assign_session (HostSession session) {
|
||||
private void assign_session (HostSession session, HostSessionProvider provider) {
|
||||
host_session = session;
|
||||
host_session.spawn_added.connect (notify_spawn_added);
|
||||
host_session.child_added.connect (notify_child_added);
|
||||
@@ -107,7 +105,11 @@ namespace Frida {
|
||||
host_session.process_crashed.connect (notify_process_crashed);
|
||||
host_session.output.connect (notify_output);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
host_session.channel_closed.connect (on_channel_closed);
|
||||
host_session.service_session_closed.connect (on_service_session_closed);
|
||||
host_session.uninjected.connect (notify_uninjected);
|
||||
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable = null) throws Error, IOError {
|
||||
@@ -123,7 +125,7 @@ namespace Frida {
|
||||
yield service.start (cancellable);
|
||||
|
||||
if (options.enable_preload) {
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
var base_host_session = host_session as LocalHostSession;
|
||||
if (base_host_session != null)
|
||||
base_host_session.preload.begin (io_cancellable);
|
||||
}
|
||||
@@ -162,9 +164,8 @@ namespace Frida {
|
||||
|
||||
yield main_handler.close (cancellable);
|
||||
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
if (base_host_session != null)
|
||||
yield base_host_session.close (cancellable);
|
||||
if (provider is PrecreatedLocalHostSessionProvider)
|
||||
yield provider.destroy (host_session, cancellable);
|
||||
|
||||
state = STOPPED;
|
||||
}
|
||||
@@ -226,17 +227,15 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void teardown_control_channel (ControlChannel channel) {
|
||||
foreach (AgentSessionId id in channel.sessions) {
|
||||
AgentSessionEntry entry = sessions[id];
|
||||
foreach (AgentSessionId id in channel.agent_sessions) {
|
||||
AgentSessionEntry entry = agent_sessions[id];
|
||||
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
if (base_host_session != null)
|
||||
base_host_session.unlink_agent_session (id);
|
||||
provider.unlink_agent_session (host_session, id);
|
||||
|
||||
AgentSession? session = entry.session;
|
||||
|
||||
if (entry.persist_timeout == 0 || session == null) {
|
||||
sessions.unset (id);
|
||||
agent_sessions.unset (id);
|
||||
if (session != null)
|
||||
session.close.begin (io_cancellable);
|
||||
} else {
|
||||
@@ -245,6 +244,24 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ChannelId id in channel.channels.to_array ()) {
|
||||
ChannelEntry entry = channels[id];
|
||||
|
||||
provider.unlink_channel (host_session, id);
|
||||
|
||||
channels.unset (id);
|
||||
|
||||
Channel? ch = entry.channel;
|
||||
if (ch != null)
|
||||
ch.close.begin (null);
|
||||
}
|
||||
|
||||
foreach (ServiceSessionId id in channel.service_sessions.to_array ()) {
|
||||
provider.unlink_service_session (host_session, id);
|
||||
|
||||
service_sessions.unset (id);
|
||||
}
|
||||
|
||||
try {
|
||||
yield disable_spawn_gating (channel);
|
||||
} catch (GLib.Error e) {
|
||||
@@ -379,12 +396,22 @@ namespace Frida {
|
||||
yield parent.reattach (id, requester, cancellable);
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, ControlChannel requester, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
return yield parent.open_channel (address, requester, cancellable);
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, ControlChannel requester, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
return yield parent.open_service (address, requester, cancellable);
|
||||
}
|
||||
|
||||
public void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port, out string token)
|
||||
throws Error {
|
||||
#if WINDOWS
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on Windows");
|
||||
#else
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
var base_host_session = host_session as LocalHostSession;
|
||||
if (base_host_session == null)
|
||||
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
|
||||
if (!base_host_session.can_pass_file_descriptors_to_agent_session (id))
|
||||
@@ -454,7 +481,7 @@ namespace Frida {
|
||||
#if !WINDOWS
|
||||
AgentSessionId session_id = transport.session_id;
|
||||
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
var base_host_session = host_session as LocalHostSession;
|
||||
if (base_host_session == null)
|
||||
throw new Error.NOT_SUPPORTED ("Not supported for remote host sessions");
|
||||
|
||||
@@ -537,12 +564,12 @@ namespace Frida {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
requester.sessions.add (id);
|
||||
requester.agent_sessions.add (id);
|
||||
|
||||
var opts = SessionOptions._deserialize (options);
|
||||
|
||||
var entry = new AgentSessionEntry (requester, id, opts.persist_timeout, io_cancellable);
|
||||
sessions[id] = entry;
|
||||
var entry = new AgentSessionEntry (requester, id, opts.persist_timeout);
|
||||
agent_sessions[id] = entry;
|
||||
entry.expired.connect (on_agent_session_expired);
|
||||
|
||||
yield link_session (id, entry, requester, cancellable);
|
||||
@@ -551,11 +578,11 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void reattach (AgentSessionId id, ControlChannel requester, Cancellable? cancellable) throws Error, IOError {
|
||||
AgentSessionEntry? entry = sessions[id];
|
||||
AgentSessionEntry? entry = agent_sessions[id];
|
||||
if (entry == null || entry.controller != null)
|
||||
throw new Error.INVALID_OPERATION ("Invalid session ID");
|
||||
|
||||
requester.sessions.add (id);
|
||||
requester.agent_sessions.add (id);
|
||||
|
||||
entry.attach_controller (requester);
|
||||
|
||||
@@ -574,28 +601,7 @@ namespace Frida {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
AgentSession session;
|
||||
var base_host_session = host_session as BaseDBusHostSession;
|
||||
if (base_host_session != null) {
|
||||
session = yield base_host_session.link_agent_session (id, sink, cancellable);
|
||||
} else {
|
||||
DBusConnection internal_connection = ((DBusProxy) host_session).g_connection;
|
||||
|
||||
try {
|
||||
session = yield internal_connection.get_proxy (null, ObjectPath.for_agent_session (id),
|
||||
DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
} catch (IOError e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
entry.internal_connection = internal_connection;
|
||||
try {
|
||||
entry.take_internal_registration (
|
||||
internal_connection.register_object (ObjectPath.for_agent_message_sink (id), sink));
|
||||
} catch (IOError e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
var session = yield provider.link_agent_session (host_session, id, sink, cancellable);
|
||||
|
||||
entry.session = session;
|
||||
try {
|
||||
@@ -606,6 +612,52 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private async ChannelId open_channel (string address, ControlChannel requester, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
ChannelId id;
|
||||
try {
|
||||
id = yield host_session.open_channel (address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
requester.channels.add (id);
|
||||
|
||||
var entry = new ChannelEntry (requester, id);
|
||||
channels[id] = entry;
|
||||
|
||||
var stream = yield provider.link_channel (host_session, id, cancellable);
|
||||
|
||||
Channel channel = new ChannelEndpoint (stream);
|
||||
entry.channel = channel;
|
||||
entry.take_controller_registration (requester.connection.register_object (ObjectPath.for_channel (id), channel));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private async ServiceSessionId open_service (string address, ControlChannel requester, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
ServiceSessionId id;
|
||||
try {
|
||||
id = yield host_session.open_service (address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
requester.service_sessions.add (id);
|
||||
|
||||
var entry = new ServiceSessionEntry (requester, id);
|
||||
service_sessions[id] = entry;
|
||||
|
||||
var session = yield provider.link_service_session (host_session, id, cancellable);
|
||||
|
||||
entry.session = session;
|
||||
entry.take_controller_registration (
|
||||
requester.connection.register_object (ObjectPath.for_service_session (id), session));
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private void notify_spawn_added (HostSpawnInfo info) {
|
||||
foreach (ControlChannel channel in spawn_gaters)
|
||||
channel.spawn_added (info);
|
||||
@@ -646,15 +698,33 @@ namespace Frida {
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
AgentSessionEntry entry;
|
||||
if (sessions.unset (id, out entry)) {
|
||||
if (agent_sessions.unset (id, out entry)) {
|
||||
ControlChannel? controller = entry.controller;
|
||||
if (controller != null) {
|
||||
controller.sessions.remove (id);
|
||||
controller.agent_sessions.remove (id);
|
||||
controller.agent_session_detached (id, reason, crash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void on_channel_closed (ChannelId id) {
|
||||
ChannelEntry entry;
|
||||
if (channels.unset (id, out entry)) {
|
||||
ControlChannel controller = entry.controller;
|
||||
controller.channels.remove (id);
|
||||
controller.channel_closed (id);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_service_session_closed (ServiceSessionId id) {
|
||||
ServiceSessionEntry entry;
|
||||
if (service_sessions.unset (id, out entry)) {
|
||||
ControlChannel controller = entry.controller;
|
||||
controller.service_sessions.remove (id);
|
||||
controller.service_session_closed (id);
|
||||
}
|
||||
}
|
||||
|
||||
private void notify_uninjected (InjectorPayloadId id) {
|
||||
all_control_channels ().foreach (channel => {
|
||||
channel.uninjected (id);
|
||||
@@ -663,7 +733,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void on_agent_session_expired (AgentSessionEntry entry) {
|
||||
sessions.unset (entry.id);
|
||||
agent_sessions.unset (entry.id);
|
||||
}
|
||||
|
||||
private interface Peer : Object {
|
||||
@@ -738,11 +808,21 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public Gee.Set<AgentSessionId?> sessions {
|
||||
public Gee.Set<AgentSessionId?> agent_sessions {
|
||||
get;
|
||||
default = new Gee.HashSet<AgentSessionId?> (AgentSessionId.hash, AgentSessionId.equal);
|
||||
}
|
||||
|
||||
public Gee.Set<ChannelId?> channels {
|
||||
get;
|
||||
default = new Gee.HashSet<ChannelId?> (ChannelId.hash, ChannelId.equal);
|
||||
}
|
||||
|
||||
public Gee.Set<ServiceSessionId?> service_sessions {
|
||||
get;
|
||||
default = new Gee.HashSet<ServiceSessionId?> (ServiceSessionId.hash, ServiceSessionId.equal);
|
||||
}
|
||||
|
||||
private Gee.Set<uint> registrations = new Gee.HashSet<uint> ();
|
||||
private TimeoutSource? ping_timer;
|
||||
|
||||
@@ -867,6 +947,14 @@ namespace Frida {
|
||||
return yield parent.host_session.inject_library_blob (pid, blob, entrypoint, data, cancellable);
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, Cancellable? cancellable) throws GLib.Error {
|
||||
return yield parent.open_channel (address, this, cancellable);
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws GLib.Error {
|
||||
return yield parent.open_service (address, this, cancellable);
|
||||
}
|
||||
|
||||
private async void open_tcp_transport (AgentSessionId id, Cancellable? cancellable, out uint16 port,
|
||||
out string token) throws Error {
|
||||
parent.open_tcp_transport (id, cancellable, out port, out token);
|
||||
@@ -890,14 +978,45 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class AgentSessionEntry {
|
||||
public signal void expired ();
|
||||
|
||||
private abstract class Entry {
|
||||
public ControlChannel? controller {
|
||||
get;
|
||||
private set;
|
||||
get {
|
||||
return _controller;
|
||||
}
|
||||
protected set {
|
||||
unregister_all ();
|
||||
_controller = value;
|
||||
}
|
||||
}
|
||||
|
||||
private ControlChannel? _controller;
|
||||
private Gee.Collection<uint> registrations = new Gee.ArrayList<uint> ();
|
||||
|
||||
protected Entry (ControlChannel? controller) {
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
~Entry () {
|
||||
unregister_all ();
|
||||
}
|
||||
|
||||
public void take_controller_registration (uint id) {
|
||||
registrations.add (id);
|
||||
}
|
||||
|
||||
private void unregister_all () {
|
||||
if (_controller == null)
|
||||
return;
|
||||
var connection = _controller.connection;
|
||||
foreach (uint id in registrations)
|
||||
connection.unregister_object (id);
|
||||
registrations.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
private class AgentSessionEntry : Entry {
|
||||
public signal void expired ();
|
||||
|
||||
public AgentSessionId id {
|
||||
get;
|
||||
private set;
|
||||
@@ -913,36 +1032,20 @@ namespace Frida {
|
||||
private set;
|
||||
}
|
||||
|
||||
public DBusConnection? internal_connection {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public Cancellable io_cancellable {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private Gee.Collection<uint> internal_registrations = new Gee.ArrayList<uint> ();
|
||||
private Gee.Collection<uint> controller_registrations = new Gee.ArrayList<uint> ();
|
||||
|
||||
private TimeoutSource? expiry_timer;
|
||||
|
||||
public AgentSessionEntry (ControlChannel controller, AgentSessionId id, uint persist_timeout,
|
||||
Cancellable io_cancellable) {
|
||||
this.controller = controller;
|
||||
public AgentSessionEntry (ControlChannel? controller, AgentSessionId id, uint persist_timeout) {
|
||||
base (controller);
|
||||
|
||||
this.id = id;
|
||||
this.persist_timeout = persist_timeout;
|
||||
this.io_cancellable = io_cancellable;
|
||||
}
|
||||
|
||||
~AgentSessionEntry () {
|
||||
stop_expiry_timer ();
|
||||
unregister_all ();
|
||||
}
|
||||
|
||||
public void detach_controller () {
|
||||
unregister_all ();
|
||||
controller = null;
|
||||
session = null;
|
||||
|
||||
@@ -956,27 +1059,6 @@ namespace Frida {
|
||||
controller = c;
|
||||
}
|
||||
|
||||
public void take_internal_registration (uint id) {
|
||||
internal_registrations.add (id);
|
||||
}
|
||||
|
||||
public void take_controller_registration (uint id) {
|
||||
controller_registrations.add (id);
|
||||
}
|
||||
|
||||
private void unregister_all () {
|
||||
if (controller != null)
|
||||
unregister_all_in (controller_registrations, controller.connection);
|
||||
if (internal_connection != null)
|
||||
unregister_all_in (internal_registrations, internal_connection);
|
||||
}
|
||||
|
||||
private void unregister_all_in (Gee.Collection<uint> ids, DBusConnection connection) {
|
||||
foreach (uint id in ids)
|
||||
connection.unregister_object (id);
|
||||
ids.clear ();
|
||||
}
|
||||
|
||||
private void start_expiry_timer () {
|
||||
if (expiry_timer != null)
|
||||
return;
|
||||
@@ -995,55 +1077,53 @@ namespace Frida {
|
||||
expiry_timer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class ControlService : Object {
|
||||
public ControlService (EndpointParameters endpoint_params, ControlServiceOptions? options = null) {
|
||||
}
|
||||
|
||||
public ControlService.with_host_session (HostSession host_session, EndpointParameters endpoint_params,
|
||||
ControlServiceOptions? options = null) {
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable = null) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Local backend not available");
|
||||
}
|
||||
|
||||
public void start_sync (Cancellable? cancellable = null) throws Error, IOError {
|
||||
create<StartTask> ().execute (cancellable);
|
||||
}
|
||||
|
||||
private class StartTask : ControlServiceTask<void> {
|
||||
protected override async void perform_operation () throws Error, IOError {
|
||||
yield parent.start (cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable = null) throws Error, IOError {
|
||||
}
|
||||
|
||||
public void stop_sync (Cancellable? cancellable = null) throws Error, IOError {
|
||||
create<StopTask> ().execute (cancellable);
|
||||
}
|
||||
|
||||
private class StopTask : ControlServiceTask<void> {
|
||||
protected override async void perform_operation () throws Error, IOError {
|
||||
yield parent.stop (cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
private T create<T> () {
|
||||
return Object.new (typeof (T), parent: this);
|
||||
}
|
||||
|
||||
private abstract class ControlServiceTask<T> : AsyncTask<T> {
|
||||
public weak ControlService parent {
|
||||
private class ChannelEntry : Entry {
|
||||
public ChannelId id {
|
||||
get;
|
||||
construct;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Channel? channel {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ChannelEntry (ControlChannel controller, ChannelId id) {
|
||||
base (controller);
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
private class ServiceSessionEntry : Entry {
|
||||
public ServiceSessionId id {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ServiceSession? session {
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ServiceSessionEntry (ControlChannel controller, ServiceSessionId id) {
|
||||
base (controller);
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private class PrecreatedLocalHostSessionProvider : LocalHostSessionProvider {
|
||||
public PrecreatedLocalHostSessionProvider (LocalHostSession session) {
|
||||
take_host_session (session);
|
||||
}
|
||||
|
||||
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
|
||||
throw new Error.NOT_SUPPORTED ("Not supported");
|
||||
}
|
||||
}
|
||||
|
||||
public class ControlServiceOptions : Object {
|
||||
public string? sysroot {
|
||||
|
||||
@@ -1,98 +1,24 @@
|
||||
namespace Frida {
|
||||
public class DarwinHostSessionBackend : Object, HostSessionBackend {
|
||||
private DarwinHostSessionProvider local_provider;
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider == null);
|
||||
local_provider = new DarwinHostSessionProvider ();
|
||||
provider_available (local_provider);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider != null);
|
||||
provider_unavailable (local_provider);
|
||||
yield local_provider.close (cancellable);
|
||||
local_provider = null;
|
||||
public class DarwinHostSessionBackend : LocalHostSessionBackend {
|
||||
protected override LocalHostSessionProvider make_provider () {
|
||||
return new DarwinHostSessionProvider ();
|
||||
}
|
||||
}
|
||||
|
||||
public class DarwinHostSessionProvider : Object, HostSessionProvider {
|
||||
public string id {
|
||||
get { return "local"; }
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return "Local System"; }
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return _icon; }
|
||||
}
|
||||
private Variant? _icon;
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
get { return HostSessionProviderKind.LOCAL; }
|
||||
}
|
||||
|
||||
private DarwinHostSession host_session;
|
||||
|
||||
construct {
|
||||
_icon = _try_extract_icon ();
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (host_session == null)
|
||||
return;
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
|
||||
public class DarwinHostSessionProvider : LocalHostSessionProvider {
|
||||
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
|
||||
var tempdir = new TemporaryDirectory ();
|
||||
|
||||
host_session = new DarwinHostSession (new DarwinHelperProcess (tempdir), tempdir);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
return new DarwinHostSession (new DarwinHelperProcess (tempdir), tempdir);
|
||||
}
|
||||
|
||||
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
|
||||
if (session != host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
|
||||
if (host_session != this.host_session)
|
||||
return;
|
||||
|
||||
this.host_session.unlink_agent_session (id);
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
protected override Variant? load_icon () {
|
||||
return _try_extract_icon ();
|
||||
}
|
||||
|
||||
public extern static Variant? _try_extract_icon ();
|
||||
}
|
||||
|
||||
public class DarwinHostSession : BaseDBusHostSession {
|
||||
public class DarwinHostSession : LocalHostSession {
|
||||
public DarwinHelper helper {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -158,6 +158,36 @@ namespace Frida {
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
|
||||
if (host_session != this.host_session)
|
||||
return;
|
||||
|
||||
this.host_session.unlink_agent_session (id);
|
||||
}
|
||||
|
||||
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return this.host_session.link_channel (id);
|
||||
}
|
||||
|
||||
public void unlink_channel (HostSession host_session, ChannelId id) {
|
||||
if (host_session != this.host_session)
|
||||
return;
|
||||
|
||||
this.host_session.unlink_channel (id);
|
||||
}
|
||||
|
||||
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
|
||||
}
|
||||
|
||||
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
}
|
||||
@@ -211,6 +241,8 @@ namespace Frida {
|
||||
private Gee.HashMap<AgentSessionId?, AgentSessionEntry> agent_sessions =
|
||||
new Gee.HashMap<AgentSessionId?, AgentSessionEntry> (AgentSessionId.hash, AgentSessionId.equal);
|
||||
|
||||
private ChannelRegistry channel_registry = new ChannelRegistry ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
private const double MIN_SERVER_CHECK_INTERVAL = 5.0;
|
||||
@@ -223,6 +255,10 @@ namespace Frida {
|
||||
);
|
||||
}
|
||||
|
||||
construct {
|
||||
channel_registry.channel_closed.connect (on_channel_closed);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (remote_server_request != null) {
|
||||
var server = yield try_get_remote_server (cancellable);
|
||||
@@ -832,6 +868,15 @@ namespace Frida {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void unlink_agent_session (AgentSessionId id) {
|
||||
AgentSessionEntry? entry = agent_sessions[id];
|
||||
if (entry == null || entry.sink_registration_id == 0)
|
||||
return;
|
||||
|
||||
entry.connection.unregister_object (entry.sink_registration_id);
|
||||
entry.sink_registration_id = 0;
|
||||
}
|
||||
|
||||
public async InjectorPayloadId inject_library_file (uint pid, string path, string entrypoint, string data,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var server = yield get_remote_server (cancellable);
|
||||
@@ -852,6 +897,31 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
var stream = yield channel_provider.open_channel (address, cancellable);
|
||||
|
||||
var id = ChannelId.generate ();
|
||||
channel_registry.register (id, stream);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public IOStream link_channel (ChannelId id) throws Error {
|
||||
return channel_registry.link (id);
|
||||
}
|
||||
|
||||
public void unlink_channel (ChannelId id) {
|
||||
channel_registry.unlink (id);
|
||||
}
|
||||
|
||||
private void on_channel_closed (ChannelId id) {
|
||||
channel_closed (id);
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
|
||||
}
|
||||
|
||||
private void on_gadget_entry_detached (GadgetEntry entry, SessionDetachReason reason) {
|
||||
AgentSessionId id = entry.local_session_id;
|
||||
var no_crash = CrashInfo.empty ();
|
||||
|
||||
@@ -1,82 +1,17 @@
|
||||
namespace Frida {
|
||||
public class FreebsdHostSessionBackend : Object, HostSessionBackend {
|
||||
private FreebsdHostSessionProvider local_provider;
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider == null);
|
||||
local_provider = new FreebsdHostSessionProvider ();
|
||||
provider_available (local_provider);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider != null);
|
||||
provider_unavailable (local_provider);
|
||||
yield local_provider.close (cancellable);
|
||||
local_provider = null;
|
||||
public class FreebsdHostSessionBackend : LocalHostSessionBackend {
|
||||
protected override LocalHostSessionProvider make_provider () {
|
||||
return new FreebsdHostSessionProvider ();
|
||||
}
|
||||
}
|
||||
|
||||
public class FreebsdHostSessionProvider : Object, HostSessionProvider {
|
||||
public string id {
|
||||
get { return "local"; }
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return "Local System"; }
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
get { return HostSessionProviderKind.LOCAL; }
|
||||
}
|
||||
|
||||
private FreebsdHostSession host_session;
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (host_session == null)
|
||||
return;
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
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 FreebsdHostSession ();
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
}
|
||||
|
||||
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
|
||||
if (session != host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
public class FreebsdHostSessionProvider : LocalHostSessionProvider {
|
||||
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
|
||||
return new FreebsdHostSession ();
|
||||
}
|
||||
}
|
||||
|
||||
public class FreebsdHostSession : BaseDBusHostSession {
|
||||
public class FreebsdHostSession : LocalHostSession {
|
||||
private AgentContainer system_session_container;
|
||||
|
||||
private AgentDescriptor agent_desc;
|
||||
|
||||
+114
-25
@@ -551,7 +551,6 @@ namespace Frida {
|
||||
private Gee.HashSet<Promise<Session>> pending_attach_requests = new Gee.HashSet<Promise<Session>> ();
|
||||
private Gee.HashMap<AgentSessionId?, Promise<bool>> pending_detach_requests =
|
||||
new Gee.HashMap<AgentSessionId?, Promise<bool>> (AgentSessionId.hash, AgentSessionId.equal);
|
||||
private Gee.Set<Service> services = new Gee.HashSet<Service> ();
|
||||
private Bus _bus;
|
||||
|
||||
public delegate bool ProcessPredicate (Process process);
|
||||
@@ -1238,11 +1237,16 @@ namespace Frida {
|
||||
public async IOStream open_channel (string address, Cancellable? cancellable = null) throws Error, IOError {
|
||||
check_open ();
|
||||
|
||||
var channel_provider = provider as HostChannelProvider;
|
||||
if (channel_provider == null)
|
||||
throw new Error.NOT_SUPPORTED ("Channels are not supported by this device");
|
||||
var host_session = yield get_host_session (cancellable);
|
||||
|
||||
return yield channel_provider.open_channel (address, cancellable);
|
||||
ChannelId id;
|
||||
try {
|
||||
id = yield host_session.open_channel (address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
return yield provider.link_channel (host_session, id, cancellable);
|
||||
}
|
||||
|
||||
public IOStream open_channel_sync (string address, Cancellable? cancellable = null) throws Error, IOError {
|
||||
@@ -1262,15 +1266,18 @@ namespace Frida {
|
||||
public async Service open_service (string address, Cancellable? cancellable = null) throws Error, IOError {
|
||||
check_open ();
|
||||
|
||||
var service_provider = provider as HostServiceProvider;
|
||||
if (service_provider == null)
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this device");
|
||||
var host_session = yield get_host_session (cancellable);
|
||||
|
||||
var service = yield service_provider.open_service (address, cancellable);
|
||||
service.close.connect (on_service_closed);
|
||||
services.add (service);
|
||||
ServiceSessionId id;
|
||||
try {
|
||||
id = yield host_session.open_service (address, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
return service;
|
||||
var service_session = yield provider.link_service_session (host_session, id, cancellable);
|
||||
|
||||
return new Service (service_session);
|
||||
}
|
||||
|
||||
public Service open_service_sync (string address, Cancellable? cancellable = null) throws Error, IOError {
|
||||
@@ -1287,10 +1294,6 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private void on_service_closed (Service service) {
|
||||
services.remove (service);
|
||||
}
|
||||
|
||||
public async void unpair (Cancellable? cancellable = null) throws Error, IOError {
|
||||
check_open ();
|
||||
|
||||
@@ -1389,10 +1392,6 @@ namespace Frida {
|
||||
close_request = new Promise<bool> ();
|
||||
|
||||
try {
|
||||
foreach (var service in services.to_array ())
|
||||
yield service.cancel (cancellable);
|
||||
services.clear ();
|
||||
|
||||
while (!pending_detach_requests.is_empty) {
|
||||
var iterator = pending_detach_requests.entries.iterator ();
|
||||
iterator.next ();
|
||||
@@ -2000,13 +1999,71 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public interface Service : Object {
|
||||
public sealed class Service : Object {
|
||||
public signal void close ();
|
||||
public signal void message (Variant message);
|
||||
|
||||
public abstract bool is_closed ();
|
||||
private ServiceSession? session;
|
||||
private bool disposed = false;
|
||||
|
||||
public abstract async void activate (Cancellable? cancellable = null) throws Error, IOError;
|
||||
internal Service (ServiceSession session) {
|
||||
this.session = session;
|
||||
|
||||
session.close.connect (on_close);
|
||||
session.message.connect (on_message);
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
if (!disposed) {
|
||||
disposed = true;
|
||||
|
||||
MainContext context = get_main_context ();
|
||||
if (context.is_owner ()) {
|
||||
abandon ();
|
||||
} else {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
abandon ();
|
||||
return false;
|
||||
});
|
||||
source.attach (context);
|
||||
}
|
||||
}
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
~Service () {
|
||||
forget_session ();
|
||||
}
|
||||
|
||||
private void abandon () {
|
||||
var s = session;
|
||||
if (s != null) {
|
||||
forget_session ();
|
||||
s.cancel.begin (null);
|
||||
}
|
||||
}
|
||||
|
||||
private void forget_session () {
|
||||
session.close.disconnect (on_close);
|
||||
session.message.disconnect (on_message);
|
||||
session = null;
|
||||
}
|
||||
|
||||
public bool is_closed () {
|
||||
return session == null;
|
||||
}
|
||||
|
||||
public async void activate (Cancellable? cancellable = null) throws Error, IOError {
|
||||
check_open ();
|
||||
|
||||
try {
|
||||
yield session.activate (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public void activate_sync (Cancellable? cancellable = null) throws Error, IOError {
|
||||
create<ActivateTask> ().execute (cancellable);
|
||||
@@ -2018,7 +2075,17 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract async void cancel (Cancellable? cancellable = null) throws IOError;
|
||||
public async void cancel (Cancellable? cancellable = null) throws IOError {
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
yield session.cancel (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel_sync (Cancellable? cancellable = null) throws IOError {
|
||||
try {
|
||||
@@ -2034,7 +2101,15 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError;
|
||||
public async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
|
||||
check_open ();
|
||||
|
||||
try {
|
||||
return yield session.request (parameters, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public Variant request_sync (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var task = create<RequestTask> () as RequestTask;
|
||||
@@ -2050,6 +2125,20 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private void check_open () throws Error {
|
||||
if (session == null)
|
||||
throw new Error.INVALID_OPERATION ("Session is gone");
|
||||
}
|
||||
|
||||
private void on_close () {
|
||||
forget_session ();
|
||||
close ();
|
||||
}
|
||||
|
||||
private void on_message (Variant v) {
|
||||
message (v);
|
||||
}
|
||||
|
||||
private T create<T> () {
|
||||
return Object.new (typeof (T), parent: this);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, HostServiceProvider, Pairable {
|
||||
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, Pairable {
|
||||
public Fruity.Device device {
|
||||
get;
|
||||
construct;
|
||||
@@ -108,6 +108,43 @@ namespace Frida {
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
|
||||
if (host_session != this.host_session)
|
||||
return;
|
||||
|
||||
this.host_session.unlink_agent_session (id);
|
||||
}
|
||||
|
||||
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return this.host_session.link_channel (id);
|
||||
}
|
||||
|
||||
public void unlink_channel (HostSession host_session, ChannelId id) {
|
||||
if (host_session != this.host_session)
|
||||
return;
|
||||
|
||||
this.host_session.unlink_channel (id);
|
||||
}
|
||||
|
||||
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return this.host_session.link_service_session (id);
|
||||
}
|
||||
|
||||
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
|
||||
if (host_session != this.host_session)
|
||||
return;
|
||||
|
||||
this.host_session.unlink_service_session (id);
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
}
|
||||
@@ -116,37 +153,6 @@ namespace Frida {
|
||||
return yield device.open_channel (address, cancellable);
|
||||
}
|
||||
|
||||
public async Service open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
string[] tokens = address.split (":", 2);
|
||||
unowned string protocol = tokens[0];
|
||||
unowned string service_name = tokens[1];
|
||||
|
||||
if (protocol == "plist") {
|
||||
var stream = yield device.open_lockdown_service (service_name, cancellable);
|
||||
|
||||
return new PlistService (stream);
|
||||
}
|
||||
|
||||
if (protocol == "dtx") {
|
||||
var connection = yield Fruity.DTXConnection.obtain (device, cancellable);
|
||||
|
||||
return new DTXService (service_name, connection);
|
||||
}
|
||||
|
||||
if (protocol == "xpc") {
|
||||
var tunnel = yield device.find_tunnel (cancellable);
|
||||
if (tunnel == null)
|
||||
throw new Error.NOT_SUPPORTED ("RemoteXPC not supported by device");
|
||||
|
||||
var service_info = tunnel.discovery.get_service (service_name);
|
||||
var stream = yield tunnel.open_tcp_connection (service_info.port, cancellable);
|
||||
|
||||
return new XpcService (new Fruity.XpcConnection (stream));
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported service address");
|
||||
}
|
||||
|
||||
private async void unpair (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
var client = yield device.get_lockdown_client (cancellable);
|
||||
@@ -179,6 +185,10 @@ namespace Frida {
|
||||
private Gee.HashMap<AgentSessionId?, AgentSessionEntry> agent_sessions =
|
||||
new Gee.HashMap<AgentSessionId?, AgentSessionEntry> (AgentSessionId.hash, AgentSessionId.equal);
|
||||
|
||||
private ChannelRegistry channel_registry = new ChannelRegistry ();
|
||||
|
||||
private ServiceSessionRegistry service_session_registry = new ServiceSessionRegistry ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
private const double MIN_SERVER_CHECK_INTERVAL = 5.0;
|
||||
@@ -196,6 +206,11 @@ namespace Frida {
|
||||
Object (device: device);
|
||||
}
|
||||
|
||||
construct {
|
||||
channel_registry.channel_closed.connect (on_channel_closed);
|
||||
service_session_registry.session_closed.connect (on_service_session_closed);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (remote_server_request != null) {
|
||||
try {
|
||||
@@ -999,6 +1014,15 @@ namespace Frida {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void unlink_agent_session (AgentSessionId id) {
|
||||
AgentSessionEntry? entry = agent_sessions[id];
|
||||
if (entry == null || entry.sink_registration_id == 0)
|
||||
return;
|
||||
|
||||
entry.connection.unregister_object (entry.sink_registration_id);
|
||||
entry.sink_registration_id = 0;
|
||||
}
|
||||
|
||||
public async InjectorPayloadId inject_library_file (uint pid, string path, string entrypoint, string data,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var server = yield get_remote_server (cancellable);
|
||||
@@ -1019,6 +1043,79 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
var stream = yield device.open_channel (address, cancellable);
|
||||
|
||||
var id = ChannelId.generate ();
|
||||
channel_registry.register (id, stream);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
public IOStream link_channel (ChannelId id) throws Error {
|
||||
return channel_registry.link (id);
|
||||
}
|
||||
|
||||
public void unlink_channel (ChannelId id) {
|
||||
channel_registry.unlink (id);
|
||||
}
|
||||
|
||||
private void on_channel_closed (ChannelId id) {
|
||||
channel_closed (id);
|
||||
}
|
||||
|
||||
private void on_service_session_closed (ServiceSessionId id) {
|
||||
service_session_closed (id);
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
var session = yield do_open_service (address, cancellable);
|
||||
|
||||
var id = ServiceSessionId.generate ();
|
||||
service_session_registry.register (id, session);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private async ServiceSession do_open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
string[] tokens = address.split (":", 2);
|
||||
unowned string protocol = tokens[0];
|
||||
unowned string service_name = tokens[1];
|
||||
|
||||
if (protocol == "plist") {
|
||||
var stream = yield device.open_lockdown_service (service_name, cancellable);
|
||||
|
||||
return new PlistServiceSession (stream);
|
||||
}
|
||||
|
||||
if (protocol == "dtx") {
|
||||
var connection = yield Fruity.DTXConnection.obtain (device, cancellable);
|
||||
|
||||
return new DTXServiceSession (service_name, connection);
|
||||
}
|
||||
|
||||
if (protocol == "xpc") {
|
||||
var tunnel = yield device.find_tunnel (cancellable);
|
||||
if (tunnel == null)
|
||||
throw new Error.NOT_SUPPORTED ("RemoteXPC not supported by device");
|
||||
|
||||
var service_info = tunnel.discovery.get_service (service_name);
|
||||
var stream = yield tunnel.open_tcp_connection (service_info.port, cancellable);
|
||||
|
||||
return new XpcServiceSession (new Fruity.XpcConnection (stream));
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported service address");
|
||||
}
|
||||
|
||||
public ServiceSession link_service_session (ServiceSessionId id) throws Error {
|
||||
return service_session_registry.link (id);
|
||||
}
|
||||
|
||||
public void unlink_service_session (ServiceSessionId id) {
|
||||
service_session_registry.unlink (id);
|
||||
}
|
||||
|
||||
private void add_lldb_session (LLDBSession session) {
|
||||
lldb_sessions[session.process.pid] = session;
|
||||
|
||||
@@ -1554,22 +1651,16 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class PlistService : Object, Service {
|
||||
private sealed class PlistServiceSession : Object, ServiceSession {
|
||||
public IOStream stream {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private State state = INACTIVE;
|
||||
private Fruity.PlistServiceClient client;
|
||||
private bool client_closed = false;
|
||||
|
||||
private enum State {
|
||||
INACTIVE,
|
||||
ACTIVE,
|
||||
}
|
||||
|
||||
public PlistService (IOStream stream) {
|
||||
public PlistServiceSession (IOStream stream) {
|
||||
Object (stream: stream);
|
||||
}
|
||||
|
||||
@@ -1578,8 +1669,8 @@ namespace Frida {
|
||||
client.closed.connect (on_client_closed);
|
||||
}
|
||||
|
||||
public bool is_closed () {
|
||||
return client_closed;
|
||||
~PlistServiceSession () {
|
||||
client.closed.disconnect (on_client_closed);
|
||||
}
|
||||
|
||||
public async void activate (Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1587,14 +1678,8 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void ensure_active () throws Error {
|
||||
if (state == INACTIVE) {
|
||||
state = ACTIVE;
|
||||
|
||||
if (client_closed) {
|
||||
close ();
|
||||
throw new Error.INVALID_OPERATION ("Service is closed");
|
||||
}
|
||||
}
|
||||
if (client_closed)
|
||||
throw new Error.INVALID_OPERATION ("Service is closed");
|
||||
}
|
||||
|
||||
public async void cancel (Cancellable? cancellable) throws IOError {
|
||||
@@ -1603,6 +1688,8 @@ namespace Frida {
|
||||
client_closed = true;
|
||||
|
||||
yield client.close (cancellable);
|
||||
|
||||
close ();
|
||||
}
|
||||
|
||||
public async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
|
||||
@@ -1757,7 +1844,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DTXService : Object, Service {
|
||||
private sealed class DTXServiceSession : Object, ServiceSession {
|
||||
public string identifier {
|
||||
get;
|
||||
construct;
|
||||
@@ -1777,7 +1864,7 @@ namespace Frida {
|
||||
ACTIVE,
|
||||
}
|
||||
|
||||
public DTXService (string identifier, Fruity.DTXConnection connection) {
|
||||
public DTXServiceSession (string identifier, Fruity.DTXConnection connection) {
|
||||
Object (identifier: identifier, connection: connection);
|
||||
}
|
||||
|
||||
@@ -1785,8 +1872,8 @@ namespace Frida {
|
||||
connection.notify["state"].connect (on_connection_state_changed);
|
||||
}
|
||||
|
||||
public bool is_closed () {
|
||||
return connection_closed;
|
||||
~DTXServiceSession () {
|
||||
connection.notify["state"].disconnect (on_connection_state_changed);
|
||||
}
|
||||
|
||||
public async void activate (Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1794,14 +1881,12 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void ensure_active () throws Error {
|
||||
if (connection_closed)
|
||||
throw new Error.INVALID_OPERATION ("Service is closed");
|
||||
|
||||
if (state == INACTIVE) {
|
||||
state = ACTIVE;
|
||||
|
||||
if (connection_closed) {
|
||||
close ();
|
||||
throw new Error.INVALID_OPERATION ("Service is closed");
|
||||
}
|
||||
|
||||
channel = connection.make_channel (identifier);
|
||||
channel.invocation.connect (on_channel_invocation);
|
||||
channel.notification.connect (on_channel_notification);
|
||||
@@ -1812,7 +1897,13 @@ namespace Frida {
|
||||
if (connection_closed)
|
||||
return;
|
||||
connection_closed = true;
|
||||
channel = null;
|
||||
|
||||
if (channel != null) {
|
||||
channel.invocation.disconnect (on_channel_invocation);
|
||||
channel.notification.disconnect (on_channel_notification);
|
||||
channel = null;
|
||||
}
|
||||
|
||||
close ();
|
||||
}
|
||||
|
||||
@@ -2026,7 +2117,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class XpcService : Object, Service {
|
||||
private sealed class XpcServiceSession : Object, ServiceSession {
|
||||
public Fruity.XpcConnection connection {
|
||||
get;
|
||||
construct;
|
||||
@@ -2040,7 +2131,7 @@ namespace Frida {
|
||||
ACTIVE,
|
||||
}
|
||||
|
||||
public XpcService (Fruity.XpcConnection connection) {
|
||||
public XpcServiceSession (Fruity.XpcConnection connection) {
|
||||
Object (connection: connection);
|
||||
}
|
||||
|
||||
@@ -2049,8 +2140,11 @@ namespace Frida {
|
||||
connection.message.connect (on_message);
|
||||
}
|
||||
|
||||
public bool is_closed () {
|
||||
return connection_closed;
|
||||
~XpcServiceSession () {
|
||||
connection.close.disconnect (on_close);
|
||||
connection.message.disconnect (on_message);
|
||||
|
||||
connection.cancel ();
|
||||
}
|
||||
|
||||
public async void activate (Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -2058,20 +2152,21 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void ensure_active () throws Error {
|
||||
if (connection_closed)
|
||||
throw new Error.INVALID_OPERATION ("Service is closed");
|
||||
|
||||
if (state == INACTIVE) {
|
||||
state = ACTIVE;
|
||||
|
||||
if (connection_closed) {
|
||||
close ();
|
||||
throw new Error.INVALID_OPERATION ("Service is closed");
|
||||
}
|
||||
|
||||
connection.activate ();
|
||||
}
|
||||
}
|
||||
|
||||
public async void cancel (Cancellable? cancellable) throws IOError {
|
||||
connection.cancel ();
|
||||
if (state == ACTIVE)
|
||||
connection.cancel ();
|
||||
else if (!connection_closed)
|
||||
on_close (null);
|
||||
}
|
||||
|
||||
public async Variant request (Variant parameters, Cancellable? cancellable = null) throws Error, IOError {
|
||||
@@ -2095,6 +2190,7 @@ namespace Frida {
|
||||
|
||||
private void on_close (Error? error) {
|
||||
connection_closed = true;
|
||||
|
||||
close ();
|
||||
}
|
||||
|
||||
|
||||
+69
-331
@@ -394,7 +394,7 @@ namespace Frida.Fruity {
|
||||
source.attach (main_context);
|
||||
}
|
||||
|
||||
private class TcpConnection : IOStream, AsyncInitable {
|
||||
private class TcpConnection : VirtualStream, AsyncInitable {
|
||||
public VirtualNetworkStack netstack {
|
||||
get;
|
||||
construct;
|
||||
@@ -405,53 +405,13 @@ namespace Frida.Fruity {
|
||||
construct;
|
||||
}
|
||||
|
||||
public State state {
|
||||
get {
|
||||
return _state;
|
||||
}
|
||||
}
|
||||
|
||||
public override InputStream input_stream {
|
||||
get {
|
||||
return _input_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public override OutputStream output_stream {
|
||||
get {
|
||||
return _output_stream;
|
||||
}
|
||||
}
|
||||
|
||||
public IOCondition pending_io {
|
||||
get {
|
||||
lock (state)
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
||||
private Promise<bool> established = new Promise<bool> ();
|
||||
|
||||
private State _state = OPENING;
|
||||
private TcpInputStream _input_stream;
|
||||
private TcpOutputStream _output_stream;
|
||||
|
||||
private unowned LWIP.TcpPcb? pcb;
|
||||
private IOCondition events = 0;
|
||||
private ByteArray rx_buf = new ByteArray.sized (64 * 1024);
|
||||
private ByteArray tx_buf = new ByteArray.sized (64 * 1024);
|
||||
private size_t tx_space_available = 0;
|
||||
|
||||
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
|
||||
|
||||
private MainContext main_context;
|
||||
|
||||
public enum State {
|
||||
OPENING,
|
||||
OPENED,
|
||||
CLOSED
|
||||
}
|
||||
|
||||
public static async TcpConnection open (VirtualNetworkStack netstack, InetSocketAddress address,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var connection = new TcpConnection (netstack, address);
|
||||
@@ -469,22 +429,28 @@ namespace Frida.Fruity {
|
||||
Object (netstack: netstack, address: address);
|
||||
}
|
||||
|
||||
construct {
|
||||
_input_stream = new TcpInputStream (this);
|
||||
_output_stream = new TcpOutputStream (this);
|
||||
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
}
|
||||
|
||||
public override void dispose () {
|
||||
_output_stream.detach ();
|
||||
_input_stream.detach ();
|
||||
|
||||
stop ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
protected override VirtualStream.State query_initial_state () {
|
||||
return OPENING;
|
||||
}
|
||||
|
||||
protected override IOCondition query_events () {
|
||||
IOCondition new_events = 0;
|
||||
|
||||
if (rx_buf.len != 0 || state == CLOSED)
|
||||
new_events |= IN;
|
||||
|
||||
if (tx_space_available != 0)
|
||||
new_events |= OUT;
|
||||
|
||||
return new_events;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
LWIP.Runtime.schedule (do_start);
|
||||
|
||||
@@ -547,8 +513,8 @@ namespace Frida.Fruity {
|
||||
return OK;
|
||||
});
|
||||
|
||||
_state = CLOSED;
|
||||
update_events ();
|
||||
state = CLOSED;
|
||||
update_pending_io ();
|
||||
}
|
||||
|
||||
private void detach_from_pcb () {
|
||||
@@ -557,12 +523,13 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private void on_connect () {
|
||||
lock (state)
|
||||
with_state_lock (() => {
|
||||
tx_space_available = pcb.query_available_send_buffer_space ();
|
||||
update_events ();
|
||||
});
|
||||
update_pending_io ();
|
||||
|
||||
schedule_on_frida_thread (() => {
|
||||
_state = OPENED;
|
||||
state = OPEN;
|
||||
|
||||
if (!established.future.ready)
|
||||
established.resolve (true);
|
||||
@@ -575,8 +542,8 @@ namespace Frida.Fruity {
|
||||
if (pbuf == null) {
|
||||
detach_from_pcb ();
|
||||
schedule_on_frida_thread (() => {
|
||||
_state = CLOSED;
|
||||
update_events ();
|
||||
state = CLOSED;
|
||||
update_pending_io ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
return;
|
||||
@@ -584,15 +551,17 @@ namespace Frida.Fruity {
|
||||
|
||||
var buffer = new uint8[pbuf.tot_len];
|
||||
unowned uint8[] chunk = pbuf.get_contiguous (buffer, pbuf.tot_len);
|
||||
lock (state)
|
||||
with_state_lock (() => {
|
||||
rx_buf.append (chunk[:pbuf.tot_len]);
|
||||
update_events ();
|
||||
});
|
||||
update_pending_io ();
|
||||
}
|
||||
|
||||
private void on_sent (uint16 len) {
|
||||
lock (state)
|
||||
with_state_lock (() => {
|
||||
tx_space_available = pcb.query_available_send_buffer_space () - tx_buf.len;
|
||||
update_events ();
|
||||
});
|
||||
update_pending_io ();
|
||||
}
|
||||
|
||||
private void on_error (LWIP.ErrorCode err) {
|
||||
@@ -602,8 +571,8 @@ namespace Frida.Fruity {
|
||||
else
|
||||
detach_from_pcb ();
|
||||
schedule_on_frida_thread (() => {
|
||||
_state = CLOSED;
|
||||
update_events ();
|
||||
state = CLOSED;
|
||||
update_pending_io ();
|
||||
|
||||
if (!established.future.ready)
|
||||
established.reject (parse_error (err));
|
||||
@@ -612,17 +581,11 @@ namespace Frida.Fruity {
|
||||
});
|
||||
}
|
||||
|
||||
public override bool close (GLib.Cancellable? cancellable) throws IOError {
|
||||
protected override void handle_close () {
|
||||
stop ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, GLib.Cancellable? cancellable) throws IOError {
|
||||
stop ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void shutdown_rx () throws IOError {
|
||||
public override void shutdown_read () throws IOError {
|
||||
check_io (netstack.perform_on_lwip_thread (() => {
|
||||
if (pcb == null)
|
||||
return OK;
|
||||
@@ -630,7 +593,7 @@ namespace Frida.Fruity {
|
||||
}));
|
||||
}
|
||||
|
||||
public void shutdown_tx () throws IOError {
|
||||
public override void shutdown_write () throws IOError {
|
||||
check_io (netstack.perform_on_lwip_thread (() => {
|
||||
if (pcb == null)
|
||||
return OK;
|
||||
@@ -638,17 +601,19 @@ namespace Frida.Fruity {
|
||||
}));
|
||||
}
|
||||
|
||||
public ssize_t recv (uint8[] buffer) throws IOError {
|
||||
public override ssize_t read (uint8[] buffer) throws IOError {
|
||||
ssize_t n = 0;
|
||||
|
||||
netstack.perform_on_lwip_thread (() => {
|
||||
lock (state) {
|
||||
with_state_lock (() => {
|
||||
n = ssize_t.min (buffer.length, rx_buf.len);
|
||||
if (n == 0)
|
||||
return OK;
|
||||
return;
|
||||
Memory.copy (buffer, rx_buf.data, n);
|
||||
rx_buf.remove_range (0, (uint) n);
|
||||
}
|
||||
});
|
||||
if (n == 0)
|
||||
return OK;
|
||||
|
||||
if (pcb == null)
|
||||
return OK;
|
||||
@@ -664,41 +629,43 @@ namespace Frida.Fruity {
|
||||
});
|
||||
|
||||
if (n == 0) {
|
||||
if (_state == CLOSED)
|
||||
if (state == CLOSED)
|
||||
return 0;
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
}
|
||||
|
||||
update_events ();
|
||||
update_pending_io ();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public ssize_t send (uint8[] buffer) throws IOError {
|
||||
public override ssize_t write (uint8[] buffer) throws IOError {
|
||||
ssize_t n = 0;
|
||||
|
||||
netstack.perform_on_lwip_thread (() => {
|
||||
if (pcb == null)
|
||||
return OK;
|
||||
|
||||
lock (state) {
|
||||
with_state_lock (() => {
|
||||
n = ssize_t.min (buffer.length, (ssize_t) tx_space_available);
|
||||
if (n == 0)
|
||||
return OK;
|
||||
return;
|
||||
tx_buf.append (buffer[:n]);
|
||||
tx_space_available -= n;
|
||||
}
|
||||
});
|
||||
if (n == 0)
|
||||
return OK;
|
||||
|
||||
size_t available_space = pcb.query_available_send_buffer_space ();
|
||||
|
||||
uint8[]? data = null;
|
||||
lock (state) {
|
||||
with_state_lock (() => {
|
||||
size_t num_bytes_to_write = size_t.min (tx_buf.len, available_space);
|
||||
if (num_bytes_to_write != 0) {
|
||||
data = tx_buf.data[:num_bytes_to_write];
|
||||
tx_buf.remove_range (0, (uint) num_bytes_to_write);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (data == null)
|
||||
return OK;
|
||||
|
||||
@@ -706,8 +673,9 @@ namespace Frida.Fruity {
|
||||
pcb.output ();
|
||||
|
||||
available_space = pcb.query_available_send_buffer_space ();
|
||||
lock (state)
|
||||
with_state_lock (() => {
|
||||
tx_space_available = available_space - tx_buf.len;
|
||||
});
|
||||
|
||||
return OK;
|
||||
});
|
||||
@@ -715,44 +683,11 @@ namespace Frida.Fruity {
|
||||
if (n == 0)
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
|
||||
update_events ();
|
||||
update_pending_io ();
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public void register_source (Source source, IOCondition condition) {
|
||||
lock (state)
|
||||
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
|
||||
}
|
||||
|
||||
public void unregister_source (Source source) {
|
||||
lock (state)
|
||||
sources.unset (source);
|
||||
}
|
||||
|
||||
private void update_events () {
|
||||
lock (state) {
|
||||
IOCondition new_events = 0;
|
||||
|
||||
if (rx_buf.len != 0 || _state == CLOSED)
|
||||
new_events |= IN;
|
||||
|
||||
if (tx_space_available != 0)
|
||||
new_events |= OUT;
|
||||
|
||||
events = new_events;
|
||||
|
||||
foreach (var entry in sources.entries) {
|
||||
unowned Source source = entry.key;
|
||||
IOCondition c = entry.value;
|
||||
if ((new_events & c) != 0)
|
||||
source.set_ready_time (0);
|
||||
}
|
||||
}
|
||||
|
||||
notify_property ("pending-io");
|
||||
}
|
||||
|
||||
private void schedule_on_frida_thread (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
@@ -760,201 +695,6 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private class TcpInputStream : InputStream, PollableInputStream {
|
||||
private weak TcpConnection connection;
|
||||
|
||||
public TcpInputStream (TcpConnection connection) {
|
||||
Object ();
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
internal void detach () {
|
||||
connection = null;
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
if (connection != null)
|
||||
connection.shutdown_rx ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override ssize_t read (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
if (connection == null)
|
||||
return 0;
|
||||
|
||||
if (!is_readable ()) {
|
||||
bool done = false;
|
||||
var mutex = Mutex ();
|
||||
var cond = Cond ();
|
||||
|
||||
ulong io_handler = connection.notify["pending-io"].connect ((obj, pspec) => {
|
||||
if (is_readable ()) {
|
||||
mutex.lock ();
|
||||
done = true;
|
||||
cond.signal ();
|
||||
mutex.unlock ();
|
||||
}
|
||||
});
|
||||
ulong cancellation_handler = 0;
|
||||
if (cancellable != null) {
|
||||
cancellation_handler = cancellable.connect (() => {
|
||||
mutex.lock ();
|
||||
done = true;
|
||||
cond.signal ();
|
||||
mutex.unlock ();
|
||||
});
|
||||
}
|
||||
|
||||
if (!is_readable ()) {
|
||||
mutex.lock ();
|
||||
while (!done)
|
||||
cond.wait (mutex);
|
||||
mutex.unlock ();
|
||||
}
|
||||
|
||||
if (cancellation_handler != 0)
|
||||
cancellable.disconnect (cancellation_handler);
|
||||
connection.disconnect (io_handler);
|
||||
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
|
||||
return connection.recv (buffer);
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_readable () {
|
||||
if (connection == null)
|
||||
return true;
|
||||
return (connection.pending_io & IOCondition.IN) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new TcpIOSource (connection, IOCondition.IN), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t read_nonblocking_fn (uint8[] buffer) throws GLib.Error {
|
||||
if (connection == null)
|
||||
return 0;
|
||||
return connection.recv (buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private class TcpOutputStream : OutputStream, PollableOutputStream {
|
||||
private weak TcpConnection? connection;
|
||||
|
||||
public TcpOutputStream (TcpConnection connection) {
|
||||
Object ();
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
internal void detach () {
|
||||
connection = null;
|
||||
}
|
||||
|
||||
public override bool close (Cancellable? cancellable) throws IOError {
|
||||
if (connection != null)
|
||||
connection.shutdown_tx ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool close_async (int io_priority, Cancellable? cancellable) throws GLib.IOError {
|
||||
return close (cancellable);
|
||||
}
|
||||
|
||||
public override bool flush (GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async bool flush_async (int io_priority, GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ssize_t write (uint8[] buffer, Cancellable? cancellable) throws IOError {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public bool can_poll () {
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool is_writable () {
|
||||
if (connection == null)
|
||||
return false;
|
||||
return (connection.pending_io & IOCondition.OUT) != 0;
|
||||
}
|
||||
|
||||
public PollableSource create_source (Cancellable? cancellable) {
|
||||
return new PollableSource.full (this, new TcpIOSource (connection, IOCondition.OUT), cancellable);
|
||||
}
|
||||
|
||||
public ssize_t write_nonblocking_fn (uint8[]? buffer) throws GLib.Error {
|
||||
if (connection == null)
|
||||
throw new IOError.CLOSED ("Connection is closed");
|
||||
return connection.send (buffer);
|
||||
}
|
||||
|
||||
public PollableReturn writev_nonblocking_fn (OutputVector[] vectors, out size_t bytes_written) throws GLib.Error {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
private class TcpIOSource : Source {
|
||||
public TcpConnection connection;
|
||||
public IOCondition condition;
|
||||
|
||||
public TcpIOSource (TcpConnection? connection, IOCondition condition) {
|
||||
this.connection = connection;
|
||||
this.condition = condition;
|
||||
|
||||
if (connection != null)
|
||||
connection.register_source (this, condition);
|
||||
}
|
||||
|
||||
~TcpIOSource () {
|
||||
if (connection != null)
|
||||
connection.unregister_source (this);
|
||||
}
|
||||
|
||||
protected override bool prepare (out int timeout) {
|
||||
timeout = -1;
|
||||
return is_ready ();
|
||||
}
|
||||
|
||||
protected override bool check () {
|
||||
return is_ready ();
|
||||
}
|
||||
|
||||
private bool is_ready () {
|
||||
IOCondition pending_io = (connection != null) ? connection.pending_io : IOCondition.IN;
|
||||
return (pending_io & condition) != 0;
|
||||
}
|
||||
|
||||
protected override bool dispatch (SourceFunc? callback) {
|
||||
set_ready_time (-1);
|
||||
|
||||
if (callback == null)
|
||||
return Source.REMOVE;
|
||||
|
||||
return callback ();
|
||||
}
|
||||
|
||||
protected static bool closure_callback (Closure closure) {
|
||||
var return_value = Value (typeof (bool));
|
||||
|
||||
closure.invoke (ref return_value, {});
|
||||
|
||||
return return_value.get_boolean ();
|
||||
}
|
||||
}
|
||||
|
||||
private class Ipv6UdpSocket : Object, UdpSocket, DatagramBased {
|
||||
public VirtualNetworkStack netstack {
|
||||
get;
|
||||
@@ -969,13 +709,13 @@ namespace Frida.Fruity {
|
||||
|
||||
public IOCondition pending_io {
|
||||
get {
|
||||
lock (events)
|
||||
return events;
|
||||
lock (_pending_io)
|
||||
return _pending_io;
|
||||
}
|
||||
}
|
||||
|
||||
private unowned LWIP.UdpPcb? pcb;
|
||||
private IOCondition events = OUT;
|
||||
private IOCondition _pending_io = OUT;
|
||||
private Gee.Queue<Packet> rx_queue = new Gee.ArrayQueue<Packet> ();
|
||||
|
||||
private Gee.Map<unowned Source, IOCondition> sources = new Gee.HashMap<unowned Source, IOCondition> ();
|
||||
@@ -1011,9 +751,9 @@ namespace Frida.Fruity {
|
||||
var sender = ip6_address_to_inet_socket_address (addr, port);
|
||||
var packet = new Packet (bytes, sender);
|
||||
|
||||
lock (events)
|
||||
lock (pending_io)
|
||||
rx_queue.offer (packet);
|
||||
update_events ();
|
||||
update_pending_io ();
|
||||
}
|
||||
|
||||
public void bind (InetSocketAddress address) throws Error {
|
||||
@@ -1047,14 +787,14 @@ namespace Frida.Fruity {
|
||||
int received;
|
||||
for (received = 0; received != messages.length; received++) {
|
||||
Packet? packet;
|
||||
lock (events)
|
||||
lock (pending_io)
|
||||
packet = rx_queue.poll ();
|
||||
if (packet == null) {
|
||||
if (received == 0)
|
||||
throw new IOError.WOULD_BLOCK ("Resource temporarily unavailable");
|
||||
break;
|
||||
}
|
||||
update_events ();
|
||||
update_pending_io ();
|
||||
|
||||
if (messages[received].address != null)
|
||||
*messages[received].address = packet.address.ref ();
|
||||
@@ -1133,28 +873,26 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public void register_source (Source source, IOCondition condition) {
|
||||
lock (events)
|
||||
lock (pending_io)
|
||||
sources[source] = condition | IOCondition.ERR | IOCondition.HUP;
|
||||
}
|
||||
|
||||
public void unregister_source (Source source) {
|
||||
lock (events)
|
||||
lock (pending_io)
|
||||
sources.unset (source);
|
||||
}
|
||||
|
||||
private void update_events () {
|
||||
lock (events) {
|
||||
IOCondition new_events = OUT;
|
||||
private void update_pending_io () {
|
||||
lock (pending_io) {
|
||||
_pending_io = OUT;
|
||||
|
||||
if (!rx_queue.is_empty)
|
||||
new_events |= IN;
|
||||
|
||||
events = new_events;
|
||||
_pending_io |= IN;
|
||||
|
||||
foreach (var entry in sources.entries) {
|
||||
unowned Source source = entry.key;
|
||||
IOCondition c = entry.value;
|
||||
if ((new_events & c) != 0)
|
||||
if ((_pending_io & c) != 0)
|
||||
source.set_ready_time (0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,16 @@ namespace Frida {
|
||||
|
||||
public abstract async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
|
||||
Cancellable? cancellable = null) throws Error, IOError;
|
||||
public abstract void unlink_agent_session (HostSession host_session, AgentSessionId id);
|
||||
public signal void agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash);
|
||||
|
||||
public abstract async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable = null)
|
||||
throws Error, IOError;
|
||||
public abstract void unlink_channel (HostSession host_session, ChannelId id);
|
||||
|
||||
public abstract async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id,
|
||||
Cancellable? cancellable = null) throws Error, IOError;
|
||||
public abstract void unlink_service_session (HostSession host_session, ServiceSessionId id);
|
||||
}
|
||||
|
||||
public enum HostSessionProviderKind {
|
||||
@@ -189,10 +198,6 @@ namespace Frida {
|
||||
public abstract async IOStream open_channel (string address, Cancellable? cancellable = null) throws Error, IOError;
|
||||
}
|
||||
|
||||
public interface HostServiceProvider : Object {
|
||||
public abstract async Service open_service (string address, Cancellable? cancellable = null) throws Error, IOError;
|
||||
}
|
||||
|
||||
public interface Pairable : Object {
|
||||
public abstract async void unpair (Cancellable? cancellable = null) throws Error, IOError;
|
||||
}
|
||||
@@ -205,7 +210,129 @@ namespace Frida {
|
||||
public abstract async void stop (Cancellable? cancellable = null) throws IOError;
|
||||
}
|
||||
|
||||
public abstract class BaseDBusHostSession : Object, HostSession, AgentController {
|
||||
public abstract class LocalHostSessionBackend : Object, HostSessionBackend {
|
||||
private LocalHostSessionProvider local_provider;
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider == null);
|
||||
local_provider = make_provider ();
|
||||
|
||||
provider_available (local_provider);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider != null);
|
||||
|
||||
provider_unavailable (local_provider);
|
||||
|
||||
yield local_provider.close (cancellable);
|
||||
local_provider = null;
|
||||
}
|
||||
|
||||
protected abstract LocalHostSessionProvider make_provider ();
|
||||
}
|
||||
|
||||
public abstract class LocalHostSessionProvider : Object, HostSessionProvider {
|
||||
public string id {
|
||||
get { return "local"; }
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return "Local System"; }
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return _icon; }
|
||||
}
|
||||
private Variant? _icon;
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
get { return HostSessionProviderKind.LOCAL; }
|
||||
}
|
||||
|
||||
private LocalHostSession host_session;
|
||||
|
||||
construct {
|
||||
_icon = load_icon ();
|
||||
}
|
||||
|
||||
protected abstract LocalHostSession make_host_session (HostSessionOptions? options) throws Error;
|
||||
|
||||
protected void take_host_session (LocalHostSession session) {
|
||||
host_session = session;
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
}
|
||||
|
||||
protected virtual Variant? load_icon () {
|
||||
return null;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (host_session == null)
|
||||
return;
|
||||
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
|
||||
take_host_session (make_host_session (options));
|
||||
|
||||
return host_session;
|
||||
}
|
||||
|
||||
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
|
||||
if (session != host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
|
||||
if (host_session != this.host_session)
|
||||
return;
|
||||
|
||||
this.host_session.unlink_agent_session (id);
|
||||
}
|
||||
|
||||
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
|
||||
}
|
||||
|
||||
public void unlink_channel (HostSession host_session, ChannelId id) {
|
||||
}
|
||||
|
||||
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
|
||||
}
|
||||
|
||||
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class LocalHostSession : Object, HostSession, AgentController {
|
||||
private Gee.HashMap<uint, Cancellable> pending_establish_ops = new Gee.HashMap<uint, Cancellable> ();
|
||||
|
||||
private Gee.HashMap<uint, Future<AgentEntry>> agent_entries = new Gee.HashMap<uint, Future<AgentEntry>> ();
|
||||
@@ -770,6 +897,14 @@ namespace Frida {
|
||||
return InjectorPayloadId (raw_id);
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
|
||||
}
|
||||
|
||||
#if !WINDOWS
|
||||
public async HostChildId prepare_to_fork (uint parent_pid, Cancellable? cancellable, out uint parent_injectee_id,
|
||||
out uint child_injectee_id, out GLib.Socket child_socket) throws Error, IOError {
|
||||
@@ -1288,7 +1423,7 @@ namespace Frida {
|
||||
public abstract class InternalAgent : Object, AgentMessageSink, RpcPeer {
|
||||
public signal void unloaded ();
|
||||
|
||||
public weak BaseDBusHostSession host_session {
|
||||
public weak LocalHostSession host_session {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
@@ -1,84 +1,18 @@
|
||||
namespace Frida {
|
||||
public class LinuxHostSessionBackend : Object, HostSessionBackend {
|
||||
private LinuxHostSessionProvider local_provider;
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider == null);
|
||||
local_provider = new LinuxHostSessionProvider ();
|
||||
provider_available (local_provider);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider != null);
|
||||
provider_unavailable (local_provider);
|
||||
yield local_provider.close (cancellable);
|
||||
local_provider = null;
|
||||
public class LinuxHostSessionBackend : LocalHostSessionBackend {
|
||||
protected override LocalHostSessionProvider make_provider () {
|
||||
return new LinuxHostSessionProvider ();
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxHostSessionProvider : Object, HostSessionProvider {
|
||||
public string id {
|
||||
get { return "local"; }
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return "Local System"; }
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
get { return HostSessionProviderKind.LOCAL; }
|
||||
}
|
||||
|
||||
private LinuxHostSession host_session;
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (host_session == null)
|
||||
return;
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
|
||||
public class LinuxHostSessionProvider : LocalHostSessionProvider {
|
||||
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
|
||||
var tempdir = new TemporaryDirectory ();
|
||||
|
||||
host_session = new LinuxHostSession (new LinuxHelperProcess (tempdir), tempdir);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
}
|
||||
|
||||
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
|
||||
if (session != host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
return new LinuxHostSession (new LinuxHelperProcess (tempdir), tempdir);
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxHostSession : BaseDBusHostSession {
|
||||
public class LinuxHostSession : LocalHostSession {
|
||||
public LinuxHelper helper {
|
||||
get;
|
||||
construct;
|
||||
|
||||
+44
-1
@@ -986,7 +986,7 @@ namespace Frida {
|
||||
if (host_session != channel)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
AgentSessionEntry entry = parent.sessions[id];
|
||||
AgentSessionEntry? entry = parent.sessions[id];
|
||||
if (entry == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid session ID");
|
||||
|
||||
@@ -1000,6 +1000,33 @@ namespace Frida {
|
||||
return entry.session;
|
||||
}
|
||||
|
||||
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
|
||||
if (host_session != channel)
|
||||
return;
|
||||
|
||||
AgentSessionEntry? entry = parent.sessions[id];
|
||||
if (entry == null)
|
||||
return;
|
||||
|
||||
entry.clear_node_registrations ();
|
||||
}
|
||||
|
||||
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
|
||||
}
|
||||
|
||||
public void unlink_channel (HostSession host_session, ChannelId id) {
|
||||
}
|
||||
|
||||
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
|
||||
}
|
||||
|
||||
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
}
|
||||
@@ -1278,6 +1305,14 @@ namespace Frida {
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not supported");
|
||||
}
|
||||
|
||||
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Channels are not supported by this backend");
|
||||
}
|
||||
|
||||
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Services are not supported by this backend");
|
||||
}
|
||||
}
|
||||
|
||||
private class BusService : Object, BusSession {
|
||||
@@ -1516,8 +1551,16 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void unregister_all () {
|
||||
clear_controller_registrations ();
|
||||
clear_node_registrations ();
|
||||
}
|
||||
|
||||
private void clear_controller_registrations () {
|
||||
if (controller != null)
|
||||
unregister_all_in (controller_registrations, controller.connection);
|
||||
}
|
||||
|
||||
public void clear_node_registrations () {
|
||||
if (node != null)
|
||||
unregister_all_in (node_registrations, node.connection);
|
||||
}
|
||||
|
||||
@@ -1,82 +1,17 @@
|
||||
namespace Frida {
|
||||
public class QnxHostSessionBackend : Object, HostSessionBackend {
|
||||
private QnxHostSessionProvider local_provider;
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider == null);
|
||||
local_provider = new QnxHostSessionProvider ();
|
||||
provider_available (local_provider);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider != null);
|
||||
provider_unavailable (local_provider);
|
||||
yield local_provider.close (cancellable);
|
||||
local_provider = null;
|
||||
public class QnxHostSessionBackend : LocalHostSessionBackend {
|
||||
protected override LocalHostSessionProvider make_provider () {
|
||||
return new QnxHostSessionProvider ();
|
||||
}
|
||||
}
|
||||
|
||||
public class QnxHostSessionProvider : Object, HostSessionProvider {
|
||||
public string id {
|
||||
get { return "local"; }
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return "Local System"; }
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
get { return HostSessionProviderKind.LOCAL; }
|
||||
}
|
||||
|
||||
private QnxHostSession host_session;
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (host_session == null)
|
||||
return;
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
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 QnxHostSession ();
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
}
|
||||
|
||||
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
|
||||
if (session != host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
public class QnxHostSessionProvider : LocalHostSessionProvider {
|
||||
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
|
||||
return new QnxHostSession ();
|
||||
}
|
||||
}
|
||||
|
||||
public class QnxHostSession : BaseDBusHostSession {
|
||||
public class QnxHostSession : LocalHostSession {
|
||||
private AgentContainer system_session_container;
|
||||
|
||||
private AgentDescriptor agent_desc;
|
||||
|
||||
@@ -199,6 +199,21 @@ namespace Frida {
|
||||
host_session_detached (entry.host_session);
|
||||
}
|
||||
|
||||
private HostEntry get_host_entry_by_session (HostSession session) throws Error {
|
||||
var entry = find_host_entry_by_session (session);
|
||||
if (entry == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
return entry;
|
||||
}
|
||||
|
||||
private HostEntry? find_host_entry_by_session (HostSession session) {
|
||||
foreach (var entry in hosts) {
|
||||
if (entry.host_session == session)
|
||||
return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void on_host_connection_closed (DBusConnection connection, bool remote_peer_vanished, GLib.Error? error) {
|
||||
bool closed_by_us = (!remote_peer_vanished && error == null);
|
||||
if (closed_by_us)
|
||||
@@ -219,11 +234,38 @@ namespace Frida {
|
||||
|
||||
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
foreach (var entry in hosts) {
|
||||
if (entry.host_session == host_session)
|
||||
return yield entry.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
var entry = get_host_entry_by_session (host_session);
|
||||
return yield entry.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
|
||||
var entry = find_host_entry_by_session (host_session);
|
||||
if (entry != null)
|
||||
entry.unlink_agent_session (id);
|
||||
}
|
||||
|
||||
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var entry = get_host_entry_by_session (host_session);
|
||||
return yield entry.link_channel (id, cancellable);
|
||||
}
|
||||
|
||||
public void unlink_channel (HostSession host_session, ChannelId id) {
|
||||
var entry = find_host_entry_by_session (host_session);
|
||||
if (entry != null)
|
||||
entry.unlink_channel (id);
|
||||
}
|
||||
|
||||
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var entry = get_host_entry_by_session (host_session);
|
||||
return yield entry.link_service_session (id, cancellable);
|
||||
}
|
||||
|
||||
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
|
||||
var entry = find_host_entry_by_session (host_session);
|
||||
if (entry != null)
|
||||
entry.unlink_service_session (id);
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
@@ -252,6 +294,8 @@ namespace Frida {
|
||||
|
||||
private Gee.HashMap<AgentSessionId?, AgentSessionEntry> agent_sessions =
|
||||
new Gee.HashMap<AgentSessionId?, AgentSessionEntry> (AgentSessionId.hash, AgentSessionId.equal);
|
||||
private ChannelRegistry channel_registry = new ChannelRegistry ();
|
||||
private ServiceSessionRegistry service_session_registry = new ServiceSessionRegistry ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
@@ -291,6 +335,10 @@ namespace Frida {
|
||||
agent_session_detached (id, reason, no_crash);
|
||||
agent_sessions.clear ();
|
||||
|
||||
channel_registry.clear ();
|
||||
|
||||
service_session_registry.clear ();
|
||||
|
||||
if (reason != CONNECTION_TERMINATED) {
|
||||
try {
|
||||
yield connection.close (cancellable);
|
||||
@@ -315,6 +363,41 @@ namespace Frida {
|
||||
return session;
|
||||
}
|
||||
|
||||
public void unlink_agent_session (AgentSessionId id) {
|
||||
AgentSessionEntry? entry = agent_sessions[id];
|
||||
if (entry == null || entry.sink_registration_id == 0)
|
||||
return;
|
||||
|
||||
entry.connection.unregister_object (entry.sink_registration_id);
|
||||
entry.sink_registration_id = 0;
|
||||
}
|
||||
|
||||
public async IOStream link_channel (ChannelId id, Cancellable? cancellable) throws Error, IOError {
|
||||
Channel channel = yield connection.get_proxy (null, ObjectPath.for_channel (id),
|
||||
DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
|
||||
var stream = new ChannelStream (channel);
|
||||
channel_registry.register (id, stream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void unlink_channel (ChannelId id) {
|
||||
channel_registry.unlink (id);
|
||||
}
|
||||
|
||||
public async ServiceSession link_service_session (ServiceSessionId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
ServiceSession session = yield connection.get_proxy (null, ObjectPath.for_service_session (id),
|
||||
DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
service_session_registry.register (id, session);
|
||||
return session;
|
||||
}
|
||||
|
||||
public void unlink_service_session (ServiceSessionId id) {
|
||||
service_session_registry.unlink (id);
|
||||
}
|
||||
|
||||
private bool on_keepalive_tick () {
|
||||
host_session.ping.begin (keepalive_interval, io_cancellable);
|
||||
return true;
|
||||
|
||||
@@ -1,91 +1,24 @@
|
||||
namespace Frida {
|
||||
public class WindowsHostSessionBackend : Object, HostSessionBackend {
|
||||
private WindowsHostSessionProvider local_provider;
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider == null);
|
||||
local_provider = new WindowsHostSessionProvider ();
|
||||
provider_available (local_provider);
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
assert (local_provider != null);
|
||||
provider_unavailable (local_provider);
|
||||
yield local_provider.close (cancellable);
|
||||
local_provider = null;
|
||||
public class WindowsHostSessionBackend : LocalHostSessionBackend {
|
||||
protected override LocalHostSessionProvider make_provider () {
|
||||
return new WindowsHostSessionProvider ();
|
||||
}
|
||||
}
|
||||
|
||||
public class WindowsHostSessionProvider : Object, HostSessionProvider {
|
||||
public string id {
|
||||
get { return "local"; }
|
||||
}
|
||||
|
||||
public string name {
|
||||
get { return "Local System"; }
|
||||
}
|
||||
|
||||
public Variant? icon {
|
||||
get { return _icon; }
|
||||
}
|
||||
private Variant? _icon;
|
||||
|
||||
public HostSessionProviderKind kind {
|
||||
get { return HostSessionProviderKind.LOCAL; }
|
||||
}
|
||||
|
||||
private WindowsHostSession host_session;
|
||||
|
||||
construct {
|
||||
_icon = _try_extract_icon ();
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (host_session == null)
|
||||
return;
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != null)
|
||||
throw new Error.INVALID_OPERATION ("Already created");
|
||||
|
||||
public class WindowsHostSessionProvider : LocalHostSessionProvider {
|
||||
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
|
||||
var tempdir = new TemporaryDirectory ();
|
||||
|
||||
host_session = new WindowsHostSession (new WindowsHelperProcess (tempdir), tempdir);
|
||||
host_session.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
return host_session;
|
||||
return new WindowsHostSession (new WindowsHelperProcess (tempdir), tempdir);
|
||||
}
|
||||
|
||||
public async void destroy (HostSession session, Cancellable? cancellable) throws Error, IOError {
|
||||
if (session != host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
host_session.agent_session_detached.disconnect (on_agent_session_detached);
|
||||
|
||||
yield host_session.close (cancellable);
|
||||
host_session = null;
|
||||
}
|
||||
|
||||
public async AgentSession link_agent_session (HostSession host_session, AgentSessionId id, AgentMessageSink sink,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
if (host_session != this.host_session)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid host session");
|
||||
|
||||
return yield this.host_session.link_agent_session (id, sink, cancellable);
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
agent_session_detached (id, reason, crash);
|
||||
protected override Variant? load_icon () {
|
||||
return _try_extract_icon ();
|
||||
}
|
||||
|
||||
public extern static Variant? _try_extract_icon ();
|
||||
}
|
||||
|
||||
public class WindowsHostSession : BaseDBusHostSession {
|
||||
public class WindowsHostSession : LocalHostSession {
|
||||
public WindowsHelper helper {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 16.7.2
|
||||
revision = 16.7.4
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -471,6 +471,27 @@ namespace Frida.HostSessionTest {
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not implemented");
|
||||
}
|
||||
|
||||
public void unlink_agent_session (HostSession host_session, AgentSessionId id) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public async IOStream link_channel (HostSession host_session, ChannelId id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not implemented");
|
||||
}
|
||||
|
||||
public void unlink_channel (HostSession host_session, ChannelId id) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
public async ServiceSession link_service_session (HostSession host_session, ServiceSessionId id,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not implemented");
|
||||
}
|
||||
|
||||
public void unlink_service_session (HostSession host_session, ServiceSessionId id) {
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_LOCAL_BACKEND
|
||||
|
||||
Reference in new issue
Block a user