Compare commits

...
52 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås 259ce04fe6 subprojects: Prepare for release 2025-03-21 14:20:50 +01:00
Ole André Vadla Ravnås 4cdfa4b09c subprojects: Bump outdated 2025-03-21 14:19:27 +01:00
Ole André Vadla Ravnås 7b54e41f88 droidy: Fix the MAX_MESSAGE_LENGTH declaration
The new maximum exceeds the range of a uint16.
2025-03-21 14:18:59 +01:00
Ole André Vadla Ravnås c7b1e17c3e subprojects: Prepare for release 2025-03-21 13:57:14 +01:00
Ole André Vadla Ravnås e844297a61 subprojects: Bump outdated 2025-03-20 20:38:52 +01:00
Ole André Vadla Ravnås 30b00eaa64 subprojects: Bump outdated 2025-03-20 17:00:25 +01:00
Ole André Vadla Ravnås 2109c38e06 submodules: Bump releng 2025-03-20 17:00:25 +01:00
Ole André Vadla Ravnås 1a3da37cf8 compiler: Bump @types/frida-gum 2025-03-20 16:59:53 +01:00
Jan S. 3d9fab2b0d fruity: Fix Input/Output Error on Windows (#1144)
By skipping devices with an empty UDID.
2025-03-19 15:12:07 +01:00
philippmao c58212fe8c droidy: Support more than ~8 ADB-connected devices (#1145)
By bumping the message size limit.
2025-03-19 15:07:44 +01:00
Ole André Vadla Ravnås bce2650980 subprojects: Bump outdated 2025-03-19 10:51:37 +01:00
Ole André Vadla Ravnås 0f03a0ee19 submodules: Bump releng 2025-03-19 10:51:36 +01:00
Ole André Vadla Ravnås d9b4d20adf linux: Include linux/unistd.h for syscall constant
Which, depending on the libc, may not be implicitly included.
2025-03-18 19:11:03 +00:00
Ole André Vadla Ravnås a8db90d6a5 subprojects: Bump outdated 2025-03-18 19:51:10 +01:00
Ole André Vadla Ravnås edcb76f066 submodules: Bump releng 2025-03-18 19:51:09 +01:00
Ole André Vadla Ravnås 1f72d5f102 subprojects: Prepare for release 2025-03-13 19:09:35 +01:00
Ole André Vadla Ravnås 8336d42b94 server: Create DeviceManager without local backend
Since we're pulling it in manually in order to run the frida-helper
in-process, we want to avoid having both implementations.

This reduces our footprint from 58M down to 49M on macOS, up from 46M
before the --device feature.
2025-03-13 19:06:33 +01:00
Ole André Vadla Ravnås 9404147517 device-manager: Support running w/o local backend
So consumers that don't need it can get a smaller footprint.
2025-03-13 19:01:20 +01:00
Ole André Vadla Ravnås ed464e9ccf subprojects: Bump outdated 2025-03-13 15:07:33 +01:00
Ole André Vadla Ravnås ce6178e125 server: Add --device for serving a specific device 2025-03-13 14:43:36 +01:00
Ole André Vadla Ravnås 0e69ec3e77 control-service: Support serving a specific Device 2025-03-13 14:42:57 +01:00
Ole André Vadla Ravnås 4eba770491 subprojects: Bump outdated 2025-03-13 14:09:11 +01:00
WorksButNotTested dc022e4bbe linux: Build helpers for arm64be and arm64beilp32 (#1141) 2025-03-13 13:47:09 +01:00
WorksButNotTested df04bb40c3 linux: Add support for arm64beilp32 (#1140) 2025-03-12 14:26:54 +01:00
WorksButNotTested 0f685ea496 linux: Support building helpers for armbe8 (#1139) 2025-03-12 10:07:43 +01:00
WorksButNotTested 48bfb1e054 linux: Add support for arm64be 2025-03-12 09:33:04 +01:00
Ole André Vadla Ravnås ab8a0a3927 compiler: Bump @types/frida-gum 2025-03-12 08:55:15 +01:00
Ole André Vadla Ravnås 9594e1a8b8 linux: Handle compat ranges during injection
So our spurious range detection doesn't make an incomplete candidate,
and we might end up picking the wrong one.
2025-03-03 23:38:47 +01:00
Ole André Vadla Ravnås 60a6770dba darwin: Fix racy leakage of memory during teardown
Where DarwinHelperBackend.close() would cancel dispatch sources, but
return before the cancel handler got a chance to run. While this leak
typically happened on teardown and wasn't necessarily a big deal
locally, the worse part was that we also leaked large regions of memory
in remote processes that we uninjected from just before teardown.
2025-02-28 17:34:49 +01:00
Ole André Vadla Ravnås c5376242e9 darwin: Fix HelperBackend MainContext assumptions
We should not blindly dispatch to the default one, but take note at
construction time.

This is to avoid surprises down the road, and to improve consistency
across the codebase.
2025-02-28 17:34:49 +01:00
Ole André Vadla Ravnås 17d6161435 linux: Avoid spurious .so ranges during injection
By checking that the mapping starts at file offset zero, and considering
all candidates ranked by the number of mappings, and whether executable
mappings exist in a given cluster.

Now that Gum.Module objects live longer, and may have an Gum.ElfModule
that has the ELF mapped, there's a much higher likelihood of running
into such spurious ranges.
2025-02-20 10:18:44 +01:00
Ole André Vadla Ravnås 26d9b9b6b1 fruity: Fix crash in the Linux getifaddrs() logic
Which failed to handle interfaces without an address.
2025-02-20 08:48:30 +01:00
Ole André Vadla Ravnås 1ed5590c77 submodules: Bump releng 2025-02-18 11:15:09 +01:00
Ole André Vadla Ravnås 25a283e43b ci: Fix the linux-x86 job
Now that ubuntu-latest refers to a newer Ubuntu release, we need to
move to the newer lib32stdc++ package version that matches that of
the gcc-multilib package.
2025-02-18 11:12:51 +01:00
Ole André Vadla Ravnås 04e04166b7 subprojects: Bump outdated 2025-02-18 11:12:51 +01:00
Ole André Vadla Ravnås 2c6c52d22a darwin: Port to the new ModuleResolver API 2025-02-18 11:12:51 +01:00
Ole André Vadla Ravnås ba7d01f232 subprojects: Prepare for release 2025-01-27 20:54:28 +01:00
Ole André Vadla Ravnås 8a16d42652 subprojects: Bump outdated 2025-01-27 20:51:53 +01:00
Ole André Vadla Ravnås 9c13120d67 fruity: Fix lockdown over CoreDevice regression
Introduced by the recent change in d1df792, where our RSDCheckin
includes an EscrowBag in order to support networked lockdown with
services such as “com.apple.crashreportmover”.

Turns out this broke support for quite a few other services, as the
presence of an EscrowBag requires that the particular service is able
to talk to AppleKeyStoreUserClient.

Not yet sure how to handle this. For now we'll maintain a list of
services that lack such privileges, so we can omit the EscrowBag for
those.

Kudos to @as0ler for reporting and helping troubleshoot.
2025-01-27 19:35:52 +01:00
Ole André Vadla Ravnås e70812a1e0 subprojects: Prepare for release 2025-01-23 20:46:57 +01:00
Ole André Vadla Ravnås 283381ca9e subprojects: Bump outdated 2025-01-23 20:31:30 +01:00
Mohamed Amine Amor 72a96226ce linux: Avoid risky code swaps with memfd regions
As they may not be writable, and unlike regular regions, ptrace() won't
help us in case of a missing writable bit.
2025-01-23 19:13:59 +01:00
Mohamed Amine Amor b6577afe81 linux: Relax injector's libc matching for Android
Due to how APEXes are bind-mounted.
2025-01-23 19:13:59 +01:00
Ole André Vadla Ravnås d1df7922bc fruity: Support networked lockdown for CoreDevice
Where we need to provide the remote unlock host key as part of the
RSDCheckin.

Kudos to @as0ler and @mrmacete for reporting and helping get to the
bottom of this one.
2025-01-23 18:41:01 +01:00
Ole André Vadla Ravnås af9c36ce6e xpc: Add support for Data objects 2025-01-23 15:27:47 +01:00
Ole André Vadla Ravnås da58033e9b subprojects: Prepare for release 2025-01-17 16:49:07 +01:00
Ole André Vadla Ravnås 702f3e83eb subprojects: Bump outdated 2025-01-17 16:46:54 +01:00
WorksButNotTested 7644f3dc6d linux: Revive support for ARM BE8 (#1130) 2025-01-17 16:30:59 +01:00
Ole André Vadla Ravnås 24d2d6f055 subprojects: Prepare for release 2025-01-14 19:35:29 +01:00
Ole André Vadla Ravnås 47d44f07ae subprojects: Bump outdated 2025-01-14 19:33:17 +01:00
Ole André Vadla Ravnås 8119fc7569 submodules: Bump releng 2025-01-14 19:33:17 +01:00
Ole André Vadla Ravnås 57409c8056 windows: Port injector to new Gum.Module API 2025-01-14 18:09:55 +01:00
42 changed files with 639 additions and 169 deletions

No files matched your search

+2 -2
View File
@@ -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: |
@@ -249,7 +249,7 @@ jobs:
check-linux-helpers:
strategy:
matrix:
arch: [x86, x86_64, arm, arm64, mips, mipsel, mips64, mips64el]
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:
+7
View File
@@ -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 ();
}
+1 -1
Submodule releng updated: 79d6dfa3d6...2eecc41cbe.
+51 -8
View File
@@ -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 ();
@@ -235,19 +238,36 @@ namespace Frida.Server {
private 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 {
+7 -7
View File
@@ -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.2",
"@types/frida-gum": "18.8.1",
"@types/glob": "^8.0.0",
"@types/node": "18.7.23",
"glob": "^8.0.3",
@@ -468,9 +468,9 @@
"dev": true
},
"node_modules/@types/frida-gum": {
"version": "18.7.2",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.2.tgz",
"integrity": "sha512-nOxTk8n45nQlFQhSSXcnePrzjw6k6JMJMESZ8HlHhUiIQqvGI4RYa69FYdUbGoBRnITn8srEq5vn0dkVwO/ezQ==",
"version": "18.8.1",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.1.tgz",
"integrity": "sha512-FhlHORjaw43QbNYIyj5Ji7iYSvk7t5nPiC+GuYDVndqc1bFheeAs88b1odp8HhmpQUrrYVU90wapapKVLlmJHg==",
"dev": true,
"license": "MIT"
},
@@ -1466,9 +1466,9 @@
"dev": true
},
"@types/frida-gum": {
"version": "18.7.2",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.7.2.tgz",
"integrity": "sha512-nOxTk8n45nQlFQhSSXcnePrzjw6k6JMJMESZ8HlHhUiIQqvGI4RYa69FYdUbGoBRnITn8srEq5vn0dkVwO/ezQ==",
"version": "18.8.1",
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.8.1.tgz",
"integrity": "sha512-FhlHORjaw43QbNYIyj5Ji7iYSvk7t5nPiC+GuYDVndqc1bFheeAs88b1odp8HhmpQUrrYVU90wapapKVLlmJHg==",
"dev": true
},
"@types/glob": {
+1 -1
View File
@@ -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.2",
"@types/frida-gum": "18.8.1",
"@types/glob": "^8.0.0",
"@types/node": "18.7.23",
"glob": "^8.0.3",
+14
View File
@@ -66,6 +66,20 @@ namespace Frida {
assign_session (session);
}
public async ControlService.with_device (Device device, EndpointParameters endpoint_params,
ControlServiceOptions? options = null, Cancellable? cancellable = null) throws Error, IOError {
ControlServiceOptions opts = (options != null) ? options : new ControlServiceOptions ();
var session = yield device.get_host_session (cancellable);
Object (
endpoint_params: endpoint_params,
options: opts
);
assign_session (session);
}
internal ControlService.with_host_session (HostSession host_session, EndpointParameters endpoint_params,
ControlServiceOptions? options = null) {
Object (
+51 -31
View File
@@ -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);
@@ -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
@@ -4114,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;
}
@@ -4143,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;
@@ -4187,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);
@@ -4292,6 +4302,7 @@ 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_by_name (resolver, "/usr/lib/system/libsystem_kernel.dylib");
@@ -4303,6 +4314,8 @@ 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_by_name (resolver, "/usr/lib/system/libsystem_pthread.dylib");
if (module == NULL)
@@ -4315,6 +4328,8 @@ 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)
{
@@ -4324,9 +4339,12 @@ frida_agent_context_init_functions (FridaAgentContext * self, GumDarwinModuleRes
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:
{
@@ -4334,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:
{
@@ -4342,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;
}
}
+61 -28
View File
@@ -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,7 +683,7 @@ 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 {
+1 -1
View File
@@ -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;
+13 -2
View File
@@ -34,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);
}
@@ -340,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);
+6 -4
View File
@@ -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");
+18 -2
View File
@@ -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);
@@ -252,6 +260,12 @@ namespace Frida.Fruity {
}
}
public Bytes? remote_unlock_host_key {
get {
return _remote_unlock_host_key;
}
}
public Darwin.Xpc.Uuid? assertion_identifier {
get {
return _assertion_identifier;
@@ -259,13 +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 {
+53 -3
View File
@@ -188,6 +188,18 @@ namespace Frida.Fruity {
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 ();
}
@@ -247,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);
@@ -283,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;
@@ -496,6 +520,10 @@ namespace Frida.Fruity {
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;
}
@@ -1002,6 +1030,11 @@ namespace Frida.Fruity {
}
unowned string udid = usb_device.udid;
// 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);
@@ -1454,13 +1487,15 @@ 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) {
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;
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");
@@ -1675,10 +1710,17 @@ namespace Frida.Fruity {
}
}
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 (
@@ -1707,11 +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.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,
@@ -1880,9 +1922,16 @@ namespace Frida.Fruity {
}
}
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;
@@ -1905,6 +1954,7 @@ namespace Frida.Fruity {
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,
+148 -15
View File
@@ -146,6 +146,11 @@ namespace Frida.Fruity {
private set;
}
public PairingPeer? established_peer {
get;
private set;
}
private Gee.Map<uint64?, Promise<ObjectReader>> requests =
new Gee.HashMap<uint64?, Promise<ObjectReader>> (Numeric.uint64_hash, Numeric.uint64_equal);
private uint64 next_control_sequence_number = 0;
@@ -181,15 +186,35 @@ namespace Frida.Fruity {
yield attempt_pair_verify (cancellable);
Bytes? shared_key = yield verify_manual_pairing (cancellable);
if (shared_key == null) {
PairingPeer? peer = yield verify_manual_pairing (cancellable);
if (peer == null) {
if (!device_options.allows_pair_setup)
throw new Error.NOT_SUPPORTED ("Device not paired and pairing not allowed on current transport");
shared_key = yield setup_manual_pairing (cancellable);
peer = yield setup_manual_pairing (cancellable);
}
client_cipher = new ChaCha20Poly1305 (derive_chacha_key (shared_key, "ClientEncrypt-main"));
server_cipher = new ChaCha20Poly1305 (derive_chacha_key (shared_key, "ServerEncrypt-main"));
if (peer.remote_unlock_host_key == null) {
bool peer_modified = false;
try {
peer.remote_unlock_host_key = yield create_remote_unlock_key (cancellable);
peer_modified = true;
} catch (Error e) {
if (e is Error.INVALID_OPERATION) {
store.forget_peer (peer);
throw e;
}
if (!(e is Error.NOT_SUPPORTED))
throw e;
}
if (peer_modified) {
try {
store.save_peer (peer);
} catch (Error e) {
}
}
}
established_peer = peer;
return true;
}
@@ -377,7 +402,7 @@ namespace Frida.Fruity {
}
}
private async Bytes? verify_manual_pairing (Cancellable? cancellable) throws Error, IOError {
private async PairingPeer? verify_manual_pairing (Cancellable? cancellable) throws Error, IOError {
Key host_keypair = make_keypair (X25519);
uint8[] raw_host_pubkey = get_raw_public_key (host_keypair).get_data ();
@@ -414,10 +439,18 @@ namespace Frida.Fruity {
var cipher = new ChaCha20Poly1305 (operation_key);
// TODO: Verify signature using peer's public key.
/* var start_inner_response = */ new VariantReader (PairingParamsParser.parse (cipher.decrypt (
var start_inner_response = new VariantReader (PairingParamsParser.parse (cipher.decrypt (
new Bytes.static ("\x00\x00\x00\x00PV-Msg02".data[:12]),
start_response.read_member ("encrypted-data").get_data_value ())));
string peer_identifier = start_inner_response
.read_member ("identifier")
.get_uuid_value ();
PairingPeer? peer = store.find_peer_by_identifier (peer_identifier);
if (peer == null) {
yield notify_pair_verify_failed (cancellable);
return null;
}
// TODO: Verify signature using peer's public key.
unowned string host_identifier = store.self_identity.identifier;
@@ -457,7 +490,9 @@ namespace Frida.Fruity {
return null;
}
return shared_key;
setup_main_encryption_keys (shared_key);
return peer;
}
private async void notify_pair_verify_failed (Cancellable? cancellable) throws Error, IOError {
@@ -476,7 +511,7 @@ namespace Frida.Fruity {
.build (), cancellable);
}
private async Bytes setup_manual_pairing (Cancellable? cancellable) throws Error, IOError {
private async PairingPeer setup_manual_pairing (Cancellable? cancellable) throws Error, IOError {
Bytes start_params = new PairingParamsBuilder ()
.add_method (0)
.add_state (1)
@@ -612,9 +647,78 @@ namespace Frida.Fruity {
inner_finish_response.end_member ();
Bytes peer_info = inner_finish_response.read_member ("info").get_data_value ();
inner_finish_response.end_member ();
store.add_peer (peer_identifier, peer_pubkey, peer_info);
return shared_key;
PairingPeer peer = store.add_peer (peer_identifier, peer_pubkey, peer_info);
setup_main_encryption_keys (shared_key);
return peer;
}
private async Bytes? create_remote_unlock_key (Cancellable? cancellable) throws Error, IOError {
string request = Json.to_string (
new Json.Builder ()
.begin_object ()
.set_member_name ("request")
.begin_object ()
.set_member_name ("_0")
.begin_object ()
.set_member_name ("createRemoteUnlockKey")
.begin_object ()
.end_object ()
.end_object ()
.end_object ()
.end_object ()
.get_root (), false);
string response = yield request_encrypted (request, cancellable);
Json.Reader reader;
try {
reader = new Json.Reader (Json.from_string (response));
} catch (GLib.Error e) {
throw new Error.PROTOCOL ("Invalid createRemoteUnlockKey response JSON");
}
reader.read_member ("response");
reader.read_member ("_1");
if (reader.read_member ("errorExtended")) {
reader.read_member ("_0");
reader.read_member ("domain");
unowned string? domain = reader.get_string_value ();
reader.end_member ();
reader.read_member ("code");
int64 code = reader.get_int_value ();
reader.end_member ();
reader.read_member ("userInfo");
reader.read_member ("NSLocalizedDescription");
unowned string? description = reader.get_string_value ();
if (domain == null || description == null)
throw new Error.PROTOCOL ("Invalid createRemoteUnlockKey error response");
if (domain == "com.apple.CoreDevice.ControlChannelConnectionError" && code == 2)
throw new Error.INVALID_OPERATION ("%s", description);
throw new Error.NOT_SUPPORTED ("%s", description);
}
reader.end_member ();
reader.read_member ("createRemoteUnlockKey");
reader.read_member ("hostKey");
unowned string? key = reader.get_string_value ();
if (key == null)
throw new Error.PROTOCOL ("Malformed createRemoteUnlockKey response");
return new Bytes (Base64.decode (key));
}
private void setup_main_encryption_keys (Bytes shared_key) {
client_cipher = new ChaCha20Poly1305 (derive_chacha_key (shared_key, "ClientEncrypt-main"));
server_cipher = new ChaCha20Poly1305 (derive_chacha_key (shared_key, "ServerEncrypt-main"));
}
private async ObjectReader request_pairing_data (Bytes payload, Cancellable? cancellable) throws Error, IOError {
@@ -1383,7 +1487,7 @@ namespace Frida.Fruity {
_peers = load_peers ();
}
public void add_peer (string identifier, Bytes public_key, Bytes info) throws Error {
public PairingPeer add_peer (string identifier, Bytes public_key, Bytes info) throws Error {
var r = new VariantReader (OpackParser.parse (info));
Bytes irk = r.read_member ("altIRK").get_data_value ();
@@ -1406,6 +1510,7 @@ namespace Frida.Fruity {
model = model,
udid = udid,
info = info,
remote_unlock_host_key = null,
};
_peers.add (peer);
@@ -1413,6 +1518,12 @@ namespace Frida.Fruity {
save_peer (peer);
} catch (Error e) {
}
return peer;
}
public PairingPeer? find_peer_by_identifier (string identifier) {
return peers.first_match (p => p.identifier == identifier);
}
public PairingPeer? find_peer_matching_service (PairingServiceDetails service) {
@@ -1480,6 +1591,10 @@ namespace Frida.Fruity {
var r = new VariantReader (OpackParser.parse (info));
unowned string udid = r.read_member ("remotepairing_udid").get_string_value ();
Bytes? remote_unlock_host_key = null;
if (plist.has ("remoteUnlockHostKey"))
remote_unlock_host_key = plist.get_bytes ("remoteUnlockHostKey");
peers.add (new PairingPeer () {
identifier = plist.get_string ("identifier"),
public_key = plist.get_bytes ("publicKey"),
@@ -1488,6 +1603,7 @@ namespace Frida.Fruity {
model = plist.get_string ("model"),
udid = udid,
info = info,
remote_unlock_host_key = remote_unlock_host_key,
});
}
} catch (GLib.Error e) {
@@ -1496,7 +1612,7 @@ namespace Frida.Fruity {
return peers;
}
private static void save_peer (PairingPeer peer) throws Error {
public void save_peer (PairingPeer peer) throws Error {
var plist = new Plist ();
plist.set_string ("identifier", peer.identifier);
plist.set_bytes ("publicKey", peer.public_key);
@@ -1504,13 +1620,29 @@ namespace Frida.Fruity {
plist.set_string ("name", peer.name);
plist.set_string ("model", peer.model);
plist.set_bytes ("info", peer.info);
save_plist (plist, query_peers_location ().get_child (peer.identifier + ".plist"));
if (peer.remote_unlock_host_key != null)
plist.set_bytes ("remoteUnlockHostKey", peer.remote_unlock_host_key);
save_plist (plist, query_peer_location (peer));
}
public void forget_peer (PairingPeer peer) {
try {
query_peer_location (peer).delete ();
} catch (GLib.Error e) {
}
_peers.remove (peer);
}
private static File query_self_identity_location () {
return query_base_location ().get_child ("self-identity.plist");
}
private static File query_peer_location (PairingPeer peer) {
return query_peers_location ().get_child (peer.identifier + ".plist");
}
private static File query_peers_location () {
return query_base_location ().get_child ("peers");
}
@@ -1557,6 +1689,7 @@ namespace Frida.Fruity {
public string model;
public string udid;
public Bytes info;
public Bytes? remote_unlock_host_key;
}
public class PairingServiceMetadata {
+22 -14
View File
@@ -9,26 +9,17 @@ namespace Frida {
public HostSessionService.with_default_backends () {
add_local_backends ();
#if !IOS && !ANDROID && !TVOS
#if HAVE_FRUITY_BACKEND
add_backend (new FruityHostSessionBackend ());
#endif
#if HAVE_DROIDY_BACKEND
add_backend (new DroidyHostSessionBackend ());
#endif
#endif
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
#endif
#if HAVE_BAREBONE_BACKEND
add_backend (new BareboneHostSessionBackend ());
#endif
add_nonlocal_backends ();
}
public HostSessionService.with_local_backend_only () {
add_local_backends ();
}
public HostSessionService.with_nonlocal_backends_only () {
add_nonlocal_backends ();
}
public HostSessionService.with_socket_backend_only () {
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
@@ -55,6 +46,23 @@ namespace Frida {
#endif
}
private void add_nonlocal_backends () {
#if !IOS && !ANDROID && !TVOS
#if HAVE_FRUITY_BACKEND
add_backend (new FruityHostSessionBackend ());
#endif
#if HAVE_DROIDY_BACKEND
add_backend (new DroidyHostSessionBackend ());
#endif
#endif
#if HAVE_SOCKET_BACKEND
add_backend (new SocketHostSessionBackend ());
#endif
#if HAVE_BAREBONE_BACKEND
add_backend (new BareboneHostSessionBackend ());
#endif
}
public async void start (Cancellable? cancellable = null) throws IOError {
var remaining = backends.size + 1;
+84 -24
View File
@@ -844,19 +844,19 @@ namespace Frida {
private const uint64 SOCK_CLOEXEC = 0x80000;
private class InjectSession : SeizeSession {
private static ProcMapsEntry local_libc;
private static ProcMapsSoEntry local_libc;
private static uint64 mmap_offset;
private static uint64 munmap_offset;
private static string fallback_ld;
private static string fallback_libc;
private static ProcMapsEntry? local_android_ld;
private static ProcMapsSoEntry? local_android_ld;
static construct {
var libc = Gum.Process.get_libc_module ();
uint local_pid = Posix.getpid ();
local_libc = ProcMapsEntry.find_by_path (local_pid, libc.path);
local_libc = ProcMapsSoEntry.find_by_path (local_pid, libc.path);
assert (local_libc != null);
mmap_offset = (uint64) (uintptr) libc.find_export_by_name ("mmap") - local_libc.base_address;
munmap_offset = (uint64) (uintptr) libc.find_export_by_name ("munmap") - local_libc.base_address;
@@ -877,7 +877,7 @@ namespace Frida {
}
#if ANDROID
local_android_ld = ProcMapsEntry.find_by_path (local_pid, fallback_ld);
local_android_ld = ProcMapsSoEntry.find_by_path (local_pid, fallback_ld);
#endif
}
@@ -1084,8 +1084,18 @@ namespace Frida {
uint64 remote_mmap = 0;
uint64 remote_munmap = 0;
ProcMapsEntry? remote_libc = ProcMapsEntry.find_by_path (pid, local_libc.path);
ProcMapsSoEntry? remote_libc = ProcMapsSoEntry.find_by_path (pid, local_libc.path);
#if ANDROID
bool same_libc = false;
if (remote_libc != null) {
bool same_device = remote_libc.identity.split (":")[0] == local_libc.identity.split (":")[0];
bool same_inode = remote_libc.identity.split (" ")[1] == local_libc.identity.split (" ")[1];
bool same_path = remote_libc.path == local_libc.path;
same_libc = same_device && same_inode && same_path;
}
#else
bool same_libc = remote_libc != null && remote_libc.identity == local_libc.identity;
#endif
if (same_libc) {
remote_mmap = remote_libc.base_address + mmap_offset;
remote_munmap = remote_libc.base_address + munmap_offset;
@@ -1186,7 +1196,7 @@ namespace Frida {
result.context.libc = &result.libc;
if (result.context.rtld_flavor == ANDROID && result.libc.dlopen == null) {
ProcMapsEntry? remote_ld = ProcMapsEntry.find_by_address (pid, (uintptr) result.context.rtld_base);
ProcMapsSoEntry? remote_ld = ProcMapsSoEntry.find_by_address (pid, (uintptr) result.context.rtld_base);
bool same_ld = remote_ld != null && local_android_ld != null && remote_ld.identity == local_android_ld.identity;
if (!same_ld)
throw new Error.NOT_SUPPORTED ("Unable to locate Android dynamic linker; please file a bug");
@@ -1236,7 +1246,7 @@ namespace Frida {
}
}
private static void * rebase_pointer (uintptr local_ptr, ProcMapsEntry local_module, ProcMapsEntry remote_module) {
private static void * rebase_pointer (uintptr local_ptr, ProcMapsSoEntry local_module, ProcMapsSoEntry remote_module) {
var offset = local_ptr - local_module.base_address;
return (void *) (remote_module.base_address + offset);
}
@@ -1988,15 +1998,15 @@ namespace Frida {
#elif ARM
target_address &= ~1;
uint32 arm_breakpoint_val = 0xe7f001f0U;
uint16 thumb_breakpoint_val = 0xde01;
uint32 arm_breakpoint_val = (0xe7f001f0U).to_little_endian ();
uint16 thumb_breakpoint_val = ((uint16) 0xde01).to_little_endian ();
bool is_thumb = (target & 1) != 0;
if (is_thumb)
breakpoint_data = (uint8[]) &thumb_breakpoint_val;
else
breakpoint_data = (uint8[]) &arm_breakpoint_val;
#elif ARM64
uint32 breakpoint_val = 0xd4200000U;
uint32 breakpoint_val = (0xd4200000U).to_little_endian ();
breakpoint_data = (uint8[]) &breakpoint_val;
#elif MIPS
uint32 breakpoint_val = 0x0000000dU;
@@ -2280,6 +2290,9 @@ namespace Frida {
this.session = session;
Gum.Linux.enumerate_ranges ((Posix.pid_t) session.pid, READ | EXECUTE, d => {
unowned Gum.FileMapping? file = d.file;
if (file != null && file.path.has_prefix ("memfd:"))
return true;
if (d.range.size >= code.length) {
code_start = d.range.base_address + d.range.size - round_size_to_page_size (code.length);
code_end = code_start + code.length;
@@ -3306,46 +3319,87 @@ namespace Frida {
}
#endif
private class ProcMapsEntry {
private class ProcMapsSoEntry {
public uint64 base_address;
public string path;
public string identity;
private ProcMapsEntry (uint64 base_address, string path, string identity) {
private ProcMapsSoEntry (uint64 base_address, string path, string identity) {
this.base_address = base_address;
this.path = path;
this.identity = identity;
}
public static ProcMapsEntry? find_by_address (uint pid, uint64 address) {
public static ProcMapsSoEntry? find_by_address (uint pid, uint64 address) {
var iter = MapsIter.for_pid (pid);
while (iter.next ()) {
uint64 start = iter.start_address;
uint64 end = iter.end_address;
if (address >= start && address < end)
return new ProcMapsEntry (start, iter.path, iter.identity);
return new ProcMapsSoEntry (start, iter.path, iter.identity);
}
return null;
}
public static ProcMapsEntry? find_by_path (uint pid, string path) {
public static ProcMapsSoEntry? find_by_path (uint pid, string path) {
var candidates = new Gee.ArrayList<Candidate> ();
Candidate? latest_candidate = null;
var iter = MapsIter.for_pid (pid);
#if ANDROID
unowned string libc_path = Gum.Process.get_libc_module ().path;
#endif
while (iter.next ()) {
string candidate_path = iter.path;
if (candidate_path == path) {
string current_path = iter.path;
if (current_path == "[page size compat]")
continue;
if (current_path != path) {
latest_candidate = null;
continue;
}
string flags = iter.flags;
#if ANDROID
if (candidate_path == libc_path && iter.flags[3] == 's')
continue;
if (current_path == libc_path && flags[3] == 's')
continue;
#endif
return new ProcMapsEntry (iter.start_address, candidate_path, iter.identity);
if (iter.file_offset == 0) {
latest_candidate = new Candidate () {
entry = new ProcMapsSoEntry (iter.start_address, current_path, iter.identity),
total_ranges = 0,
executable_ranges = 0,
};
candidates.add (latest_candidate);
}
if (latest_candidate != null) {
latest_candidate.total_ranges++;
if (flags[2] == 'x')
latest_candidate.executable_ranges++;
}
}
return null;
candidates.sort ((a, b) => b.score () - a.score ());
if (candidates.is_empty)
return null;
return candidates.first ().entry;
}
private class Candidate {
public ProcMapsSoEntry entry;
public uint total_ranges;
public uint executable_ranges;
public int score () {
int result = (int) total_ranges;
if (executable_ranges == 0)
result = -result;
return result;
}
}
private class MapsIter {
@@ -3371,15 +3425,21 @@ namespace Frida {
}
}
public uint64 file_offset {
get {
return uint64.parse (info.fetch (4), 16);
}
}
public string identity {
owned get {
return info.fetch (4);
return info.fetch (5);
}
}
public string path {
owned get {
return info.fetch (5);
return info.fetch (6);
}
}
@@ -3394,7 +3454,7 @@ namespace Frida {
return;
}
if (!/^([0-9a-f]+)-([0-9a-f]+) (\S{4}) [0-9a-f]+ ([0-9a-f]{2,}:[0-9a-f]{2,} \d+) +([^\n]+)$/m.match (
if (!/^([0-9a-f]+)-([0-9a-f]+) (\S{4}) ([0-9a-f]+) ([0-9a-f]{2,}:[0-9a-f]{2,} \d+) +([^\n]+)$/m.match (
contents, 0, out info)) {
assert_not_reached ();
}
+6
View File
@@ -204,8 +204,14 @@ namespace Frida {
private async LinuxHelper obtain_for_64bit (Cancellable? cancellable) throws Error, IOError {
if (factory64 == null) {
var store = get_resource_store ();
#if !ARM64
/*
* If we are building for ARM64 and our pointer size is not 8-bytes, then we must be building for
* ILP32, so avoid the spurious error.
*/
if (sizeof (void *) != 8 && store.helper64 == null)
throw new Error.NOT_SUPPORTED ("Unable to handle 64-bit processes due to build configuration");
#endif
factory64 = new HelperFactory (store.helper64, store, main_context);
factory64.lost.connect (on_factory_lost);
factory64.output.connect (on_factory_output);
+1
View File
@@ -1,6 +1,7 @@
#ifndef __FRIDA_SYSCALL_H__
#define __FRIDA_SYSCALL_H__
#include <linux/unistd.h>
#include <sys/syscall.h>
#undef SYS_process_vm_readv
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -9,7 +9,7 @@
#endif
#ifndef __ELF_NATIVE_CLASS
# if defined (__x86_64__) || defined (__aarch64__) || defined (__mips64)
# if defined (__x86_64__) || (defined (__aarch64__) && !defined (__ILP32__)) || defined (__mips64)
# define __ELF_NATIVE_CLASS 64
# else
# define __ELF_NATIVE_CLASS 32
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+24 -16
View File
@@ -303,16 +303,20 @@ frida_receive_chunk (int sockfd, void * buffer, size_t length, const FridaLibcAp
.iov_base = cursor,
.iov_len = remaining
};
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &io,
.msg_iovlen = 1,
.msg_control = NULL,
.msg_controllen = 0,
};
struct msghdr msg;
ssize_t n;
/*
* Avoid inline initialization to prevent the compiler attempting to insert
* a call to memset.
*/
msg.msg_name = NULL,
msg.msg_namelen = 0,
msg.msg_iov = &io,
msg.msg_iovlen = 1,
msg.msg_control = NULL,
msg.msg_controllen = 0,
n = libc->recvmsg (sockfd, &msg, 0);
if (n <= 0)
return false;
@@ -334,14 +338,18 @@ frida_receive_fd (int sockfd, const FridaLibcApi * libc)
.iov_len = sizeof (dummy)
};
FridaControlMessage control;
struct msghdr msg = {
.msg_name = NULL,
.msg_namelen = 0,
.msg_iov = &io,
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof (control),
};
struct msghdr msg;
/*
* Avoid inline initialization to prevent the compiler attempting to insert
* a call to memset.
*/
msg.msg_name = NULL,
msg.msg_namelen = 0,
msg.msg_iov = &io,
msg.msg_iovlen = 1,
msg.msg_control = &control,
msg.msg_controllen = sizeof (control),
res = libc->recvmsg (sockfd, &msg, 0);
if (res == -1 || res == 0 || msg.msg_controllen == 0)
+23 -3
View File
@@ -197,20 +197,40 @@ diff --git a/tools/include/nolibc/std.h b/tools/include/nolibc/std.h
index 1747ae125..5f30e6e0c 100644
--- a/tools/include/nolibc/std.h
+++ b/tools/include/nolibc/std.h
@@ -25,25 +25,53 @@ typedef unsigned short uint16_t;
@@ -25,25 +25,71 @@ typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
-typedef unsigned long long uint64_t;
-typedef signed long long int64_t;
+#if defined(__x86_64__) || defined(__aarch64__) || defined(__mips64) || defined(__riscv)
+#if defined(__ILP32__)
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+#else
+typedef unsigned long uint64_t;
+typedef signed long int64_t;
+#endif
typedef unsigned long size_t;
typedef signed long ssize_t;
typedef unsigned long uintptr_t;
typedef signed long intptr_t;
-typedef unsigned long uintptr_t;
-typedef signed long intptr_t;
+#if defined(__ILP32__)
+typedef unsigned int uintptr_t;
+typedef signed int intptr_t;
+#else
+typedef unsigned long uintptr_t;
+typedef signed long intptr_t;
+#endif
typedef signed long ptrdiff_t;
+#elif defined(__arm__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+typedef unsigned int size_t;
+typedef signed int ssize_t;
+typedef unsigned long uintptr_t;
+typedef signed int intptr_t;
+typedef signed int ptrdiff_t;
+#else
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+15 -3
View File
@@ -48,7 +48,10 @@ ARCHS=(
x86
x86_64
arm
armbe8
arm64
arm64be
arm64beilp32
mips
mipsel
mips64
@@ -66,11 +69,14 @@ build_arch () {
exit 1
fi
if [[ "$ARCH" == arm* ]]; then
case "$ARCH" in
arm | arm64)
export FRIDA_HOST=android-$ARCH
else
;;
*)
export FRIDA_HOST=linux-$ARCH
fi
;;
esac
EXTRA_FLAGS=()
if [ "$FRIDA_HOST" == "linux-x86" ]; then
@@ -78,6 +84,12 @@ build_arch () {
export CC="gcc -m32" CXX="g++ -m32" STRIP="strip"
fi
case "$ARCH" in
armbe8 | arm64be | arm64beilp32)
EXTRA_FLAGS+=("--without-prebuilds=sdk:host")
;;
esac
cd "$FRIDA_CORE_DIR"
rm -rf "$BUILD_DIR"
+5
View File
@@ -319,6 +319,11 @@ if have_local_backend
else
host_lowlevel_abi = host_abi
endif
if host_lowlevel_abi == 'arm' and host_machine.endian() == 'big'
host_lowlevel_abi = 'armbe8'
elif host_lowlevel_abi == 'arm64' and host_machine.endian() == 'big'
host_lowlevel_abi = 'arm64be'
endif
fs = import('fs')
helper_backend_data = custom_target('frida-data-helper-backend',
input: [
+4 -1
View File
@@ -306,6 +306,7 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
{
gpointer code;
guint code_size;
GumModule * kernel32;
SIZE_T page_size, alloc_size;
DWORD old_protect;
@@ -316,7 +317,9 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
memset (rwc, 0, sizeof (FridaRemoteWorkerContext));
gum_module_enumerate_exports ("kernel32.dll", frida_remote_worker_context_collect_kernel32_export, rwc);
kernel32 = gum_process_find_module_by_name ("kernel32.dll");
gum_module_enumerate_exports (kernel32, frida_remote_worker_context_collect_kernel32_export, rwc);
g_object_unref (kernel32);
if (!frida_remote_worker_context_has_resolved_all_kernel32_functions (rwc))
goto failed_to_resolve_kernel32_functions;
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/frida-gum.git
revision = 16.6.2
revision = 16.7.2
depth = 1
[provide]
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/lwip.git
revision = 2b3efb7862866904f67dc369c9147f527a2982ca
revision = 267d9f082e9e38083b643421a93c03d4a7046fd0
depth = 1
[provide]
+18
View File
@@ -76,6 +76,20 @@ namespace Darwin.Xpc {
public uint64 get_value ();
}
[Compact]
[CCode (cname = "gpointer")]
public class Data : Object {
[CCode (cname = "XPC_TYPE_DATA")]
public static Type TYPE;
[CCode (cname = "xpc_data_create")]
public Data (uint8[] bytes);
public size_t get_length ();
public void * get_bytes_ptr ();
}
[Compact]
[CCode (cname = "gpointer")]
public class String : Object {
@@ -133,6 +147,10 @@ namespace Darwin.Xpc {
public uint64 get_uint64 (string key);
public void set_uint64 (string key, uint64 val);
[CCode (array_length_type = "size_t")]
public unowned uint8[]? get_data (string key);
public void set_data (string key, uint8[] bytes);
public unowned string? get_string (string key);
public void set_string (string key, string val);