mirror of
https://github.com/frida/frida-core.git
synced 2026-11-13 04:31:07 -10:00
Compare commits
No files matched your search
@@ -0,0 +1 @@
|
||||
github: frida
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
if: matrix.id == 'linux-x86'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gcc-multilib lib32stdc++-11-dev
|
||||
sudo apt-get install gcc-multilib lib32stdc++-13-dev
|
||||
- name: Build
|
||||
if: ${{ startsWith(matrix.id, 'windows-') }}
|
||||
run: |
|
||||
@@ -150,6 +150,14 @@ jobs:
|
||||
certificates-p12: ${{ secrets.APPLE_CERTIFICATES_P12 }}
|
||||
certificates-password: ${{ secrets.APPLE_CERTIFICATES_PASSWORD }}
|
||||
keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
|
||||
- uses: nttld/setup-ndk@v1
|
||||
if: startsWith(matrix.id, 'android-')
|
||||
id: setup-ndk
|
||||
with:
|
||||
ndk-version: r25c
|
||||
- name: Add NDK to env
|
||||
if: startsWith(matrix.id, 'android-')
|
||||
run: echo ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }} >> $GITHUB_ENV
|
||||
- name: Build
|
||||
run: |
|
||||
./configure --host=${{ matrix.id }} ${{ env.FRIDA_CORE_OPTIONS }}
|
||||
@@ -233,3 +241,35 @@ jobs:
|
||||
cp -a tests/frida-tests tests/labrats lib/agent/frida-agent.so /tmp/pkg/
|
||||
tar -C /tmp/pkg -cf /tmp/runner.tar .
|
||||
/opt/sabrelite/run.sh /tmp/runner.tar /opt/frida/frida-tests
|
||||
|
||||
# This job is used to check that the Linux helpers are built correctly for all
|
||||
# supported architectures. We do this because the binary artifacts are checked
|
||||
# in, and we want to ensure that they are up-to-date with respect to the source
|
||||
# for all architectures.
|
||||
check-linux-helpers:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86, x86_64, arm, armbe8, arm64, arm64be, arm64beilp32, mips, mipsel, mips64, mips64el]
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/frida/core-linux-helpers-${{ matrix.arch }}:latest
|
||||
steps:
|
||||
- name: Check out repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build
|
||||
run: |
|
||||
./src/linux/helpers/rebuild.sh ${{ matrix.arch }}
|
||||
- name: Check for unexpected changes
|
||||
run: |
|
||||
git config --global --add safe.directory "$(realpath .)"
|
||||
status_output="$(git status --porcelain)"
|
||||
if [ -n "$status_output" ]; then
|
||||
echo "Unexpected changes detected:"
|
||||
echo "$status_output"
|
||||
echo "Diff:"
|
||||
git diff
|
||||
exit 1
|
||||
else
|
||||
echo "No unexpected changes detected."
|
||||
fi
|
||||
+4
-3
@@ -4,6 +4,7 @@ import base64
|
||||
from collections import OrderedDict
|
||||
from dataclasses import dataclass, field
|
||||
import itertools
|
||||
import locale
|
||||
import os
|
||||
from pathlib import Path
|
||||
import pickle
|
||||
@@ -400,7 +401,7 @@ def compile(privdir: Path, state: State):
|
||||
**kwargs,
|
||||
"stdout": subprocess.PIPE,
|
||||
"stderr": subprocess.STDOUT,
|
||||
"encoding": "utf-8",
|
||||
"encoding": locale.getpreferredencoding(),
|
||||
}
|
||||
else:
|
||||
silenced_kwargs = kwargs
|
||||
@@ -603,10 +604,10 @@ def grab_subprojects_from_parent(subprojects: dict[str, Path], releng_location:
|
||||
continue
|
||||
|
||||
try:
|
||||
subp_here.symlink_to(location, target_is_directory=True)
|
||||
subp_here.symlink_to(Path("..") / ".." / name, target_is_directory=True)
|
||||
continue
|
||||
except OSError as e:
|
||||
if not getattr(e, "winerror") == 1314:
|
||||
if not getattr(e, "winerror", None) == 1314:
|
||||
raise e
|
||||
|
||||
subprocess.run(["git", "worktree", "add", subp_here],
|
||||
|
||||
+63
-34
@@ -147,7 +147,7 @@ namespace Frida.Inject {
|
||||
public extern void init ();
|
||||
}
|
||||
|
||||
public class Application : Object {
|
||||
public sealed class Application : Object {
|
||||
public string? device_id {
|
||||
get;
|
||||
construct;
|
||||
@@ -388,7 +388,7 @@ namespace Frida.Inject {
|
||||
return false;
|
||||
}
|
||||
|
||||
script_runner.on_stdin (line);
|
||||
script_runner.on_stdin (line, null);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -414,14 +414,19 @@ namespace Frida.Inject {
|
||||
|
||||
buf[n] = 0;
|
||||
|
||||
script_runner.on_stdin ((string) buf);
|
||||
if (script_runner.terminal_mode == TerminalMode.BINARY) {
|
||||
var bytes = new Bytes (buf[:n]);
|
||||
script_runner.on_stdin ("", bytes);
|
||||
} else {
|
||||
script_runner.on_stdin ((string) buf, null);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private class ScriptRunner : Object, RpcPeer {
|
||||
private sealed class ScriptRunner : Object, RpcPeer {
|
||||
public TerminalMode terminal_mode {
|
||||
get;
|
||||
private set;
|
||||
@@ -549,7 +554,7 @@ namespace Frida.Inject {
|
||||
var stage = new Json.Node.alloc ().init_string ("early");
|
||||
|
||||
try {
|
||||
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, io_cancellable);
|
||||
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
@@ -591,7 +596,7 @@ namespace Frida.Inject {
|
||||
private async TerminalMode query_terminal_mode () {
|
||||
Json.Node mode_value;
|
||||
try {
|
||||
mode_value = yield rpc_client.call ("getFridaTerminalMode", new Json.Node[] {}, io_cancellable);
|
||||
mode_value = yield rpc_client.call ("getFridaTerminalMode", new Json.Node[] {}, null, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
return COOKED;
|
||||
}
|
||||
@@ -599,7 +604,14 @@ namespace Frida.Inject {
|
||||
if (mode_value.get_value_type () != typeof (string))
|
||||
return COOKED;
|
||||
|
||||
return (mode_value.get_string () == "raw") ? TerminalMode.RAW : TerminalMode.COOKED;
|
||||
switch (mode_value.get_string ()) {
|
||||
case "raw":
|
||||
return TerminalMode.RAW;
|
||||
case "binary":
|
||||
return TerminalMode.BINARY;
|
||||
default:
|
||||
return TerminalMode.COOKED;
|
||||
}
|
||||
}
|
||||
|
||||
private void apply_terminal_mode (TerminalMode mode) throws Error {
|
||||
@@ -632,10 +644,10 @@ namespace Frida.Inject {
|
||||
}
|
||||
#endif
|
||||
|
||||
public void on_stdin (string data) {
|
||||
var data_value = new Json.Node.alloc ().init_string (data);
|
||||
public void on_stdin (string str, Bytes? data) {
|
||||
var str_value = new Json.Node.alloc ().init_string (str);
|
||||
|
||||
rpc_client.call.begin ("onFridaStdin", new Json.Node[] { data_value }, io_cancellable);
|
||||
rpc_client.call.begin ("onFridaStdin", new Json.Node[] { str_value }, data, io_cancellable);
|
||||
}
|
||||
|
||||
private void on_script_file_changed (File file, File? other_file, FileMonitorEvent event_type) {
|
||||
@@ -675,7 +687,7 @@ namespace Frida.Inject {
|
||||
handled = try_handle_log_message (message);
|
||||
break;
|
||||
case "send":
|
||||
handled = try_handle_stdout_message (message);
|
||||
handled = try_handle_stdout_message (message, data);
|
||||
break;
|
||||
default:
|
||||
handled = false;
|
||||
@@ -722,20 +734,19 @@ namespace Frida.Inject {
|
||||
*
|
||||
* {"type":"send","payload":["frida:stdout","DATA"]}
|
||||
*/
|
||||
private bool try_handle_stdout_message (Json.Object message) {
|
||||
private bool try_handle_stdout_message (Json.Object message, Bytes? data) {
|
||||
var payload = message.get_member ("payload");
|
||||
if (payload.get_node_type () != Json.NodeType.ARRAY)
|
||||
return false;
|
||||
|
||||
var tuple = payload.get_array ();
|
||||
if (tuple.get_length () != 2)
|
||||
var tuple_len = tuple.get_length ();
|
||||
if (tuple_len == 0)
|
||||
return false;
|
||||
|
||||
var first_element = tuple.get_element (0);
|
||||
if (first_element.get_value_type () != typeof (string))
|
||||
var type = tuple.get_element (0).get_string ();
|
||||
if (type == null)
|
||||
return false;
|
||||
|
||||
var type = first_element.get_string ();
|
||||
switch (type) {
|
||||
case "frida:stdout":
|
||||
case "frida:stderr":
|
||||
@@ -744,31 +755,49 @@ namespace Frida.Inject {
|
||||
return false;
|
||||
}
|
||||
|
||||
var second_element = tuple.get_element (1);
|
||||
if (second_element.get_value_type () != typeof (string))
|
||||
return false;
|
||||
var str = second_element.get_string ();
|
||||
|
||||
switch (type) {
|
||||
case "frida:stdout":
|
||||
stdout.write (str.data);
|
||||
stdout.flush ();
|
||||
return true;
|
||||
case "frida:stderr":
|
||||
stderr.write (str.data);
|
||||
return true;
|
||||
default:
|
||||
if (tuple_len >= 2) {
|
||||
var str = tuple.get_element (1).get_string ();
|
||||
if (str == null)
|
||||
return false;
|
||||
|
||||
switch (type) {
|
||||
case "frida:stdout":
|
||||
stdout.write (str.data);
|
||||
stdout.flush ();
|
||||
break;
|
||||
case "frida:stderr":
|
||||
stderr.write (str.data);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (data != null) {
|
||||
switch (type) {
|
||||
case "frida:stdout":
|
||||
stdout.write (data.get_data ());
|
||||
stdout.flush ();
|
||||
break;
|
||||
case "frida:stderr":
|
||||
stderr.write (data.get_data ());
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
|
||||
script.post (json, null);
|
||||
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
script.post (json, data);
|
||||
}
|
||||
}
|
||||
|
||||
private enum TerminalMode {
|
||||
COOKED,
|
||||
RAW
|
||||
RAW,
|
||||
BINARY
|
||||
}
|
||||
}
|
||||
+17
-18
@@ -11,7 +11,7 @@ namespace Frida.Agent {
|
||||
PROCESS_TRANSITION
|
||||
}
|
||||
|
||||
private class Runner : Object, ProcessInvader, AgentSessionProvider, ExitHandler, ForkHandler, SpawnHandler {
|
||||
private sealed class Runner : Object, ProcessInvader, AgentSessionProvider, ExitHandler, ForkHandler, SpawnHandler {
|
||||
public static Runner shared_instance = null;
|
||||
public static Mutex shared_mutex;
|
||||
private static string? cached_agent_path = null;
|
||||
@@ -1509,13 +1509,14 @@ namespace Frida.Agent {
|
||||
}
|
||||
|
||||
public static NativeBridgeApi open () throws Error {
|
||||
string? nb_mod = null;
|
||||
string? vm_mod = null;
|
||||
Gum.Process.enumerate_modules ((details) => {
|
||||
if (/\/lib(64)?\/libnativebridge.so$/.match (details.path))
|
||||
nb_mod = details.path;
|
||||
else if (/^lib(art|dvm).so$/.match (details.name) && !/\/system\/fake-libs/.match (details.path))
|
||||
vm_mod = details.path;
|
||||
Gum.Module? nb_mod = null;
|
||||
Gum.Module? vm_mod = null;
|
||||
Gum.Process.enumerate_modules (module => {
|
||||
unowned string path = module.path;
|
||||
if (/\/lib(64)?\/libnativebridge.so$/.match (path))
|
||||
nb_mod = module;
|
||||
else if (/^lib(art|dvm).so$/.match (module.name) && !/\/system\/fake-libs/.match (path))
|
||||
vm_mod = module;
|
||||
bool carry_on = nb_mod == null || vm_mod == null;
|
||||
return carry_on;
|
||||
});
|
||||
@@ -1530,29 +1531,27 @@ namespace Frida.Agent {
|
||||
NBUnloadLibraryFunc? unload;
|
||||
NBGetTrampolineFunc get_trampoline;
|
||||
|
||||
load = (NBLoadLibraryFunc) Gum.Module.find_export_by_name (nb_mod, "NativeBridgeLoadLibrary");;
|
||||
load = (NBLoadLibraryFunc) nb_mod.find_export_by_name ("NativeBridgeLoadLibrary");;
|
||||
if (load != null) {
|
||||
flavor = MODERN;
|
||||
load_ext = (NBLoadLibraryExtFunc) Gum.Module.find_export_by_name (nb_mod, "NativeBridgeLoadLibraryExt");
|
||||
load_ext = (NBLoadLibraryExtFunc) nb_mod.find_export_by_name ("NativeBridgeLoadLibraryExt");
|
||||
// XXX: NativeBridgeUnloadLibrary() is only a stub as of Android 11 w/ libndk_translation.so
|
||||
unload = null;
|
||||
get_trampoline = (NBGetTrampolineFunc) Gum.Module.find_export_by_name (nb_mod,
|
||||
"NativeBridgeGetTrampoline");
|
||||
get_trampoline = (NBGetTrampolineFunc) nb_mod.find_export_by_name ("NativeBridgeGetTrampoline");
|
||||
} else {
|
||||
flavor = LEGACY;
|
||||
load = (NBLoadLibraryFunc) Gum.Module.find_export_by_name (nb_mod,
|
||||
"_ZN7android23NativeBridgeLoadLibraryEPKci");
|
||||
load_ext = (NBLoadLibraryExtFunc) Gum.Module.find_export_by_name (nb_mod,
|
||||
load = (NBLoadLibraryFunc) nb_mod.find_export_by_name ("_ZN7android23NativeBridgeLoadLibraryEPKci");
|
||||
load_ext = (NBLoadLibraryExtFunc) nb_mod.find_export_by_name (
|
||||
"_ZN7android26NativeBridgeLoadLibraryExtEPKciPNS_25native_bridge_namespace_tE");
|
||||
// XXX: Unload implementation seems to be unreliable.
|
||||
unload = null;
|
||||
get_trampoline = (NBGetTrampolineFunc) Gum.Module.find_export_by_name (nb_mod,
|
||||
get_trampoline = (NBGetTrampolineFunc) nb_mod.find_export_by_name (
|
||||
"_ZN7android25NativeBridgeGetTrampolineEPvPKcS2_j");
|
||||
}
|
||||
if (load == null || get_trampoline == null)
|
||||
throw new Error.NOT_SUPPORTED ("NativeBridge API is not available on this system");
|
||||
|
||||
var get_vms = (JNIGetCreatedJavaVMsFunc) Gum.Module.find_export_by_name (vm_mod, "JNI_GetCreatedJavaVMs");
|
||||
var get_vms = (JNIGetCreatedJavaVMsFunc) vm_mod.find_export_by_name ("JNI_GetCreatedJavaVMs");
|
||||
if (get_vms == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to locate Java VM");
|
||||
|
||||
@@ -1622,7 +1621,7 @@ namespace Frida.Agent {
|
||||
}
|
||||
#endif
|
||||
|
||||
private class LiveAgentSession : BaseAgentSession {
|
||||
private sealed class LiveAgentSession : BaseAgentSession {
|
||||
public uint registration_id {
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
global:
|
||||
frida_agent_main;
|
||||
JNI_OnLoad;
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
global:
|
||||
frida_agent_main;
|
||||
JNI_OnLoad;
|
||||
|
||||
local:
|
||||
*;
|
||||
|
||||
@@ -28,7 +28,7 @@ if host_os_family == 'windows'
|
||||
elif host_os_family == 'darwin'
|
||||
extra_link_args += '-Wl,-exported_symbol,_frida_agent_main'
|
||||
else
|
||||
symscript = 'frida-agent.version'
|
||||
symscript = (host_os == 'android') ? 'frida-agent-android.version' : 'frida-agent.version'
|
||||
extra_link_args += '-Wl,--version-script,' + meson.current_source_dir() / symscript
|
||||
extra_link_depends += symscript
|
||||
endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class BufferBuilder : Object {
|
||||
public sealed class BufferBuilder : Object {
|
||||
public ByteOrder byte_order {
|
||||
get;
|
||||
construct;
|
||||
@@ -307,7 +307,7 @@ namespace Frida {
|
||||
NUL
|
||||
}
|
||||
|
||||
public class Buffer : Object {
|
||||
public sealed class Buffer : Object {
|
||||
public Bytes bytes {
|
||||
get;
|
||||
construct;
|
||||
@@ -466,7 +466,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class BufferReader {
|
||||
public sealed class BufferReader {
|
||||
public size_t available {
|
||||
get {
|
||||
return buffer.bytes.get_size () - offset;
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
namespace Frida {
|
||||
public sealed 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 sealed 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 sealed 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,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class CFArray<T> {
|
||||
public sealed class CFArray<T> {
|
||||
private CoreFoundation.Array handle;
|
||||
|
||||
public static CFArray<T> wrap<T> (void * handle) {
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ namespace Frida {
|
||||
return yield connection.get_proxy (null, ObjectPath.HOST_SESSION, DBusProxyFlags.NONE, cancellable);
|
||||
}
|
||||
|
||||
private class DummyInputStream : InputStream {
|
||||
private sealed class DummyInputStream : InputStream {
|
||||
private bool done = false;
|
||||
private Mutex mutex;
|
||||
private Cond cond;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
base_sources = [
|
||||
'session.vala',
|
||||
'session-glue.c',
|
||||
'channel.vala',
|
||||
'service.vala',
|
||||
'stream.vala',
|
||||
'rpc.vala',
|
||||
'p2p.vala',
|
||||
'p2p-glue.c',
|
||||
@@ -43,14 +46,17 @@ if host_os_family == 'darwin'
|
||||
]
|
||||
endif
|
||||
|
||||
base_gir_name = f'FridaBase-@[email protected]'
|
||||
|
||||
base = static_library('frida-base-' + api_version, base_sources,
|
||||
c_args: frida_component_cflags,
|
||||
vala_args: [base_vala_args, '--pkg=libsoup-3.0'] + extra_vala_args,
|
||||
vala_header: 'frida-base.h',
|
||||
vala_gir: base_gir_name,
|
||||
vala_vapi: f'frida-base-@[email protected]',
|
||||
dependencies: [glib_dep, gobject_dep, gio_dep, libsoup_dep, nice_dep, usrsctp_dep, gee_dep, json_glib_dep, gum_dep, openssl_dep] + extra_deps,
|
||||
install: true,
|
||||
install_dir: [true, header_install_dir, true],
|
||||
install_dir: [true, header_install_dir, true, false],
|
||||
)
|
||||
|
||||
base_dep = declare_dependency(
|
||||
|
||||
+35
-233
@@ -169,7 +169,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class PeerSocket : Object, DatagramBased {
|
||||
public sealed class PeerSocket : Object, DatagramBased {
|
||||
public Nice.Agent agent {
|
||||
get;
|
||||
construct;
|
||||
@@ -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 sealed 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;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class Promise<T> {
|
||||
public sealed class Promise<T> {
|
||||
private Impl<T> impl;
|
||||
|
||||
public Future<T> future {
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class RpcClient : Object {
|
||||
public sealed class RpcClient : Object {
|
||||
public weak RpcPeer peer {
|
||||
get;
|
||||
construct;
|
||||
@@ -11,7 +11,7 @@ namespace Frida {
|
||||
Object (peer: peer);
|
||||
}
|
||||
|
||||
public async Json.Node call (string method, Json.Node[] args, Cancellable? cancellable) throws Error, IOError {
|
||||
public async Json.Node call (string method, Json.Node[] args, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
string request_id = Uuid.string_random ();
|
||||
|
||||
var request = new Json.Builder ();
|
||||
@@ -39,7 +39,7 @@ namespace Frida {
|
||||
pending_responses[request_id] = pending;
|
||||
|
||||
try {
|
||||
yield peer.post_rpc_message (raw_request, cancellable);
|
||||
yield peer.post_rpc_message (raw_request, data, cancellable);
|
||||
} catch (Error e) {
|
||||
if (pending_responses.unset (request_id))
|
||||
pending.complete_with_error (e);
|
||||
@@ -158,6 +158,6 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public interface RpcPeer : Object {
|
||||
public abstract async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace Frida {
|
||||
public sealed 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+96
-16
@@ -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);
|
||||
}
|
||||
|
||||
@@ -145,7 +151,7 @@ namespace Frida {
|
||||
DEBUGGER
|
||||
}
|
||||
|
||||
public class AgentMessageTransmitter : Object {
|
||||
public sealed class AgentMessageTransmitter : Object {
|
||||
public signal void closed ();
|
||||
public signal void new_candidates (string[] candidate_sdps);
|
||||
public signal void candidate_gathering_done ();
|
||||
@@ -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,
|
||||
@@ -770,7 +793,7 @@ namespace Frida {
|
||||
public abstract async string authenticate (string token, Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
public class StaticAuthenticationService : Object, AuthenticationService {
|
||||
public sealed class StaticAuthenticationService : Object, AuthenticationService {
|
||||
public string token_hash {
|
||||
get;
|
||||
construct;
|
||||
@@ -795,13 +818,13 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class NullAuthenticationService : Object, AuthenticationService {
|
||||
public sealed class NullAuthenticationService : Object, AuthenticationService {
|
||||
public async string authenticate (string token, Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.INVALID_OPERATION ("Authentication not expected");
|
||||
}
|
||||
}
|
||||
|
||||
public class UnauthorizedHostSession : Object, HostSession {
|
||||
public sealed class UnauthorizedHostSession : Object, HostSession {
|
||||
public async void ping (uint interval_seconds, Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_authorized ();
|
||||
}
|
||||
@@ -875,9 +898,17 @@ 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 {
|
||||
public sealed class UnauthorizedPortalSession : Object, PortalSession {
|
||||
public async void join (HostApplicationInfo app, SpawnStartState current_state,
|
||||
AgentSessionId[] interrupted_sessions, HashTable<string, Variant> options,
|
||||
Cancellable? cancellable, out SpawnStartState next_state) throws Error, IOError {
|
||||
@@ -885,7 +916,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class UnauthorizedBusSession : Object, BusSession {
|
||||
public sealed class UnauthorizedBusSession : Object, BusSession {
|
||||
public async void attach (Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_authorized ();
|
||||
}
|
||||
@@ -1083,7 +1114,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class FrontmostQueryOptions : Object {
|
||||
public sealed class FrontmostQueryOptions : Object {
|
||||
public Scope scope {
|
||||
get;
|
||||
set;
|
||||
@@ -1113,7 +1144,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class ApplicationQueryOptions : Object {
|
||||
public sealed class ApplicationQueryOptions : Object {
|
||||
public Scope scope {
|
||||
get;
|
||||
set;
|
||||
@@ -1171,7 +1202,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class ProcessQueryOptions : Object {
|
||||
public sealed class ProcessQueryOptions : Object {
|
||||
public Scope scope {
|
||||
get;
|
||||
set;
|
||||
@@ -1313,7 +1344,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class SessionOptions : Object {
|
||||
public sealed class SessionOptions : Object {
|
||||
public Realm realm {
|
||||
get;
|
||||
set;
|
||||
@@ -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;
|
||||
|
||||
@@ -1518,7 +1589,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class ScriptOptions : Object {
|
||||
public sealed class ScriptOptions : Object {
|
||||
public string? name {
|
||||
get;
|
||||
set;
|
||||
@@ -1608,7 +1679,7 @@ namespace Frida {
|
||||
SHARED_MEMORY
|
||||
}
|
||||
|
||||
public class SnapshotOptions : Object {
|
||||
public sealed class SnapshotOptions : Object {
|
||||
public string? warmup_script {
|
||||
get;
|
||||
set;
|
||||
@@ -1683,7 +1754,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class PortalOptions : Object {
|
||||
public sealed class PortalOptions : Object {
|
||||
public TlsCertificate? certificate {
|
||||
get;
|
||||
set;
|
||||
@@ -1746,7 +1817,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class PeerOptions : Object {
|
||||
public sealed class PeerOptions : Object {
|
||||
public string? stun_server {
|
||||
get;
|
||||
set;
|
||||
@@ -1807,7 +1878,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Relay : Object {
|
||||
public sealed class Relay : Object {
|
||||
public string address {
|
||||
get;
|
||||
construct;
|
||||
@@ -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 {
|
||||
|
||||
+501
-603
File diff suppressed because it is too large.
Load diff
@@ -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 sealed 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 sealed 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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -38,7 +38,7 @@ namespace Frida {
|
||||
public abstract unowned string get_uuid_value () throws Error;
|
||||
}
|
||||
|
||||
public class VariantReader : Object, ObjectReader {
|
||||
public sealed class VariantReader : Object, ObjectReader {
|
||||
public Variant root_object {
|
||||
get {
|
||||
return scopes.peek_head ().val;
|
||||
@@ -180,7 +180,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonObjectBuilder : Object, ObjectBuilder {
|
||||
public sealed class JsonObjectBuilder : Object, ObjectBuilder {
|
||||
private Json.Builder builder = new Json.Builder ();
|
||||
private Gee.Map<string, Bytes> raw_values = new Gee.HashMap<string, Bytes> ();
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonObjectReader : Object, ObjectReader {
|
||||
public sealed class JsonObjectReader : Object, ObjectReader {
|
||||
private Json.Reader reader;
|
||||
|
||||
public JsonObjectReader (string json) throws Error {
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class XpcClient : Object {
|
||||
public sealed class XpcClient : Object {
|
||||
public signal void message (Darwin.Xpc.Object obj);
|
||||
|
||||
public State state {
|
||||
@@ -123,7 +123,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class XpcObjectReader {
|
||||
public sealed class XpcObjectReader {
|
||||
public Darwin.Xpc.Object root_object {
|
||||
get {
|
||||
return scopes.peek_head ().object;
|
||||
@@ -204,6 +204,13 @@ namespace Frida {
|
||||
return peek_scope ().get_object<Darwin.Xpc.UInt64> (Darwin.Xpc.UInt64.TYPE).get_value ();
|
||||
}
|
||||
|
||||
public unowned uint8[] get_data_value () throws Error {
|
||||
var data = peek_scope ().get_object<Darwin.Xpc.Data> (Darwin.Xpc.Data.TYPE);
|
||||
unowned uint8[] buf = (uint8[]) data.get_bytes_ptr ();
|
||||
buf.length = (int) data.get_length ();
|
||||
return buf;
|
||||
}
|
||||
|
||||
public unowned string get_string_value () throws Error {
|
||||
return peek_scope ().get_object<Darwin.Xpc.String> (Darwin.Xpc.String.TYPE).get_string_ptr ();
|
||||
}
|
||||
|
||||
+89
-28
@@ -1,5 +1,5 @@
|
||||
namespace Frida.Gadget {
|
||||
private class Config : Object, Json.Serializable {
|
||||
private sealed class Config : Object, Json.Serializable {
|
||||
public Object interaction {
|
||||
get;
|
||||
set;
|
||||
@@ -93,7 +93,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ScriptInteraction : Object, Json.Serializable {
|
||||
private sealed class ScriptInteraction : Object, Json.Serializable {
|
||||
public string path {
|
||||
get;
|
||||
set;
|
||||
@@ -145,7 +145,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ScriptDirectoryInteraction : Object {
|
||||
private sealed class ScriptDirectoryInteraction : Object {
|
||||
public string path {
|
||||
get;
|
||||
set;
|
||||
@@ -164,7 +164,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ScriptConfig : Object, Json.Serializable {
|
||||
private sealed class ScriptConfig : Object, Json.Serializable {
|
||||
public ProcessFilter? filter {
|
||||
get;
|
||||
set;
|
||||
@@ -216,7 +216,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ProcessFilter : Object {
|
||||
private sealed class ProcessFilter : Object {
|
||||
public string[] executables {
|
||||
get;
|
||||
set;
|
||||
@@ -258,7 +258,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ListenInteraction : SocketInteraction {
|
||||
private sealed class ListenInteraction : SocketInteraction {
|
||||
public PortConflictBehavior on_port_conflict {
|
||||
get;
|
||||
set;
|
||||
@@ -287,7 +287,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ConnectInteraction : SocketInteraction, Json.Serializable {
|
||||
private sealed class ConnectInteraction : SocketInteraction, Json.Serializable {
|
||||
public string[]? acl {
|
||||
get;
|
||||
set;
|
||||
@@ -328,7 +328,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class Location : Object {
|
||||
private sealed class Location : Object {
|
||||
public string executable_name {
|
||||
get;
|
||||
construct;
|
||||
@@ -751,7 +751,7 @@ namespace Frida.Gadget {
|
||||
|
||||
string config_data;
|
||||
try {
|
||||
FileUtils.get_contents (config_path, out config_data);
|
||||
load_asset_text (config_path, out config_data);
|
||||
} catch (FileError e) {
|
||||
if (e is FileError.NOENT)
|
||||
return new Config ();
|
||||
@@ -988,7 +988,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ScriptRunner : BaseController {
|
||||
private sealed class ScriptRunner : BaseController {
|
||||
private ScriptEngine engine;
|
||||
private Script script;
|
||||
|
||||
@@ -1036,7 +1036,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ScriptDirectoryRunner : BaseController {
|
||||
private sealed class ScriptDirectoryRunner : BaseController {
|
||||
public string directory_path {
|
||||
get;
|
||||
construct;
|
||||
@@ -1226,7 +1226,7 @@ namespace Frida.Gadget {
|
||||
private ScriptConfig load_config (string path) throws Error {
|
||||
string data;
|
||||
try {
|
||||
FileUtils.get_contents (path, out data);
|
||||
load_asset_text (path, out data);
|
||||
} catch (FileError e) {
|
||||
if (e is FileError.NOENT)
|
||||
return new ScriptConfig ();
|
||||
@@ -1241,7 +1241,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class Script : Object, RpcPeer {
|
||||
private sealed class Script : Object, RpcPeer {
|
||||
private const uint8 QUICKJS_BYTECODE_MAGIC = 0x02;
|
||||
|
||||
public enum ChangeBehavior {
|
||||
@@ -1349,9 +1349,9 @@ namespace Frida.Gadget {
|
||||
try {
|
||||
var path = this.path;
|
||||
|
||||
uint8[] contents;
|
||||
Bytes contents;
|
||||
try {
|
||||
FileUtils.get_data (path, out contents);
|
||||
load_asset_bytes (path, out contents);
|
||||
} catch (FileError e) {
|
||||
throw new Error.INVALID_ARGUMENT ("%s", e.message);
|
||||
}
|
||||
@@ -1360,11 +1360,10 @@ namespace Frida.Gadget {
|
||||
options.name = Path.get_basename (path).split (".", 2)[0];
|
||||
|
||||
ScriptEngine.ScriptInstance instance;
|
||||
if (contents.length > 0 && contents[0] == QUICKJS_BYTECODE_MAGIC) {
|
||||
instance = yield engine.create_script (null, new Bytes (contents), options);
|
||||
} else {
|
||||
instance = yield engine.create_script ((string) contents, null, options);
|
||||
}
|
||||
if (contents.length > 0 && contents[0] == QUICKJS_BYTECODE_MAGIC)
|
||||
instance = yield engine.create_script (null, contents, options);
|
||||
else
|
||||
instance = yield engine.create_script ((string) contents.get_data (), null, options);
|
||||
|
||||
if (id.handle != 0)
|
||||
yield engine.destroy_script (id);
|
||||
@@ -1381,7 +1380,7 @@ namespace Frida.Gadget {
|
||||
var stage = new Json.Node.alloc ().init_string ((peek_state () == State.CREATED) ? "early" : "late");
|
||||
|
||||
try {
|
||||
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null);
|
||||
yield rpc_client.call ("init", new Json.Node[] { stage, parameters }, null, null);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
@@ -1449,12 +1448,12 @@ namespace Frida.Gadget {
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
|
||||
engine.post_to_script (id, json);
|
||||
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
engine.post_to_script (id, json, data);
|
||||
}
|
||||
}
|
||||
|
||||
private class ControlServer : BaseController {
|
||||
private sealed class ControlServer : BaseController {
|
||||
public EndpointParameters endpoint_params {
|
||||
get;
|
||||
construct;
|
||||
@@ -1695,7 +1694,7 @@ namespace Frida.Gadget {
|
||||
public abstract async void close (Cancellable? cancellable = null) throws IOError;
|
||||
}
|
||||
|
||||
private class AuthenticationChannel : Object, Peer, AuthenticationService {
|
||||
private sealed class AuthenticationChannel : Object, Peer, AuthenticationService {
|
||||
public weak ControlServer parent {
|
||||
get;
|
||||
construct;
|
||||
@@ -1743,7 +1742,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ControlChannel : Object, Peer, HostSession {
|
||||
private sealed class ControlChannel : Object, Peer, HostSession {
|
||||
public weak ControlServer parent {
|
||||
get;
|
||||
construct;
|
||||
@@ -1928,13 +1927,21 @@ 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");
|
||||
}
|
||||
}
|
||||
|
||||
private class LiveAgentSession : BaseAgentSession {
|
||||
private sealed class LiveAgentSession : BaseAgentSession {
|
||||
public ControlChannel? controller {
|
||||
get;
|
||||
set;
|
||||
@@ -1959,7 +1966,7 @@ namespace Frida.Gadget {
|
||||
}
|
||||
}
|
||||
|
||||
private class ClusterClient : BaseController {
|
||||
private sealed class ClusterClient : BaseController {
|
||||
public SocketConnectable connectable {
|
||||
get;
|
||||
construct;
|
||||
@@ -2076,6 +2083,60 @@ namespace Frida.Gadget {
|
||||
}
|
||||
#endif
|
||||
|
||||
private void load_asset_text (string filename, out string text) throws FileError {
|
||||
Bytes raw_contents;
|
||||
load_asset_bytes (filename, out raw_contents);
|
||||
|
||||
unowned string str = (string) raw_contents.get_data ();
|
||||
if (!str.validate ())
|
||||
throw new FileError.FAILED ("%s: invalid UTF-8", filename);
|
||||
|
||||
text = str;
|
||||
}
|
||||
|
||||
private void load_asset_bytes (string filename, out Bytes bytes) throws FileError {
|
||||
#if ANDROID
|
||||
if (maybe_load_asset_bytes_from_apk (filename, out bytes))
|
||||
return;
|
||||
#endif
|
||||
|
||||
uint8[] data;
|
||||
FileUtils.get_data (filename, out data);
|
||||
bytes = new Bytes.take ((owned) data);
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
private bool maybe_load_asset_bytes_from_apk (string filename, out Bytes contents) throws FileError {
|
||||
contents = null;
|
||||
|
||||
var tokens = filename.split ("!", 2);
|
||||
if (tokens.length != 2 || !tokens[0].has_suffix (".apk"))
|
||||
return false;
|
||||
unowned string apk_path = tokens[0];
|
||||
unowned string file_path = tokens[1];
|
||||
|
||||
var reader = Minizip.Reader.create ();
|
||||
try {
|
||||
if (reader.open_file (apk_path) != OK)
|
||||
throw new FileError.FAILED ("Unable to open APK");
|
||||
|
||||
if (reader.locate_entry (file_path[1:], true) != OK)
|
||||
throw new FileError.FAILED ("Unable to locate %s inside APK", file_path);
|
||||
|
||||
var size = reader.entry_save_buffer_length ();
|
||||
var data = new uint8[size + 1];
|
||||
if (reader.entry_save_buffer (data[:size]) != OK)
|
||||
throw new FileError.FAILED ("Unable to extract %s from APK", file_path);
|
||||
|
||||
contents = new Bytes.take ((owned) data);
|
||||
return true;
|
||||
} finally {
|
||||
reader.close ();
|
||||
Minizip.Reader.destroy (ref reader);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private Json.Node make_empty_json_object () {
|
||||
return new Json.Node.alloc ().init_object (new Json.Object ());
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ if host_os_family == 'darwin'
|
||||
extra_link_args += '-Wl,-framework,Foundation'
|
||||
endif
|
||||
|
||||
if host_os == 'android'
|
||||
extra_vala_args += '--pkg=minizip'
|
||||
platform_deps += minizip_dep
|
||||
endif
|
||||
|
||||
if host_os_family == 'windows'
|
||||
if host_toolchain != 'microsoft'
|
||||
symfile = 'frida-gadget.symbols'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
|
||||
public sealed class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
|
||||
#if IOS || TVOS
|
||||
private Gee.Map<string, InetAddress> interfaces = new Gee.HashMap<string, InetAddress> ();
|
||||
private Gee.Map<string, DynamicInterface> interfaces = new Gee.HashMap<string, DynamicInterface> ();
|
||||
|
||||
private Darwin.SystemConfiguration.DynamicStore? store;
|
||||
private Darwin.GCD.DispatchQueue event_queue =
|
||||
@@ -62,9 +62,9 @@ public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
|
||||
foreach (var key in CFArray.wrap<CoreFoundation.String> (changed_keys)) {
|
||||
string name = key.to_string ().split ("/")[3];
|
||||
|
||||
InetAddress? address = null;
|
||||
var val = (CoreFoundation.Dictionary) store.copy_value (key);
|
||||
if (val != null) {
|
||||
InetAddress? address = null;
|
||||
foreach (var raw_address in CFArray.wrap<CoreFoundation.String> (val[addresses_str])) {
|
||||
var str = raw_address.to_string ();
|
||||
bool is_reserved_ipv6_range = str.has_prefix ("fc") || str.has_prefix ("fd");
|
||||
@@ -75,12 +75,14 @@ public class Frida.TunnelInterfaceObserver : Object, DynamicInterfaceObserver {
|
||||
}
|
||||
}
|
||||
if (address != null && !interfaces.has_key (name)) {
|
||||
interfaces[name] = address;
|
||||
interface_attached (name, address);
|
||||
var iface = new DynamicInterface (name, address);
|
||||
interfaces[name] = iface;
|
||||
interface_attached (iface);
|
||||
}
|
||||
} else {
|
||||
if (interfaces.unset (name, out address))
|
||||
interface_detached (name, address);
|
||||
DynamicInterface iface;
|
||||
if (interfaces.unset (name, out iface))
|
||||
interface_detached (iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-24
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class ThreadIgnoreScope {
|
||||
public sealed class ThreadIgnoreScope {
|
||||
public enum Kind {
|
||||
APPLICATION_THREAD,
|
||||
FRIDA_THREAD
|
||||
@@ -43,7 +43,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
public class ThreadCountCloaker : Object {
|
||||
public sealed class ThreadCountCloaker : Object {
|
||||
private ReadFunc * read_slot;
|
||||
private static ReadFunc old_read_impl;
|
||||
|
||||
@@ -53,15 +53,18 @@ namespace Frida {
|
||||
private delegate ssize_t ReadFunc (int fd, void * buf, size_t count);
|
||||
|
||||
construct {
|
||||
Gum.Module.enumerate_imports ("libart.so", imp => {
|
||||
if (imp.name == "read") {
|
||||
read_slot = (ReadFunc *) imp.slot;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (read_slot != null)
|
||||
old_read_impl = update_read_slot (on_read);
|
||||
var art = Gum.Process.find_module_by_name ("libart.so");
|
||||
if (art != null) {
|
||||
art.enumerate_imports (imp => {
|
||||
if (imp.name == "read") {
|
||||
read_slot = (ReadFunc *) imp.slot;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (read_slot != null)
|
||||
old_read_impl = update_read_slot (on_read);
|
||||
}
|
||||
}
|
||||
|
||||
~ThreadCountCloaker () {
|
||||
@@ -153,12 +156,12 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class ThreadCountCloaker : Object {
|
||||
public sealed class ThreadCountCloaker : Object {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX
|
||||
public class ThreadListCloaker : Object, DirListFilter {
|
||||
public sealed class ThreadListCloaker : Object, DirListFilter {
|
||||
private string our_dir_by_pid;
|
||||
private DirListCloaker cloaker;
|
||||
|
||||
@@ -177,7 +180,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class FDListCloaker : Object, DirListFilter {
|
||||
public sealed class FDListCloaker : Object, DirListFilter {
|
||||
private string our_dir_by_pid;
|
||||
private DirListCloaker cloaker;
|
||||
|
||||
@@ -196,7 +199,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class DirListCloaker : Object {
|
||||
private sealed class DirListCloaker : Object {
|
||||
public weak DirListFilter filter {
|
||||
get;
|
||||
construct;
|
||||
@@ -212,34 +215,34 @@ namespace Frida {
|
||||
construct {
|
||||
var interceptor = Gum.Interceptor.obtain ();
|
||||
|
||||
unowned string libc = Gum.Process.query_libc_name ();
|
||||
var libc = Gum.Process.get_libc_module ();
|
||||
|
||||
var open_listener = new OpenDirListener (this);
|
||||
listeners.add (open_listener);
|
||||
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, "opendir"), open_listener);
|
||||
interceptor.attach ((void *) libc.find_export_by_name ("opendir"), open_listener);
|
||||
|
||||
var close_listener = new CloseDirListener (this);
|
||||
listeners.add (close_listener);
|
||||
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, "closedir"), close_listener);
|
||||
interceptor.attach ((void *) libc.find_export_by_name ("closedir"), close_listener);
|
||||
|
||||
var readdir_impl = Gum.Module.find_export_by_name (libc, "readdir");
|
||||
var readdir_impl = libc.find_export_by_name ("readdir");
|
||||
var readdir_listener = new ReadDirListener (this, LEGACY);
|
||||
listeners.add (readdir_listener);
|
||||
interceptor.attach ((void *) readdir_impl, readdir_listener);
|
||||
|
||||
var readdir64_impl = Gum.Module.find_export_by_name (libc, "readdir64");
|
||||
var readdir64_impl = libc.find_export_by_name ("readdir64");
|
||||
if (readdir64_impl != 0 && readdir64_impl != readdir_impl) {
|
||||
var listener = new ReadDirListener (this, MODERN);
|
||||
listeners.add (listener);
|
||||
interceptor.attach ((void *) readdir64_impl, listener);
|
||||
}
|
||||
|
||||
var readdir_r_impl = Gum.Module.find_export_by_name (libc, "readdir_r");
|
||||
var readdir_r_impl = libc.find_export_by_name ("readdir_r");
|
||||
var readdir_r_listener = new ReadDirRListener (this, LEGACY);
|
||||
listeners.add (readdir_r_listener);
|
||||
interceptor.attach ((void *) readdir_r_impl, readdir_r_listener);
|
||||
|
||||
var readdir64_r_impl = Gum.Module.find_export_by_name (libc, "readdir64_r");
|
||||
var readdir64_r_impl = libc.find_export_by_name ("readdir64_r");
|
||||
if (readdir64_r_impl != 0 && readdir64_r_impl != readdir_r_impl) {
|
||||
var listener = new ReadDirRListener (this, MODERN);
|
||||
listeners.add (listener);
|
||||
@@ -464,10 +467,10 @@ namespace Frida {
|
||||
public abstract bool matches_file (string name);
|
||||
}
|
||||
#else
|
||||
public class ThreadListCloaker : Object {
|
||||
public sealed class ThreadListCloaker : Object {
|
||||
}
|
||||
|
||||
public class FDListCloaker : Object {
|
||||
public sealed class FDListCloaker : Object {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class ExitMonitor : Object, Gum.InvocationListener {
|
||||
public sealed class ExitMonitor : Object, Gum.InvocationListener {
|
||||
public weak ExitHandler handler {
|
||||
get;
|
||||
construct;
|
||||
@@ -32,16 +32,17 @@ namespace Frida {
|
||||
unowned Gum.InvocationListener listener = this;
|
||||
|
||||
#if WINDOWS
|
||||
interceptor.attach ((void *) Gum.Module.find_export_by_name ("kernel32.dll", "ExitProcess"), listener);
|
||||
interceptor.attach ((void *) Gum.Process.find_module_by_name ("kernel32.dll").find_export_by_name ("ExitProcess"),
|
||||
listener);
|
||||
#else
|
||||
unowned string libc = Gum.Process.query_libc_name ();
|
||||
var libc = Gum.Process.get_libc_module ();
|
||||
const string[] apis = {
|
||||
"exit",
|
||||
"_exit",
|
||||
"abort",
|
||||
};
|
||||
foreach (var symbol in apis) {
|
||||
interceptor.attach ((void *) Gum.Module.find_export_by_name (libc, symbol), listener);
|
||||
interceptor.attach ((void *) libc.find_export_by_name (symbol), listener);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Frida {
|
||||
#if WINDOWS
|
||||
public class FileDescriptorGuard : Object {
|
||||
public sealed class FileDescriptorGuard : Object {
|
||||
public Gum.MemoryRange agent_range {
|
||||
get;
|
||||
construct;
|
||||
@@ -11,7 +11,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class FileDescriptorGuard : Object {
|
||||
public sealed class FileDescriptorGuard : Object {
|
||||
public Gum.MemoryRange agent_range {
|
||||
get;
|
||||
construct;
|
||||
@@ -26,7 +26,7 @@ namespace Frida {
|
||||
construct {
|
||||
var interceptor = Gum.Interceptor.obtain ();
|
||||
|
||||
var close = Gum.Module.find_export_by_name (Gum.Process.query_libc_name (), "close");
|
||||
var close = Gum.Process.get_libc_module ().find_export_by_name ("close");
|
||||
close_listener = new CloseListener (this);
|
||||
interceptor.attach ((void *) close, close_listener);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Frida {
|
||||
#if WINDOWS
|
||||
public class FileDescriptorTablePadder {
|
||||
public sealed class FileDescriptorTablePadder {
|
||||
public static FileDescriptorTablePadder obtain () {
|
||||
return new FileDescriptorTablePadder ();
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class FileDescriptorTablePadder {
|
||||
public sealed class FileDescriptorTablePadder {
|
||||
private const int MIN_TABLE_SIZE = 32;
|
||||
|
||||
private static unowned FileDescriptorTablePadder shared_instance = null;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Frida {
|
||||
#if WINDOWS
|
||||
public class ForkMonitor : Object {
|
||||
public sealed class ForkMonitor : Object {
|
||||
public weak ForkHandler handler {
|
||||
get;
|
||||
construct;
|
||||
@@ -11,7 +11,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class ForkMonitor : Object, Gum.InvocationListener {
|
||||
public sealed class ForkMonitor : Object, Gum.InvocationListener {
|
||||
public weak ForkHandler handler {
|
||||
get;
|
||||
construct;
|
||||
@@ -45,9 +45,9 @@ namespace Frida {
|
||||
}
|
||||
|
||||
static construct {
|
||||
unowned string libc = Gum.Process.query_libc_name ();
|
||||
fork_impl = (void *) Gum.Module.find_export_by_name (libc, "fork");
|
||||
vfork_impl = (void *) Gum.Module.find_export_by_name (libc, "vfork");
|
||||
var libc = Gum.Process.get_libc_module ();
|
||||
fork_impl = (void *) libc.find_export_by_name ("fork");
|
||||
vfork_impl = (void *) libc.find_export_by_name ("vfork");
|
||||
}
|
||||
|
||||
construct {
|
||||
@@ -61,15 +61,21 @@ namespace Frida {
|
||||
string cmdline;
|
||||
FileUtils.get_contents ("/proc/self/cmdline", out cmdline);
|
||||
if (cmdline == "zygote" || cmdline == "zygote64" || cmdline == "usap32" || cmdline == "usap64") {
|
||||
var set_argv0 = (void *) Gum.Module.find_export_by_name ("libandroid_runtime.so", "_Z27android_os_Process_setArgV0P7_JNIEnvP8_jobjectP8_jstring");
|
||||
if (set_argv0 != null) {
|
||||
interceptor.attach (set_argv0, listener, (void *) HookId.SET_ARGV0);
|
||||
child_recovery_behavior = DEFERRED_UNTIL_SET_ARGV0;
|
||||
var runtime = Gum.Process.find_module_by_name ("libandroid_runtime.so");
|
||||
if (runtime != null) {
|
||||
var set_argv0 = (void *) runtime.find_export_by_name ("_Z27android_os_Process_setArgV0P7_JNIEnvP8_jobjectP8_jstring");
|
||||
if (set_argv0 != null) {
|
||||
interceptor.attach (set_argv0, listener, (void *) HookId.SET_ARGV0);
|
||||
child_recovery_behavior = DEFERRED_UNTIL_SET_ARGV0;
|
||||
}
|
||||
}
|
||||
|
||||
var setcontext = (void *) Gum.Module.find_export_by_name ("libselinux.so", "selinux_android_setcontext");
|
||||
if (setcontext != null)
|
||||
interceptor.attach (setcontext, listener, (void *) HookId.SET_CTX);
|
||||
var selinux = Gum.Process.find_module_by_name ("libselinux.so");
|
||||
if (selinux != null) {
|
||||
var setcontext = (void *) selinux.find_export_by_name ("selinux_android_setcontext");
|
||||
if (setcontext != null)
|
||||
interceptor.attach (setcontext, listener, (void *) HookId.SET_CTX);
|
||||
}
|
||||
}
|
||||
} catch (FileError e) {
|
||||
}
|
||||
|
||||
@@ -25,6 +25,24 @@ frida_run_atexit_handlers (void)
|
||||
{
|
||||
}
|
||||
|
||||
# ifdef HAVE_ASAN
|
||||
|
||||
__attribute__ ((constructor)) static void
|
||||
frida_init_memory (void)
|
||||
{
|
||||
asm volatile ("");
|
||||
}
|
||||
|
||||
# ifndef HAVE_DARWIN
|
||||
__attribute__ ((destructor)) static void
|
||||
frida_deinit_memory (void)
|
||||
{
|
||||
asm volatile ("");
|
||||
}
|
||||
# endif
|
||||
|
||||
# endif
|
||||
|
||||
#else
|
||||
|
||||
#define FRIDA_SHIM_LOCK() gum_spinlock_acquire (&frida_shim_lock)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class PortalClient : Object, AgentSessionProvider {
|
||||
public sealed class PortalClient : Object, AgentSessionProvider {
|
||||
public signal void resume ();
|
||||
public signal void kill ();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class ScriptEngine : Object {
|
||||
public sealed class ScriptEngine : Object {
|
||||
public signal void message_from_script (AgentScriptId script_id, string json, Bytes? data);
|
||||
public signal void message_from_debugger (AgentScriptId script_id, string message);
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace Frida {
|
||||
source.attach (Gum.ScriptBackend.get_scheduler ().get_js_context ());
|
||||
}
|
||||
|
||||
public class ScriptInstance : Object, RpcPeer {
|
||||
public sealed class ScriptInstance : Object, RpcPeer {
|
||||
public signal void closed ();
|
||||
public signal void message (string json, Bytes? data);
|
||||
public signal void debug_message (string message);
|
||||
@@ -420,7 +420,7 @@ namespace Frida {
|
||||
var reason_value = new Json.Node.alloc ().init_string (reason.to_nick ());
|
||||
|
||||
try {
|
||||
yield rpc_client.call ("dispose", new Json.Node[] { reason_value }, null);
|
||||
yield rpc_client.call ("dispose", new Json.Node[] { reason_value }, null, null);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
@@ -467,10 +467,10 @@ namespace Frida {
|
||||
this.debug_message (message);
|
||||
}
|
||||
|
||||
private async void post_rpc_message (string json, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void post_rpc_message (string json, Bytes? data, Cancellable? cancellable) throws Error, IOError {
|
||||
if (script == null)
|
||||
throw new Error.INVALID_OPERATION ("Script is destroyed");
|
||||
script.post (json);
|
||||
script.post (json, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class SpawnMonitor : Object, Gum.InvocationListener {
|
||||
public sealed class SpawnMonitor : Object, Gum.InvocationListener {
|
||||
public weak SpawnHandler handler {
|
||||
get;
|
||||
construct;
|
||||
@@ -38,21 +38,24 @@ namespace Frida {
|
||||
var interceptor = Gum.Interceptor.obtain ();
|
||||
|
||||
#if WINDOWS
|
||||
var create_process_internal = Gum.Module.find_export_by_name ("kernelbase.dll", "CreateProcessInternalW");
|
||||
if (create_process_internal == 0)
|
||||
create_process_internal = Gum.Module.find_export_by_name ("kernel32.dll", "CreateProcessInternalW");
|
||||
var kernelbase = Gum.Process.find_module_by_name ("kernelbase.dll");
|
||||
var create_process_internal = (kernelbase != null) ? kernelbase.find_export_by_name ("CreateProcessInternalW") : 0;
|
||||
if (create_process_internal == 0) {
|
||||
create_process_internal = Gum.Process.find_module_by_name ("kernel32.dll")
|
||||
.find_export_by_name ("CreateProcessInternalW");
|
||||
}
|
||||
assert (create_process_internal != 0);
|
||||
interceptor.attach ((void *) create_process_internal, this);
|
||||
#else
|
||||
unowned string libc = Gum.Process.query_libc_name ();
|
||||
var libc = Gum.Process.get_libc_module ();
|
||||
#if DARWIN
|
||||
posix_spawn = (PosixSpawnFunc) Gum.Module.find_export_by_name (libc, "posix_spawn");
|
||||
posix_spawnattr_init = (PosixSpawnAttrInitFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_init");
|
||||
posix_spawnattr_destroy = (PosixSpawnAttrDestroyFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_destroy");
|
||||
posix_spawnattr_getflags = (PosixSpawnAttrSetFlagsFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_getflags");
|
||||
posix_spawnattr_setflags = (PosixSpawnAttrSetFlagsFunc) Gum.Module.find_export_by_name (libc, "posix_spawnattr_setflags");
|
||||
posix_spawn = (PosixSpawnFunc) libc.find_export_by_name ("posix_spawn");
|
||||
posix_spawnattr_init = (PosixSpawnAttrInitFunc) libc.find_export_by_name ("posix_spawnattr_init");
|
||||
posix_spawnattr_destroy = (PosixSpawnAttrDestroyFunc) libc.find_export_by_name ("posix_spawnattr_destroy");
|
||||
posix_spawnattr_getflags = (PosixSpawnAttrSetFlagsFunc) libc.find_export_by_name ("posix_spawnattr_getflags");
|
||||
posix_spawnattr_setflags = (PosixSpawnAttrSetFlagsFunc) libc.find_export_by_name ("posix_spawnattr_setflags");
|
||||
|
||||
execve = (void *) Gum.Module.find_export_by_name (libc, "execve");
|
||||
execve = (void *) libc.find_export_by_name ("execve");
|
||||
|
||||
interceptor.attach ((void *) posix_spawn, this);
|
||||
|
||||
@@ -60,10 +63,10 @@ namespace Frida {
|
||||
#else
|
||||
Gum.Address execve = 0;
|
||||
#if ANDROID
|
||||
execve = Gum.Module.find_symbol_by_name (libc, "__execve");
|
||||
execve = libc.find_symbol_by_name ("__execve");
|
||||
#endif
|
||||
if (execve == 0)
|
||||
execve = Gum.Module.find_export_by_name (libc, "execve");
|
||||
execve = libc.find_export_by_name ("execve");
|
||||
interceptor.attach ((void *) execve, this);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Frida {
|
||||
#if DARWIN
|
||||
public class ThreadSuspendMonitor : Object {
|
||||
public sealed class ThreadSuspendMonitor : Object {
|
||||
public weak ProcessInvader invader {
|
||||
get;
|
||||
construct;
|
||||
@@ -26,9 +26,10 @@ namespace Frida {
|
||||
construct {
|
||||
var interceptor = Gum.Interceptor.obtain ();
|
||||
|
||||
task_threads = (TaskThreadsFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "task_threads");
|
||||
thread_suspend = (ThreadSuspendFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "thread_suspend");
|
||||
thread_resume = (ThreadResumeFunc) Gum.Module.find_export_by_name (LIBSYSTEM_KERNEL, "thread_resume");
|
||||
var kernel = Gum.Process.find_module_by_name (LIBSYSTEM_KERNEL);
|
||||
task_threads = (TaskThreadsFunc) kernel.find_export_by_name ("task_threads");
|
||||
thread_suspend = (ThreadSuspendFunc) kernel.find_export_by_name ("thread_suspend");
|
||||
thread_resume = (ThreadResumeFunc) kernel.find_export_by_name ("thread_resume");
|
||||
|
||||
interceptor.replace ((void *) task_threads, (void *) replacement_task_threads, this);
|
||||
interceptor.replace ((void *) thread_suspend, (void *) replacement_thread_suspend, this);
|
||||
@@ -130,7 +131,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class ThreadSuspendMonitor : Object {
|
||||
public sealed class ThreadSuspendMonitor : Object {
|
||||
public weak ProcessInvader invader {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -300,6 +300,7 @@ static gpointer
|
||||
frida_find_vtable (void)
|
||||
{
|
||||
GumAddress result = 0;
|
||||
GumModule * libunwind;
|
||||
GumAddress export;
|
||||
uint64_t address;
|
||||
G_GNUC_UNUSED cs_err err;
|
||||
@@ -309,11 +310,15 @@ frida_find_vtable (void)
|
||||
size_t size;
|
||||
const size_t max_size = 2048;
|
||||
|
||||
export = gum_module_find_export_by_name (FRIDA_LIBUNWIND_PATH, "unw_init_local");
|
||||
libunwind = gum_process_find_module_by_name (FRIDA_LIBUNWIND_PATH);
|
||||
if (libunwind == NULL)
|
||||
goto beach;
|
||||
|
||||
export = gum_module_find_export_by_name (libunwind, "unw_init_local");
|
||||
if (export == 0)
|
||||
export = gum_module_find_export_by_name (FRIDA_LIBUNWIND_PATH, "_Unwind_RaiseException");
|
||||
export = gum_module_find_export_by_name (libunwind, "_Unwind_RaiseException");
|
||||
if (export == 0)
|
||||
return NULL;
|
||||
goto beach;
|
||||
export = gum_strip_code_address (export);
|
||||
address = export;
|
||||
|
||||
@@ -340,7 +345,7 @@ frida_find_vtable (void)
|
||||
GumMemoryRange bss_range;
|
||||
|
||||
bss_range.base_address = 0;
|
||||
gum_module_enumerate_sections (FRIDA_LIBUNWIND_PATH, (GumFoundSectionFunc) frida_find_bss_range, &bss_range);
|
||||
gum_module_enumerate_sections (libunwind, (GumFoundSectionFunc) frida_find_bss_range, &bss_range);
|
||||
|
||||
while (cs_disasm_iter (capstone, &code, &size, &address, insn))
|
||||
{
|
||||
@@ -404,6 +409,9 @@ frida_find_vtable (void)
|
||||
cs_free (insn, 1);
|
||||
cs_close (&capstone);
|
||||
|
||||
beach:
|
||||
g_clear_object (&libunwind);
|
||||
|
||||
return GSIZE_TO_POINTER (result);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Frida {
|
||||
#if DARWIN
|
||||
public class UnwindSitter : Object {
|
||||
public sealed class UnwindSitter : Object {
|
||||
public weak ProcessInvader invader {
|
||||
get;
|
||||
construct;
|
||||
@@ -21,7 +21,7 @@ namespace Frida {
|
||||
var interceptor = Gum.Interceptor.obtain ();
|
||||
|
||||
dyld_find_unwind_sections = (DyldFindUnwindSectionsFunc)
|
||||
Gum.Module.find_export_by_name (LIBDYLD, "_dyld_find_unwind_sections");
|
||||
Gum.Process.find_module_by_name (LIBDYLD).find_export_by_name ("_dyld_find_unwind_sections");
|
||||
|
||||
interceptor.replace ((void *) dyld_find_unwind_sections, (void *) replacement_dyld_find_unwind_sections, this);
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Frida {
|
||||
public extern static void _unhook_libunwind ();
|
||||
}
|
||||
#else
|
||||
public class UnwindSitter : Object {
|
||||
public sealed class UnwindSitter : Object {
|
||||
public weak ProcessInvader invader {
|
||||
get;
|
||||
construct;
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class PipeTransport : Object {
|
||||
public sealed class PipeTransport : Object {
|
||||
public string local_address {
|
||||
get;
|
||||
construct;
|
||||
@@ -42,7 +42,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
public class WindowsPipe : IOStream {
|
||||
public sealed class WindowsPipe : IOStream {
|
||||
public string address {
|
||||
get;
|
||||
construct;
|
||||
|
||||
+9
-3
@@ -97,7 +97,7 @@ if cc.sizeof('void *') == 8
|
||||
else
|
||||
host_cpu_mode = '32'
|
||||
endif
|
||||
host_docks_mobile_devices = host_os in ['windows', 'macos', 'linux', 'freebsd'] and host_cpu_mode == '64'
|
||||
host_docks_mobile_devices = host_os in ['windows', 'macos', 'linux'] and host_cpu_mode == '64'
|
||||
gumjs_archs = {
|
||||
'x86': 'ia32',
|
||||
'x86_64': 'x64',
|
||||
@@ -347,6 +347,13 @@ else
|
||||
gio_unix_dep = dependency('gio-unix-2.0')
|
||||
endif
|
||||
|
||||
if host_os == 'android'
|
||||
minizip_dep = dependency('minizip', required: false, default_options: [
|
||||
'zlib=enabled',
|
||||
'lzma=disabled',
|
||||
])
|
||||
endif
|
||||
|
||||
have_local_backend = get_option('local_backend').allowed()
|
||||
have_fruity_backend = get_option('fruity_backend') \
|
||||
.disable_auto_if(not host_docks_mobile_devices) \
|
||||
@@ -424,7 +431,6 @@ if gi_dep.found()
|
||||
else
|
||||
girdir = get_option('prefix') / get_option('datadir') / 'gir-1.0'
|
||||
endif
|
||||
uninstalled_girdir = meson.current_build_dir() / 'src'
|
||||
|
||||
vapidir = get_option('prefix') / get_option('datadir') / 'vala' / 'vapi'
|
||||
|
||||
@@ -474,7 +480,7 @@ if have_fruity_backend
|
||||
'dns=disabled',
|
||||
'arp=disabled',
|
||||
'ethernet=enabled',
|
||||
'tcp_mss=1360',
|
||||
'tcp_mss=4036',
|
||||
'tcp_snd_buf=65535',
|
||||
'tcp_wnd=65535',
|
||||
]
|
||||
|
||||
+1
-1
@@ -142,7 +142,7 @@ namespace Frida.Portal {
|
||||
return application.run ();
|
||||
}
|
||||
|
||||
private class Application : Object {
|
||||
private sealed class Application : Object {
|
||||
public signal void ready (bool success);
|
||||
|
||||
public PortalService service {
|
||||
|
||||
+1
-1
Submodule releng updated: f1f6586b6a...2eecc41cbe.
+52
-9
@@ -3,6 +3,7 @@ namespace Frida.Server {
|
||||
|
||||
private const string DEFAULT_DIRECTORY = "re.frida.server";
|
||||
private static bool output_version = false;
|
||||
private static string? device_id = null;
|
||||
private static string? listen_address = null;
|
||||
private static string? certpath = null;
|
||||
private static string? origin = null;
|
||||
@@ -30,6 +31,7 @@ namespace Frida.Server {
|
||||
|
||||
const OptionEntry[] option_entries = {
|
||||
{ "version", 0, 0, OptionArg.NONE, ref output_version, "Output version information and exit", null },
|
||||
{ "device", 0, 0, OptionArg.STRING, ref device_id, "Serve device with the given ID", "ID" },
|
||||
{ "listen", 'l', 0, OptionArg.STRING, ref listen_address, "Listen on ADDRESS", "ADDRESS" },
|
||||
{ "certificate", 0, 0, OptionArg.FILENAME, ref certpath, "Enable TLS using CERTIFICATE", "CERTIFICATE" },
|
||||
{ "origin", 0, 0, OptionArg.STRING, ref origin, "Only accept requests with “Origin” header matching ORIGIN " +
|
||||
@@ -178,7 +180,7 @@ namespace Frida.Server {
|
||||
|
||||
#if DARWIN
|
||||
var worker = new Thread<int> ("frida-server-main-loop", () => {
|
||||
var exit_code = run_application (endpoint_params, options, on_ready);
|
||||
var exit_code = run_application (device_id, endpoint_params, options, on_ready);
|
||||
|
||||
_stop_run_loop ();
|
||||
|
||||
@@ -190,15 +192,16 @@ namespace Frida.Server {
|
||||
|
||||
return exit_code;
|
||||
#else
|
||||
return run_application (endpoint_params, options, on_ready);
|
||||
return run_application (device_id, endpoint_params, options, on_ready);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static int run_application (EndpointParameters endpoint_params, ControlServiceOptions options, ReadyHandler on_ready) {
|
||||
private static int run_application (string? device_id, EndpointParameters endpoint_params, ControlServiceOptions options,
|
||||
ReadyHandler on_ready) {
|
||||
TemporaryDirectory.always_use ((directory != null) ? directory : DEFAULT_DIRECTORY);
|
||||
TemporaryDirectory.use_sysroot (options.sysroot);
|
||||
|
||||
application = new Application (new ControlService (endpoint_params, options));
|
||||
application = new Application (device_id, endpoint_params, options);
|
||||
|
||||
Posix.signal (Posix.Signal.INT, (sig) => {
|
||||
application.stop ();
|
||||
@@ -232,22 +235,39 @@ namespace Frida.Server {
|
||||
}
|
||||
#endif
|
||||
|
||||
private class Application : Object {
|
||||
private sealed class Application : Object {
|
||||
public signal void ready (bool success);
|
||||
|
||||
public ControlService service {
|
||||
public string? device_id {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public EndpointParameters endpoint_params {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public ControlServiceOptions options {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private DeviceManager? manager;
|
||||
private ControlService? service;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
private MainLoop loop = new MainLoop ();
|
||||
private int exit_code;
|
||||
private bool stopping;
|
||||
|
||||
public Application (ControlService service) {
|
||||
Object (service: service);
|
||||
public Application (string? device_id, EndpointParameters endpoint_params, ControlServiceOptions options) {
|
||||
Object (
|
||||
device_id: device_id,
|
||||
endpoint_params: endpoint_params,
|
||||
options: options
|
||||
);
|
||||
}
|
||||
|
||||
public int run () {
|
||||
@@ -265,6 +285,17 @@ namespace Frida.Server {
|
||||
|
||||
private async void start () {
|
||||
try {
|
||||
if (device_id != null && device_id != "local") {
|
||||
manager = new DeviceManager.with_nonlocal_backends_only ();
|
||||
|
||||
var device = yield manager.get_device_by_id (device_id, 0, io_cancellable);
|
||||
device.lost.connect (on_device_lost);
|
||||
|
||||
service = yield new ControlService.with_device (device, endpoint_params, options);
|
||||
} else {
|
||||
service = new ControlService (endpoint_params, options);
|
||||
}
|
||||
|
||||
yield service.start (io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
@@ -297,7 +328,15 @@ namespace Frida.Server {
|
||||
io_cancellable.cancel ();
|
||||
|
||||
try {
|
||||
yield service.stop ();
|
||||
if (service != null) {
|
||||
yield service.stop ();
|
||||
service = null;
|
||||
}
|
||||
|
||||
if (manager != null) {
|
||||
yield manager.close ();
|
||||
manager = null;
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
@@ -306,6 +345,10 @@ namespace Frida.Server {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private void on_device_lost () {
|
||||
stop ();
|
||||
}
|
||||
}
|
||||
|
||||
private TlsCertificate? parse_certificate (string? path) throws GLib.Error {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class AgentContainer : Object, AgentSessionProvider {
|
||||
public sealed class AgentContainer : Object, AgentSessionProvider {
|
||||
public DBusConnection connection {
|
||||
get;
|
||||
private set;
|
||||
|
||||
+141198
File diff suppressed because it is too large.
Load diff
+127
-33
@@ -1,36 +1,51 @@
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
from typing import List, Set
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
CORE_NAMESPACE = "http://www.gtk.org/introspection/core/1.0"
|
||||
C_NAMESPACE = "http://www.gtk.org/introspection/c/1.0"
|
||||
GLIB_NAMESPACE = "http://www.gtk.org/introspection/glib/1.0"
|
||||
GIR_NAMESPACES = {
|
||||
"": CORE_NAMESPACE,
|
||||
"c": C_NAMESPACE,
|
||||
"glib": GLIB_NAMESPACE,
|
||||
}
|
||||
|
||||
CORE_TAG_FIELD = f"{{{CORE_NAMESPACE}}}field"
|
||||
CORE_TAG_CONSTRUCTOR = f"{{{CORE_NAMESPACE}}}constructor"
|
||||
CORE_TAG_METHOD = f"{{{CORE_NAMESPACE}}}method"
|
||||
|
||||
OBJECT_TYPE_PATTERN = re.compile(r"\bpublic\s+(sealed )?(class|interface)\s+(\w+)\b")
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate refined Frida API definitions")
|
||||
parser.add_argument('--output', dest='output_type', choices=['bundle', 'header', 'gir', 'vapi', 'vapi-stamp'], default='bundle')
|
||||
parser.add_argument('api_version', metavar='api-version', type=str)
|
||||
parser.add_argument('core_header', metavar='/path/to/frida-core.h', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('core_gir', metavar='/path/to/Frida-x.y.gir', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('core_vapi', metavar='/path/to/frida-core.vapi', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('core_header', metavar='/path/to/frida-core.h', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('base_vapi', metavar='/path/to/frida-base.vapi', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('base_header', metavar='/path/to/frida-base.h', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('base_gir', metavar='/path/to/FridaBase-x.y.gir', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('base_vapi', metavar='/path/to/frida-base.vapi', type=argparse.FileType('r', encoding='utf-8'))
|
||||
parser.add_argument('output_dir', metavar='/output/dir')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
output_type = args.output_type
|
||||
api_version = args.api_version
|
||||
core_header = args.core_header.read()
|
||||
core_gir = args.core_gir.read()
|
||||
core_vapi = args.core_vapi.read()
|
||||
core_header = args.core_header.read()
|
||||
base_vapi = args.base_vapi.read()
|
||||
base_header = args.base_header.read()
|
||||
base_gir = args.base_gir.read()
|
||||
base_vapi = args.base_vapi.read()
|
||||
output_dir = Path(args.output_dir)
|
||||
|
||||
if output_type == 'gir':
|
||||
(output_dir / f"Frida-{api_version}.gir").write_text(core_gir, encoding='utf-8')
|
||||
return
|
||||
|
||||
if output_type == 'vapi-stamp':
|
||||
(output_dir / f"frida-core-{api_version}.vapi.stamp").write_bytes(b"")
|
||||
return
|
||||
@@ -45,30 +60,36 @@ def main():
|
||||
toplevel_sources = []
|
||||
src_dir = Path(__file__).parent.parent.resolve()
|
||||
for name in toplevel_names:
|
||||
with open(src_dir / name, 'r', encoding='utf-8') as f:
|
||||
toplevel_sources.append(f.read())
|
||||
toplevel_sources.append((src_dir / name).read_text(encoding='utf-8'))
|
||||
toplevel_code = "\n".join(toplevel_sources)
|
||||
|
||||
enable_header = False
|
||||
enable_gir = False
|
||||
enable_vapi = False
|
||||
if output_type == 'bundle':
|
||||
enable_header = True
|
||||
enable_gir = True
|
||||
enable_vapi = True
|
||||
elif output_type == 'header':
|
||||
enable_header = True
|
||||
elif output_type == 'gir':
|
||||
enable_gir = True
|
||||
elif output_type == 'vapi':
|
||||
enable_vapi = True
|
||||
|
||||
api = parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, base_header)
|
||||
api = parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi)
|
||||
|
||||
if enable_header:
|
||||
emit_header(api, output_dir)
|
||||
|
||||
if enable_gir:
|
||||
emit_gir(api, core_gir, base_gir, output_dir)
|
||||
|
||||
if enable_vapi:
|
||||
emit_vapi(api, output_dir)
|
||||
|
||||
def emit_header(api, output_dir):
|
||||
with open(output_dir / 'frida-core.h', 'w', encoding='utf-8') as output_header_file:
|
||||
with OutputFile(output_dir / 'frida-core.h') as output_header_file:
|
||||
output_header_file.write("#ifndef __FRIDA_CORE_H__\n#define __FRIDA_CORE_H__\n\n")
|
||||
|
||||
output_header_file.write("#include <glib.h>\n#include <glib-object.h>\n#include <gio/gio.h>\n#include <json-glib/json-glib.h>\n")
|
||||
@@ -79,7 +100,6 @@ def emit_header(api, output_dir):
|
||||
output_header_file.write("\ntypedef struct _%s %s;" % (object_type.c_name, object_type.c_name))
|
||||
if object_type.c_iface_definition is not None:
|
||||
output_header_file.write("\ntypedef struct _%sIface %sIface;" % (object_type.c_name, object_type.c_name))
|
||||
output_header_file.write("\ntypedef struct _FridaHostSession FridaHostSession;")
|
||||
|
||||
for enum in api.enum_types:
|
||||
output_header_file.write("\n\n" + enum.c_definition)
|
||||
@@ -150,8 +170,61 @@ def emit_header(api, output_dir):
|
||||
|
||||
output_header_file.write("\n\n#endif\n")
|
||||
|
||||
def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> str:
|
||||
ET.register_namespace("", CORE_NAMESPACE)
|
||||
ET.register_namespace("c", C_NAMESPACE)
|
||||
ET.register_namespace("glib", GLIB_NAMESPACE)
|
||||
|
||||
core_tree = ET.ElementTree(ET.fromstring(core_gir))
|
||||
base_tree = ET.ElementTree(ET.fromstring(base_gir))
|
||||
|
||||
core_root = core_tree.getroot()
|
||||
base_root = base_tree.getroot()
|
||||
|
||||
merged_root = ET.Element(core_root.tag, core_root.attrib)
|
||||
|
||||
for elem in core_root.findall("include", GIR_NAMESPACES):
|
||||
name = elem.get("name")
|
||||
if name in {"GLib", "GObject", "Gio"}:
|
||||
merged_root.append(elem)
|
||||
|
||||
for tag in ["package", "c:include"]:
|
||||
for elem in core_root.findall(tag, GIR_NAMESPACES):
|
||||
merged_root.append(elem)
|
||||
|
||||
core_namespace = core_root.find("namespace", GIR_NAMESPACES)
|
||||
merged_namespace = ET.SubElement(merged_root, core_namespace.tag, core_namespace.attrib)
|
||||
|
||||
object_type_names = {obj.name for obj in api.object_types}
|
||||
enum_type_names = {enum.name for enum in api.enum_types}
|
||||
error_type_names = {error.name for error in api.error_types}
|
||||
|
||||
def merge_and_transform_elements(tag_name: str, spec_set: Set[str]):
|
||||
core_elements = filter_elements(core_root.findall(f".//{tag_name}", GIR_NAMESPACES), spec_set)
|
||||
base_elements = filter_elements(base_root.findall(f".//{tag_name}", GIR_NAMESPACES), spec_set)
|
||||
for elem in core_elements + base_elements:
|
||||
if tag_name == "class":
|
||||
for child in list(elem):
|
||||
if child.tag == CORE_TAG_FIELD or child.get("name").startswith("_"):
|
||||
elem.remove(child)
|
||||
merged_namespace.append(elem)
|
||||
|
||||
merge_and_transform_elements("class", object_type_names)
|
||||
merge_and_transform_elements("interface", object_type_names)
|
||||
merge_and_transform_elements("enumeration", enum_type_names | error_type_names)
|
||||
|
||||
ET.indent(merged_root, space=" ")
|
||||
result = ET.tostring(merged_root,
|
||||
encoding="unicode",
|
||||
xml_declaration=True)
|
||||
with OutputFile(output_dir / f"Frida-{api.version}.gir") as output_gir:
|
||||
output_gir.write(result)
|
||||
|
||||
def filter_elements(elements: List[ET.Element], spec_set: Set[str]):
|
||||
return [elem for elem in elements if elem.get("name") in spec_set]
|
||||
|
||||
def emit_vapi(api, output_dir):
|
||||
with open(output_dir / "frida-core-{0}.vapi".format(api.version), "w", encoding='utf-8') as output_vapi_file:
|
||||
with OutputFile(output_dir / f"frida-core-{api.version}.vapi") as output_vapi_file:
|
||||
output_vapi_file.write("[CCode (cheader_filename = \"frida-core.h\", cprefix = \"Frida\", lower_case_cprefix = \"frida_\")]")
|
||||
output_vapi_file.write("\nnamespace Frida {")
|
||||
output_vapi_file.write("\n\tpublic static void init ();")
|
||||
@@ -189,12 +262,12 @@ def emit_vapi(api, output_dir):
|
||||
|
||||
output_vapi_file.write("\n}\n")
|
||||
|
||||
with open(output_dir / "frida-core-{0}.deps".format(api.version), "w", encoding='utf-8') as output_deps_file:
|
||||
with OutputFile(output_dir / f"frida-core-{api.version}.deps") as output_deps_file:
|
||||
output_deps_file.write("glib-2.0\n")
|
||||
output_deps_file.write("gobject-2.0\n")
|
||||
output_deps_file.write("gio-2.0\n")
|
||||
|
||||
def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, base_header):
|
||||
def parse_api(api_version, toplevel_code, core_header, core_vapi, base_header, base_vapi):
|
||||
all_headers = core_header + "\n" + base_header
|
||||
|
||||
all_enum_names = [m.group(1) for m in re.finditer(r"^\t+public\s+enum\s+(\w+)\s+", toplevel_code + "\n" + base_vapi, re.MULTILINE)]
|
||||
@@ -217,12 +290,18 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
|
||||
"StaticAuthenticationService": "AuthenticationService",
|
||||
}
|
||||
internal_type_prefixes = [
|
||||
"AgentMessageKind",
|
||||
"Fruity",
|
||||
"HostSession",
|
||||
"MessageType",
|
||||
"PeerSetup",
|
||||
"PortConflictBehavior",
|
||||
"ResultCode",
|
||||
"SpawnStartState",
|
||||
"State",
|
||||
"StringTerminator",
|
||||
"UnloadPolicy",
|
||||
"WebService",
|
||||
"Winjector"
|
||||
]
|
||||
seen_enum_names = set()
|
||||
@@ -287,8 +366,7 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
|
||||
method_cname_lc = object_type.c_name_lc + '_' + method_name
|
||||
if method_cname_lc not in seen_cfunctions:
|
||||
seen_cfunctions.add(method_cname_lc)
|
||||
if method_name not in ('construct', 'construct_with_host_session', 'get_main_context', 'get_provider', 'get_session') \
|
||||
and not (object_type.name in ("Session", "Script") and method_name == 'get_id'):
|
||||
if method_name != 'construct':
|
||||
if (object_type.c_name + '*') in m.group(0):
|
||||
if method_name == 'new' or method_name.startswith('new_'):
|
||||
object_type.c_constructors.append(method_cprototype)
|
||||
@@ -342,8 +420,8 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
|
||||
current_enum.vapi_declaration = stripped_line
|
||||
else:
|
||||
ignoring = True
|
||||
elif stripped_line.startswith("public class") or stripped_line.startswith("public interface"):
|
||||
name = re.match(r"^public (class|interface) (\w+) ", stripped_line).group(2)
|
||||
elif (match := OBJECT_TYPE_PATTERN.match(stripped_line)) is not None:
|
||||
name = match.group(3)
|
||||
if name not in object_type_by_name:
|
||||
ignoring = True
|
||||
else:
|
||||
@@ -362,8 +440,7 @@ def parse_api(api_version, toplevel_code, core_vapi, core_header, base_vapi, bas
|
||||
current_object_type.vapi_signals.append(stripped_line)
|
||||
elif "{ get" in stripped_line:
|
||||
name = re.match(r".+?(\w+)\s+{", stripped_line).group(1)
|
||||
if name not in ('main_context', 'provider', 'session'):
|
||||
current_object_type.vapi_properties.append(stripped_line)
|
||||
current_object_type.vapi_properties.append(stripped_line)
|
||||
else:
|
||||
m = re.match(r".+?(\w+)\s+\(", stripped_line)
|
||||
if m is not None:
|
||||
@@ -400,19 +477,19 @@ def function_is_public(name):
|
||||
"negotiate_connection"
|
||||
]
|
||||
|
||||
def parse_vala_object_types(source):
|
||||
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)]
|
||||
def parse_vala_object_types(source) -> List[ApiObjectType]:
|
||||
return [ApiObjectType(m.group(3), m.group(2)) for m in OBJECT_TYPE_PATTERN.finditer(source, re.MULTILINE)]
|
||||
|
||||
def parse_vapi_functions(vapi):
|
||||
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)]
|
||||
|
||||
@dataclass
|
||||
class ApiSpec:
|
||||
def __init__(self, version, object_types, functions, enum_types, error_types):
|
||||
self.version = version
|
||||
self.object_types = object_types
|
||||
self.functions = functions
|
||||
self.enum_types = enum_types
|
||||
self.error_types = error_types
|
||||
version: str
|
||||
object_types: List[ApiObjectType]
|
||||
functions: List[ApiFunction]
|
||||
enum_types: List[ApiEnum]
|
||||
error_types: List[ApiEnum]
|
||||
|
||||
class ApiEnum:
|
||||
def __init__(self, name):
|
||||
@@ -514,6 +591,23 @@ def fuzzysort(items, keys):
|
||||
result.extend(remaining)
|
||||
return result
|
||||
|
||||
class OutputFile:
|
||||
def __init__(self, output_path):
|
||||
self._output_path = output_path
|
||||
self._io = StringIO()
|
||||
|
||||
def __enter__(self):
|
||||
return self._io
|
||||
|
||||
def __exit__(self, *exc):
|
||||
result = self._io.getvalue()
|
||||
if self._output_path.exists():
|
||||
existing_contents = self._output_path.read_text(encoding='utf-8')
|
||||
if existing_contents == result:
|
||||
return False
|
||||
self._output_path.write_text(result, encoding='utf-8')
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+19
-33
@@ -1,55 +1,42 @@
|
||||
fs = import('fs')
|
||||
|
||||
generate_api = [python, files('generate.py')]
|
||||
|
||||
generate_api_args = [
|
||||
api_version,
|
||||
core_build_dir / 'frida-core.h',
|
||||
core_build_dir / core_gir_name,
|
||||
core_build_dir / 'frida-core.vapi',
|
||||
core_build_dir / 'frida-core.h',
|
||||
base_build_dir / f'frida-base-@[email protected]',
|
||||
base_build_dir / 'frida-base.h',
|
||||
base_build_dir / base_gir_name,
|
||||
base_build_dir / f'frida-base-@[email protected]',
|
||||
meson.current_build_dir(),
|
||||
]
|
||||
|
||||
core_public_h = custom_target('frida-core-header',
|
||||
input: [core, base],
|
||||
output: ['frida-core.h'],
|
||||
command: [
|
||||
generate_api,
|
||||
'--output=header',
|
||||
generate_api_args,
|
||||
],
|
||||
install: true,
|
||||
install_dir: header_install_dir,
|
||||
)
|
||||
|
||||
core_public_gir = custom_target('frida-core-gir',
|
||||
input: [core, base],
|
||||
output: [core_gir_name],
|
||||
command: [
|
||||
generate_api,
|
||||
'--output=gir',
|
||||
generate_api_args,
|
||||
],
|
||||
install: true,
|
||||
install_dir: girdir,
|
||||
)
|
||||
|
||||
core_public_vapi = custom_target('frida-core-vapi',
|
||||
core_api = custom_target('frida-core-api',
|
||||
input: [core, base],
|
||||
output: [
|
||||
'frida-core.h',
|
||||
core_gir_name,
|
||||
f'frida-core-@[email protected]',
|
||||
f'frida-core-@[email protected]',
|
||||
],
|
||||
command: [
|
||||
generate_api,
|
||||
'--output=vapi',
|
||||
'--output=bundle',
|
||||
generate_api_args,
|
||||
],
|
||||
install: true,
|
||||
install_dir: vapidir,
|
||||
install_dir: [header_install_dir, girdir, vapidir, vapidir],
|
||||
)
|
||||
|
||||
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
|
||||
core_public_h = core_api[0]
|
||||
core_public_gir = core_api[1]
|
||||
core_public_vapi = [core_api[2], core_api[3]]
|
||||
|
||||
gio_gir = fs.copyfile('Gio-2.0.gir')
|
||||
|
||||
core_public_vapi_stamp = custom_target('frida-core-vapi-stamp',
|
||||
input: core_public_vapi,
|
||||
output: f'frida-core-@[email protected]',
|
||||
command: [
|
||||
@@ -76,8 +63,6 @@ core_public_lib = custom_target('frida-core-library',
|
||||
install_dir: get_option('libdir'),
|
||||
)
|
||||
|
||||
fs = import('fs')
|
||||
|
||||
core_public_extra_headers = []
|
||||
if host_os == 'android'
|
||||
core_public_extra_headers += fs.copyfile(frida_selinux_h)
|
||||
@@ -88,6 +73,7 @@ if build_machine.system() == 'windows'
|
||||
else
|
||||
uninstalled_incdir = meson.current_build_dir()
|
||||
endif
|
||||
uninstalled_girdir = uninstalled_incdir
|
||||
uninstalled_vapidir = uninstalled_incdir
|
||||
|
||||
uninstalled_variables = {
|
||||
@@ -103,7 +89,7 @@ installed_variables = {
|
||||
}
|
||||
|
||||
core_public_dep = declare_dependency(
|
||||
sources: [core_public_h, core_public_gir, core_public_vapi_stamp, core_public_extra_headers],
|
||||
sources: [core_public_h, core_public_gir, gio_gir, core_public_vapi_stamp, core_public_extra_headers],
|
||||
include_directories: include_directories('.'),
|
||||
link_args: backend_libs_private,
|
||||
link_with: core_public_lib,
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Frida.Barebone {
|
||||
public abstract async void deallocate (Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
|
||||
public class SimpleAllocator : Object, Allocator {
|
||||
public sealed class SimpleAllocator : Object, Allocator {
|
||||
public size_t page_size {
|
||||
get { return _page_size; }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public class ArmMachine : Object, Machine {
|
||||
public sealed class ArmMachine : Object, Machine {
|
||||
public override GDB.Client gdb {
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public class Arm64Machine : Object, Machine {
|
||||
public sealed class Arm64Machine : Object, Machine {
|
||||
public override GDB.Client gdb {
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public class UnknownMachine : Object, Machine {
|
||||
public sealed class UnknownMachine : Object, Machine {
|
||||
public GDB.Client gdb {
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public class IA32Machine : Object, Machine {
|
||||
public sealed class IA32Machine : Object, Machine {
|
||||
public override GDB.Client gdb {
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public class X64Machine : Object, Machine {
|
||||
public sealed class X64Machine : Object, Machine {
|
||||
public override GDB.Client gdb {
|
||||
get;
|
||||
set;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class BareboneHostSessionBackend : Object, HostSessionBackend {
|
||||
public sealed class BareboneHostSessionBackend : Object, HostSessionBackend {
|
||||
private BareboneHostSessionProvider? provider;
|
||||
|
||||
private const uint16 DEFAULT_PORT = 3333;
|
||||
@@ -30,7 +30,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class BareboneHostSessionProvider : Object, HostSessionProvider {
|
||||
public sealed class BareboneHostSessionProvider : Object, HostSessionProvider {
|
||||
public string id {
|
||||
get { return "barebone"; }
|
||||
}
|
||||
@@ -141,12 +141,35 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
public class BareboneHostSession : Object, HostSession {
|
||||
public sealed class BareboneHostSession : Object, HostSession {
|
||||
public Barebone.Services services {
|
||||
get;
|
||||
construct;
|
||||
@@ -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;
|
||||
|
||||
@@ -282,7 +321,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class BareboneAgentSession : Object, AgentSession {
|
||||
private sealed class BareboneAgentSession : Object, AgentSession {
|
||||
public signal void closed ();
|
||||
|
||||
public AgentSessionId id {
|
||||
@@ -525,7 +564,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
namespace Barebone {
|
||||
public class Services : Object {
|
||||
public sealed class Services : Object {
|
||||
public Machine machine {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public class Callback : Object {
|
||||
public sealed class Callback : Object {
|
||||
private uint64 code;
|
||||
private CallbackHandler handler;
|
||||
private Machine machine;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
public class Interceptor : Object {
|
||||
public sealed class Interceptor : Object {
|
||||
public Machine machine {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaBarebone", gir_version = "1.0")]
|
||||
namespace Frida.Barebone {
|
||||
private class RustModule : Object {
|
||||
private sealed class RustModule : Object {
|
||||
public signal void console_output (string message);
|
||||
|
||||
public Gee.List<Export> exports {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
private class BareboneScript : Object {
|
||||
private sealed class BareboneScript : Object {
|
||||
public signal void message (string json, Bytes? data);
|
||||
|
||||
public AgentScriptId id {
|
||||
@@ -420,7 +420,9 @@ namespace Frida {
|
||||
|
||||
public void post (string json, Bytes? data) {
|
||||
var json_val = ctx.make_string (json);
|
||||
invoke_void (dispatch_message_func, { json_val }, runtime_obj);
|
||||
var data_val = (data != null) ? ctx.make_array_buffer (data.get_data ()) : QuickJS.Null;
|
||||
invoke_void (dispatch_message_func, { json_val, data_val }, runtime_obj);
|
||||
ctx.free_value (data_val);
|
||||
ctx.free_value (json_val);
|
||||
|
||||
perform_pending_io ();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class Compiler : Object {
|
||||
public sealed class Compiler : Object {
|
||||
public signal void starting ();
|
||||
public signal void finished ();
|
||||
public signal void output (string bundle);
|
||||
@@ -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,
|
||||
@@ -275,7 +275,7 @@ namespace Frida {
|
||||
new Json.Node.alloc ().init_string (entrypoint),
|
||||
new Json.Node.alloc ().init_string (opts.source_maps.to_nick ()),
|
||||
new Json.Node.alloc ().init_string (opts.compression.to_nick ()),
|
||||
}, cancellable);
|
||||
}, null, cancellable);
|
||||
return bundle.get_string ();
|
||||
}
|
||||
|
||||
@@ -288,7 +288,7 @@ namespace Frida {
|
||||
new Json.Node.alloc ().init_string (entrypoint),
|
||||
new Json.Node.alloc ().init_string (opts.source_maps.to_nick ()),
|
||||
new Json.Node.alloc ().init_string (opts.compression.to_nick ()),
|
||||
}, cancellable);
|
||||
}, null, cancellable);
|
||||
}
|
||||
|
||||
private static string compute_project_root (string entrypoint, CompilerOptions options) {
|
||||
@@ -465,10 +465,10 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class BuildOptions : CompilerOptions {
|
||||
public sealed class BuildOptions : CompilerOptions {
|
||||
}
|
||||
|
||||
public class WatchOptions : CompilerOptions {
|
||||
public sealed class WatchOptions : CompilerOptions {
|
||||
}
|
||||
|
||||
public enum SourceMaps {
|
||||
|
||||
Generated
+16
-15
@@ -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.7.0",
|
||||
"@types/frida-gum": "18.8.2",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/node": "18.7.23",
|
||||
"glob": "^8.0.3",
|
||||
@@ -468,10 +468,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "18.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.0.tgz",
|
||||
"integrity": "sha512-HhBomXE23fLDAWXEKi3BjJLrlH9wAv9IEQNfO/PaYHQNNbh0Bi06gx6JbXTspVpbqlbVqkWAuU7n6HaS9B6yXA==",
|
||||
"dev": true
|
||||
"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"
|
||||
},
|
||||
"node_modules/@types/glob": {
|
||||
"version": "8.0.0",
|
||||
@@ -640,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",
|
||||
@@ -1465,9 +1466,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/frida-gum": {
|
||||
"version": "18.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.0.tgz",
|
||||
"integrity": "sha512-HhBomXE23fLDAWXEKi3BjJLrlH9wAv9IEQNfO/PaYHQNNbh0Bi06gx6JbXTspVpbqlbVqkWAuU7n6HaS9B6yXA==",
|
||||
"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": {
|
||||
@@ -1596,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.7.0",
|
||||
"@types/frida-gum": "18.8.2",
|
||||
"@types/glob": "^8.0.0",
|
||||
"@types/node": "18.7.23",
|
||||
"glob": "^8.0.3",
|
||||
|
||||
+520
-304
File diff suppressed because it is too large.
Load diff
@@ -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 sealed 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 sealed 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 sealed class DarwinHostSession : LocalHostSession {
|
||||
public DarwinHelper helper {
|
||||
get;
|
||||
construct;
|
||||
@@ -410,7 +336,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
#if IOS || TVOS
|
||||
private class FruitController : Object, MappedAgentContainer {
|
||||
private sealed class FruitController : Object, MappedAgentContainer {
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
public signal void process_crashed (CrashInfo crash);
|
||||
@@ -815,7 +741,7 @@ namespace Frida {
|
||||
crash_deliveries.unset (delivery.pid);
|
||||
}
|
||||
|
||||
private class CrashDelivery : Object {
|
||||
private sealed class CrashDelivery : Object {
|
||||
public signal void expired ();
|
||||
|
||||
public uint pid {
|
||||
@@ -882,7 +808,7 @@ namespace Frida {
|
||||
|
||||
private delegate void FoundMappedAgentFunc (MappedAgent agent);
|
||||
|
||||
private class MappedAgent {
|
||||
private sealed class MappedAgent {
|
||||
public uint id {
|
||||
get;
|
||||
private set;
|
||||
@@ -905,7 +831,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class LaunchdAgent : InternalAgent {
|
||||
private sealed class LaunchdAgent : InternalAgent {
|
||||
public signal void app_launch_started (string identifier, uint pid);
|
||||
public signal void app_launch_completed (string identifier, uint pid, GLib.Error? error);
|
||||
public signal void spawn_preparation_started (HostSpawnInfo info);
|
||||
@@ -933,27 +859,28 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void prepare_for_launch (string identifier, Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("prepareForLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, cancellable);
|
||||
yield call ("prepareForLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, null,
|
||||
cancellable);
|
||||
}
|
||||
|
||||
public async void cancel_launch (string identifier, Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("cancelLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, cancellable);
|
||||
yield call ("cancelLaunch", new Json.Node[] { new Json.Node.alloc ().init_string (identifier) }, null, cancellable);
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("enableSpawnGating", new Json.Node[] {}, cancellable);
|
||||
yield call ("enableSpawnGating", new Json.Node[] {}, null, cancellable);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("disableSpawnGating", new Json.Node[] {}, cancellable);
|
||||
yield call ("disableSpawnGating", new Json.Node[] {}, null, cancellable);
|
||||
}
|
||||
|
||||
public async void claim_process (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("claimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, cancellable);
|
||||
yield call ("claimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, null, cancellable);
|
||||
}
|
||||
|
||||
public async void unclaim_process (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
yield call ("unclaimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, cancellable);
|
||||
yield call ("unclaimProcess", new Json.Node[] { new Json.Node.alloc ().init_int (pid) }, null, cancellable);
|
||||
}
|
||||
|
||||
protected override void on_event (string type, Json.Array event) {
|
||||
@@ -1014,7 +941,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class XpcProxyAgent : InternalAgent {
|
||||
private sealed class XpcProxyAgent : InternalAgent {
|
||||
public string identifier {
|
||||
get;
|
||||
construct;
|
||||
@@ -1056,7 +983,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class ReportCrashAgent : InternalAgent {
|
||||
private sealed class ReportCrashAgent : InternalAgent {
|
||||
public signal void crash_detected (uint pid);
|
||||
public signal void crash_received (CrashInfo crash);
|
||||
|
||||
@@ -1288,7 +1215,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class OSAnalyticsAgent : InternalAgent {
|
||||
private sealed class OSAnalyticsAgent : InternalAgent {
|
||||
public uint pid {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
# define CORE_FOUNDATION "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
|
||||
#endif
|
||||
|
||||
typedef struct _FridaHelperContext FridaHelperContext;
|
||||
typedef struct _FridaDispatchContext FridaDispatchContext;
|
||||
typedef struct _FridaSpawnInstance FridaSpawnInstance;
|
||||
typedef guint FridaDyldFlavor;
|
||||
typedef struct _FridaSpawnInstanceDyldData FridaSpawnInstanceDyldData;
|
||||
@@ -106,7 +106,7 @@ typedef union _FridaDebugState FridaDebugState;
|
||||
typedef int FridaConvertThreadStateDirection;
|
||||
typedef guint FridaAslr;
|
||||
|
||||
struct _FridaHelperContext
|
||||
struct _FridaDispatchContext
|
||||
{
|
||||
dispatch_queue_t dispatch_queue;
|
||||
};
|
||||
@@ -376,7 +376,7 @@ enum _FridaAslr
|
||||
};
|
||||
|
||||
static FridaSpawnInstance * frida_spawn_instance_new (FridaDarwinHelperBackend * backend);
|
||||
static void frida_spawn_instance_free (FridaSpawnInstance * instance);
|
||||
static void frida_spawn_instance_close (FridaSpawnInstance * instance);
|
||||
static void frida_spawn_instance_resume (FridaSpawnInstance * self);
|
||||
|
||||
static void frida_spawn_instance_on_server_cancel (void * context);
|
||||
@@ -391,12 +391,12 @@ static void frida_spawn_instance_set_libc_initialized (FridaSpawnInstance * self
|
||||
static kern_return_t frida_spawn_instance_create_dyld_data (FridaSpawnInstance * self);
|
||||
static void frida_spawn_instance_destroy_dyld_data (FridaSpawnInstance * self);
|
||||
#if defined (HAVE_IOS) || defined (HAVE_TVOS)
|
||||
static gboolean frida_pick_ios_tvos_bootstrapper (const GumModuleDetails * details, gpointer user_data);
|
||||
static gboolean frida_pick_ios_tvos_bootstrapper (GumModule * module, gpointer user_data);
|
||||
#endif
|
||||
static void frida_spawn_instance_unset_helpers (FridaSpawnInstance * self);
|
||||
static void frida_spawn_instance_call_set_helpers (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state, mach_vm_address_t helpers);
|
||||
static void frida_spawn_instance_call_dlopen (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state, mach_vm_address_t lib_name, int mode);
|
||||
static gboolean frida_find_cf_initialize (const GumModuleDetails * details, gpointer user_data);
|
||||
static gboolean frida_find_cf_initialize (GumModule * module, gpointer user_data);
|
||||
static void frida_spawn_instance_call_cf_initialize (FridaSpawnInstance * self, GumDarwinUnifiedThreadState * state);
|
||||
static void frida_spawn_instance_set_nth_breakpoint (FridaSpawnInstance * self, guint n, GumAddress break_at, FridaBreakpointRepeat repeat);
|
||||
static void frida_spawn_instance_enable_nth_breakpoint (FridaSpawnInstance * self, guint n);
|
||||
@@ -410,7 +410,7 @@ static void frida_configure_terminal_attributes (gint fd);
|
||||
|
||||
static FridaInjectInstance * frida_inject_instance_new (FridaDarwinHelperBackend * backend, guint id, guint pid);
|
||||
static FridaInjectInstance * frida_inject_instance_clone (const FridaInjectInstance * instance, guint id);
|
||||
static void frida_inject_instance_free (FridaInjectInstance * instance);
|
||||
static void frida_inject_instance_close (FridaInjectInstance * instance);
|
||||
static gboolean frida_inject_instance_task_did_not_exec (FridaInjectInstance * instance);
|
||||
|
||||
static gboolean frida_inject_instance_start_thread (FridaInjectInstance * self, GError ** error);
|
||||
@@ -762,30 +762,30 @@ permission_denied:
|
||||
}
|
||||
|
||||
void
|
||||
_frida_darwin_helper_backend_create_context (FridaDarwinHelperBackend * self)
|
||||
_frida_darwin_helper_backend_create_dispatch_context (FridaDarwinHelperBackend * self)
|
||||
{
|
||||
FridaHelperContext * ctx;
|
||||
FridaDispatchContext * ctx;
|
||||
|
||||
ctx = g_slice_new (FridaHelperContext);
|
||||
ctx = g_slice_new (FridaDispatchContext);
|
||||
ctx->dispatch_queue = dispatch_queue_create ("re.frida.helper.queue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
self->context = ctx;
|
||||
self->dispatch_context = ctx;
|
||||
}
|
||||
|
||||
void
|
||||
_frida_darwin_helper_backend_destroy_context (FridaDarwinHelperBackend * self)
|
||||
_frida_darwin_helper_backend_destroy_dispatch_context (FridaDarwinHelperBackend * self)
|
||||
{
|
||||
FridaHelperContext * ctx = self->context;
|
||||
FridaDispatchContext * ctx = self->dispatch_context;
|
||||
|
||||
dispatch_release (ctx->dispatch_queue);
|
||||
|
||||
g_slice_free (FridaHelperContext, ctx);
|
||||
g_slice_free (FridaDispatchContext, ctx);
|
||||
}
|
||||
|
||||
void
|
||||
_frida_darwin_helper_backend_schedule_on_dispatch_queue (FridaDarwinHelperBackend * self, FridaDarwinHelperBackendDispatchWorker worker, gpointer user_data)
|
||||
{
|
||||
FridaHelperContext * ctx = self->context;
|
||||
FridaDispatchContext * ctx = self->dispatch_context;
|
||||
|
||||
dispatch_async (ctx->dispatch_queue, ^
|
||||
{
|
||||
@@ -934,7 +934,7 @@ any_failure:
|
||||
{
|
||||
if (instance->pid != 0)
|
||||
kill (instance->pid, SIGKILL);
|
||||
frida_spawn_instance_free (instance);
|
||||
frida_spawn_instance_close (instance);
|
||||
|
||||
pid = 0;
|
||||
|
||||
@@ -1869,7 +1869,7 @@ void
|
||||
_frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHelperBackend * self, void * opaque_instance, guint task, GError ** error)
|
||||
{
|
||||
FridaSpawnInstance * instance = opaque_instance;
|
||||
FridaHelperContext * ctx = self->context;
|
||||
FridaDispatchContext * ctx = self->dispatch_context;
|
||||
const gchar * failed_operation;
|
||||
kern_return_t kr;
|
||||
mach_port_t self_task, child_thread;
|
||||
@@ -2200,9 +2200,9 @@ _frida_darwin_helper_backend_resume_spawn_instance (FridaDarwinHelperBackend * s
|
||||
}
|
||||
|
||||
void
|
||||
_frida_darwin_helper_backend_free_spawn_instance (FridaDarwinHelperBackend * self, void * instance)
|
||||
_frida_darwin_helper_backend_close_spawn_instance (FridaDarwinHelperBackend * self, void * instance)
|
||||
{
|
||||
frida_spawn_instance_free (instance);
|
||||
frida_spawn_instance_close (instance);
|
||||
}
|
||||
|
||||
guint
|
||||
@@ -2489,7 +2489,7 @@ mach_failure:
|
||||
}
|
||||
failure:
|
||||
{
|
||||
frida_inject_instance_free (instance);
|
||||
frida_inject_instance_close (instance);
|
||||
goto beach;
|
||||
}
|
||||
beach:
|
||||
@@ -2599,7 +2599,7 @@ frida_inject_instance_start_thread (FridaInjectInstance * self, GError ** error)
|
||||
mach_msg_type_number_t thread_state_count;
|
||||
kern_return_t kr;
|
||||
const gchar * failed_operation;
|
||||
FridaHelperContext * ctx = self->backend->context;
|
||||
FridaDispatchContext * ctx = self->backend->dispatch_context;
|
||||
dispatch_source_t source;
|
||||
|
||||
thread_state = g_alloca (self->thread_state_count * sizeof (natural_t));
|
||||
@@ -2646,7 +2646,7 @@ frida_inject_instance_on_thread_monitor_cancel (void * context)
|
||||
FridaInjectInstance * self = context;
|
||||
|
||||
dispatch_release (g_steal_pointer (&self->thread_monitor_source));
|
||||
frida_inject_instance_free (self);
|
||||
frida_inject_instance_close (self);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2705,7 +2705,7 @@ _frida_darwin_helper_backend_join_inject_instance_posix_thread (FridaDarwinHelpe
|
||||
static void
|
||||
frida_inject_instance_join_posix_thread (FridaInjectInstance * self, mach_port_t posix_thread)
|
||||
{
|
||||
FridaHelperContext * ctx = self->backend->context;
|
||||
FridaDispatchContext * ctx = self->backend->dispatch_context;
|
||||
mach_port_t self_task;
|
||||
dispatch_source_t source;
|
||||
|
||||
@@ -2738,9 +2738,9 @@ _frida_darwin_helper_backend_get_pid_of_inject_instance (FridaDarwinHelperBacken
|
||||
}
|
||||
|
||||
void
|
||||
_frida_darwin_helper_backend_free_inject_instance (FridaDarwinHelperBackend * self, void * instance)
|
||||
_frida_darwin_helper_backend_close_inject_instance (FridaDarwinHelperBackend * self, void * instance)
|
||||
{
|
||||
frida_inject_instance_free (instance);
|
||||
frida_inject_instance_close (instance);
|
||||
}
|
||||
|
||||
static FridaSpawnInstance *
|
||||
@@ -2750,7 +2750,7 @@ frida_spawn_instance_new (FridaDarwinHelperBackend * backend)
|
||||
guint i;
|
||||
|
||||
instance = g_slice_new0 (FridaSpawnInstance);
|
||||
instance->backend = backend;
|
||||
instance->backend = g_object_ref (backend);
|
||||
instance->thread = MACH_PORT_NULL;
|
||||
|
||||
instance->server_port = MACH_PORT_NULL;
|
||||
@@ -2773,11 +2773,13 @@ frida_spawn_instance_new (FridaDarwinHelperBackend * backend)
|
||||
instance->page_pool[i].scratch_page = 0;
|
||||
}
|
||||
|
||||
_frida_darwin_helper_backend_on_instance_created (backend, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void
|
||||
frida_spawn_instance_free (FridaSpawnInstance * instance)
|
||||
frida_spawn_instance_close (FridaSpawnInstance * instance)
|
||||
{
|
||||
task_t self_task;
|
||||
FridaExceptionPortSet * previous_ports;
|
||||
@@ -2816,6 +2818,9 @@ frida_spawn_instance_free (FridaSpawnInstance * instance)
|
||||
if (instance->dyld != NULL)
|
||||
g_object_unref (instance->dyld);
|
||||
|
||||
_frida_darwin_helper_backend_on_instance_destroyed (instance->backend, instance);
|
||||
g_object_unref (instance->backend);
|
||||
|
||||
g_slice_free (FridaSpawnInstance, instance);
|
||||
}
|
||||
|
||||
@@ -2858,7 +2863,7 @@ frida_spawn_instance_on_server_cancel (void * context)
|
||||
FridaSpawnInstance * self = context;
|
||||
|
||||
dispatch_release (g_steal_pointer (&self->server_recv_source));
|
||||
frida_spawn_instance_free (self);
|
||||
frida_spawn_instance_close (self);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3646,22 +3651,25 @@ frida_spawn_instance_destroy_dyld_data (FridaSpawnInstance * self)
|
||||
#if defined (HAVE_IOS) || defined (HAVE_TVOS)
|
||||
|
||||
static gboolean
|
||||
frida_pick_ios_tvos_bootstrapper (const GumModuleDetails * details, gpointer user_data)
|
||||
frida_pick_ios_tvos_bootstrapper (GumModule * module, gpointer user_data)
|
||||
{
|
||||
FridaSpawnInstanceDyldData * data = user_data;
|
||||
const gchar * path;
|
||||
const gchar * candidates[] = {
|
||||
"/usr/lib/substitute-inserter.dylib",
|
||||
"/usr/lib/pspawn_payload-stg2.dylib"
|
||||
};
|
||||
guint i;
|
||||
|
||||
path = gum_module_get_path (module);
|
||||
|
||||
for (i = 0; i != G_N_ELEMENTS (candidates); i++)
|
||||
{
|
||||
const gchar * bootstrapper = candidates[i];
|
||||
|
||||
if (strcmp (details->path, bootstrapper) == 0)
|
||||
if (strcmp (path, bootstrapper) == 0)
|
||||
{
|
||||
strcpy (data->bootstrapper, details->path);
|
||||
strcpy (data->bootstrapper, path);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
@@ -3824,15 +3832,15 @@ frida_spawn_instance_call_dlopen (FridaSpawnInstance * self, GumDarwinUnifiedThr
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_find_cf_initialize (const GumModuleDetails * details, gpointer user_data)
|
||||
frida_find_cf_initialize (GumModule * module, gpointer user_data)
|
||||
{
|
||||
FridaSpawnInstance * self = user_data;
|
||||
GumDarwinModule * core_foundation;
|
||||
|
||||
if (strcmp (details->path, CORE_FOUNDATION) != 0)
|
||||
if (strcmp (gum_module_get_path (module), CORE_FOUNDATION) != 0)
|
||||
return TRUE;
|
||||
|
||||
core_foundation = gum_darwin_module_new_from_memory (CORE_FOUNDATION, self->task, details->range->base_address,
|
||||
core_foundation = gum_darwin_module_new_from_memory (CORE_FOUNDATION, self->task, gum_module_get_range (module)->base_address,
|
||||
GUM_DARWIN_MODULE_FLAGS_NONE, NULL);
|
||||
|
||||
self->cf_initialize_address = gum_darwin_module_resolve_symbol_address (core_foundation, "___CFInitialize");
|
||||
@@ -4111,6 +4119,8 @@ frida_inject_instance_new (FridaDarwinHelperBackend * backend, guint id, guint p
|
||||
|
||||
instance->backend = g_object_ref (backend);
|
||||
|
||||
_frida_darwin_helper_backend_on_instance_created (backend, instance);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -4140,11 +4150,13 @@ frida_inject_instance_clone (const FridaInjectInstance * instance, guint id)
|
||||
|
||||
g_object_ref (clone->backend);
|
||||
|
||||
_frida_darwin_helper_backend_on_instance_created (clone->backend, clone);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
static void
|
||||
frida_inject_instance_free (FridaInjectInstance * instance)
|
||||
frida_inject_instance_close (FridaInjectInstance * instance)
|
||||
{
|
||||
FridaAgentContext * agent_context = instance->agent_context;
|
||||
task_t self_task;
|
||||
@@ -4184,6 +4196,7 @@ frida_inject_instance_free (FridaInjectInstance * instance)
|
||||
if (instance->task != MACH_PORT_NULL)
|
||||
mach_port_deallocate (self_task, instance->task);
|
||||
|
||||
_frida_darwin_helper_backend_on_instance_destroyed (instance->backend, instance);
|
||||
g_object_unref (instance->backend);
|
||||
|
||||
g_slice_free (FridaInjectInstance, instance);
|
||||
@@ -4289,9 +4302,10 @@ frida_agent_context_init (FridaAgentContext * self, const FridaAgentDetails * de
|
||||
static gboolean
|
||||
frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleResolver * resolver, GumDarwinMapper * mapper, GError ** error)
|
||||
{
|
||||
gboolean success = FALSE;
|
||||
GumDarwinModule * module;
|
||||
|
||||
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libsystem_kernel.dylib");
|
||||
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libsystem_kernel.dylib");
|
||||
if (module == NULL)
|
||||
goto no_libc;
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (mach_task_self);
|
||||
@@ -4300,8 +4314,10 @@ frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleRes
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (mach_msg_receive);
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (mach_port_destroy);
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (thread_terminate);
|
||||
g_object_unref (module);
|
||||
module = NULL;
|
||||
|
||||
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libsystem_pthread.dylib");
|
||||
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libsystem_pthread.dylib");
|
||||
if (module == NULL)
|
||||
goto no_libc;
|
||||
FRIDA_AGENT_CONTEXT_TRY_RESOLVE (pthread_create_from_mach_thread);
|
||||
@@ -4312,18 +4328,23 @@ frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleRes
|
||||
FRIDA_AGENT_CONTEXT_TRY_RESOLVE (pthread_threadid_np);
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (pthread_detach);
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (pthread_self);
|
||||
g_object_unref (module);
|
||||
module = NULL;
|
||||
|
||||
if (mapper == NULL)
|
||||
{
|
||||
module = gum_darwin_module_resolver_find_module (resolver, "/usr/lib/system/libdyld.dylib");
|
||||
module = gum_darwin_module_resolver_find_module_by_name (resolver, "/usr/lib/system/libdyld.dylib");
|
||||
if (module == NULL)
|
||||
goto no_libc;
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (dlopen);
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (dlsym);
|
||||
FRIDA_AGENT_CONTEXT_RESOLVE (dlclose);
|
||||
g_object_unref (module);
|
||||
module = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
success = TRUE;
|
||||
goto beach;
|
||||
|
||||
no_libc:
|
||||
{
|
||||
@@ -4331,7 +4352,7 @@ no_libc:
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unable to attach to processes without Apple's libc (for now)");
|
||||
goto failure;
|
||||
goto beach;
|
||||
}
|
||||
missing_symbol:
|
||||
{
|
||||
@@ -4339,11 +4360,13 @@ missing_symbol:
|
||||
FRIDA_ERROR,
|
||||
FRIDA_ERROR_NOT_SUPPORTED,
|
||||
"Unexpected error while resolving functions");
|
||||
goto failure;
|
||||
goto beach;
|
||||
}
|
||||
failure:
|
||||
beach:
|
||||
{
|
||||
return FALSE;
|
||||
g_clear_object (&module);
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace Frida {
|
||||
protected delegate void DispatchWorker ();
|
||||
protected delegate void LaunchCompletionHandler (owned StdioPipes? pipes, owned Error? error);
|
||||
|
||||
public void * context;
|
||||
private MainContext main_context;
|
||||
public void * dispatch_context;
|
||||
|
||||
public Gee.HashMap<uint, void *> spawn_instances = new Gee.HashMap<uint, void *> ();
|
||||
private Gee.HashMap<uint, OutputStream> stdin_streams = new Gee.HashMap<uint, OutputStream> ();
|
||||
@@ -32,6 +33,9 @@ namespace Frida {
|
||||
private Gee.HashMap<void *, uint> inject_cleaner_by_instance = new Gee.HashMap<void *, uint> ();
|
||||
private Gee.HashMap<uint, uint> inject_expiry_timers = new Gee.HashMap<uint, uint> ();
|
||||
|
||||
private Gee.Set<void *> instances = new Gee.HashSet<void *> ();
|
||||
private SourceFunc? on_last_instance_destroyed = null;
|
||||
|
||||
public uint next_id = 1;
|
||||
|
||||
private PolicySoftener policy_softener;
|
||||
@@ -40,7 +44,8 @@ namespace Frida {
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
construct {
|
||||
_create_context ();
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
_create_dispatch_context ();
|
||||
|
||||
dtrace_agent = DTraceAgent.try_open ();
|
||||
if (dtrace_agent != null) {
|
||||
@@ -63,11 +68,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
~DarwinHelperBackend () {
|
||||
foreach (var instance in spawn_instances.values)
|
||||
_free_spawn_instance (instance);
|
||||
foreach (var instance in inject_instances.values)
|
||||
_free_inject_instance (instance);
|
||||
_destroy_context ();
|
||||
_destroy_dispatch_context ();
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
@@ -77,11 +78,24 @@ namespace Frida {
|
||||
pending.complete ();
|
||||
|
||||
foreach (var entry in inject_cleaner_by_instance.entries) {
|
||||
_free_inject_instance (entry.key);
|
||||
_close_inject_instance (entry.key);
|
||||
Source.remove (entry.value);
|
||||
}
|
||||
inject_cleaner_by_instance.clear ();
|
||||
|
||||
foreach (var instance in spawn_instances.values)
|
||||
_close_spawn_instance (instance);
|
||||
spawn_instances.clear ();
|
||||
|
||||
bool any_pending = false;
|
||||
lock (instances) {
|
||||
any_pending = !instances.is_empty;
|
||||
if (any_pending)
|
||||
on_last_instance_destroyed = close.callback;
|
||||
}
|
||||
if (any_pending)
|
||||
yield;
|
||||
|
||||
if (dtrace_agent != null) {
|
||||
dtrace_agent.spawn_added.disconnect (on_dtrace_agent_spawn_added);
|
||||
dtrace_agent.spawn_removed.disconnect (on_dtrace_agent_spawn_removed);
|
||||
@@ -139,11 +153,11 @@ namespace Frida {
|
||||
Error error = null;
|
||||
|
||||
_launch (identifier, options, (p, e) => {
|
||||
Idle.add (() => {
|
||||
schedule_on_frida_thread (() => {
|
||||
pipes = p;
|
||||
error = e;
|
||||
launch.callback ();
|
||||
return false;
|
||||
return Source.REMOVE;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -187,7 +201,7 @@ namespace Frida {
|
||||
inject_cleaner_by_instance.unset (instance, out source_id);
|
||||
Source.remove (source_id);
|
||||
|
||||
_free_inject_instance (instance);
|
||||
_close_inject_instance (instance);
|
||||
}
|
||||
|
||||
policy_softener.forget (pid);
|
||||
@@ -205,7 +219,7 @@ namespace Frida {
|
||||
|
||||
void * instance;
|
||||
if (spawn_instances.unset (pid, out instance))
|
||||
_free_spawn_instance (instance);
|
||||
_close_spawn_instance (instance);
|
||||
|
||||
child_dead (pid);
|
||||
}
|
||||
@@ -293,7 +307,7 @@ namespace Frida {
|
||||
void * instance;
|
||||
if (spawn_instances.unset (pid, out instance)) {
|
||||
_resume_spawn_instance (instance);
|
||||
_free_spawn_instance (instance);
|
||||
_close_spawn_instance (instance);
|
||||
} else {
|
||||
resume_with_validation (pid);
|
||||
}
|
||||
@@ -412,7 +426,7 @@ namespace Frida {
|
||||
} catch (GLib.Error e) {
|
||||
if (instance_created_here) {
|
||||
spawn_instances.unset (pid);
|
||||
_free_spawn_instance (spawn_instance);
|
||||
_close_spawn_instance (spawn_instance);
|
||||
}
|
||||
|
||||
throw_api_error (e);
|
||||
@@ -495,21 +509,21 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public void _on_spawn_instance_ready (uint pid) {
|
||||
Idle.add (() => {
|
||||
schedule_on_frida_thread (() => {
|
||||
spawn_instance_ready (pid);
|
||||
return false;
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
public void _on_spawn_instance_error (uint pid, Error error) {
|
||||
Idle.add (() => {
|
||||
schedule_on_frida_thread (() => {
|
||||
spawn_instance_error (pid, error);
|
||||
return false;
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
public void _on_mach_thread_dead (uint id, void * posix_thread) {
|
||||
Idle.add (() => {
|
||||
schedule_on_frida_thread (() => {
|
||||
var instance = inject_instances[id];
|
||||
assert (instance != null);
|
||||
|
||||
@@ -518,14 +532,14 @@ namespace Frida {
|
||||
else
|
||||
_destroy_inject_instance (id);
|
||||
|
||||
return false;
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
public void _on_posix_thread_dead (uint id) {
|
||||
Idle.add (() => {
|
||||
schedule_on_frida_thread (() => {
|
||||
_destroy_inject_instance (id);
|
||||
return false;
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -545,7 +559,7 @@ namespace Frida {
|
||||
private void schedule_inject_instance_cleanup (void * instance) {
|
||||
var cleanup_source = new TimeoutSource (50);
|
||||
cleanup_source.set_callback (() => {
|
||||
_free_inject_instance (instance);
|
||||
_close_inject_instance (instance);
|
||||
|
||||
var removed = inject_cleaner_by_instance.unset (instance);
|
||||
assert (removed);
|
||||
@@ -601,6 +615,19 @@ namespace Frida {
|
||||
policy_softener.forget (pid);
|
||||
}
|
||||
|
||||
public void _on_instance_created (void * instance) {
|
||||
lock (instances)
|
||||
instances.add (instance);
|
||||
}
|
||||
|
||||
public void _on_instance_destroyed (void * instance) {
|
||||
lock (instances) {
|
||||
instances.remove (instance);
|
||||
if (on_last_instance_destroyed != null && instances.is_empty)
|
||||
schedule_on_frida_thread ((owned) on_last_instance_destroyed);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_dtrace_agent_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
@@ -611,11 +638,17 @@ namespace Frida {
|
||||
|
||||
private async void flush_dispatch_queue () {
|
||||
_schedule_on_dispatch_queue (() => {
|
||||
Idle.add (flush_dispatch_queue.callback);
|
||||
schedule_on_frida_thread (flush_dispatch_queue.callback);
|
||||
});
|
||||
yield;
|
||||
}
|
||||
|
||||
private void schedule_on_frida_thread (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (main_context);
|
||||
}
|
||||
|
||||
public extern static PipeEndpoints make_pipe_endpoints (uint local_task, uint remote_pid, uint remote_task) throws Error;
|
||||
public extern static void make_pipe_endpoint_from_socket (uint pid, uint task, GLib.Socket sock, out string address)
|
||||
throws Error;
|
||||
@@ -626,8 +659,8 @@ namespace Frida {
|
||||
public extern static bool is_mmap_available ();
|
||||
public extern static MappedLibraryBlob mmap (uint task, Bytes blob) throws Error;
|
||||
|
||||
protected extern void _create_context ();
|
||||
protected extern void _destroy_context ();
|
||||
protected extern void _create_dispatch_context ();
|
||||
protected extern void _destroy_dispatch_context ();
|
||||
protected extern void _schedule_on_dispatch_queue (DispatchWorker worker);
|
||||
|
||||
protected extern uint _spawn (string path, HostSpawnOptions options, out StdioPipes? pipes) throws Error;
|
||||
@@ -642,7 +675,7 @@ namespace Frida {
|
||||
protected extern void * _create_spawn_instance (uint pid);
|
||||
protected extern void _prepare_spawn_instance_for_injection (void * instance, uint task) throws Error;
|
||||
protected extern void _resume_spawn_instance (void * instance);
|
||||
protected extern void _free_spawn_instance (void * instance);
|
||||
protected extern void _close_spawn_instance (void * instance);
|
||||
|
||||
protected extern uint _inject_into_task (uint pid, uint task, string path_or_name, MappedLibraryBlob? blob, string entrypoint, string data) throws Error;
|
||||
protected extern void _demonitor (void * instance);
|
||||
@@ -650,10 +683,10 @@ namespace Frida {
|
||||
protected extern void _recreate_injectee_thread (void * instance, uint pid, uint task) throws Error;
|
||||
protected extern void _join_inject_instance_posix_thread (void * instance, void * posix_thread);
|
||||
protected extern uint _get_pid_of_inject_instance (void * instance);
|
||||
protected extern void _free_inject_instance (void * instance);
|
||||
protected extern void _close_inject_instance (void * instance);
|
||||
}
|
||||
|
||||
public class DTraceAgent : Object {
|
||||
public sealed class DTraceAgent : Object {
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
|
||||
@@ -812,7 +845,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class PendingLaunch : Object {
|
||||
private sealed class PendingLaunch : Object {
|
||||
public signal void completed ();
|
||||
|
||||
public string identifier {
|
||||
@@ -856,7 +889,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class StdioPipes : Object {
|
||||
public sealed class StdioPipes : Object {
|
||||
public int input {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class DarwinHelperBackend : Object, DarwinHelper {
|
||||
public sealed class DarwinHelperBackend : Object, DarwinHelper {
|
||||
public signal void idle ();
|
||||
public signal void child_dead (uint pid);
|
||||
public signal void spawn_instance_ready (uint pid);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class DarwinHelperProcess : Object, DarwinHelper {
|
||||
public sealed class DarwinHelperProcess : Object, DarwinHelper {
|
||||
public uint pid {
|
||||
get {
|
||||
return (uint) process_pid;
|
||||
@@ -473,7 +473,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class ResourceStore {
|
||||
private sealed class ResourceStore {
|
||||
public HelperFile helper {
|
||||
get;
|
||||
private set;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Frida {
|
||||
public extern void _start_run_loop ();
|
||||
public extern void _stop_run_loop ();
|
||||
|
||||
public class DarwinHelperService : Object, DarwinRemoteHelper {
|
||||
public sealed class DarwinHelperService : Object, DarwinRemoteHelper {
|
||||
public string parent_address {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class Fruitjector : Object, Injector {
|
||||
public sealed class Fruitjector : Object, Injector {
|
||||
public signal void injected (uint id, uint pid, bool has_mapped_module, DarwinModuleDetails mapped_module);
|
||||
|
||||
public DarwinHelper helper {
|
||||
@@ -117,7 +117,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class AgentResource : Object {
|
||||
public sealed class AgentResource : Object {
|
||||
public string name {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Frida {
|
||||
public abstract void forget (uint pid);
|
||||
}
|
||||
|
||||
public class NullPolicySoftener : Object, PolicySoftener {
|
||||
public sealed class NullPolicySoftener : Object, PolicySoftener {
|
||||
public void soften (uint pid) throws Error {
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class InternalIOSTVOSPolicySoftener : IOSTVOSPolicySoftener {
|
||||
public sealed class InternalIOSTVOSPolicySoftener : IOSTVOSPolicySoftener {
|
||||
private static bool enabled = false;
|
||||
|
||||
public static void enable () {
|
||||
@@ -184,7 +184,7 @@ namespace Frida {
|
||||
private extern static void _soften (uint pid) throws Error;
|
||||
}
|
||||
|
||||
public class ElectraPolicySoftener : IOSTVOSPolicySoftener {
|
||||
public sealed class ElectraPolicySoftener : IOSTVOSPolicySoftener {
|
||||
private const string LIBJAILBREAK_PATH = "/usr/lib/libjailbreak.dylib";
|
||||
|
||||
private Module libjailbreak;
|
||||
@@ -237,7 +237,7 @@ namespace Frida {
|
||||
private extern static int _internal_jb_entitle_now (void * jbd_call, uint connection, uint pid);
|
||||
}
|
||||
|
||||
public class Unc0verPolicySoftener : IOSTVOSPolicySoftener {
|
||||
public sealed class Unc0verPolicySoftener : IOSTVOSPolicySoftener {
|
||||
private const string SUBSTITUTED_PATH = "/usr/libexec/substituted";
|
||||
|
||||
private uint connection;
|
||||
|
||||
@@ -151,10 +151,12 @@ frida_replacement_xpc_look_up_endpoint (const char * service_name, uint32_t type
|
||||
static FridaXpcLookUpEndpointFunc
|
||||
frida_find_xpc_look_up_endpoint_implementation (void)
|
||||
{
|
||||
GumModule * libxpc;
|
||||
guint32 * cursor;
|
||||
|
||||
cursor = GSIZE_TO_POINTER (
|
||||
gum_strip_code_address (gum_module_find_export_by_name ("/usr/lib/system/libxpc.dylib", "xpc_endpoint_create_bs_named")));
|
||||
libxpc = gum_process_find_module_by_name ("/usr/lib/system/libxpc.dylib");
|
||||
cursor = GSIZE_TO_POINTER (gum_strip_code_address (gum_module_find_export_by_name (libxpc, "xpc_endpoint_create_bs_named")));
|
||||
g_object_unref (libxpc);
|
||||
if (cursor == NULL)
|
||||
return NULL;
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Frida.AXML {
|
||||
}
|
||||
}
|
||||
|
||||
public class ElementTree : Object {
|
||||
public sealed class ElementTree : Object {
|
||||
public string name {
|
||||
get;
|
||||
set;
|
||||
@@ -135,7 +135,7 @@ namespace Frida.AXML {
|
||||
}
|
||||
}
|
||||
|
||||
public class ResourceValue : Object {
|
||||
public sealed class ResourceValue : Object {
|
||||
private uint16 size;
|
||||
private uint8 unused;
|
||||
private ResourceType type;
|
||||
@@ -173,7 +173,7 @@ namespace Frida.AXML {
|
||||
}
|
||||
}
|
||||
|
||||
public class Attribute : Object {
|
||||
public sealed class Attribute : Object {
|
||||
private uint32 namespace;
|
||||
private uint32 name;
|
||||
private uint32 unused;
|
||||
@@ -238,7 +238,7 @@ namespace Frida.AXML {
|
||||
}
|
||||
}
|
||||
|
||||
private class ResourceMap {
|
||||
private sealed class ResourceMap {
|
||||
private Gee.ArrayList<uint32> resources = new Gee.ArrayList<uint32> ();
|
||||
|
||||
public ResourceMap.with_stream (DataInputStream input, uint32 size) throws IOError {
|
||||
@@ -247,7 +247,7 @@ namespace Frida.AXML {
|
||||
}
|
||||
}
|
||||
|
||||
private class StringPool {
|
||||
private sealed class StringPool {
|
||||
private uint32 flags;
|
||||
private Gee.ArrayList<string> strings = new Gee.ArrayList<string> ();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaDroidy", gir_version = "1.0")]
|
||||
namespace Frida.Droidy {
|
||||
public class DeviceTracker : Object {
|
||||
public sealed class DeviceTracker : Object {
|
||||
public signal void device_attached (DeviceDetails details);
|
||||
public signal void device_detached (string serial);
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace Frida.Droidy {
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceDetails : Object {
|
||||
public sealed class DeviceDetails : Object {
|
||||
public string serial {
|
||||
get;
|
||||
construct;
|
||||
@@ -205,7 +205,7 @@ namespace Frida.Droidy {
|
||||
}
|
||||
}
|
||||
|
||||
public class ShellSession : Object {
|
||||
public sealed class ShellSession : Object {
|
||||
public signal void output (StdioPipe pipe, Bytes bytes);
|
||||
public signal void closed ();
|
||||
|
||||
@@ -594,7 +594,7 @@ namespace Frida.Droidy {
|
||||
STDERR
|
||||
}
|
||||
|
||||
public class ShellCommandResult : Object {
|
||||
public sealed class ShellCommandResult : Object {
|
||||
public uint8 status {
|
||||
get;
|
||||
construct;
|
||||
@@ -686,7 +686,7 @@ namespace Frida.Droidy {
|
||||
}
|
||||
}
|
||||
|
||||
public class FileMetadata : Object {
|
||||
public sealed class FileMetadata : Object {
|
||||
public uint32 mode {
|
||||
get;
|
||||
set;
|
||||
@@ -700,7 +700,7 @@ namespace Frida.Droidy {
|
||||
}
|
||||
}
|
||||
|
||||
public class JDWPTracker : Object {
|
||||
public sealed class JDWPTracker : Object {
|
||||
public signal void debugger_attached (uint pid);
|
||||
public signal void debugger_detached (uint pid);
|
||||
|
||||
@@ -768,7 +768,7 @@ namespace Frida.Droidy {
|
||||
}
|
||||
}
|
||||
|
||||
public class Client : Object {
|
||||
public sealed class Client : Object {
|
||||
public signal void closed ();
|
||||
public signal void message (string payload);
|
||||
|
||||
@@ -791,7 +791,7 @@ namespace Frida.Droidy {
|
||||
}
|
||||
|
||||
private const uint16 ADB_SERVER_DEFAULT_PORT = 5037;
|
||||
private const uint16 MAX_MESSAGE_LENGTH = 1024;
|
||||
private const size_t MAX_MESSAGE_LENGTH = 65536;
|
||||
|
||||
public static async Client open (Cancellable? cancellable = null) throws Error, IOError {
|
||||
SocketConnectable? connectable = null;
|
||||
@@ -1154,7 +1154,7 @@ namespace Frida.Droidy {
|
||||
}
|
||||
}
|
||||
|
||||
public class SyncSession : Object {
|
||||
public sealed class SyncSession : Object {
|
||||
public Client client {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class DroidyHostSessionBackend : Object, HostSessionBackend {
|
||||
public sealed class DroidyHostSessionBackend : Object, HostSessionBackend {
|
||||
private Droidy.DeviceTracker tracker;
|
||||
|
||||
private Gee.HashMap<string, DroidyHostSessionProvider> providers = new Gee.HashMap<string, DroidyHostSessionProvider> ();
|
||||
@@ -86,7 +86,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class DroidyHostSessionProvider : Object, HostSessionProvider, HostChannelProvider {
|
||||
public sealed class DroidyHostSessionProvider : Object, HostSessionProvider, HostChannelProvider {
|
||||
public string id {
|
||||
get { return device_details.serial; }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -182,7 +212,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class DroidyHostSession : Object, HostSession {
|
||||
public sealed class DroidyHostSession : Object, HostSession {
|
||||
public Droidy.DeviceDetails device_details {
|
||||
get;
|
||||
construct;
|
||||
@@ -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 ();
|
||||
@@ -1217,7 +1287,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class HelperClient : Object, AsyncInitable {
|
||||
public sealed class HelperClient : Object, AsyncInitable {
|
||||
public signal void closed ();
|
||||
|
||||
public string device_serial {
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Frida.Droidy.Injector {
|
||||
return yield session.run (cancellable);
|
||||
}
|
||||
|
||||
public class GadgetDetails : Object {
|
||||
public sealed class GadgetDetails : Object {
|
||||
public uint pid {
|
||||
get;
|
||||
construct;
|
||||
@@ -31,7 +31,7 @@ namespace Frida.Droidy.Injector {
|
||||
}
|
||||
}
|
||||
|
||||
private class Session : Object {
|
||||
private sealed class Session : Object {
|
||||
public InputStream gadget {
|
||||
get;
|
||||
construct;
|
||||
|
||||
+57
-57
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaJDWP", gir_version = "1.0")]
|
||||
namespace Frida.JDWP {
|
||||
public class Client : GLib.Object, AsyncInitable {
|
||||
public sealed class Client : GLib.Object, AsyncInitable {
|
||||
public signal void closed ();
|
||||
public signal void events_received (Events events);
|
||||
|
||||
@@ -593,7 +593,7 @@ namespace Frida.JDWP {
|
||||
public abstract string to_string ();
|
||||
}
|
||||
|
||||
public class Byte : Value {
|
||||
public sealed class Byte : Value {
|
||||
public uint8 val {
|
||||
get;
|
||||
construct;
|
||||
@@ -608,7 +608,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Char : Value {
|
||||
public sealed class Char : Value {
|
||||
public string val {
|
||||
get;
|
||||
construct;
|
||||
@@ -623,7 +623,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Double : Value {
|
||||
public sealed class Double : Value {
|
||||
public double val {
|
||||
get;
|
||||
construct;
|
||||
@@ -638,7 +638,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Float : Value {
|
||||
public sealed class Float : Value {
|
||||
public float val {
|
||||
get;
|
||||
construct;
|
||||
@@ -653,7 +653,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Int : Value {
|
||||
public sealed class Int : Value {
|
||||
public int32 val {
|
||||
get;
|
||||
construct;
|
||||
@@ -668,7 +668,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Long : Value {
|
||||
public sealed class Long : Value {
|
||||
public int64 val {
|
||||
get;
|
||||
construct;
|
||||
@@ -698,7 +698,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Short : Value {
|
||||
public sealed class Short : Value {
|
||||
public int16 val {
|
||||
get;
|
||||
construct;
|
||||
@@ -713,7 +713,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Void : Value {
|
||||
public sealed class Void : Value {
|
||||
public Void () {
|
||||
GLib.Object (tag: ValueTag.VOID);
|
||||
}
|
||||
@@ -723,7 +723,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Boolean : Value {
|
||||
public sealed class Boolean : Value {
|
||||
public bool val {
|
||||
get;
|
||||
construct;
|
||||
@@ -738,43 +738,43 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Array : Object {
|
||||
public sealed class Array : Object {
|
||||
public Array (ObjectID val) {
|
||||
GLib.Object (tag: ValueTag.ARRAY, val: val);
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassObject : Object {
|
||||
public sealed class ClassObject : Object {
|
||||
public ClassObject (ObjectID val) {
|
||||
GLib.Object (tag: ValueTag.CLASS_OBJECT, val: val);
|
||||
}
|
||||
}
|
||||
|
||||
public class ThreadGroup : Object {
|
||||
public sealed class ThreadGroup : Object {
|
||||
public ThreadGroup (ObjectID val) {
|
||||
GLib.Object (tag: ValueTag.THREAD_GROUP, val: val);
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassLoader : Object {
|
||||
public sealed class ClassLoader : Object {
|
||||
public ClassLoader (ObjectID val) {
|
||||
GLib.Object (tag: ValueTag.CLASS_LOADER, val: val);
|
||||
}
|
||||
}
|
||||
|
||||
public class String : Object {
|
||||
public sealed class String : Object {
|
||||
public String (ObjectID val) {
|
||||
GLib.Object (tag: ValueTag.STRING, val: val);
|
||||
}
|
||||
}
|
||||
|
||||
public class Thread : Object {
|
||||
public sealed class Thread : Object {
|
||||
public Thread (ObjectID val) {
|
||||
GLib.Object (tag: ValueTag.THREAD, val: val);
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassInfo : GLib.Object {
|
||||
public sealed class ClassInfo : GLib.Object {
|
||||
public TaggedReferenceTypeID ref_type {
|
||||
get;
|
||||
construct;
|
||||
@@ -815,7 +815,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class MethodInfo : GLib.Object {
|
||||
public sealed class MethodInfo : GLib.Object {
|
||||
public MethodID id {
|
||||
get;
|
||||
construct;
|
||||
@@ -992,7 +992,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class Location : GLib.Object {
|
||||
public sealed class Location : GLib.Object {
|
||||
public TaggedReferenceTypeID declaring {
|
||||
get;
|
||||
construct;
|
||||
@@ -1071,7 +1071,7 @@ namespace Frida.JDWP {
|
||||
ALL = 2,
|
||||
}
|
||||
|
||||
public class Events : GLib.Object {
|
||||
public sealed class Events : GLib.Object {
|
||||
public SuspendPolicy suspend_policy {
|
||||
get;
|
||||
construct;
|
||||
@@ -1119,7 +1119,7 @@ namespace Frida.JDWP {
|
||||
public abstract string to_string ();
|
||||
}
|
||||
|
||||
public class SingleStepEvent : Event {
|
||||
public sealed class SingleStepEvent : Event {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -1155,7 +1155,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class BreakpointEvent : Event {
|
||||
public sealed class BreakpointEvent : Event {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -1191,7 +1191,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class FramePopEvent : Event {
|
||||
public sealed class FramePopEvent : Event {
|
||||
public override string to_string () {
|
||||
return "FramePopEvent()";
|
||||
}
|
||||
@@ -1201,7 +1201,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ExceptionEvent : Event {
|
||||
public sealed class ExceptionEvent : Event {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -1254,7 +1254,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class UserDefinedEvent : Event {
|
||||
public sealed class UserDefinedEvent : Event {
|
||||
public override string to_string () {
|
||||
return "UserDefinedEvent()";
|
||||
}
|
||||
@@ -1264,7 +1264,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ThreadStartEvent : Event {
|
||||
public sealed class ThreadStartEvent : Event {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -1292,7 +1292,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ThreadDeathEvent : Event {
|
||||
public sealed class ThreadDeathEvent : Event {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -1320,7 +1320,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassPrepareEvent : Event {
|
||||
public sealed class ClassPrepareEvent : Event {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -1373,7 +1373,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassUnloadEvent : Event {
|
||||
public sealed class ClassUnloadEvent : Event {
|
||||
public string signature {
|
||||
get;
|
||||
construct;
|
||||
@@ -1398,7 +1398,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassLoadEvent : Event {
|
||||
public sealed class ClassLoadEvent : Event {
|
||||
public override string to_string () {
|
||||
return "ClassLoadEvent()";
|
||||
}
|
||||
@@ -1435,7 +1435,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class FieldAccessEvent : FieldEvent {
|
||||
public sealed class FieldAccessEvent : FieldEvent {
|
||||
public FieldAccessEvent (EventRequestID request, ThreadID thread, Location location, TaggedReferenceTypeID ref_type,
|
||||
FieldID field, TaggedObjectID object) {
|
||||
GLib.Object (
|
||||
@@ -1471,7 +1471,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class FieldModificationEvent : FieldEvent {
|
||||
public sealed class FieldModificationEvent : FieldEvent {
|
||||
public Value value_to_be {
|
||||
get;
|
||||
construct;
|
||||
@@ -1516,7 +1516,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ExceptionCatchEvent : Event {
|
||||
public sealed class ExceptionCatchEvent : Event {
|
||||
public override string to_string () {
|
||||
return "ExceptionCatchEvent()";
|
||||
}
|
||||
@@ -1538,7 +1538,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class MethodEntryEvent : MethodEvent {
|
||||
public sealed class MethodEntryEvent : MethodEvent {
|
||||
public MethodEntryEvent (EventRequestID request, ThreadID thread, Location location) {
|
||||
GLib.Object (
|
||||
kind: EventKind.METHOD_ENTRY,
|
||||
@@ -1564,7 +1564,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class MethodExitEvent : MethodEvent {
|
||||
public sealed class MethodExitEvent : MethodEvent {
|
||||
public MethodExitEvent (EventRequestID request, ThreadID thread, Location location) {
|
||||
GLib.Object (
|
||||
kind: EventKind.METHOD_EXIT,
|
||||
@@ -1590,7 +1590,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class MethodExitWithReturnValueEvent : MethodEvent {
|
||||
public sealed class MethodExitWithReturnValueEvent : MethodEvent {
|
||||
public Value retval {
|
||||
get;
|
||||
construct;
|
||||
@@ -1641,7 +1641,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class MonitorContendedEnterEvent : MonitorEvent {
|
||||
public sealed class MonitorContendedEnterEvent : MonitorEvent {
|
||||
public MonitorContendedEnterEvent (EventRequestID request, ThreadID thread, TaggedObjectID object, Location location) {
|
||||
GLib.Object (
|
||||
kind: EventKind.MONITOR_CONTENDED_ENTER,
|
||||
@@ -1670,7 +1670,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class MonitorContendedEnteredEvent : MonitorEvent {
|
||||
public sealed class MonitorContendedEnteredEvent : MonitorEvent {
|
||||
public MonitorContendedEnteredEvent (EventRequestID request, ThreadID thread, TaggedObjectID object, Location location) {
|
||||
GLib.Object (
|
||||
kind: EventKind.MONITOR_CONTENDED_ENTERED,
|
||||
@@ -1699,7 +1699,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class MonitorWaitEvent : MonitorEvent {
|
||||
public sealed class MonitorWaitEvent : MonitorEvent {
|
||||
public int64 timeout {
|
||||
get;
|
||||
construct;
|
||||
@@ -1736,7 +1736,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class MonitorWaitedEvent : MonitorEvent {
|
||||
public sealed class MonitorWaitedEvent : MonitorEvent {
|
||||
public bool timed_out {
|
||||
get;
|
||||
construct;
|
||||
@@ -1774,7 +1774,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class VMStartEvent : Event {
|
||||
public sealed class VMStartEvent : Event {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -1799,7 +1799,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class VMDeathEvent : Event {
|
||||
public sealed class VMDeathEvent : Event {
|
||||
public VMDeathEvent (EventRequestID request) {
|
||||
GLib.Object (kind: EventKind.VM_DEATH, request: request);
|
||||
}
|
||||
@@ -1814,7 +1814,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class VMDisconnectedEvent : Event {
|
||||
public sealed class VMDisconnectedEvent : Event {
|
||||
public override string to_string () {
|
||||
return "VMDisconnectedEvent()";
|
||||
}
|
||||
@@ -1828,7 +1828,7 @@ namespace Frida.JDWP {
|
||||
internal abstract void serialize (PacketBuilder builder);
|
||||
}
|
||||
|
||||
public class CountModifier : EventModifier {
|
||||
public sealed class CountModifier : EventModifier {
|
||||
public int32 count {
|
||||
get;
|
||||
construct;
|
||||
@@ -1845,7 +1845,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ThreadOnlyModifier : EventModifier {
|
||||
public sealed class ThreadOnlyModifier : EventModifier {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -1862,7 +1862,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassOnlyModifier : EventModifier {
|
||||
public sealed class ClassOnlyModifier : EventModifier {
|
||||
public ReferenceTypeID clazz {
|
||||
get;
|
||||
construct;
|
||||
@@ -1879,7 +1879,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassMatchModifier : EventModifier {
|
||||
public sealed class ClassMatchModifier : EventModifier {
|
||||
public string class_pattern {
|
||||
get;
|
||||
construct;
|
||||
@@ -1896,7 +1896,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassExcludeModifier : EventModifier {
|
||||
public sealed class ClassExcludeModifier : EventModifier {
|
||||
public string class_pattern {
|
||||
get;
|
||||
construct;
|
||||
@@ -1913,7 +1913,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class LocationOnlyModifier : EventModifier {
|
||||
public sealed class LocationOnlyModifier : EventModifier {
|
||||
public Location location {
|
||||
get;
|
||||
construct;
|
||||
@@ -1929,7 +1929,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class ExceptionOnlyModifier : EventModifier {
|
||||
public sealed class ExceptionOnlyModifier : EventModifier {
|
||||
public ReferenceTypeID exception_or_null {
|
||||
get;
|
||||
construct;
|
||||
@@ -1962,7 +1962,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class FieldOnlyModifier : EventModifier {
|
||||
public sealed class FieldOnlyModifier : EventModifier {
|
||||
public ReferenceTypeID declaring {
|
||||
get;
|
||||
construct;
|
||||
@@ -1988,7 +1988,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class StepModifier : EventModifier {
|
||||
public sealed class StepModifier : EventModifier {
|
||||
public ThreadID thread {
|
||||
get;
|
||||
construct;
|
||||
@@ -2032,7 +2032,7 @@ namespace Frida.JDWP {
|
||||
OUT = 2,
|
||||
}
|
||||
|
||||
public class InstanceOnlyModifier : EventModifier {
|
||||
public sealed class InstanceOnlyModifier : EventModifier {
|
||||
public ObjectID instance {
|
||||
get;
|
||||
construct;
|
||||
@@ -2049,7 +2049,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
public class SourceNameMatchModifier : EventModifier {
|
||||
public sealed class SourceNameMatchModifier : EventModifier {
|
||||
public string source_name_pattern {
|
||||
get;
|
||||
construct;
|
||||
@@ -2149,7 +2149,7 @@ namespace Frida.JDWP {
|
||||
REPLY = (1 << 7),
|
||||
}
|
||||
|
||||
private class CommandBuilder : PacketBuilder {
|
||||
private sealed class CommandBuilder : PacketBuilder {
|
||||
public CommandBuilder (uint32 id, CommandSet command_set, uint8 command, IDSizes id_sizes) {
|
||||
base (id, 0, id_sizes);
|
||||
|
||||
@@ -2354,7 +2354,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
private class PacketReader {
|
||||
private sealed class PacketReader {
|
||||
public size_t available_bytes {
|
||||
get {
|
||||
return end - cursor;
|
||||
@@ -2574,7 +2574,7 @@ namespace Frida.JDWP {
|
||||
}
|
||||
}
|
||||
|
||||
private class IDSizes {
|
||||
private sealed class IDSizes {
|
||||
private bool valid;
|
||||
private int field_id_size = -1;
|
||||
private int method_id_size = -1;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class FileMonitor : Object {
|
||||
public sealed class FileMonitor : Object {
|
||||
public signal void change (string file_path, string? other_file_path, FileMonitorEvent event);
|
||||
|
||||
public string path {
|
||||
|
||||
@@ -408,7 +408,7 @@ namespace Frida {
|
||||
protected extern InputStream _get_fifo_for_inject_instance (void * instance);
|
||||
protected extern void _free_inject_instance (void * instance, UnloadPolicy unload_policy);
|
||||
|
||||
public class ResourceStore {
|
||||
public sealed class ResourceStore {
|
||||
public TemporaryDirectory tempdir {
|
||||
get;
|
||||
private set;
|
||||
@@ -439,7 +439,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class AgentDescriptor : Object {
|
||||
public sealed class AgentDescriptor : Object {
|
||||
public string name {
|
||||
get;
|
||||
construct;
|
||||
@@ -553,7 +553,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoteModule {
|
||||
private sealed class RemoteModule {
|
||||
private Gum.Address base_address;
|
||||
private Gum.ElfModule module;
|
||||
|
||||
@@ -587,7 +587,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoteThreadSession : Object {
|
||||
private sealed class RemoteThreadSession : Object {
|
||||
public signal void ended (UnloadPolicy unload_policy);
|
||||
|
||||
public uint id {
|
||||
|
||||
@@ -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 sealed 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 sealed class FreebsdHostSessionProvider : LocalHostSessionProvider {
|
||||
protected override LocalHostSession make_host_session (HostSessionOptions? options) throws Error {
|
||||
return new FreebsdHostSession ();
|
||||
}
|
||||
}
|
||||
|
||||
public class FreebsdHostSession : BaseDBusHostSession {
|
||||
public sealed class FreebsdHostSession : LocalHostSession {
|
||||
private AgentContainer system_session_container;
|
||||
|
||||
private AgentDescriptor agent_desc;
|
||||
|
||||
+201
-135
@@ -1,3 +1,4 @@
|
||||
[CCode (gir_namespace = "Frida", gir_version = "1.0")]
|
||||
namespace Frida {
|
||||
public extern void init ();
|
||||
public extern void init_with_runtime (Runtime runtime);
|
||||
@@ -23,7 +24,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceManager : Object {
|
||||
public sealed class DeviceManager : Object {
|
||||
public signal void added (Device device);
|
||||
public signal void removed (Device device);
|
||||
public signal void changed ();
|
||||
@@ -33,12 +34,24 @@ namespace Frida {
|
||||
private Promise<bool> start_request;
|
||||
private Promise<bool> stop_request;
|
||||
|
||||
private HostSessionService service = null;
|
||||
private HostSessionService service;
|
||||
private Gee.ArrayList<Device> devices = new Gee.ArrayList<Device> ();
|
||||
private Gee.ArrayList<DeviceObserverEntry> on_device_added = new Gee.ArrayList<DeviceObserverEntry> ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
public DeviceManager () {
|
||||
service = new HostSessionService.with_default_backends ();
|
||||
}
|
||||
|
||||
public DeviceManager.with_nonlocal_backends_only () {
|
||||
service = new HostSessionService.with_nonlocal_backends_only ();
|
||||
}
|
||||
|
||||
public DeviceManager.with_socket_backend_only () {
|
||||
service = new HostSessionService.with_socket_backend_only ();
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable = null) throws IOError {
|
||||
yield stop_service (cancellable);
|
||||
}
|
||||
@@ -303,7 +316,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public void _release_device (Device device) {
|
||||
internal void _release_device (Device device) {
|
||||
var device_did_exist = devices.remove (device);
|
||||
assert (device_did_exist);
|
||||
}
|
||||
@@ -339,7 +352,6 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void start_service () {
|
||||
service = new HostSessionService.with_default_backends ();
|
||||
try {
|
||||
service.provider_available.connect (on_provider_available);
|
||||
service.provider_unavailable.connect (on_provider_unavailable);
|
||||
@@ -456,7 +468,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class DeviceList : Object {
|
||||
public sealed class DeviceList : Object {
|
||||
private Gee.List<Device> items;
|
||||
|
||||
internal DeviceList (Gee.List<Device> items) {
|
||||
@@ -472,7 +484,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Device : Object {
|
||||
public sealed class Device : Object {
|
||||
public signal void spawn_added (Spawn spawn);
|
||||
public signal void spawn_removed (Spawn spawn);
|
||||
public signal void child_added (Child child);
|
||||
@@ -524,18 +536,10 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public HostSessionProvider provider {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public weak DeviceManager? manager {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private string? _id;
|
||||
private string? _name;
|
||||
internal HostSessionProvider provider;
|
||||
private unowned DeviceManager? manager;
|
||||
|
||||
private HostSessionOptions? host_session_options;
|
||||
private Promise<HostSession>? host_session_request;
|
||||
@@ -547,29 +551,30 @@ 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);
|
||||
|
||||
internal Device (DeviceManager? manager, HostSessionProvider provider, string? id = null, string? name = null,
|
||||
internal Device (DeviceManager? mgr, HostSessionProvider prov, string? id = null, string? name = null,
|
||||
HostSessionOptions? options = null) {
|
||||
Object (
|
||||
manager: manager,
|
||||
provider: provider,
|
||||
icon: provider.icon
|
||||
);
|
||||
Object (icon: prov.icon);
|
||||
|
||||
_id = id;
|
||||
_name = name;
|
||||
manager = mgr;
|
||||
host_session_options = options;
|
||||
|
||||
assign_provider (prov);
|
||||
}
|
||||
|
||||
construct {
|
||||
_bus = new Bus (this);
|
||||
}
|
||||
|
||||
private void assign_provider (HostSessionProvider prov) {
|
||||
provider = prov;
|
||||
provider.host_session_detached.connect (on_host_session_detached);
|
||||
provider.agent_session_detached.connect (on_agent_session_detached);
|
||||
|
||||
_bus = new Bus (this);
|
||||
}
|
||||
|
||||
public bool is_lost () {
|
||||
@@ -1232,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 {
|
||||
@@ -1256,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 {
|
||||
@@ -1281,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 ();
|
||||
|
||||
@@ -1310,7 +1319,7 @@ namespace Frida {
|
||||
throw new Error.INVALID_OPERATION ("Device is gone");
|
||||
}
|
||||
|
||||
public async HostSession get_host_session (Cancellable? cancellable) throws Error, IOError {
|
||||
internal async HostSession get_host_session (Cancellable? cancellable) throws Error, IOError {
|
||||
while (host_session_request != null) {
|
||||
try {
|
||||
return yield host_session_request.future.wait_async (cancellable);
|
||||
@@ -1338,16 +1347,6 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public HostSession get_host_session_sync (Cancellable? cancellable = null) throws Error, IOError {
|
||||
return create<GetHostSessionTask> ().execute (cancellable);
|
||||
}
|
||||
|
||||
private class GetHostSessionTask : DeviceTask<HostSession> {
|
||||
protected override async HostSession perform_operation () throws Error, IOError {
|
||||
return yield parent.get_host_session (cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_host_session_detached (HostSession session) {
|
||||
if (session != current_host_session)
|
||||
return;
|
||||
@@ -1393,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 ();
|
||||
@@ -1462,7 +1457,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public async void _release_session (Session session, bool may_block, Cancellable? cancellable) throws IOError {
|
||||
internal async void _release_session (Session session, bool may_block, Cancellable? cancellable) throws IOError {
|
||||
AgentSessionId? session_id = null;
|
||||
foreach (var entry in agent_sessions.entries) {
|
||||
if (entry.value == session) {
|
||||
@@ -1550,7 +1545,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class RemoteDeviceOptions : Object {
|
||||
public sealed class RemoteDeviceOptions : Object {
|
||||
public TlsCertificate? certificate {
|
||||
get;
|
||||
set;
|
||||
@@ -1573,7 +1568,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class ApplicationList : Object {
|
||||
public sealed class ApplicationList : Object {
|
||||
private Gee.List<Application> items;
|
||||
|
||||
internal ApplicationList (Gee.List<Application> items) {
|
||||
@@ -1589,7 +1584,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Application : Object {
|
||||
public sealed class Application : Object {
|
||||
public string identifier {
|
||||
get;
|
||||
construct;
|
||||
@@ -1615,7 +1610,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class ProcessList : Object {
|
||||
public sealed class ProcessList : Object {
|
||||
private Gee.List<Process> items;
|
||||
|
||||
internal ProcessList (Gee.List<Process> items) {
|
||||
@@ -1631,7 +1626,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Process : Object {
|
||||
public sealed class Process : Object {
|
||||
public uint pid {
|
||||
get;
|
||||
construct;
|
||||
@@ -1652,7 +1647,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class ProcessMatchOptions : Object {
|
||||
public sealed class ProcessMatchOptions : Object {
|
||||
public int timeout {
|
||||
get;
|
||||
set;
|
||||
@@ -1666,7 +1661,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class SpawnOptions : Object {
|
||||
public sealed class SpawnOptions : Object {
|
||||
public string[]? argv {
|
||||
get;
|
||||
set;
|
||||
@@ -1700,7 +1695,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class SpawnList : Object {
|
||||
public sealed class SpawnList : Object {
|
||||
private Gee.List<Spawn> items;
|
||||
|
||||
internal SpawnList (Gee.List<Spawn> items) {
|
||||
@@ -1716,7 +1711,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Spawn : Object {
|
||||
public sealed class Spawn : Object {
|
||||
public uint pid {
|
||||
get;
|
||||
construct;
|
||||
@@ -1740,7 +1735,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class ChildList : Object {
|
||||
public sealed class ChildList : Object {
|
||||
private Gee.List<Child> items;
|
||||
|
||||
internal ChildList (Gee.List<Child> items) {
|
||||
@@ -1756,7 +1751,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Child : Object {
|
||||
public sealed class Child : Object {
|
||||
public uint pid {
|
||||
get;
|
||||
construct;
|
||||
@@ -1820,7 +1815,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Crash : Object {
|
||||
public sealed class Crash : Object {
|
||||
public uint pid {
|
||||
get;
|
||||
construct;
|
||||
@@ -1869,7 +1864,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Bus : Object {
|
||||
public sealed class Bus : Object {
|
||||
public signal void detached ();
|
||||
public signal void message (string json, Bytes? data);
|
||||
|
||||
@@ -2004,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);
|
||||
@@ -2022,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 {
|
||||
@@ -2038,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;
|
||||
@@ -2054,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);
|
||||
}
|
||||
@@ -2066,7 +2151,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Session : Object, AgentMessageSink {
|
||||
public sealed class Session : Object, AgentMessageSink {
|
||||
public signal void detached (SessionDetachReason reason, Crash? crash);
|
||||
|
||||
public uint pid {
|
||||
@@ -2074,26 +2159,13 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public AgentSessionId id {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public AgentSession session {
|
||||
get {
|
||||
return active_session;
|
||||
}
|
||||
}
|
||||
|
||||
public uint persist_timeout {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public weak Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
private AgentSessionId id;
|
||||
private unowned Device device;
|
||||
|
||||
private State state = ATTACHED;
|
||||
private Promise<bool> close_request;
|
||||
@@ -2131,12 +2203,10 @@ namespace Frida {
|
||||
}
|
||||
|
||||
internal Session (Device device, uint pid, AgentSessionId id, SessionOptions options) {
|
||||
Object (
|
||||
pid: pid,
|
||||
id: id,
|
||||
persist_timeout: options.persist_timeout,
|
||||
device: device
|
||||
);
|
||||
Object (pid: pid, persist_timeout: options.persist_timeout);
|
||||
|
||||
this.id = id;
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public bool is_detached () {
|
||||
@@ -2192,7 +2262,7 @@ namespace Frida {
|
||||
|
||||
uint last_tx_batch_id;
|
||||
try {
|
||||
yield session.resume (last_rx_batch_id, cancellable, out last_tx_batch_id);
|
||||
yield active_session.resume (last_rx_batch_id, cancellable, out last_tx_batch_id);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -2224,7 +2294,7 @@ namespace Frida {
|
||||
check_open ();
|
||||
|
||||
try {
|
||||
yield session.enable_child_gating (cancellable);
|
||||
yield active_session.enable_child_gating (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -2244,7 +2314,7 @@ namespace Frida {
|
||||
check_open ();
|
||||
|
||||
try {
|
||||
yield session.disable_child_gating (cancellable);
|
||||
yield active_session.disable_child_gating (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -2268,7 +2338,7 @@ namespace Frida {
|
||||
|
||||
AgentScriptId script_id;
|
||||
try {
|
||||
script_id = yield session.create_script (source, raw_options, cancellable);
|
||||
script_id = yield active_session.create_script (source, raw_options, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -2306,7 +2376,7 @@ namespace Frida {
|
||||
|
||||
AgentScriptId script_id;
|
||||
try {
|
||||
script_id = yield session.create_script_from_bytes (bytes.get_data (), raw_options,
|
||||
script_id = yield active_session.create_script_from_bytes (bytes.get_data (), raw_options,
|
||||
cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
@@ -2345,7 +2415,7 @@ namespace Frida {
|
||||
|
||||
uint8[] data;
|
||||
try {
|
||||
data = yield session.compile_script (source, raw_options, cancellable);
|
||||
data = yield active_session.compile_script (source, raw_options, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -2378,7 +2448,7 @@ namespace Frida {
|
||||
|
||||
uint8[] data;
|
||||
try {
|
||||
data = yield session.snapshot_script (embed_script, raw_options, cancellable);
|
||||
data = yield active_session.snapshot_script (embed_script, raw_options, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -2782,7 +2852,7 @@ namespace Frida {
|
||||
|
||||
PortalMembershipId membership_id;
|
||||
try {
|
||||
membership_id = yield session.join_portal (address, raw_options, cancellable);
|
||||
membership_id = yield active_session.join_portal (address, raw_options, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -2944,7 +3014,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public void _release_script (AgentScriptId script_id) {
|
||||
internal void _release_script (AgentScriptId script_id) {
|
||||
var script_did_exist = scripts.unset (script_id);
|
||||
assert (script_did_exist);
|
||||
}
|
||||
@@ -3010,7 +3080,7 @@ namespace Frida {
|
||||
|
||||
private async void close_session_and_peer_connection (Cancellable? cancellable) throws IOError {
|
||||
try {
|
||||
yield session.close (cancellable);
|
||||
yield active_session.close (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED) {
|
||||
discard_peer_connection ();
|
||||
@@ -3033,6 +3103,10 @@ namespace Frida {
|
||||
}
|
||||
#endif
|
||||
|
||||
public DBusConnection _get_connection () {
|
||||
return ((DBusProxy) active_session).g_connection;
|
||||
}
|
||||
|
||||
private T create<T> () {
|
||||
return Object.new (typeof (T), parent: this);
|
||||
}
|
||||
@@ -3045,26 +3119,22 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class Script : Object {
|
||||
public sealed class Script : Object {
|
||||
public signal void destroyed ();
|
||||
public signal void message (string json, Bytes? data);
|
||||
|
||||
public AgentScriptId id {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public weak Session session {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
private AgentScriptId id;
|
||||
private unowned Session session;
|
||||
|
||||
private Promise<bool> close_request;
|
||||
|
||||
private Gum.InspectorServer? inspector_server;
|
||||
|
||||
internal Script (Session session, AgentScriptId script_id) {
|
||||
Object (id: script_id, session: session);
|
||||
Object ();
|
||||
|
||||
this.id = script_id;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public bool is_destroyed () {
|
||||
@@ -3075,7 +3145,7 @@ namespace Frida {
|
||||
check_open ();
|
||||
|
||||
try {
|
||||
yield session.session.load_script (id, cancellable);
|
||||
yield session.active_session.load_script (id, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -3111,7 +3181,7 @@ namespace Frida {
|
||||
check_open ();
|
||||
|
||||
try {
|
||||
yield session.session.eternalize_script (id, cancellable);
|
||||
yield session.active_session.eternalize_script (id, cancellable);
|
||||
|
||||
yield _do_close (false, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
@@ -3162,7 +3232,7 @@ namespace Frida {
|
||||
inspector_server.message.connect (on_debugger_message_from_frontend);
|
||||
|
||||
try {
|
||||
yield session.session.enable_debugger (id, cancellable);
|
||||
yield session.active_session.enable_debugger (id, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
inspector_server = null;
|
||||
|
||||
@@ -3176,7 +3246,7 @@ namespace Frida {
|
||||
inspector_server = null;
|
||||
|
||||
try {
|
||||
yield session.session.disable_debugger (id, cancellable);
|
||||
yield session.active_session.disable_debugger (id, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
@@ -3210,7 +3280,7 @@ namespace Frida {
|
||||
inspector_server = null;
|
||||
|
||||
try {
|
||||
yield session.session.disable_debugger (id, cancellable);
|
||||
yield session.active_session.disable_debugger (id, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
@@ -3264,7 +3334,7 @@ namespace Frida {
|
||||
|
||||
if (may_block) {
|
||||
try {
|
||||
yield parent.session.destroy_script (id, cancellable);
|
||||
yield parent.active_session.destroy_script (id, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
@@ -3286,24 +3356,20 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class PortalMembership : Object {
|
||||
public uint id {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Session session {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
public sealed class PortalMembership : Object {
|
||||
private uint id;
|
||||
private Session session;
|
||||
|
||||
internal PortalMembership (Session session, PortalMembershipId membership_id) {
|
||||
Object (id: membership_id.handle, session: session);
|
||||
Object ();
|
||||
|
||||
this.id = membership_id.handle;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public async void terminate (Cancellable? cancellable = null) throws Error, IOError {
|
||||
try {
|
||||
yield session.session.leave_portal (PortalMembershipId (id), cancellable);
|
||||
yield session.active_session.leave_portal (PortalMembershipId (id), cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class LinuxFruitFinder : Object, FruitFinder {
|
||||
public sealed class LinuxFruitFinder : Object, FruitFinder {
|
||||
public string? udid_from_iface (string ifname) throws Error {
|
||||
var net = "/sys/class/net";
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Frida.Fruity {
|
||||
return null;
|
||||
var iface_stream = new DataInputStream (iface.read ());
|
||||
string iface_name = iface_stream.read_line ();
|
||||
if (iface_name != "NCM Control" && iface_name != "AppleUSBEthernet")
|
||||
if (iface_name != "NCM Control")
|
||||
return null;
|
||||
|
||||
var serial = File.new_build_filename (dev_path, "..", "..", "..", "serial");
|
||||
@@ -27,14 +27,14 @@ namespace Frida.Fruity {
|
||||
return null;
|
||||
|
||||
var serial_stream = new DataInputStream (serial.read ());
|
||||
return serial_stream.read_line ();
|
||||
return UsbDevice.udid_from_serial_number (serial_stream.read_line ());
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("%s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class LinuxPairingBrowser : Object, PairingBrowser {
|
||||
public sealed class LinuxPairingBrowser : Object, PairingBrowser {
|
||||
private Resolved.Manager? resolved;
|
||||
private Source? timer;
|
||||
|
||||
@@ -177,12 +177,14 @@ namespace Frida.Fruity {
|
||||
Linux.Network.IfAddrs ifaddrs;
|
||||
Linux.Network.getifaddrs (out ifaddrs);
|
||||
for (unowned Linux.Network.IfAddrs candidate = ifaddrs; candidate != null; candidate = candidate.ifa_next) {
|
||||
unowned Posix.SockAddr? address = candidate.ifa_addr;
|
||||
if (address == null || address.sa_family != Posix.AF_INET6)
|
||||
continue;
|
||||
|
||||
if (candidate.ifa_name != ifname)
|
||||
continue;
|
||||
if (candidate.ifa_addr.sa_family != Posix.AF_INET6)
|
||||
continue;
|
||||
return (InetSocketAddress)
|
||||
SocketAddress.from_native ((void *) candidate.ifa_addr, sizeof (Posix.SockAddrIn6));
|
||||
|
||||
return (InetSocketAddress) SocketAddress.from_native ((void *) address, sizeof (Posix.SockAddrIn6));
|
||||
}
|
||||
|
||||
throw new Error.NOT_SUPPORTED ("Unable to resolve interface address");
|
||||
@@ -195,6 +197,164 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
namespace Network {
|
||||
public async void wait_until_interfaces_ready (Gee.Collection<string> interface_names, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
try {
|
||||
var connection = yield GLib.Bus.get (BusType.SYSTEM, cancellable);
|
||||
|
||||
NetworkManager.Service? nm = null;
|
||||
Networkd.Service? netd = null;
|
||||
if (yield system_has_service (NetworkManager.SERVICE_NAME, connection, cancellable)) {
|
||||
nm = yield connection.get_proxy (NetworkManager.SERVICE_NAME, NetworkManager.SERVICE_PATH,
|
||||
DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
} else if (yield system_has_service (Networkd.SERVICE_NAME, connection, cancellable)) {
|
||||
netd = yield connection.get_proxy (Networkd.SERVICE_NAME, Networkd.SERVICE_PATH,
|
||||
DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
var remaining = interface_names.size + 1;
|
||||
|
||||
NotifyCompleteFunc on_complete = () => {
|
||||
remaining--;
|
||||
if (remaining == 0)
|
||||
wait_until_interfaces_ready.callback ();
|
||||
};
|
||||
|
||||
foreach (var name in interface_names) {
|
||||
if (nm != null) {
|
||||
NetworkManager.wait_until_interface_ready.begin (name, nm, connection, cancellable,
|
||||
on_complete);
|
||||
} else {
|
||||
Networkd.wait_until_interface_ready.begin (name, netd, connection, cancellable,
|
||||
on_complete);
|
||||
}
|
||||
}
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
on_complete ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
|
||||
yield;
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private async bool system_has_service (string name, DBusConnection connection, Cancellable? cancellable) throws GLib.Error {
|
||||
var v = yield connection.call (
|
||||
"org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
|
||||
"NameHasOwner",
|
||||
new Variant.tuple ({ name }),
|
||||
new VariantType.tuple ({ VariantType.BOOLEAN }),
|
||||
DBusCallFlags.NONE, -1, cancellable);
|
||||
|
||||
bool has_owner;
|
||||
v.get ("(b)", out has_owner);
|
||||
return has_owner;
|
||||
}
|
||||
}
|
||||
|
||||
private delegate void NotifyCompleteFunc ();
|
||||
|
||||
namespace NetworkManager {
|
||||
private async void wait_until_interface_ready (string name, Service service, DBusConnection connection,
|
||||
Cancellable? cancellable, NotifyCompleteFunc on_complete) {
|
||||
try {
|
||||
string device_path = yield service.get_device_by_ip_iface (name);
|
||||
|
||||
Device device = yield connection.get_proxy (SERVICE_NAME, device_path, DBusProxyFlags.NONE, cancellable);
|
||||
|
||||
var device_proxy = (DBusProxy) device;
|
||||
|
||||
ulong handler = device_proxy.g_properties_changed.connect ((changed, invalidated) => {
|
||||
if (changed.lookup_value ("StateReason", null) != null)
|
||||
wait_until_interface_ready.callback ();
|
||||
});
|
||||
|
||||
while (!cancellable.is_cancelled ()) {
|
||||
uint32 state, reason;
|
||||
device_proxy.get_cached_property ("StateReason").get ("(uu)", out state, out reason);
|
||||
if (state == DEVICE_STATE_ACTIVATED)
|
||||
break;
|
||||
if (state == DEVICE_STATE_DISCONNECTED && reason != DEVICE_STATE_REASON_NONE)
|
||||
break;
|
||||
yield;
|
||||
}
|
||||
|
||||
device_proxy.disconnect (handler);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
on_complete ();
|
||||
}
|
||||
|
||||
private const string SERVICE_NAME = "org.freedesktop.NetworkManager";
|
||||
private const string SERVICE_PATH = "/org/freedesktop/NetworkManager";
|
||||
|
||||
[DBus (name = "org.freedesktop.NetworkManager")]
|
||||
private interface Service : Object {
|
||||
public abstract async string get_device_by_ip_iface (string iface) throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.NetworkManager.Device")]
|
||||
private interface Device : Object {
|
||||
}
|
||||
|
||||
private const uint32 DEVICE_STATE_DISCONNECTED = 30;
|
||||
private const uint32 DEVICE_STATE_ACTIVATED = 100;
|
||||
|
||||
private const uint32 DEVICE_STATE_REASON_NONE = 0;
|
||||
}
|
||||
|
||||
namespace Networkd {
|
||||
private async void wait_until_interface_ready (string name, Service service, DBusConnection connection,
|
||||
Cancellable? cancellable, NotifyCompleteFunc on_complete) {
|
||||
try {
|
||||
int32 ifindex;
|
||||
string link_path;
|
||||
yield service.get_link_by_name (name, out ifindex, out link_path);
|
||||
|
||||
Link link = yield connection.get_proxy (SERVICE_NAME, link_path, DBusProxyFlags.NONE, cancellable);
|
||||
|
||||
var link_proxy = (DBusProxy) link;
|
||||
|
||||
ulong handler = link_proxy.g_properties_changed.connect ((changed, invalidated) => {
|
||||
wait_until_interface_ready.callback ();
|
||||
});
|
||||
|
||||
while (!cancellable.is_cancelled ()) {
|
||||
string operational_state;
|
||||
link_proxy.get_cached_property ("OperationalState").get ("s", out operational_state);
|
||||
if (operational_state != "carrier")
|
||||
break;
|
||||
yield;
|
||||
}
|
||||
|
||||
link_proxy.disconnect (handler);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
on_complete ();
|
||||
}
|
||||
|
||||
private const string SERVICE_NAME = "org.freedesktop.network1";
|
||||
private const string SERVICE_PATH = "/org/freedesktop/network1";
|
||||
|
||||
[DBus (name = "org.freedesktop.network1.Manager")]
|
||||
private interface Service : Object {
|
||||
public abstract async void get_link_by_name (string name, out int32 ifindex, out string path) throws GLib.Error;
|
||||
}
|
||||
|
||||
[DBus (name = "org.freedesktop.network1.Link")]
|
||||
private interface Link : Object {
|
||||
}
|
||||
}
|
||||
|
||||
namespace Resolved {
|
||||
public const string SERVICE_NAME = "org.freedesktop.resolve1";
|
||||
public const string SERVICE_PATH = "/org/freedesktop/resolve1";
|
||||
|
||||
@@ -129,6 +129,7 @@ namespace Frida.Fruity {
|
||||
private ConnectionType _connection_type;
|
||||
private string _udid;
|
||||
private string _name;
|
||||
private Bytes? _remote_unlock_host_key;
|
||||
|
||||
private Promise<Tunnel>? tunnel_request;
|
||||
|
||||
@@ -153,6 +154,13 @@ namespace Frida.Fruity {
|
||||
var reader = new XpcObjectReader (device_info);
|
||||
_name = reader.read_member ("name").get_string_value ();
|
||||
reader.end_member ();
|
||||
|
||||
if (reader.has_member ("remoteUnlockHostKey")) {
|
||||
_remote_unlock_host_key = new Bytes (reader.read_member ("remoteUnlockHostKey").get_data_value ());
|
||||
reader.end_member ();
|
||||
} else {
|
||||
_remote_unlock_host_key = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void on_state_changed (Object obj, ParamSpec pspec) {
|
||||
@@ -224,7 +232,7 @@ namespace Frida.Fruity {
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
var tunnel = new MacOSTunnel (pairing_device);
|
||||
var tunnel = new MacOSTunnel (pairing_device, _remote_unlock_host_key);
|
||||
yield tunnel.attach (cancellable);
|
||||
|
||||
tunnel_request.resolve (tunnel);
|
||||
@@ -246,6 +254,18 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public int64 opened_at {
|
||||
get {
|
||||
return _opened_at;
|
||||
}
|
||||
}
|
||||
|
||||
public Bytes? remote_unlock_host_key {
|
||||
get {
|
||||
return _remote_unlock_host_key;
|
||||
}
|
||||
}
|
||||
|
||||
public Darwin.Xpc.Uuid? assertion_identifier {
|
||||
get {
|
||||
return _assertion_identifier;
|
||||
@@ -253,12 +273,15 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private XpcClient pairing_device;
|
||||
private Bytes? _remote_unlock_host_key;
|
||||
private Darwin.Xpc.Uuid? _assertion_identifier;
|
||||
private InetAddress? tunnel_device_address;
|
||||
private DiscoveryService? _discovery;
|
||||
private int64 _opened_at = -1;
|
||||
|
||||
public MacOSTunnel (XpcClient pairing_device) {
|
||||
public MacOSTunnel (XpcClient pairing_device, Bytes? remote_unlock_host_key) {
|
||||
this.pairing_device = pairing_device;
|
||||
_remote_unlock_host_key = remote_unlock_host_key;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
@@ -275,6 +298,8 @@ namespace Frida.Fruity {
|
||||
r.body.set_int64 ("flags", 0);
|
||||
var response = yield pairing_device.request (r.message, cancellable);
|
||||
|
||||
_opened_at = get_monotonic_time ();
|
||||
|
||||
var reader = new XpcObjectReader (response);
|
||||
reader.read_member ("response");
|
||||
|
||||
@@ -386,7 +411,7 @@ namespace Frida.Fruity {
|
||||
return new PcbList (pcbs);
|
||||
}
|
||||
|
||||
public class PcbList {
|
||||
public sealed class PcbList {
|
||||
private uint8[] pcbs;
|
||||
|
||||
internal PcbList (owned uint8[] pcbs) {
|
||||
@@ -397,7 +422,7 @@ namespace Frida.Fruity {
|
||||
return new Iterator (this);
|
||||
}
|
||||
|
||||
public class Iterator {
|
||||
public sealed class Iterator {
|
||||
private PcbList list;
|
||||
private InetItem * cursor;
|
||||
|
||||
@@ -436,7 +461,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class Item {
|
||||
public sealed class Item {
|
||||
public SocketFamily family {
|
||||
get {
|
||||
return ((pcb.version_flag & Darwin.XNU.InetVersionFlags.IPV6) != 0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class WindowsPairingBrowser : Object, PairingBrowser {
|
||||
public sealed class WindowsPairingBrowser : Object, PairingBrowser {
|
||||
private Gee.Map<string, Monitor> monitors = new Gee.HashMap<string, Monitor> ();
|
||||
|
||||
private MainContext main_context = MainContext.ref_thread_default ();
|
||||
|
||||
+512
-201
@@ -163,9 +163,7 @@ namespace Frida.Fruity {
|
||||
|
||||
public string name {
|
||||
get {
|
||||
var transport = transports.first_match (t => t.name != null && t.connection_type == USB);
|
||||
if (transport == null)
|
||||
transport = transports.first_match (t => t.name != null);
|
||||
var transport = transports.first_match (t => t.name != null);
|
||||
if (transport == null)
|
||||
return "iOS Device";
|
||||
return transport.name;
|
||||
@@ -183,13 +181,25 @@ namespace Frida.Fruity {
|
||||
|
||||
public Gee.Set<Transport> transports {
|
||||
get;
|
||||
default = new Gee.HashSet<Transport> ();
|
||||
default = new Gee.TreeSet<Transport> (compare_transports);
|
||||
}
|
||||
|
||||
private Gee.Queue<UsbmuxLockdownServiceRequest> usbmux_lockdown_service_requests =
|
||||
new Gee.ArrayQueue<UsbmuxLockdownServiceRequest> ();
|
||||
private LockdownClient? cached_usbmux_lockdown_client;
|
||||
|
||||
private const string[] LOCKDOWN_SERVICES_WITHOUT_ESCROW_BAG_SUPPORT = {
|
||||
"com.apple.accessibility.axAuditDaemon.remoteserver",
|
||||
"com.apple.afc",
|
||||
"com.apple.companion_proxy",
|
||||
"com.apple.crashreportcopymobile",
|
||||
"com.apple.GPUTools.MobileService",
|
||||
"com.apple.idamd",
|
||||
"com.apple.PurpleReverseProxy.Conn",
|
||||
"com.apple.streaming_zip_conduit",
|
||||
"com.apple.webinspector",
|
||||
};
|
||||
|
||||
internal void close () {
|
||||
transports.clear ();
|
||||
}
|
||||
@@ -249,6 +259,9 @@ namespace Frida.Fruity {
|
||||
checkin.set_string ("Request", "RSDCheckin");
|
||||
checkin.set_string ("Label", "Xcode");
|
||||
checkin.set_string ("ProtocolVersion", "2");
|
||||
unowned Bytes? key = tunnel.remote_unlock_host_key;
|
||||
if (key != null && lockdown_service_supports_escrow_bag (service_name))
|
||||
checkin.set_bytes ("EscrowBag", key);
|
||||
|
||||
try {
|
||||
yield service.query (checkin, cancellable);
|
||||
@@ -285,6 +298,15 @@ namespace Frida.Fruity {
|
||||
return yield request.promise.future.wait_async (cancellable);
|
||||
}
|
||||
|
||||
// FIXME: Replace with `element in array`-check once Vala compiler bug has been fixed so generated C code is warning-free.
|
||||
private static bool lockdown_service_supports_escrow_bag (string name) {
|
||||
foreach (unowned string s in LOCKDOWN_SERVICES_WITHOUT_ESCROW_BAG_SUPPORT) {
|
||||
if (s == name)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async void process_usbmux_lockdown_service_requests () {
|
||||
UsbmuxLockdownServiceRequest? req;
|
||||
bool already_invalidated = false;
|
||||
@@ -326,81 +348,8 @@ namespace Frida.Fruity {
|
||||
unowned string location = tokens[1];
|
||||
|
||||
if (protocol == "tcp") {
|
||||
var tunnel = yield find_tunnel (cancellable);
|
||||
|
||||
uint16 port;
|
||||
ulong raw_port;
|
||||
if (ulong.try_parse (location, out raw_port)) {
|
||||
if (raw_port == 0 || raw_port > uint16.MAX)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid TCP port");
|
||||
port = (uint16) raw_port;
|
||||
} else {
|
||||
if (tunnel == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to resolve port name; tunnel not available");
|
||||
var service_info = tunnel.discovery.get_service (location);
|
||||
port = service_info.port;
|
||||
}
|
||||
|
||||
Error? pending_error = null;
|
||||
|
||||
if (tunnel != null) {
|
||||
try {
|
||||
return yield tunnel.open_tcp_connection (port, cancellable);
|
||||
} catch (Error e) {
|
||||
if (e is Error.SERVER_NOT_RUNNING)
|
||||
pending_error = e;
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
var usbmux_device = find_usbmux_device ();
|
||||
if (usbmux_device != null) {
|
||||
if (usbmux_device.connection_type == USB) {
|
||||
UsbmuxClient client = null;
|
||||
try {
|
||||
client = yield UsbmuxClient.open (cancellable);
|
||||
|
||||
yield client.connect_to_port (usbmux_device.id, port, cancellable);
|
||||
|
||||
return client.connection;
|
||||
} catch (GLib.Error e) {
|
||||
if (client != null)
|
||||
client.close.begin ();
|
||||
|
||||
if (e is UsbmuxError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
InetSocketAddress device_address = usbmux_device.network_address;
|
||||
var target_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: device_address.address,
|
||||
port: port,
|
||||
flowinfo: device_address.flowinfo,
|
||||
scope_id: device_address.scope_id
|
||||
);
|
||||
|
||||
var client = new SocketClient ();
|
||||
try {
|
||||
var connection = yield client.connect_async (target_address, cancellable);
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return connection;
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (pending_error != null)
|
||||
throw pending_error;
|
||||
throw new Error.TRANSPORT ("No viable transport found");
|
||||
var channel = yield open_tcp_channel (location, ALLOW_ANY_TRANSPORT, cancellable);
|
||||
return channel.stream;
|
||||
}
|
||||
|
||||
if (protocol == "lockdown")
|
||||
@@ -409,6 +358,101 @@ namespace Frida.Fruity {
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported channel address");
|
||||
}
|
||||
|
||||
public async TcpChannel open_tcp_channel (string location, OpenTcpChannelFlags flags, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var usbmux_device = find_usbmux_device ();
|
||||
var tunnel = yield find_tunnel (cancellable);
|
||||
|
||||
uint16 port;
|
||||
ulong raw_port;
|
||||
if (ulong.try_parse (location, out raw_port)) {
|
||||
if (raw_port == 0 || raw_port > uint16.MAX)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid TCP port");
|
||||
port = (uint16) raw_port;
|
||||
} else {
|
||||
if (tunnel == null)
|
||||
throw new Error.NOT_SUPPORTED ("Unable to resolve port name; tunnel not available");
|
||||
if ((flags & OpenTcpChannelFlags.ALLOW_TUNNEL) == 0)
|
||||
throw new Error.NOT_SUPPORTED ("Connection to tunnel service not allowed by flags");
|
||||
var service_info = tunnel.discovery.get_service (location);
|
||||
port = service_info.port;
|
||||
}
|
||||
|
||||
Error? pending_error = null;
|
||||
|
||||
if ((flags & OpenTcpChannelFlags.ALLOW_TUNNEL) != 0 && tunnel != null) {
|
||||
try {
|
||||
var stream = yield tunnel.open_tcp_connection (port, cancellable);
|
||||
return new TcpChannel () { stream = stream, kind = TUNNEL };
|
||||
} catch (Error e) {
|
||||
if (e is Error.SERVER_NOT_RUNNING)
|
||||
pending_error = e;
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & OpenTcpChannelFlags.ALLOW_USBMUX) != 0 && usbmux_device != null) {
|
||||
if (usbmux_device.connection_type == USB) {
|
||||
UsbmuxClient client = null;
|
||||
try {
|
||||
client = yield UsbmuxClient.open (cancellable);
|
||||
|
||||
yield client.connect_to_port (usbmux_device.id, port, cancellable);
|
||||
|
||||
return new TcpChannel () { stream = client.connection, kind = USBMUX };
|
||||
} catch (GLib.Error e) {
|
||||
if (client != null)
|
||||
client.close.begin ();
|
||||
|
||||
if (e is UsbmuxError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
InetSocketAddress device_address = usbmux_device.network_address;
|
||||
var target_address = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: device_address.address,
|
||||
port: port,
|
||||
flowinfo: device_address.flowinfo,
|
||||
scope_id: device_address.scope_id
|
||||
);
|
||||
|
||||
var client = new SocketClient ();
|
||||
try {
|
||||
var connection = yield client.connect_async (target_address, cancellable);
|
||||
|
||||
Tcp.enable_nodelay (connection.socket);
|
||||
|
||||
return new TcpChannel () { stream = connection, kind = USBMUX };
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CONNECTION_REFUSED)
|
||||
throw new Error.SERVER_NOT_RUNNING ("%s", e.message);
|
||||
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
if (pending_error != null)
|
||||
throw pending_error;
|
||||
throw new Error.TRANSPORT ("No viable transport found");
|
||||
}
|
||||
|
||||
private static int compare_transports (Transport a, Transport b) {
|
||||
return score_transport (b) - score_transport (a);
|
||||
}
|
||||
|
||||
private static int score_transport (Transport t) {
|
||||
int score = 0;
|
||||
if (t.connection_type == USB)
|
||||
score++;
|
||||
if (t.usbmux_device != null)
|
||||
score++;
|
||||
return score;
|
||||
}
|
||||
|
||||
private class UsbmuxLockdownServiceRequest {
|
||||
public string service_name;
|
||||
public Cancellable? cancellable;
|
||||
@@ -421,6 +465,23 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class TcpChannel {
|
||||
public IOStream stream;
|
||||
public Kind kind;
|
||||
|
||||
public enum Kind {
|
||||
USBMUX,
|
||||
TUNNEL
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum OpenTcpChannelFlags {
|
||||
ALLOW_USBMUX,
|
||||
ALLOW_TUNNEL,
|
||||
ALLOW_ANY_TRANSPORT = ALLOW_USBMUX | ALLOW_TUNNEL,
|
||||
}
|
||||
|
||||
public interface Transport : Object {
|
||||
public abstract ConnectionType connection_type {
|
||||
get;
|
||||
@@ -455,6 +516,14 @@ namespace Frida.Fruity {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract int64 opened_at {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract Bytes? remote_unlock_host_key {
|
||||
get;
|
||||
}
|
||||
|
||||
public abstract async void close (Cancellable? cancellable) throws IOError;
|
||||
public abstract async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError;
|
||||
}
|
||||
@@ -715,14 +784,18 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public bool modeswitch_allowed {
|
||||
get {
|
||||
return _modeswitch_allowed;
|
||||
}
|
||||
}
|
||||
|
||||
private State state = CREATED;
|
||||
|
||||
private Gee.Set<PortableCoreDeviceUsbTransport> usb_transports = new Gee.HashSet<PortableCoreDeviceUsbTransport> ();
|
||||
private Promise<bool> usb_started = new Promise<bool> ();
|
||||
private Promise<bool> usb_stopped = new Promise<bool> ();
|
||||
private bool modeswitch_allowed = false;
|
||||
private Promise<bool>? modeswitch_activated;
|
||||
private Gee.Set<string>? modeswitch_udids_pending;
|
||||
private bool _modeswitch_allowed = false;
|
||||
|
||||
private Thread<void>? usb_worker;
|
||||
private LibUSB.Context? usb_context;
|
||||
@@ -757,6 +830,8 @@ namespace Frida.Fruity {
|
||||
private const uint16 PRODUCT_ID_IPHONE = 0x12a8;
|
||||
private const uint16 PRODUCT_ID_IPAD = 0x12ab;
|
||||
|
||||
private delegate void NotifyCompleteFunc ();
|
||||
|
||||
construct {
|
||||
main_context = MainContext.ref_thread_default ();
|
||||
|
||||
@@ -764,7 +839,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
state = STARTING;
|
||||
lock (state)
|
||||
state = STARTING;
|
||||
|
||||
usb_worker = new Thread<void> ("frida-core-device-usb", perform_usb_work);
|
||||
|
||||
@@ -776,11 +852,13 @@ namespace Frida.Fruity {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
state = STARTED;
|
||||
lock (state)
|
||||
state = STARTED;
|
||||
}
|
||||
|
||||
public async void stop (Cancellable? cancellable) throws IOError {
|
||||
state = FLUSHING;
|
||||
lock (state)
|
||||
state = FLUSHING;
|
||||
|
||||
io_cancellable.cancel ();
|
||||
|
||||
@@ -808,31 +886,45 @@ namespace Frida.Fruity {
|
||||
|
||||
usb_context = null;
|
||||
|
||||
state = STOPPED;
|
||||
lock (state)
|
||||
state = STOPPED;
|
||||
}
|
||||
|
||||
public async void activate_modeswitch_support (Cancellable? cancellable) throws IOError {
|
||||
modeswitch_allowed = true;
|
||||
_modeswitch_allowed = true;
|
||||
|
||||
modeswitch_udids_pending = new Gee.HashSet<string> ();
|
||||
foreach (var transport in usb_transports.to_array ()) {
|
||||
var usb_device = transport.usb_device;
|
||||
try {
|
||||
if (yield usb_device.maybe_modeswitch (cancellable))
|
||||
modeswitch_udids_pending.add (usb_device.udid);
|
||||
} catch (Error e) {
|
||||
}
|
||||
var pending_transports = usb_transports.to_array ();
|
||||
var remaining = pending_transports.length + 1;
|
||||
|
||||
NotifyCompleteFunc on_complete = () => {
|
||||
remaining--;
|
||||
if (remaining == 0)
|
||||
activate_modeswitch_support.callback ();
|
||||
};
|
||||
|
||||
foreach (var transport in pending_transports)
|
||||
do_activate_modeswitch_support.begin (transport, cancellable, on_complete);
|
||||
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
on_complete ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
|
||||
yield;
|
||||
|
||||
on_complete = null;
|
||||
}
|
||||
|
||||
private async void do_activate_modeswitch_support (PortableCoreDeviceUsbTransport transport, Cancellable? cancellable,
|
||||
NotifyCompleteFunc on_complete) {
|
||||
try {
|
||||
yield transport.open (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
if (!modeswitch_udids_pending.is_empty) {
|
||||
modeswitch_activated = new Promise<bool> ();
|
||||
try {
|
||||
yield modeswitch_activated.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
modeswitch_activated = null;
|
||||
}
|
||||
modeswitch_udids_pending = null;
|
||||
|
||||
on_complete ();
|
||||
}
|
||||
|
||||
private void perform_usb_work () {
|
||||
@@ -892,7 +984,7 @@ namespace Frida.Fruity {
|
||||
polled_usb_timer = null;
|
||||
}
|
||||
|
||||
lock (pending_usb_ops) {
|
||||
lock (state) {
|
||||
if (pending_usb_ops.is_empty)
|
||||
state = STOPPING;
|
||||
}
|
||||
@@ -929,58 +1021,59 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
private async void handle_usb_device_arrival (LibUSB.Device raw_device) {
|
||||
UsbDevice? device = null;
|
||||
|
||||
uint delays[] = { 0, 50, 250 };
|
||||
for (uint attempts = 0; attempts != delays.length; attempts++) {
|
||||
uint delay = delays[attempts];
|
||||
if (delay != 0) {
|
||||
var timeout_source = new TimeoutSource (delay);
|
||||
timeout_source.set_callback (handle_usb_device_arrival.callback);
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (io_cancellable);
|
||||
cancel_source.set_callback (handle_usb_device_arrival.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
timeout_source.destroy ();
|
||||
|
||||
if (io_cancellable.is_cancelled ())
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
UsbDevice usb_device;
|
||||
try {
|
||||
device = yield UsbDevice.open (raw_device, this, io_cancellable);
|
||||
|
||||
if (modeswitch_allowed && yield device.maybe_modeswitch (io_cancellable))
|
||||
break;
|
||||
|
||||
var transport = new PortableCoreDeviceUsbTransport (device, pairing_store);
|
||||
usb_transports.add (transport);
|
||||
transport_attached (transport);
|
||||
|
||||
break;
|
||||
} catch (GLib.Error e) {
|
||||
// We might still be waiting for a udev rule to run...
|
||||
if (!(e is Error.PERMISSION_DENIED))
|
||||
break;
|
||||
usb_device = new UsbDevice (raw_device, this);
|
||||
} catch (Error e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (AtomicUint.dec_and_test (ref pending_usb_device_arrivals) && state == STARTING)
|
||||
usb_started.resolve (true);
|
||||
unowned string udid = usb_device.udid;
|
||||
|
||||
if (device != null && modeswitch_udids_pending != null && modeswitch_udids_pending.remove (device.udid)) {
|
||||
if (modeswitch_udids_pending.is_empty && modeswitch_activated != null)
|
||||
modeswitch_activated.resolve (true);
|
||||
// iPhones before iOS 17 on Windows are sometimes misdetected with an empty udid -> skip those devices
|
||||
if (udid.length == 0)
|
||||
return;
|
||||
|
||||
var transport = usb_transports.first_match (t => t.udid == udid);
|
||||
|
||||
bool may_need_time_to_settle = state != STARTING && (transport == null || transport.modeswitch_in_progress);
|
||||
if (may_need_time_to_settle) {
|
||||
try {
|
||||
yield sleep (250, io_cancellable);
|
||||
} catch (IOError e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (transport != null) {
|
||||
if (!transport.try_complete_modeswitch (raw_device))
|
||||
transport = null;
|
||||
}
|
||||
|
||||
if (io_cancellable.is_cancelled ())
|
||||
return;
|
||||
|
||||
if (transport == null) {
|
||||
transport = new PortableCoreDeviceUsbTransport (this, usb_device, pairing_store);
|
||||
usb_transports.add (transport);
|
||||
|
||||
if (state != STARTING) {
|
||||
try {
|
||||
yield transport.open (io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
transport_attached (transport);
|
||||
}
|
||||
} finally {
|
||||
if (AtomicUint.dec_and_test (ref pending_usb_device_arrivals) && state == STARTING)
|
||||
usb_started.resolve (true);
|
||||
}
|
||||
}
|
||||
|
||||
private async void handle_usb_device_departure (LibUSB.Device raw_device) {
|
||||
var transport = usb_transports.first_match (t => t.usb_device.raw_device == raw_device);
|
||||
var transport = usb_transports.first_match (t => t.usb_device.raw_device == raw_device && !t.modeswitch_in_progress);
|
||||
if (transport == null)
|
||||
return;
|
||||
|
||||
@@ -1019,17 +1112,38 @@ namespace Frida.Fruity {
|
||||
polled_usb_devices = current_devices;
|
||||
}
|
||||
|
||||
private UsbOperation allocate_usb_operation () {
|
||||
private UsbOperation allocate_usb_operation () throws Error {
|
||||
var op = new PendingUsbOperation (this);
|
||||
lock (pending_usb_ops)
|
||||
pending_usb_ops.add (op);
|
||||
|
||||
bool added = false;
|
||||
lock (state) {
|
||||
switch (state) {
|
||||
case CREATED:
|
||||
break;
|
||||
case STARTING:
|
||||
case STARTED:
|
||||
pending_usb_ops.add (op);
|
||||
added = true;
|
||||
break;
|
||||
case FLUSHING:
|
||||
case STOPPING:
|
||||
case STOPPED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!added)
|
||||
throw new Error.INVALID_OPERATION ("Unable to allocate USB operation in the current state");
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
private void on_usb_operation_complete (PendingUsbOperation op) {
|
||||
lock (pending_usb_ops)
|
||||
lock (state)
|
||||
pending_usb_ops.remove (op);
|
||||
usb_context.interrupt_event_handler ();
|
||||
|
||||
if (usb_context != null)
|
||||
usb_context.interrupt_event_handler ();
|
||||
}
|
||||
|
||||
private static uint32 make_usb_device_id (LibUSB.Device device, LibUSB.DeviceDescriptor desc) {
|
||||
@@ -1092,13 +1206,9 @@ namespace Frida.Fruity {
|
||||
|
||||
private sealed class PortableCoreDeviceUsbTransport : Object, Transport {
|
||||
public UsbDevice usb_device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public PairingStore pairing_store {
|
||||
get;
|
||||
construct;
|
||||
get {
|
||||
return _usb_device;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionType connection_type {
|
||||
@@ -1109,13 +1219,13 @@ namespace Frida.Fruity {
|
||||
|
||||
public string udid {
|
||||
get {
|
||||
return usb_device.udid;
|
||||
return _usb_device.udid;
|
||||
}
|
||||
}
|
||||
|
||||
public string? name {
|
||||
get {
|
||||
return null;
|
||||
return _name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,11 +1241,114 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private Promise<Tunnel>? tunnel_request;
|
||||
public PairingStore pairing_store {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public bool modeswitch_in_progress {
|
||||
get {
|
||||
return modeswitch_request != null;
|
||||
}
|
||||
}
|
||||
|
||||
private unowned PortableCoreDeviceBackend parent;
|
||||
private UsbDevice _usb_device;
|
||||
private Gee.List<InetSocketAddress> ncm_ifaddrs;
|
||||
private UsbNcmConfig? ncm_config;
|
||||
private string? _name;
|
||||
|
||||
private Promise<UsbDevice>? device_request;
|
||||
private Promise<LibUSB.Device>? modeswitch_request;
|
||||
private Promise<Tunnel?>? tunnel_request;
|
||||
private NcmPeer? ncm_peer;
|
||||
|
||||
public PortableCoreDeviceUsbTransport (UsbDevice device, PairingStore store) {
|
||||
Object (usb_device: device, pairing_store: store);
|
||||
public PortableCoreDeviceUsbTransport (PortableCoreDeviceBackend parent, UsbDevice device, PairingStore store) {
|
||||
Object (pairing_store: store);
|
||||
|
||||
this.parent = parent;
|
||||
_usb_device = device;
|
||||
|
||||
char product[LibUSB.DEVICE_STRING_BYTES_MAX + 1];
|
||||
var res = device.raw_device.get_device_string (PRODUCT, product);
|
||||
if (res >= LibUSB.Error.SUCCESS) {
|
||||
product[res] = '\0';
|
||||
_name = (string) product;
|
||||
}
|
||||
}
|
||||
|
||||
public async UsbDevice open (Cancellable? cancellable) throws Error, IOError {
|
||||
while (device_request != null) {
|
||||
try {
|
||||
return yield device_request.future.wait_async (cancellable);
|
||||
} catch (Error e) {
|
||||
throw e;
|
||||
} catch (IOError e) {
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
device_request = new Promise<UsbDevice> ();
|
||||
|
||||
try {
|
||||
ncm_ifaddrs = yield NcmPeer.detect_ncm_ifaddrs_on_system (_usb_device, cancellable);
|
||||
if (ncm_ifaddrs.is_empty) {
|
||||
_usb_device.ensure_open ();
|
||||
|
||||
if (parent.modeswitch_allowed) {
|
||||
modeswitch_request = new Promise<LibUSB.Device> ();
|
||||
if (yield _usb_device.maybe_modeswitch (cancellable)) {
|
||||
var source = new TimeoutSource.seconds (2);
|
||||
source.set_callback (() => {
|
||||
if (modeswitch_request != null) {
|
||||
modeswitch_request.reject (new Error.TRANSPORT ("Modeswitch timed out"));
|
||||
modeswitch_request = null;
|
||||
}
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
|
||||
LibUSB.Device raw_device = null;
|
||||
try {
|
||||
raw_device = yield modeswitch_request.future.wait_async (cancellable);
|
||||
} finally {
|
||||
source.destroy ();
|
||||
}
|
||||
|
||||
_usb_device = new UsbDevice (raw_device, parent);
|
||||
_usb_device.ensure_open ();
|
||||
} else {
|
||||
modeswitch_request = null;
|
||||
}
|
||||
}
|
||||
|
||||
bool device_configuration_changed;
|
||||
try {
|
||||
ncm_config = UsbNcmConfig.prepare (_usb_device, out device_configuration_changed);
|
||||
if (device_configuration_changed)
|
||||
yield sleep (250, cancellable);
|
||||
} catch (Error e) {
|
||||
}
|
||||
|
||||
ncm_ifaddrs = yield NcmPeer.detect_ncm_ifaddrs_on_system (_usb_device, cancellable);
|
||||
}
|
||||
|
||||
device_request.resolve (_usb_device);
|
||||
|
||||
return _usb_device;
|
||||
} catch (GLib.Error e) {
|
||||
device_request.reject (e);
|
||||
device_request = null;
|
||||
|
||||
throw_api_error (e);
|
||||
}
|
||||
}
|
||||
|
||||
public bool try_complete_modeswitch (LibUSB.Device device) {
|
||||
if (modeswitch_request == null)
|
||||
return false;
|
||||
modeswitch_request.resolve (device);
|
||||
modeswitch_request = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
@@ -1152,7 +1365,15 @@ namespace Frida.Fruity {
|
||||
|
||||
ncm_peer = null;
|
||||
|
||||
yield usb_device.close (cancellable);
|
||||
if (device_request != null) {
|
||||
try {
|
||||
var usb_device = yield device_request.future.wait_async (cancellable);
|
||||
yield usb_device.close (cancellable);
|
||||
} catch (Error e) {
|
||||
} finally {
|
||||
device_request = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Tunnel? find_tunnel (UsbmuxDevice? device, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1168,6 +1389,8 @@ namespace Frida.Fruity {
|
||||
tunnel_request = new Promise<Tunnel> ();
|
||||
|
||||
try {
|
||||
var usb_device = yield open (cancellable);
|
||||
|
||||
bool supported_by_os = true;
|
||||
if (device != null) {
|
||||
try {
|
||||
@@ -1186,16 +1409,26 @@ namespace Frida.Fruity {
|
||||
|
||||
PortableUsbTunnel? tunnel = null;
|
||||
if (supported_by_os) {
|
||||
if (ncm_peer == null)
|
||||
ncm_peer = yield NcmPeer.locate (usb_device, cancellable);
|
||||
if (ncm_peer == null) {
|
||||
if (!ncm_ifaddrs.is_empty) {
|
||||
ncm_peer = yield NcmPeer.locate_on_system_netifs (ncm_ifaddrs, cancellable);
|
||||
} else if (ncm_config != null) {
|
||||
ncm_peer = yield NcmPeer.establish_using_our_driver (usb_device, ncm_config,
|
||||
cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
tunnel = new PortableUsbTunnel (usb_device, ncm_peer, pairing_store);
|
||||
tunnel.lost.connect (on_tunnel_lost);
|
||||
try {
|
||||
yield tunnel.open (cancellable);
|
||||
} catch (Error e) {
|
||||
if (!(e is Error.NOT_SUPPORTED))
|
||||
throw e;
|
||||
if (ncm_peer != null) {
|
||||
tunnel = new PortableUsbTunnel (usb_device, ncm_peer, pairing_store);
|
||||
tunnel.lost.connect (on_tunnel_lost);
|
||||
try {
|
||||
yield tunnel.open (cancellable);
|
||||
} catch (Error e) {
|
||||
if (e is Error.NOT_SUPPORTED)
|
||||
tunnel = null;
|
||||
else
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1225,38 +1458,61 @@ namespace Frida.Fruity {
|
||||
ncm.close ();
|
||||
}
|
||||
|
||||
public static async NcmPeer locate (UsbDevice usb_device, Cancellable? cancellable) throws Error, IOError {
|
||||
var device_ifaddrs = detect_ncm_ifaddrs_on_system (usb_device);
|
||||
if (device_ifaddrs.size == 2)
|
||||
return yield locate_on_system_netifs (device_ifaddrs, cancellable);
|
||||
return yield establish_using_our_driver (usb_device, cancellable);
|
||||
}
|
||||
|
||||
private static Gee.List<InetSocketAddress> detect_ncm_ifaddrs_on_system (UsbDevice usb_device) throws Error {
|
||||
public static async Gee.List<InetSocketAddress> detect_ncm_ifaddrs_on_system (UsbDevice usb_device,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var device_ifaddrs = new Gee.ArrayList<InetSocketAddress> ();
|
||||
|
||||
#if LINUX
|
||||
var fruit_finder = FruitFinder.make_default ();
|
||||
unowned string udid = usb_device.udid;
|
||||
string raw_udid = udid.replace ("-", "");
|
||||
|
||||
var ncm_interfaces = new Gee.HashSet<string> ();
|
||||
var names = if_nameindex ();
|
||||
try {
|
||||
for (Linux.Network.IfNameindex * cur = names; cur->if_index != 0; cur++) {
|
||||
string? candidate_udid = fruit_finder.udid_from_iface (cur->if_name);
|
||||
if (candidate_udid != udid)
|
||||
continue;
|
||||
|
||||
ncm_interfaces.add (cur->if_name);
|
||||
}
|
||||
} finally {
|
||||
if_freenameindex (names);
|
||||
}
|
||||
if (ncm_interfaces.is_empty)
|
||||
return device_ifaddrs;
|
||||
|
||||
yield Network.wait_until_interfaces_ready (ncm_interfaces, cancellable);
|
||||
|
||||
Linux.Network.IfAddrs ifaddrs;
|
||||
Linux.Network.getifaddrs (out ifaddrs);
|
||||
for (unowned Linux.Network.IfAddrs candidate = ifaddrs; candidate != null; candidate = candidate.ifa_next) {
|
||||
if (candidate.ifa_addr.sa_family != Posix.AF_INET6)
|
||||
unowned Posix.SockAddr? address = candidate.ifa_addr;
|
||||
if (address == null || address.sa_family != Posix.AF_INET6)
|
||||
continue;
|
||||
|
||||
string? candidate_udid = fruit_finder.udid_from_iface (candidate.ifa_name);
|
||||
if (candidate_udid != raw_udid)
|
||||
if (!ncm_interfaces.contains (candidate.ifa_name))
|
||||
continue;
|
||||
|
||||
device_ifaddrs.add ((InetSocketAddress) SocketAddress.from_native ((void *) candidate.ifa_addr, sizeof (Posix.SockAddrIn6)));
|
||||
device_ifaddrs.add ((InetSocketAddress) SocketAddress.from_native ((void *) address,
|
||||
sizeof (Posix.SockAddrIn6)));
|
||||
}
|
||||
if (device_ifaddrs.is_empty && !ncm_interfaces.is_empty)
|
||||
throw new Error.NOT_SUPPORTED ("no IPv6 address on NCM network interface");
|
||||
#endif
|
||||
|
||||
return device_ifaddrs;
|
||||
}
|
||||
|
||||
private static async NcmPeer locate_on_system_netifs (Gee.List<InetSocketAddress> ifaddrs, Cancellable? cancellable)
|
||||
#if LINUX
|
||||
[CCode (cheader_filename = "net/if.h", cname = "if_nameindex")]
|
||||
private extern static Linux.Network.IfNameindex* if_nameindex ();
|
||||
|
||||
[CCode (cheader_filename = "net/if.h", cname = "if_freenameindex")]
|
||||
private extern static void if_freenameindex (Linux.Network.IfNameindex* index);
|
||||
#endif
|
||||
|
||||
public static async NcmPeer locate_on_system_netifs (Gee.List<InetSocketAddress> ifaddrs, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var main_context = MainContext.ref_thread_default ();
|
||||
|
||||
@@ -1357,9 +1613,9 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private static async NcmPeer establish_using_our_driver (UsbDevice usb_device, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var ncm = yield UsbNcmDriver.open (usb_device, cancellable);
|
||||
public static async NcmPeer establish_using_our_driver (UsbDevice usb_device, UsbNcmConfig ncm_config,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var ncm = yield UsbNcmDriver.open (usb_device, ncm_config, cancellable);
|
||||
|
||||
if (ncm.remote_ipv6_address == null) {
|
||||
ulong change_handler = ncm.notify["remote-ipv6-address"].connect ((obj, pspec) => {
|
||||
@@ -1448,9 +1704,23 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public int64 opened_at {
|
||||
get {
|
||||
return _opened_at;
|
||||
}
|
||||
}
|
||||
|
||||
public Bytes? remote_unlock_host_key {
|
||||
get {
|
||||
return _remote_unlock_host_key;
|
||||
}
|
||||
}
|
||||
|
||||
private UsbNcmDriver? ncm;
|
||||
private TunnelConnection? tunnel_connection;
|
||||
private DiscoveryService? _discovery_service;
|
||||
private int64 _opened_at = -1;
|
||||
private Bytes? _remote_unlock_host_key;
|
||||
|
||||
public PortableUsbTunnel (UsbDevice device, NcmPeer peer, PairingStore store) {
|
||||
Object (
|
||||
@@ -1479,9 +1749,11 @@ namespace Frida.Fruity {
|
||||
);
|
||||
var pairing_transport = new XpcPairingTransport (yield netstack.open_tcp_connection (tunnel_endpoint, cancellable));
|
||||
var pairing_service = yield PairingService.open (pairing_transport, pairing_store, cancellable);
|
||||
|
||||
TunnelConnection tc = yield pairing_service.open_tunnel (ncm_peer.ip, netstack, cancellable);
|
||||
tc.close.connect (on_tunnel_connection_close);
|
||||
tc.closed.connect (on_tunnel_connection_close);
|
||||
|
||||
_opened_at = get_monotonic_time ();
|
||||
_remote_unlock_host_key = pairing_service.established_peer.remote_unlock_host_key;
|
||||
|
||||
var rsd_endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: tc.remote_address,
|
||||
@@ -1498,7 +1770,7 @@ namespace Frida.Fruity {
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
_discovery_service.close ();
|
||||
|
||||
tunnel_connection.cancel ();
|
||||
yield tunnel_connection.close (cancellable);
|
||||
|
||||
if (ncm != null)
|
||||
ncm.close ();
|
||||
@@ -1644,8 +1916,24 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public int64 opened_at {
|
||||
get {
|
||||
return _opened_at;
|
||||
}
|
||||
}
|
||||
|
||||
public Bytes? remote_unlock_host_key {
|
||||
get {
|
||||
return _remote_unlock_host_key;
|
||||
}
|
||||
}
|
||||
|
||||
private TunnelConnection? tunnel_connection;
|
||||
private DiscoveryService? _discovery_service;
|
||||
private int64 _opened_at = -1;
|
||||
private Bytes? _remote_unlock_host_key;
|
||||
|
||||
private const uint PAIRING_CONNECTION_TIMEOUT = 2000;
|
||||
|
||||
public PortableNetworkTunnel (InetSocketAddress endpoint, InetSocketAddress interface_address, PairingStore store) {
|
||||
Object (
|
||||
@@ -1658,12 +1946,16 @@ namespace Frida.Fruity {
|
||||
public async void open (Cancellable? cancellable) throws Error, IOError {
|
||||
var netstack = new SystemNetworkStack (interface_address.get_address (), interface_address.scope_id);
|
||||
|
||||
var pairing_connection = yield netstack.open_tcp_connection (endpoint, cancellable);
|
||||
var pairing_connection = yield netstack.open_tcp_connection_with_timeout (endpoint, PAIRING_CONNECTION_TIMEOUT,
|
||||
cancellable);
|
||||
var pairing_transport = new PlainPairingTransport (pairing_connection);
|
||||
var pairing_service = yield PairingService.open (pairing_transport, pairing_store, cancellable);
|
||||
|
||||
TunnelConnection tc = yield pairing_service.open_tunnel (endpoint.get_address (), netstack, cancellable);
|
||||
|
||||
_opened_at = get_monotonic_time ();
|
||||
_remote_unlock_host_key = pairing_service.established_peer.remote_unlock_host_key;
|
||||
|
||||
var rsd_endpoint = (InetSocketAddress) Object.new (typeof (InetSocketAddress),
|
||||
address: tc.remote_address,
|
||||
port: tc.remote_rsd_port,
|
||||
@@ -1679,7 +1971,7 @@ namespace Frida.Fruity {
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
_discovery_service.close ();
|
||||
|
||||
tunnel_connection.cancel ();
|
||||
yield tunnel_connection.close (cancellable);
|
||||
}
|
||||
|
||||
public async IOStream open_tcp_connection (uint16 port, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -1705,7 +1997,7 @@ namespace Frida.Fruity {
|
||||
public abstract string? udid_from_iface (string ifname) throws Error;
|
||||
}
|
||||
|
||||
public class NullFruitFinder : Object, FruitFinder {
|
||||
public sealed class NullFruitFinder : Object, FruitFinder {
|
||||
public string? udid_from_iface (string ifname) throws Error {
|
||||
return null;
|
||||
}
|
||||
@@ -1730,7 +2022,7 @@ namespace Frida.Fruity {
|
||||
public abstract async void stop (Cancellable? cancellable) throws IOError;
|
||||
}
|
||||
|
||||
public class NullPairingBrowser : Object, PairingBrowser {
|
||||
public sealed class NullPairingBrowser : Object, PairingBrowser {
|
||||
public async void start (Cancellable? cancellable) throws IOError {
|
||||
}
|
||||
|
||||
@@ -1744,4 +2036,23 @@ namespace Frida.Fruity {
|
||||
public InetSocketAddress endpoint;
|
||||
public InetSocketAddress interface_address;
|
||||
}
|
||||
|
||||
private async void sleep (uint duration_msec, Cancellable? cancellable) throws IOError {
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var delay_source = new TimeoutSource (duration_msec);
|
||||
delay_source.set_callback (sleep.callback);
|
||||
delay_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (sleep.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
delay_source.destroy ();
|
||||
|
||||
cancellable.set_error_if_cancelled ();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class DnsPacketReader {
|
||||
public sealed class DnsPacketReader {
|
||||
private BufferReader reader;
|
||||
|
||||
public DnsPacketReader (Bytes packet) {
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class DeviceInfoService : Object, AsyncInitable {
|
||||
public sealed class DeviceInfoService : Object, AsyncInitable {
|
||||
public HostChannelProvider channel_provider {
|
||||
get;
|
||||
construct;
|
||||
@@ -75,7 +75,7 @@ namespace Frida.Fruity {
|
||||
return name;
|
||||
}
|
||||
|
||||
public class ProcessInfo : Object {
|
||||
public sealed class ProcessInfo : Object {
|
||||
public uint pid {
|
||||
get;
|
||||
set;
|
||||
@@ -108,7 +108,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class ApplicationListingService : Object, AsyncInitable {
|
||||
public sealed class ApplicationListingService : Object, AsyncInitable {
|
||||
public HostChannelProvider channel_provider {
|
||||
get;
|
||||
construct;
|
||||
@@ -211,7 +211,7 @@ namespace Frida.Fruity {
|
||||
return result;
|
||||
}
|
||||
|
||||
public class ApplicationInfo : Object {
|
||||
public sealed class ApplicationInfo : Object {
|
||||
public ApplicationType app_type {
|
||||
get;
|
||||
set;
|
||||
@@ -306,7 +306,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class ProcessControlService : Object, AsyncInitable {
|
||||
public sealed class ProcessControlService : Object, AsyncInitable {
|
||||
public HostChannelProvider channel_provider {
|
||||
get;
|
||||
construct;
|
||||
@@ -346,7 +346,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class DTXConnection : Object, DTXTransport {
|
||||
public sealed class DTXConnection : Object, DTXTransport {
|
||||
public IOStream stream {
|
||||
get;
|
||||
construct;
|
||||
@@ -847,7 +847,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private class DTXControlChannel : DTXChannel {
|
||||
private sealed class DTXControlChannel : DTXChannel {
|
||||
public DTXControlChannel (DTXTransport transport) {
|
||||
Object (code: 0, transport: transport);
|
||||
}
|
||||
@@ -1116,7 +1116,7 @@ namespace Frida.Fruity {
|
||||
NSObject? obj = NSKeyedArchive.decode (buf);
|
||||
if (obj != null) {
|
||||
var gval = Value (Type.from_instance (obj));
|
||||
gval.set_boxed (obj);
|
||||
gval.set_instance (obj);
|
||||
elements += (owned) gval;
|
||||
} else {
|
||||
var gval = Value (typeof (NSObject));
|
||||
@@ -1161,7 +1161,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class DTXArgumentListBuilder {
|
||||
public sealed class DTXArgumentListBuilder {
|
||||
private PrimitiveBuilder blob = new PrimitiveBuilder ();
|
||||
|
||||
public DTXArgumentListBuilder () {
|
||||
@@ -1227,7 +1227,7 @@ namespace Frida.Fruity {
|
||||
|
||||
private const size_t PRIMITIVE_DICTIONARY_HEADER_SIZE = 16;
|
||||
|
||||
private class PrimitiveReader {
|
||||
private sealed class PrimitiveReader {
|
||||
public size_t available_bytes {
|
||||
get {
|
||||
return end - cursor;
|
||||
@@ -1317,7 +1317,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private class PrimitiveBuilder {
|
||||
private sealed class PrimitiveBuilder {
|
||||
public size_t offset {
|
||||
get {
|
||||
return cursor;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace Frida {
|
||||
public class FruityHostSessionBackend : Object, HostSessionBackend {
|
||||
public sealed class FruityHostSessionBackend : Object, HostSessionBackend {
|
||||
private Fruity.DeviceMonitor device_monitor = new Fruity.DeviceMonitor ();
|
||||
private Gee.Map<Fruity.Device, FruityHostSessionProvider> providers =
|
||||
new Gee.HashMap<Fruity.Device, FruityHostSessionProvider> ();
|
||||
@@ -42,7 +42,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class FruityHostSessionProvider : Object, HostSessionProvider, HostChannelProvider, HostServiceProvider, Pairable {
|
||||
public sealed 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);
|
||||
@@ -159,7 +165,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public class FruityHostSession : Object, HostSession {
|
||||
public sealed class FruityHostSession : Object, HostSession {
|
||||
public Fruity.Device device {
|
||||
get;
|
||||
construct;
|
||||
@@ -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,14 +206,22 @@ 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) {
|
||||
var server = yield try_get_remote_server (cancellable);
|
||||
if (server != null) {
|
||||
try {
|
||||
yield server.connection.close (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
try {
|
||||
var server = yield try_get_remote_server (cancellable);
|
||||
if (server != null) {
|
||||
try {
|
||||
yield server.connection.close (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,7 +986,7 @@ namespace Frida {
|
||||
var transport_broker = server.transport_broker;
|
||||
if (transport_broker != null) {
|
||||
try {
|
||||
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, device,
|
||||
entry.connection = yield establish_direct_connection (transport_broker, remote_session_id, server,
|
||||
cancellable);
|
||||
} catch (Error e) {
|
||||
if (e is Error.NOT_SUPPORTED)
|
||||
@@ -996,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);
|
||||
@@ -1016,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;
|
||||
|
||||
@@ -1052,11 +1152,13 @@ namespace Frida {
|
||||
entry.close.begin (io_cancellable);
|
||||
}
|
||||
|
||||
private async RemoteServer? try_get_remote_server (Cancellable? cancellable) throws IOError {
|
||||
private async RemoteServer? try_get_remote_server (Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
return yield get_remote_server (cancellable);
|
||||
} catch (Error e) {
|
||||
return null;
|
||||
if (e is Error.SERVER_NOT_RUNNING)
|
||||
return null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1082,10 +1184,9 @@ namespace Frida {
|
||||
|
||||
DBusConnection? connection = null;
|
||||
try {
|
||||
var stream = yield device.open_channel (
|
||||
("tcp:%" + uint16.FORMAT_MODIFIER + "u").printf (DEFAULT_CONTROL_PORT),
|
||||
cancellable);
|
||||
var channel = yield connect_to_remote_server (cancellable);
|
||||
|
||||
IOStream stream = channel.stream;
|
||||
WebServiceTransport transport = PLAIN;
|
||||
string? origin = null;
|
||||
|
||||
@@ -1113,7 +1214,7 @@ namespace Frida {
|
||||
if (connection.closed)
|
||||
throw new Error.SERVER_NOT_RUNNING ("Unable to connect to remote frida-server");
|
||||
|
||||
var server = new RemoteServer (session, connection, flavor, transport_broker);
|
||||
var server = new RemoteServer (flavor, session, connection, channel, device, transport_broker);
|
||||
attach_remote_server (server);
|
||||
current_remote_server = server;
|
||||
last_server_check_timer = null;
|
||||
@@ -1131,8 +1232,8 @@ namespace Frida {
|
||||
last_server_check_timer = null;
|
||||
last_server_check_error = null;
|
||||
} else {
|
||||
if (e is Error.SERVER_NOT_RUNNING) {
|
||||
api_error = new Error.SERVER_NOT_RUNNING ("Unable to connect to remote frida-server");
|
||||
if (e is Error) {
|
||||
api_error = e;
|
||||
} else if (connection != null) {
|
||||
api_error = new Error.PROTOCOL ("Incompatible frida-server version");
|
||||
} else {
|
||||
@@ -1150,6 +1251,51 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private async Fruity.TcpChannel connect_to_remote_server (Cancellable? cancellable) throws Error, IOError {
|
||||
var tunnel = yield device.find_tunnel (cancellable);
|
||||
bool tunnel_recently_opened = tunnel != null && get_monotonic_time () - tunnel.opened_at < 1000000;
|
||||
|
||||
uint delays[] = { 0, 50, 250 };
|
||||
uint max_attempts = tunnel_recently_opened ? delays.length : 1;
|
||||
var main_context = MainContext.ref_thread_default ();
|
||||
|
||||
Error? pending_error = null;
|
||||
for (uint attempts = 0; attempts != max_attempts; attempts++) {
|
||||
uint delay = delays[attempts];
|
||||
if (delay != 0) {
|
||||
var timeout_source = new TimeoutSource (delay);
|
||||
timeout_source.set_callback (connect_to_remote_server.callback);
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (connect_to_remote_server.callback);
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
yield;
|
||||
|
||||
cancel_source.destroy ();
|
||||
timeout_source.destroy ();
|
||||
|
||||
if (cancellable.is_cancelled ())
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_last_attempt = attempts == max_attempts - 1;
|
||||
var open_flags = is_last_attempt
|
||||
? Fruity.OpenTcpChannelFlags.ALLOW_ANY_TRANSPORT
|
||||
: Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL;
|
||||
|
||||
try {
|
||||
return yield device.open_tcp_channel (DEFAULT_CONTROL_PORT.to_string (), open_flags, cancellable);
|
||||
} catch (Error e) {
|
||||
pending_error = e;
|
||||
if (!(e is Error.SERVER_NOT_RUNNING))
|
||||
break;
|
||||
}
|
||||
}
|
||||
throw pending_error;
|
||||
}
|
||||
|
||||
private void attach_remote_server (RemoteServer server) {
|
||||
server.connection.on_closed.connect (on_remote_connection_closed);
|
||||
|
||||
@@ -1445,7 +1591,12 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoteServer : Object {
|
||||
private class RemoteServer : Object, HostChannelProvider {
|
||||
public Flavor flavor {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public HostSession session {
|
||||
get;
|
||||
construct;
|
||||
@@ -1456,7 +1607,12 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public Flavor flavor {
|
||||
public Fruity.TcpChannel channel {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public Fruity.Device device {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
@@ -1471,34 +1627,40 @@ namespace Frida {
|
||||
set;
|
||||
}
|
||||
|
||||
public RemoteServer (HostSession session, DBusConnection connection, Flavor flavor,
|
||||
TransportBroker? transport_broker) {
|
||||
public RemoteServer (Flavor flavor, HostSession session, DBusConnection connection, Fruity.TcpChannel channel,
|
||||
Fruity.Device device, TransportBroker? transport_broker) {
|
||||
Object (
|
||||
flavor: flavor,
|
||||
session: session,
|
||||
connection: connection,
|
||||
flavor: flavor,
|
||||
channel: channel,
|
||||
device: device,
|
||||
transport_broker: transport_broker
|
||||
);
|
||||
}
|
||||
|
||||
public async IOStream open_channel (string address, Cancellable? cancellable) throws Error, IOError {
|
||||
if (!address.has_prefix ("tcp:"))
|
||||
throw new Error.NOT_SUPPORTED ("Unsupported channel address");
|
||||
var flags = (channel.kind == TUNNEL)
|
||||
? Fruity.OpenTcpChannelFlags.ALLOW_TUNNEL
|
||||
: Fruity.OpenTcpChannelFlags.ALLOW_USBMUX;
|
||||
var channel = yield device.open_tcp_channel (address[4:], flags, cancellable);
|
||||
return channel.stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1507,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 {
|
||||
@@ -1516,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 {
|
||||
@@ -1532,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 {
|
||||
@@ -1686,7 +1844,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DTXService : Object, Service {
|
||||
private sealed class DTXServiceSession : Object, ServiceSession {
|
||||
public string identifier {
|
||||
get;
|
||||
construct;
|
||||
@@ -1706,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);
|
||||
}
|
||||
|
||||
@@ -1714,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 {
|
||||
@@ -1723,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);
|
||||
@@ -1741,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 ();
|
||||
}
|
||||
|
||||
@@ -1955,7 +2117,7 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class XpcService : Object, Service {
|
||||
private sealed class XpcServiceSession : Object, ServiceSession {
|
||||
public Fruity.XpcConnection connection {
|
||||
get;
|
||||
construct;
|
||||
@@ -1969,7 +2131,7 @@ namespace Frida {
|
||||
ACTIVE,
|
||||
}
|
||||
|
||||
public XpcService (Fruity.XpcConnection connection) {
|
||||
public XpcServiceSession (Fruity.XpcConnection connection) {
|
||||
Object (connection: connection);
|
||||
}
|
||||
|
||||
@@ -1978,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 {
|
||||
@@ -1987,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 {
|
||||
@@ -2024,6 +2190,7 @@ namespace Frida {
|
||||
|
||||
private void on_close (Error? error) {
|
||||
connection_closed = true;
|
||||
|
||||
close ();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Frida.Fruity.Injector {
|
||||
return yield session.run (cancellable);
|
||||
}
|
||||
|
||||
public class GadgetDetails : Object {
|
||||
public sealed class GadgetDetails : Object {
|
||||
public uint16 port {
|
||||
get;
|
||||
construct;
|
||||
@@ -21,7 +21,7 @@ namespace Frida.Fruity.Injector {
|
||||
UNSUPPORTED
|
||||
}
|
||||
|
||||
private class Session : Object {
|
||||
private sealed class Session : Object {
|
||||
public Gum.DarwinModule module {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class InstallationProxyClient : Object, AsyncInitable {
|
||||
public sealed class InstallationProxyClient : Object, AsyncInitable {
|
||||
public Device device {
|
||||
get;
|
||||
construct;
|
||||
@@ -193,7 +193,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class ApplicationDetails : Object {
|
||||
public sealed class ApplicationDetails : Object {
|
||||
public string identifier {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class NSNumber : NSObject {
|
||||
public sealed class NSNumber : NSObject {
|
||||
public Kind kind {
|
||||
get;
|
||||
private set;
|
||||
@@ -108,7 +108,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class NSString : NSObject {
|
||||
public sealed class NSString : NSObject {
|
||||
public string str {
|
||||
get;
|
||||
private set;
|
||||
@@ -134,7 +134,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class NSData : NSObject {
|
||||
public sealed class NSData : NSObject {
|
||||
public Bytes bytes {
|
||||
get;
|
||||
private set;
|
||||
@@ -160,7 +160,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class NSDictionary : NSObject {
|
||||
public sealed class NSDictionary : NSObject {
|
||||
public int size {
|
||||
get {
|
||||
return storage.size;
|
||||
@@ -221,7 +221,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class NSDictionaryRaw : NSObject {
|
||||
public sealed class NSDictionaryRaw : NSObject {
|
||||
public int size {
|
||||
get {
|
||||
return storage.size;
|
||||
@@ -255,7 +255,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class NSArray : NSObject {
|
||||
public sealed class NSArray : NSObject {
|
||||
public int length {
|
||||
get {
|
||||
return storage.size;
|
||||
@@ -279,7 +279,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class NSDate : NSObject {
|
||||
public sealed class NSDate : NSObject {
|
||||
public double time {
|
||||
get;
|
||||
private set;
|
||||
@@ -298,7 +298,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class NSError : NSObject {
|
||||
public sealed class NSError : NSObject {
|
||||
public NSString domain {
|
||||
get;
|
||||
private set;
|
||||
@@ -321,7 +321,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class DTTapMessage : NSObject {
|
||||
public sealed class DTTapMessage : NSObject {
|
||||
public NSDictionary plist {
|
||||
get;
|
||||
private set;
|
||||
@@ -658,7 +658,7 @@ namespace Frida.Fruity {
|
||||
return new DTTapMessage ((NSDictionary) decode_value (instance.get_uid ("DTTapMessagePlist"), ctx));
|
||||
}
|
||||
|
||||
private class EncodingContext {
|
||||
private sealed class EncodingContext {
|
||||
public PlistArray objects;
|
||||
|
||||
private Gee.HashMap<string, PlistUid> classes = new Gee.HashMap<string, PlistUid> ();
|
||||
@@ -709,7 +709,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private class DecodingContext {
|
||||
private sealed class DecodingContext {
|
||||
public PlistArray objects;
|
||||
|
||||
public DecodingContext (PlistArray objects) {
|
||||
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaLLDB", gir_version = "1.0")]
|
||||
namespace Frida.LLDB {
|
||||
public class Client : GDB.Client {
|
||||
public sealed class Client : GDB.Client {
|
||||
public Process? process {
|
||||
get {
|
||||
return _process;
|
||||
@@ -438,7 +438,7 @@ namespace Frida.LLDB {
|
||||
}
|
||||
}
|
||||
|
||||
public class LaunchOptions : Object {
|
||||
public sealed class LaunchOptions : Object {
|
||||
public string[] env {
|
||||
get;
|
||||
set;
|
||||
@@ -457,7 +457,7 @@ namespace Frida.LLDB {
|
||||
}
|
||||
}
|
||||
|
||||
public class Process : Object {
|
||||
public sealed class Process : Object {
|
||||
public uint pid {
|
||||
get;
|
||||
set;
|
||||
@@ -556,7 +556,7 @@ namespace Frida.LLDB {
|
||||
ARM64E = 2,
|
||||
}
|
||||
|
||||
public class Thread : GDB.Thread {
|
||||
public sealed class Thread : GDB.Thread {
|
||||
private const uint PAGE_SIZE = 16384U;
|
||||
private const uint32 THREAD_MAGIC = 0x54485244U;
|
||||
|
||||
@@ -656,7 +656,7 @@ namespace Frida.LLDB {
|
||||
return address & ~((uint64) (PAGE_SIZE - 1));
|
||||
}
|
||||
|
||||
public class Snapshot {
|
||||
public sealed class Snapshot {
|
||||
public uint handle {
|
||||
get;
|
||||
private set;
|
||||
@@ -667,7 +667,7 @@ namespace Frida.LLDB {
|
||||
}
|
||||
}
|
||||
|
||||
public class Frame {
|
||||
public sealed class Frame {
|
||||
public uint64 address {
|
||||
get;
|
||||
private set;
|
||||
@@ -695,7 +695,7 @@ namespace Frida.LLDB {
|
||||
}
|
||||
}
|
||||
|
||||
public class Module : Object {
|
||||
public sealed class Module : Object {
|
||||
public uint64 load_address {
|
||||
get;
|
||||
construct;
|
||||
@@ -719,7 +719,7 @@ namespace Frida.LLDB {
|
||||
);
|
||||
}
|
||||
|
||||
public class Segment {
|
||||
public sealed class Segment {
|
||||
public string name;
|
||||
|
||||
public uint64 vmaddr;
|
||||
@@ -730,7 +730,7 @@ namespace Frida.LLDB {
|
||||
}
|
||||
}
|
||||
|
||||
public class Exception : GDB.Exception {
|
||||
public sealed class Exception : GDB.Exception {
|
||||
public MachExceptionType metype {
|
||||
get;
|
||||
construct;
|
||||
@@ -896,7 +896,7 @@ namespace Frida.LLDB {
|
||||
}
|
||||
}
|
||||
|
||||
public class AppleDyldFields : Object {
|
||||
public sealed class AppleDyldFields : Object {
|
||||
public uint64 all_image_info {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class LockdownClient : Object {
|
||||
public sealed class LockdownClient : Object {
|
||||
public signal void closed ();
|
||||
|
||||
public PlistServiceClient service {
|
||||
|
||||
+378
-157
@@ -6,6 +6,11 @@ namespace Frida.Fruity {
|
||||
construct;
|
||||
}
|
||||
|
||||
public UsbNcmConfig config {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public VirtualNetworkStack netstack {
|
||||
get {
|
||||
return _netstack;
|
||||
@@ -18,33 +23,32 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
private uint8 data_iface;
|
||||
private int data_altsetting;
|
||||
private uint8 rx_address;
|
||||
private uint8 tx_address;
|
||||
|
||||
private uint32 ntb_in_max_size;
|
||||
private uint32 ntb_out_max_size;
|
||||
private uint16 ndp_out_divisor;
|
||||
private uint16 ndp_out_payload_remainder;
|
||||
private uint16 ndp_out_alignment;
|
||||
private uint16 ntb_out_max_datagrams;
|
||||
private size_t max_in_transfers;
|
||||
private size_t max_out_transfers;
|
||||
private VirtualNetworkStack? _netstack;
|
||||
private Gee.Queue<Bytes> pending_output = new Gee.ArrayQueue<Bytes> ();
|
||||
private bool writing = false;
|
||||
private uint16 next_outgoing_sequence = 1;
|
||||
|
||||
private InetAddress? _remote_ipv6_address;
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
private enum UsbDescriptorType {
|
||||
INTERFACE = 0x04,
|
||||
private const uint16 TRANSFER_HEADER_SIZE = 4 + 2 + 2 + 2 + 2;
|
||||
private const size_t MAX_TRANSFER_MEMORY = 60 * 1518;
|
||||
|
||||
private enum CdcRequest {
|
||||
GET_NTB_PARAMETERS = 0x80,
|
||||
SET_NTB_INPUT_SIZE = 0x86,
|
||||
}
|
||||
|
||||
private enum UsbCommSubclass {
|
||||
NCM = 0x0d,
|
||||
}
|
||||
|
||||
private enum UsbDataSubclass {
|
||||
UNDEFINED = 0x00,
|
||||
}
|
||||
|
||||
private enum UsbCdcDescriptorSubtype {
|
||||
ETHERNET = 0x0f,
|
||||
}
|
||||
private const uint16 NTB_PARAMETERS_MIN_SIZE = 28;
|
||||
|
||||
private enum EtherType {
|
||||
IPV6 = 0x86dd,
|
||||
@@ -54,8 +58,9 @@ namespace Frida.Fruity {
|
||||
UDP = 0x11,
|
||||
}
|
||||
|
||||
public static async UsbNcmDriver open (UsbDevice device, Cancellable? cancellable = null) throws Error, IOError {
|
||||
var driver = new UsbNcmDriver (device);
|
||||
public static async UsbNcmDriver open (UsbDevice device, UsbNcmConfig config, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
var driver = new UsbNcmDriver (device, config);
|
||||
|
||||
try {
|
||||
yield driver.init_async (Priority.DEFAULT, cancellable);
|
||||
@@ -66,69 +71,15 @@ namespace Frida.Fruity {
|
||||
return driver;
|
||||
}
|
||||
|
||||
private UsbNcmDriver (UsbDevice device) {
|
||||
Object (device: device);
|
||||
private UsbNcmDriver (UsbDevice device, UsbNcmConfig config) {
|
||||
Object (device: device, config: config);
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
unowned LibUSB.Device raw_device = device.raw_device;
|
||||
unowned LibUSB.DeviceHandle handle = device.handle;
|
||||
|
||||
var dev_desc = LibUSB.DeviceDescriptor (raw_device);
|
||||
|
||||
LibUSB.ConfigDescriptor current_config;
|
||||
Usb.check (raw_device.get_active_config_descriptor (out current_config), "Failed to get active config descriptor");
|
||||
|
||||
int desired_config_value = -1;
|
||||
bool found_cdc_header = false;
|
||||
bool found_data_interface = false;
|
||||
uint8 mac_address_index = 0;
|
||||
for (uint8 config_value = dev_desc.bNumConfigurations; config_value != 0; config_value--) {
|
||||
LibUSB.ConfigDescriptor config;
|
||||
Usb.check (raw_device.get_config_descriptor_by_value (config_value, out config),
|
||||
"Failed to get config descriptor");
|
||||
|
||||
foreach (var iface in config.@interface) {
|
||||
foreach (var setting in iface.altsetting) {
|
||||
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
|
||||
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
|
||||
try {
|
||||
parse_cdc_header (setting.extra, out mac_address_index);
|
||||
found_cdc_header = true;
|
||||
} catch (Error e) {
|
||||
break;
|
||||
}
|
||||
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
|
||||
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
|
||||
setting.endpoint.length == 2) {
|
||||
found_data_interface = true;
|
||||
|
||||
data_iface = setting.bInterfaceNumber;
|
||||
data_altsetting = setting.bAlternateSetting;
|
||||
|
||||
foreach (var ep in setting.endpoint) {
|
||||
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) ==
|
||||
LibUSB.EndpointDirection.IN) {
|
||||
rx_address = ep.bEndpointAddress;
|
||||
} else {
|
||||
tx_address = ep.bEndpointAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_cdc_header || found_data_interface) {
|
||||
desired_config_value = config_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_cdc_header || !found_data_interface)
|
||||
throw new Error.NOT_SUPPORTED ("%s", make_user_error_message ("No USB CDC-NCM interface found"));
|
||||
var language_id = yield device.query_default_language_id (cancellable);
|
||||
|
||||
uint8 mac_address[6];
|
||||
string mac_address_str = yield device.read_string_descriptor (mac_address_index, device.default_language_id,
|
||||
cancellable);
|
||||
string mac_address_str = yield device.read_string_descriptor (config.mac_address_index, language_id, cancellable);
|
||||
if (mac_address_str.length != 12)
|
||||
throw new Error.PROTOCOL ("Invalid MAC address");
|
||||
for (uint i = 0; i != 6; i++) {
|
||||
@@ -137,22 +88,62 @@ namespace Frida.Fruity {
|
||||
mac_address[i] = (uint8) v;
|
||||
}
|
||||
|
||||
if (current_config.bConfigurationValue != desired_config_value) {
|
||||
foreach (var iface in current_config.@interface) {
|
||||
unowned LibUSB.InterfaceDescriptor setting = iface.altsetting[0];
|
||||
var res = handle.kernel_driver_active (setting.bInterfaceNumber);
|
||||
if (res == 1)
|
||||
handle.detach_kernel_driver (setting.bInterfaceNumber);
|
||||
}
|
||||
Usb.check (handle.set_configuration (desired_config_value), "Failed to set configuration");
|
||||
}
|
||||
unowned LibUSB.DeviceHandle handle = device.handle;
|
||||
try {
|
||||
Usb.check (handle.claim_interface (data_iface), "Failed to claim USB interface");
|
||||
Usb.check (handle.claim_interface (config.ctrl_iface), "Failed to claim control interface");
|
||||
Usb.check (handle.claim_interface (config.data_iface), "Failed to claim data interface");
|
||||
} catch (Error e) {
|
||||
throw new Error.PERMISSION_DENIED ("%s",
|
||||
make_user_error_message (@"Unable to claim USB CDC-NCM interface ($(e.message))"));
|
||||
}
|
||||
Usb.check (handle.set_interface_alt_setting (data_iface, data_altsetting),
|
||||
|
||||
uint8 raw_ntb_params[NTB_PARAMETERS_MIN_SIZE];
|
||||
var raw_ntb_params_size = yield device.control_transfer (
|
||||
LibUSB.RequestRecipient.INTERFACE | LibUSB.RequestType.CLASS | LibUSB.EndpointDirection.IN,
|
||||
CdcRequest.GET_NTB_PARAMETERS,
|
||||
0,
|
||||
config.ctrl_iface,
|
||||
raw_ntb_params,
|
||||
1000,
|
||||
cancellable);
|
||||
if (raw_ntb_params_size < NTB_PARAMETERS_MIN_SIZE)
|
||||
throw new Error.PROTOCOL ("Truncated NTB parameters response");
|
||||
var ntb_params = new Buffer (new Bytes (raw_ntb_params[:raw_ntb_params_size]), LITTLE_ENDIAN);
|
||||
uint32 device_ntb_in_max_size = ntb_params.read_uint32 (4);
|
||||
ntb_in_max_size = uint32.min (device_ntb_in_max_size, 16384);
|
||||
ntb_out_max_size = uint32.min (ntb_params.read_uint32 (16), 16384);
|
||||
ndp_out_divisor = ntb_params.read_uint16 (20);
|
||||
ndp_out_payload_remainder = ntb_params.read_uint16 (22);
|
||||
ndp_out_alignment = ntb_params.read_uint16 (24);
|
||||
ntb_out_max_datagrams = uint16.min (ntb_params.read_uint16 (26), 16);
|
||||
|
||||
if (ntb_in_max_size != device_ntb_in_max_size) {
|
||||
var ntb_size_buf = new BufferBuilder (LITTLE_ENDIAN)
|
||||
.append_uint32 (ntb_in_max_size)
|
||||
.build ();
|
||||
yield device.control_transfer (
|
||||
LibUSB.RequestRecipient.INTERFACE | LibUSB.RequestType.CLASS | LibUSB.EndpointDirection.OUT,
|
||||
CdcRequest.SET_NTB_INPUT_SIZE,
|
||||
0,
|
||||
config.ctrl_iface,
|
||||
ntb_size_buf.get_data (),
|
||||
1000,
|
||||
cancellable);
|
||||
}
|
||||
|
||||
var speed = device.raw_device.get_device_speed ();
|
||||
if (speed >= LibUSB.Speed.SUPER) {
|
||||
max_in_transfers = (5 * MAX_TRANSFER_MEMORY) / ntb_in_max_size;
|
||||
max_out_transfers = (5 * MAX_TRANSFER_MEMORY) / ntb_out_max_size;
|
||||
} else if (speed == LibUSB.Speed.HIGH) {
|
||||
max_in_transfers = MAX_TRANSFER_MEMORY / ntb_in_max_size;
|
||||
max_out_transfers = MAX_TRANSFER_MEMORY / ntb_out_max_size;
|
||||
} else {
|
||||
max_in_transfers = 4;
|
||||
max_out_transfers = 4;
|
||||
}
|
||||
|
||||
Usb.check (handle.set_interface_alt_setting (config.data_iface, config.data_altsetting),
|
||||
"Failed to set USB interface alt setting");
|
||||
|
||||
_netstack = new VirtualNetworkStack (new Bytes (mac_address), null, 1500);
|
||||
@@ -163,35 +154,46 @@ namespace Frida.Fruity {
|
||||
return true;
|
||||
}
|
||||
|
||||
private string make_user_error_message (string message) {
|
||||
#if WINDOWS
|
||||
return message + "; use https://zadig.akeo.ie to switch from Apple's official driver onto Microsoft's WinUSB " +
|
||||
"driver, so libusb can access it";
|
||||
#else
|
||||
return message;
|
||||
#endif
|
||||
}
|
||||
|
||||
public void close () {
|
||||
io_cancellable.cancel ();
|
||||
_netstack.stop ();
|
||||
}
|
||||
|
||||
private async void process_incoming_datagrams () {
|
||||
var data = new uint8[64 * 1024];
|
||||
|
||||
var pending = new Gee.ArrayQueue<Promise<Bytes>> ();
|
||||
while (true) {
|
||||
for (uint i = pending.size; i != max_in_transfers; i++) {
|
||||
var request = transfer_next_input_batch ();
|
||||
pending.offer (request);
|
||||
}
|
||||
|
||||
try {
|
||||
size_t n = yield device.bulk_transfer (rx_address, data, uint.MAX, io_cancellable);
|
||||
handle_ncm_frame (data[:n]);
|
||||
var frame = yield pending.poll ().future.wait_async (io_cancellable);
|
||||
handle_ncm_frame (frame);
|
||||
} catch (GLib.Error e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handle_ncm_frame (uint8[] data) throws GLib.Error {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_data (data));
|
||||
private Promise<Bytes> transfer_next_input_batch () {
|
||||
var request = new Promise<Bytes> ();
|
||||
do_transfer_input_batch.begin (request);
|
||||
return request;
|
||||
}
|
||||
|
||||
private async void do_transfer_input_batch (Promise<Bytes> request) {
|
||||
var data = new uint8[ntb_in_max_size];
|
||||
try {
|
||||
size_t n = yield device.bulk_transfer (config.rx_address, data, uint.MAX, io_cancellable);
|
||||
request.resolve (new Bytes (data[:n]));
|
||||
} catch (GLib.Error e) {
|
||||
request.reject (e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handle_ncm_frame (Bytes frame) throws GLib.Error {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_bytes (frame));
|
||||
input.byte_order = LITTLE_ENDIAN;
|
||||
|
||||
uint8 raw_signature[4 + 1];
|
||||
@@ -239,47 +241,274 @@ namespace Frida.Fruity {
|
||||
} while (ndp_index != 0);
|
||||
}
|
||||
|
||||
private async void on_netif_outgoing_datagram (Bytes datagram) {
|
||||
uint16 transfer_header_length = 12;
|
||||
uint16 ndp_header_length = 16;
|
||||
uint16 alignment_padding_length = 2;
|
||||
private void on_netif_outgoing_datagram (Bytes datagram) {
|
||||
pending_output.offer (datagram);
|
||||
|
||||
uint16 datagram_start_index = transfer_header_length + ndp_header_length + alignment_padding_length;
|
||||
uint16 datagram_length = (uint16) datagram.length;
|
||||
|
||||
uint16 sentinel_start_index = 0;
|
||||
uint16 sentinel_size = 0;
|
||||
|
||||
uint16 sequence = next_outgoing_sequence++;
|
||||
uint16 block_length = datagram_start_index + datagram_length;
|
||||
uint16 ndp_index = transfer_header_length;
|
||||
uint16 next_ndp_index = 0;
|
||||
|
||||
uint16 alignment_padding_value = 0;
|
||||
|
||||
var frame = new BufferBuilder (LITTLE_ENDIAN)
|
||||
.append_string ("NCMH", StringTerminator.NONE)
|
||||
.append_uint16 (transfer_header_length)
|
||||
.append_uint16 (sequence)
|
||||
.append_uint16 (block_length)
|
||||
.append_uint16 (ndp_index)
|
||||
.append_string ("NCM0", StringTerminator.NONE)
|
||||
.append_uint16 (ndp_header_length)
|
||||
.append_uint16 (next_ndp_index)
|
||||
.append_uint16 (datagram_start_index)
|
||||
.append_uint16 (datagram_length)
|
||||
.append_uint16 (sentinel_start_index)
|
||||
.append_uint16 (sentinel_size)
|
||||
.append_uint16 (alignment_padding_value)
|
||||
.append_bytes (datagram)
|
||||
.build ();
|
||||
|
||||
try {
|
||||
yield device.bulk_transfer (tx_address, frame.get_data (), uint.MAX, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
if (!writing) {
|
||||
writing = true;
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
process_pending_output.begin ();
|
||||
return false;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
}
|
||||
|
||||
private async void process_pending_output () {
|
||||
try {
|
||||
while (!pending_output.is_empty) {
|
||||
var pending = new Gee.ArrayList<Promise<uint>> ();
|
||||
|
||||
do {
|
||||
var request = transfer_next_output_batch ();
|
||||
pending.add (request);
|
||||
} while (pending.size < max_out_transfers && !pending_output.is_empty);
|
||||
|
||||
foreach (var request in pending)
|
||||
yield request.future.wait_async (io_cancellable);
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
} finally {
|
||||
writing = false;
|
||||
}
|
||||
}
|
||||
|
||||
private Promise<uint> transfer_next_output_batch () {
|
||||
size_t num_datagrams = ntb_out_max_datagrams;
|
||||
TransferLayout layout;
|
||||
while ((layout = TransferLayout.compute (pending_output, num_datagrams, ndp_out_alignment,
|
||||
ndp_out_divisor, ndp_out_payload_remainder)).size > ntb_out_max_size) {
|
||||
num_datagrams--;
|
||||
}
|
||||
|
||||
var batch = new Gee.ArrayList<Bytes> ();
|
||||
for (var i = 0; i != layout.offsets.size; i++)
|
||||
batch.add (pending_output.poll ());
|
||||
|
||||
var transfer = build_output_transfer (batch, layout, next_outgoing_sequence++);
|
||||
|
||||
var request = new Promise<uint> ();
|
||||
do_transfer_output_batch.begin (transfer, request);
|
||||
return request;
|
||||
}
|
||||
|
||||
private async void do_transfer_output_batch (Bytes transfer, Promise<uint> request) {
|
||||
try {
|
||||
var size = yield device.bulk_transfer (config.tx_address, transfer.get_data (), uint.MAX, io_cancellable);
|
||||
request.resolve ((uint) size);
|
||||
} catch (GLib.Error e) {
|
||||
request.reject (e);
|
||||
}
|
||||
}
|
||||
|
||||
private class TransferLayout {
|
||||
public uint16 size;
|
||||
public uint16 ndp_header_offset;
|
||||
public uint16 ndp_header_size;
|
||||
public Gee.List<uint16> offsets;
|
||||
|
||||
public static TransferLayout compute (Gee.Collection<Bytes> datagrams, size_t max_datagrams, size_t ndp_alignment,
|
||||
size_t datagram_modulus, size_t datagram_remainder) {
|
||||
size_t ndp_header_base_size = 4 + 2 + 2;
|
||||
size_t ndp_entry_size = 2 + 2;
|
||||
size_t ethernet_header_size = 14;
|
||||
|
||||
size_t ndp_header_offset = align (TRANSFER_HEADER_SIZE, ndp_alignment, 0);
|
||||
size_t num_datagram_slots = size_t.min (datagrams.size, max_datagrams);
|
||||
size_t ndp_header_size = ndp_header_base_size + ((num_datagram_slots + 1) * ndp_entry_size);
|
||||
|
||||
size_t current_transfer_size = ndp_header_offset + ndp_header_size;
|
||||
var offsets = new Gee.ArrayList<uint16> ();
|
||||
|
||||
uint i = 0;
|
||||
foreach (var datagram in datagrams) {
|
||||
var size = (uint16) datagram.get_size ();
|
||||
|
||||
size_t start_offset =
|
||||
align (current_transfer_size + ethernet_header_size, datagram_modulus, datagram_remainder);
|
||||
size_t end_offset = start_offset + size;
|
||||
if (end_offset > uint16.MAX)
|
||||
break;
|
||||
|
||||
current_transfer_size = end_offset;
|
||||
offsets.add ((uint16) start_offset);
|
||||
|
||||
i++;
|
||||
if (i == max_datagrams)
|
||||
break;
|
||||
}
|
||||
|
||||
return new TransferLayout () {
|
||||
size = (uint16) current_transfer_size,
|
||||
ndp_header_offset = (uint16) ndp_header_offset,
|
||||
ndp_header_size = (uint16) ndp_header_size,
|
||||
offsets = offsets,
|
||||
};
|
||||
}
|
||||
|
||||
private static size_t align (size_t val, size_t modulus, size_t remainder) {
|
||||
var delta = val % modulus;
|
||||
if (delta != remainder)
|
||||
return val + modulus - delta + remainder;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
private static Bytes build_output_transfer (Gee.List<Bytes> datagrams, TransferLayout layout, uint16 sequence_number) {
|
||||
var builder = new BufferBuilder (LITTLE_ENDIAN)
|
||||
.append_string ("NCMH", StringTerminator.NONE)
|
||||
.append_uint16 (TRANSFER_HEADER_SIZE)
|
||||
.append_uint16 (sequence_number)
|
||||
.append_uint16 (layout.size)
|
||||
.append_uint16 (layout.ndp_header_offset);
|
||||
|
||||
uint16 next_ndp_index = 0;
|
||||
builder
|
||||
.seek (layout.ndp_header_offset)
|
||||
.append_string ("NCM0", StringTerminator.NONE)
|
||||
.append_uint16 (layout.ndp_header_size)
|
||||
.append_uint16 (next_ndp_index);
|
||||
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
foreach (var datagram in datagrams) {
|
||||
builder
|
||||
.append_uint16 (layout.offsets[i])
|
||||
.append_uint16 ((uint16) datagram.get_size ());
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
foreach (var datagram in datagrams) {
|
||||
builder
|
||||
.seek (layout.offsets[i])
|
||||
.append_bytes (datagram);
|
||||
i++;
|
||||
}
|
||||
|
||||
builder.seek (layout.size);
|
||||
|
||||
return builder.build ();
|
||||
}
|
||||
|
||||
private static InetAddress? try_infer_remote_address_from_datagram (Bytes datagram) {
|
||||
if (datagram.get_size () < 0x3e)
|
||||
return null;
|
||||
|
||||
var buf = new Buffer (datagram, BIG_ENDIAN);
|
||||
|
||||
var ethertype = (EtherType) buf.read_uint16 (12);
|
||||
if (ethertype != IPV6)
|
||||
return null;
|
||||
|
||||
var next_header = (IPV6NextHeader) buf.read_uint8 (20);
|
||||
if (next_header != UDP)
|
||||
return null;
|
||||
|
||||
return new InetAddress.from_bytes (datagram[22:22 + 16].get_data (), IPV6);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class UsbNcmConfig {
|
||||
public uint8 ctrl_iface;
|
||||
public uint8 data_iface;
|
||||
public int data_altsetting;
|
||||
public uint8 rx_address;
|
||||
public uint8 tx_address;
|
||||
public uint8 mac_address_index;
|
||||
|
||||
private enum UsbDescriptorType {
|
||||
INTERFACE = 0x04,
|
||||
}
|
||||
|
||||
private enum UsbCommSubclass {
|
||||
NCM = 0x0d,
|
||||
}
|
||||
|
||||
private enum UsbDataSubclass {
|
||||
UNDEFINED = 0x00,
|
||||
}
|
||||
|
||||
private enum UsbCdcDescriptorSubtype {
|
||||
ETHERNET = 0x0f,
|
||||
}
|
||||
|
||||
public static UsbNcmConfig prepare (UsbDevice device, out bool device_configuration_changed) throws Error {
|
||||
unowned LibUSB.Device raw_device = device.raw_device;
|
||||
|
||||
var dev_desc = LibUSB.DeviceDescriptor (raw_device);
|
||||
|
||||
LibUSB.ConfigDescriptor current_config;
|
||||
Usb.check (raw_device.get_active_config_descriptor (out current_config), "Failed to get active config descriptor");
|
||||
|
||||
var config = new UsbNcmConfig ();
|
||||
int desired_config_value = -1;
|
||||
bool found_cdc_header = false;
|
||||
bool found_data_interface = false;
|
||||
for (uint8 config_value = dev_desc.bNumConfigurations; config_value != 0; config_value--) {
|
||||
LibUSB.ConfigDescriptor config_desc;
|
||||
Usb.check (raw_device.get_config_descriptor_by_value (config_value, out config_desc),
|
||||
"Failed to get config descriptor");
|
||||
|
||||
foreach (var iface in config_desc.@interface) {
|
||||
foreach (var setting in iface.altsetting) {
|
||||
if (setting.bInterfaceClass == LibUSB.ClassCode.COMM &&
|
||||
setting.bInterfaceSubClass == UsbCommSubclass.NCM) {
|
||||
config.ctrl_iface = setting.bInterfaceNumber;
|
||||
|
||||
try {
|
||||
parse_cdc_header (setting.extra, out config.mac_address_index);
|
||||
found_cdc_header = true;
|
||||
} catch (Error e) {
|
||||
break;
|
||||
}
|
||||
} else if (setting.bInterfaceClass == LibUSB.ClassCode.DATA &&
|
||||
setting.bInterfaceSubClass == UsbDataSubclass.UNDEFINED &&
|
||||
setting.endpoint.length == 2) {
|
||||
found_data_interface = true;
|
||||
|
||||
config.data_iface = setting.bInterfaceNumber;
|
||||
config.data_altsetting = setting.bAlternateSetting;
|
||||
|
||||
foreach (var ep in setting.endpoint) {
|
||||
if ((ep.bEndpointAddress & LibUSB.EndpointDirection.MASK) ==
|
||||
LibUSB.EndpointDirection.IN) {
|
||||
config.rx_address = ep.bEndpointAddress;
|
||||
} else {
|
||||
config.tx_address = ep.bEndpointAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_cdc_header || found_data_interface) {
|
||||
desired_config_value = config_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_cdc_header || !found_data_interface)
|
||||
throw new Error.NOT_SUPPORTED ("%s", make_user_error_message ("No USB CDC-NCM interface found"));
|
||||
|
||||
if (current_config.bConfigurationValue != desired_config_value) {
|
||||
unowned LibUSB.DeviceHandle handle = device.handle;
|
||||
foreach (var iface in current_config.@interface) {
|
||||
unowned LibUSB.InterfaceDescriptor setting = iface.altsetting[0];
|
||||
var res = handle.kernel_driver_active (setting.bInterfaceNumber);
|
||||
if (res == 1)
|
||||
handle.detach_kernel_driver (setting.bInterfaceNumber);
|
||||
}
|
||||
Usb.check (handle.set_configuration (desired_config_value), "Failed to set configuration");
|
||||
device_configuration_changed = true;
|
||||
} else {
|
||||
device_configuration_changed = false;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private static void parse_cdc_header (uint8[] header, out uint8 mac_address_index) throws Error {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_data (header));
|
||||
input.set_byte_order (LITTLE_ENDIAN);
|
||||
@@ -309,22 +538,14 @@ namespace Frida.Fruity {
|
||||
|
||||
throw new Error.PROTOCOL ("CDC Ethernet descriptor not found");
|
||||
}
|
||||
}
|
||||
|
||||
private static InetAddress? try_infer_remote_address_from_datagram (Bytes datagram) {
|
||||
if (datagram.get_size () < 0x3e)
|
||||
return null;
|
||||
|
||||
var buf = new Buffer (datagram, BIG_ENDIAN);
|
||||
|
||||
var ethertype = (EtherType) buf.read_uint16 (12);
|
||||
if (ethertype != IPV6)
|
||||
return null;
|
||||
|
||||
var next_header = (IPV6NextHeader) buf.read_uint8 (20);
|
||||
if (next_header != UDP)
|
||||
return null;
|
||||
|
||||
return new InetAddress.from_bytes (datagram[22:22 + 16].get_data (), IPV6);
|
||||
}
|
||||
private string make_user_error_message (string message) {
|
||||
#if WINDOWS
|
||||
return message + "; use https://zadig.akeo.ie to switch from Apple's official driver onto Microsoft's WinUSB " +
|
||||
"driver, so libusb can access it";
|
||||
#else
|
||||
return message;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+116
-332
@@ -11,6 +11,42 @@ namespace Frida.Fruity {
|
||||
|
||||
public abstract async IOStream open_tcp_connection (InetSocketAddress address, Cancellable? cancellable)
|
||||
throws Error, IOError;
|
||||
|
||||
public async IOStream open_tcp_connection_with_timeout (InetSocketAddress address, uint timeout, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
bool timed_out = false;
|
||||
var open_cancellable = new Cancellable ();
|
||||
|
||||
var main_context = MainContext.get_thread_default ();
|
||||
|
||||
var timeout_source = new TimeoutSource (timeout);
|
||||
timeout_source.set_callback (() => {
|
||||
timed_out = true;
|
||||
open_cancellable.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
timeout_source.attach (main_context);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
open_cancellable.cancel ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
cancel_source.attach (main_context);
|
||||
|
||||
try {
|
||||
return yield open_tcp_connection (address, open_cancellable);
|
||||
} catch (IOError e) {
|
||||
assert (e is IOError.CANCELLED);
|
||||
if (timed_out)
|
||||
throw new Error.TIMED_OUT ("Networked Apple device is not responding");
|
||||
throw e;
|
||||
} finally {
|
||||
timeout_source.destroy ();
|
||||
cancel_source.destroy ();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract UdpSocket create_udp_socket () throws Error;
|
||||
}
|
||||
|
||||
@@ -160,7 +196,7 @@ namespace Frida.Fruity {
|
||||
STOPPED
|
||||
}
|
||||
|
||||
public class VirtualNetworkStack (Bytes? ethernet_address, InetAddress? ipv6_address, uint16 mtu) {
|
||||
public VirtualNetworkStack (Bytes? ethernet_address, InetAddress? ipv6_address, uint16 mtu) {
|
||||
Object (
|
||||
ethernet_address: ethernet_address,
|
||||
ipv6_address: ipv6_address,
|
||||
@@ -358,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;
|
||||
@@ -369,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);
|
||||
@@ -433,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);
|
||||
|
||||
@@ -482,6 +484,7 @@ namespace Frida.Fruity {
|
||||
if (self != null)
|
||||
self->on_error (err);
|
||||
});
|
||||
pcb.set_flags (TIMESTAMP | SACK);
|
||||
pcb.nagle_disable ();
|
||||
pcb.bind_netif (&netstack.handle);
|
||||
|
||||
@@ -509,6 +512,9 @@ namespace Frida.Fruity {
|
||||
pcb = null;
|
||||
return OK;
|
||||
});
|
||||
|
||||
state = CLOSED;
|
||||
update_pending_io ();
|
||||
}
|
||||
|
||||
private void detach_from_pcb () {
|
||||
@@ -517,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);
|
||||
@@ -535,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;
|
||||
@@ -544,22 +551,28 @@ 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) {
|
||||
detach_from_pcb ();
|
||||
bool pcb_already_freed = err == ABRT;
|
||||
if (pcb_already_freed)
|
||||
pcb = null;
|
||||
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));
|
||||
@@ -568,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;
|
||||
@@ -586,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;
|
||||
@@ -594,20 +601,22 @@ 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 (() => {
|
||||
if (pcb == null)
|
||||
return OK;
|
||||
|
||||
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;
|
||||
|
||||
size_t remainder = n;
|
||||
while (remainder != 0) {
|
||||
@@ -620,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;
|
||||
|
||||
@@ -662,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;
|
||||
});
|
||||
@@ -671,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);
|
||||
@@ -716,199 +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 ();
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -923,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> ();
|
||||
@@ -965,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 {
|
||||
@@ -1001,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 ();
|
||||
@@ -1087,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class OpackBuilder {
|
||||
public sealed class OpackBuilder {
|
||||
protected BufferBuilder builder = new BufferBuilder (LITTLE_ENDIAN);
|
||||
private Gee.Deque<Scope> scopes = new Gee.ArrayQueue<Scope> ();
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class OpackParser {
|
||||
public sealed class OpackParser {
|
||||
private BufferReader reader;
|
||||
|
||||
[Flags]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class PlistServiceClient : Object {
|
||||
public sealed class PlistServiceClient : Object {
|
||||
public signal void closed ();
|
||||
|
||||
public IOStream stream {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class Plist : PlistDict {
|
||||
public sealed class Plist : PlistDict {
|
||||
public enum Format {
|
||||
AUTO,
|
||||
BINARY,
|
||||
@@ -1355,7 +1355,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class PlistArray : Object {
|
||||
public sealed class PlistArray : Object {
|
||||
public bool is_empty {
|
||||
get {
|
||||
return storage.is_empty;
|
||||
@@ -1511,10 +1511,10 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class PlistNull : Object {
|
||||
public sealed class PlistNull : Object {
|
||||
}
|
||||
|
||||
public class PlistDate : Object {
|
||||
public sealed class PlistDate : Object {
|
||||
private DateTime time;
|
||||
|
||||
public PlistDate (DateTime time) {
|
||||
@@ -1526,7 +1526,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public class PlistUid : Object {
|
||||
public sealed class PlistUid : Object {
|
||||
public uint64 uid {
|
||||
get;
|
||||
construct;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class SpringboardServicesClient : Object, AsyncInitable {
|
||||
public sealed class SpringboardServicesClient : Object, AsyncInitable {
|
||||
public Device device {
|
||||
get;
|
||||
construct;
|
||||
|
||||
+64
-65
@@ -1,6 +1,11 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
internal sealed class UsbDevice : Object, AsyncInitable {
|
||||
internal sealed class UsbDevice : Object {
|
||||
public string udid {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public LibUSB.Device? raw_device {
|
||||
get {
|
||||
return _raw_device;
|
||||
@@ -18,22 +23,8 @@ namespace Frida.Fruity {
|
||||
}
|
||||
}
|
||||
|
||||
public string udid {
|
||||
get {
|
||||
return _udid;
|
||||
}
|
||||
}
|
||||
|
||||
public uint16 default_language_id {
|
||||
get {
|
||||
return _default_language_id;
|
||||
}
|
||||
}
|
||||
|
||||
private LibUSB.Device? _raw_device;
|
||||
private LibUSB.DeviceHandle? _handle;
|
||||
private string _udid;
|
||||
private uint16 _default_language_id;
|
||||
private uint num_pending_operations;
|
||||
private Promise<bool>? pending_operations_completed;
|
||||
|
||||
@@ -45,38 +36,24 @@ namespace Frida.Fruity {
|
||||
private const string MODE_INITIAL_UNTETHERED = "3:3:3:0"; // => 5:3:3:0
|
||||
private const string MODE_INITIAL_TETHERED = "4:4:3:4"; // => 5:4:3:4
|
||||
|
||||
public static async UsbDevice open (LibUSB.Device raw_device, UsbDeviceBackend backend, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
var device = new UsbDevice (raw_device, backend);
|
||||
public UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) throws Error {
|
||||
char serial[LibUSB.DEVICE_STRING_BYTES_MAX + 1];
|
||||
var res = raw_device.get_device_string (SERIAL_NUMBER, serial);
|
||||
Usb.check (res, "Failed to get serial number");
|
||||
serial[res] = '\0';
|
||||
|
||||
try {
|
||||
yield device.init_async (Priority.DEFAULT, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_api_error (e);
|
||||
}
|
||||
Object (
|
||||
udid: udid_from_serial_number ((string) serial),
|
||||
backend: backend
|
||||
);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
private UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) {
|
||||
Object (backend: backend);
|
||||
_raw_device = raw_device;
|
||||
}
|
||||
|
||||
private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError {
|
||||
public void ensure_open (Cancellable? cancellable = null) throws Error {
|
||||
if (_handle != null)
|
||||
return;
|
||||
Usb.check (_raw_device.open (out _handle), "Failed to open USB device");
|
||||
|
||||
Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable);
|
||||
if (language_ids_response.get_size () < sizeof (uint16))
|
||||
throw new Error.PROTOCOL ("Invalid language IDs response");
|
||||
Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN);
|
||||
_default_language_id = language_ids.read_uint16 (0);
|
||||
|
||||
var dev_desc = LibUSB.DeviceDescriptor (_raw_device);
|
||||
string serial_number = yield read_string_descriptor (dev_desc.iSerialNumber, _default_language_id, cancellable);
|
||||
_udid = udid_from_serial_number (serial_number);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
@@ -95,46 +72,59 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
public async bool maybe_modeswitch (Cancellable? cancellable) throws Error, IOError {
|
||||
var response = yield control_transfer (
|
||||
uint8 current_mode[4];
|
||||
var n = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
|
||||
AppleSpecificRequest.GET_MODE,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
current_mode,
|
||||
1000,
|
||||
cancellable);
|
||||
string mode = parse_mode (response);
|
||||
string mode = parse_mode (current_mode[:n]);
|
||||
bool is_initial_mode = mode == MODE_INITIAL_UNTETHERED || mode == MODE_INITIAL_TETHERED;
|
||||
if (!is_initial_mode)
|
||||
return false;
|
||||
|
||||
response = yield control_transfer (
|
||||
uint8 set_mode_result[1];
|
||||
var set_mode_result_size = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.VENDOR | LibUSB.EndpointDirection.IN,
|
||||
AppleSpecificRequest.SET_MODE,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
set_mode_result,
|
||||
1000,
|
||||
cancellable);
|
||||
if (response.get_size () != 1 || response[0] != 0x00)
|
||||
if (set_mode_result_size != 1 || set_mode_result[0] != 0x00)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string parse_mode (Bytes mode) throws Error {
|
||||
if (mode.get_size () != 4)
|
||||
throw new Error.PROTOCOL ("Invalid mode response");
|
||||
unowned uint8[] m = mode.get_data ();
|
||||
return "%u:%u:%u:%u".printf (m[0], m[1], m[2], m[3]);
|
||||
private static string parse_mode (uint8[] mode) throws Error {
|
||||
var result = new StringBuilder.sized (7);
|
||||
foreach (uint8 byte in mode) {
|
||||
if (result.len != 0)
|
||||
result.append_c (':');
|
||||
result.append_printf ("%u", byte);
|
||||
}
|
||||
return result.str;
|
||||
}
|
||||
|
||||
private static string udid_from_serial_number (string serial) {
|
||||
public static string udid_from_serial_number (string serial) {
|
||||
if (serial.length == 24)
|
||||
return serial.substring (0, 8) + "-" + serial.substring (8);
|
||||
return serial[:8] + "-" + serial[8:];
|
||||
return serial;
|
||||
}
|
||||
|
||||
public async uint16 query_default_language_id (Cancellable? cancellable) throws Error, IOError {
|
||||
Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable);
|
||||
if (language_ids_response.get_size () < sizeof (uint16))
|
||||
throw new Error.PROTOCOL ("Invalid language IDs response");
|
||||
Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN);
|
||||
return language_ids.read_uint16 (0);
|
||||
}
|
||||
|
||||
public async string read_string_descriptor (uint8 index, uint16 language_id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var response = yield read_string_descriptor_bytes (index, language_id, cancellable);
|
||||
@@ -159,16 +149,17 @@ namespace Frida.Fruity {
|
||||
|
||||
public async Bytes read_string_descriptor_bytes (uint8 index, uint16 language_id, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
var response = yield control_transfer (
|
||||
uint8 response[1024];
|
||||
var response_size = yield control_transfer (
|
||||
LibUSB.RequestRecipient.DEVICE | LibUSB.RequestType.STANDARD | LibUSB.EndpointDirection.IN,
|
||||
LibUSB.StandardRequest.GET_DESCRIPTOR,
|
||||
(LibUSB.DescriptorType.STRING << 8) | index,
|
||||
language_id,
|
||||
1024,
|
||||
response,
|
||||
1000,
|
||||
cancellable);
|
||||
try {
|
||||
var input = new DataInputStream (new MemoryInputStream.from_bytes (response));
|
||||
var input = new DataInputStream (new MemoryInputStream.from_data (response[:response_size]));
|
||||
input.byte_order = LITTLE_ENDIAN;
|
||||
|
||||
uint8 length = input.read_byte ();
|
||||
@@ -179,26 +170,29 @@ namespace Frida.Fruity {
|
||||
if (type != LibUSB.DescriptorType.STRING)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor type");
|
||||
|
||||
size_t remainder = response.get_size () - 2;
|
||||
size_t remainder = response_size - 2;
|
||||
length -= 2;
|
||||
if (length > remainder)
|
||||
throw new Error.PROTOCOL ("Invalid string descriptor length");
|
||||
|
||||
return response[2:2 + length];
|
||||
return new Bytes (response[2:2 + length]);
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.PROTOCOL ("%s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Bytes control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint16 length,
|
||||
public async size_t control_transfer (uint8 request_type, uint8 request, uint16 val, uint16 index, uint8[] buffer,
|
||||
uint timeout, Cancellable? cancellable) throws Error, IOError {
|
||||
var op = backend.allocate_usb_operation ();
|
||||
unowned LibUSB.Transfer transfer = op.transfer;
|
||||
var ready_closure = new TransferReadyClosure (control_transfer.callback);
|
||||
|
||||
var buffer = new uint8[sizeof (LibUSB.ControlSetup) + length];
|
||||
LibUSB.Transfer.fill_control_setup (buffer, request_type, request, val, index, length);
|
||||
transfer.fill_control_transfer (_handle, buffer, on_transfer_ready, ready_closure, timeout);
|
||||
size_t control_setup_size = 8;
|
||||
var transfer_buffer = new uint8[control_setup_size + buffer.length];
|
||||
LibUSB.Transfer.fill_control_setup (transfer_buffer, request_type, request, val, index, (uint16) buffer.length);
|
||||
if ((request_type & LibUSB.EndpointDirection.IN) == 0)
|
||||
Memory.copy ((uint8 *) transfer_buffer + control_setup_size, buffer, buffer.length);
|
||||
transfer.fill_control_transfer (_handle, transfer_buffer, on_transfer_ready, ready_closure, timeout);
|
||||
|
||||
var cancel_source = new CancellableSource (cancellable);
|
||||
cancel_source.set_callback (() => {
|
||||
@@ -218,7 +212,12 @@ namespace Frida.Fruity {
|
||||
|
||||
Usb.check_transfer (transfer.status, "Control transfer failed");
|
||||
|
||||
return new Bytes (((uint8[]) transfer.control_get_data ())[:transfer.actual_length]);
|
||||
var n = transfer.actual_length;
|
||||
|
||||
if ((request_type & LibUSB.EndpointDirection.IN) != 0)
|
||||
Memory.copy (buffer, transfer.control_get_data (), n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public async size_t bulk_transfer (uint8 endpoint, uint8[] buffer, uint timeout, Cancellable? cancellable)
|
||||
@@ -287,7 +286,7 @@ namespace Frida.Fruity {
|
||||
}
|
||||
|
||||
internal interface UsbDeviceBackend : Object {
|
||||
public abstract UsbOperation allocate_usb_operation ();
|
||||
public abstract UsbOperation allocate_usb_operation () throws Error;
|
||||
}
|
||||
|
||||
internal interface UsbOperation : Object {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[CCode (gir_namespace = "FridaFruity", gir_version = "1.0")]
|
||||
namespace Frida.Fruity {
|
||||
public class UsbmuxClient : Object, AsyncInitable {
|
||||
public sealed class UsbmuxClient : Object, AsyncInitable {
|
||||
public signal void device_attached (UsbmuxDevice device);
|
||||
public signal void device_detached (UsbmuxDevice device);
|
||||
|
||||
@@ -548,7 +548,7 @@ namespace Frida.Fruity {
|
||||
PROTOCOL
|
||||
}
|
||||
|
||||
public class UsbmuxDevice : Object {
|
||||
public sealed class UsbmuxDevice : Object {
|
||||
public ConnectionType connection_type {
|
||||
get;
|
||||
construct;
|
||||
|
||||
+657
-156
File diff suppressed because it is too large.
Load diff
Loaded 100 of 166 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user