Compare commits

...
29 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås 329c4075d0 subprojects: Prepare for release 2025-10-31 20:24:06 +01:00
Ole André Vadla Ravnås c4198498d5 subprojects: Bump outdated 2025-10-31 20:21:36 +01:00
Ole André Vadla Ravnås 20ef7bb318 simmy: Degrade functionality when SIP is enabled
As get_frontmost_application() and icons rely on being able to inject
our internal agent into SpringBoard.

Down the road we might be able to reimplement this functionality as a
helper program that we spawn inside the Simulator runtime. The uncertain
part is whether we'll be able to give that program the appropriate
entitlements.
2025-10-31 20:09:20 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 367f8cf22b simmy: Handle app enumeration with selected IDs
Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-30 14:58:35 +01:00
Ole André Vadla Ravnås 9f04675b87 subprojects: Prepare for release 2025-10-29 16:47:53 +01:00
Ole André Vadla Ravnås cd8bda0513 subprojects: Bump outdated 2025-10-29 16:45:40 +01:00
Ole André Vadla RavnåsandHåvard Sørbø e09e09fb85 simmy: Wire up application icons
Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-29 15:08:50 +01:00
Ole André Vadla RavnåsandHåvard Sørbø c24a5da82c simmy: Implement get_frontmost_application()
Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-29 15:01:15 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 05ec6563d1 internal-agent: Support non-local HostSession
So we can use it in the Simmy backend, too.

Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-28 14:12:01 +01:00
Ole André Vadla RavnåsandHåvard Sørbø 9dc9d30fa4 simmy: Fix the reported hardware.product
In query_system_parameters().

So it's e.g. `iPhone18,2` instead of `iPhone 17 Pro Max`.

Co-authored-by: Håvard Sørbø <[email protected]>
2025-10-28 13:15:38 +01:00
Ole André Vadla Ravnås 0b57936285 subprojects: Prepare for release 2025-10-24 15:25:15 +02:00
Ole André Vadla Ravnås 41be857a91 subprojects: Bump outdated 2025-10-24 15:23:37 +02:00
Ole André Vadla Ravnås afa847b898 submodules: Bump releng 2025-10-24 15:23:37 +02:00
Ole André Vadla Ravnås 3e43478de0 android: Bump frida-java-bridge in system-server 2025-10-24 15:22:18 +02:00
Ole André Vadla Ravnås a847a012c9 freebsd: Wire up PTY support 2025-10-12 08:11:13 +02:00
Ole André Vadla Ravnås c34fad1ff0 freebsd: Port remaining bits to the new stdio API 2025-10-12 07:50:00 +02:00
Ole André Vadla Ravnås ceef1d696b darwin: Fix a stylistic inconsistency 2025-10-12 07:47:09 +02:00
Ole André Vadla Ravnås d0cfe69dba subprojects: Prepare for release 2025-10-11 19:22:49 +02:00
Ole André Vadla Ravnås a35aff1865 api: Exclude HostSessionHub from .gir
As it's an internal interface.
2025-10-11 11:46:28 +02:00
Ole André Vadla Ravnås be8a59607a android: Bump frida-java-bridge in system-server 2025-10-11 11:07:51 +02:00
Ole André Vadla Ravnås 726740f96a simmy: Move to CoreSimulator.framework 2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås 9e11d57a76 Add new Simmy backend
For interacting with Apple's simulators on macOS.
2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås d8c3c9bfb5 darwin: Support early instrumentation of dyld_sim 2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås 2720968791 base: Add StdioPipes and FileDescriptor helpers
Factored out of the Darwin and Linux backends.
2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås b8998e52cd host-session: Support cross-backend communication 2025-10-10 12:34:41 +02:00
Ole André Vadla Ravnås c23f046968 value: Add VariantReader.list_members() 2025-10-10 12:34:41 +02:00
Francesco Tamagni 2d521bbeb7 fruity: Unpair on InvalidHostID
So the error doesn't become persistent, and the
device can be paired again.
2025-10-07 11:37:57 +02:00
Ole André Vadla Ravnås cdae790ba9 darwin: Port to the latest Gum.Darwin API 2025-10-01 10:52:33 +02:00
Ole André Vadla Ravnås 90d2d93799 subprojects: Bump outdated 2025-10-01 10:49:57 +02:00
37 changed files with 2250 additions and 437 deletions

No files matched your search

-23
View File
@@ -11,29 +11,6 @@ namespace Frida {
return (linux_major == major && linux_minor >= minor) || linux_major > major;
}
public class FileDescriptor : Object, FileDescriptorBased {
public int handle;
public FileDescriptor (int handle) {
this.handle = handle;
}
~FileDescriptor () {
if (handle != -1)
Posix.close (handle);
}
public int steal () {
int result = handle;
handle = -1;
return result;
}
public int get_fd () {
return handle;
}
}
public sealed class PidFileDescriptor : FileDescriptor {
private uint pid;
+12 -1
View File
@@ -19,6 +19,13 @@ base_sources = [
extra_vala_args = []
if host_os == 'windows'
base_sources += 'windows.vala'
else
base_sources += 'posix.vala'
extra_vala_args += '--pkg=posix'
endif
if host_os == 'macos'
base_sources += [
'xpc.vala',
@@ -28,6 +35,7 @@ endif
if host_os_family == 'darwin'
base_sources += 'corefoundation.vala'
extra_vala_args += '--pkg=darwin-xnu'
endif
if host_os_family == 'linux'
@@ -35,10 +43,13 @@ if host_os_family == 'linux'
extra_vala_args += [
'--pkg=linux',
'--pkg=linux-system',
'--pkg=posix',
]
endif
if host_os_family == 'freebsd'
extra_vala_args += '--pkg=freebsd'
endif
extra_deps = []
if host_os_family != 'windows'
extra_vala_args += '--pkg=gio-unix-2.0'
+153
View File
@@ -0,0 +1,153 @@
namespace Frida {
public sealed class StdioPipes : Object {
public OutputStream? input {
get;
construct;
}
public InputStream output {
get;
construct;
}
public InputStream error {
get;
construct;
}
public StdioPipes (FileDescriptor? input, FileDescriptor output, FileDescriptor error) {
Object (
input: (input != null) ? new UnixOutputStream (ensure_nonblocking (input.steal ()), true) : null,
output: new UnixInputStream (ensure_nonblocking (output.steal ()), true),
error: new UnixInputStream (ensure_nonblocking (error.steal ()), true)
);
}
private static int ensure_nonblocking (int fd) {
try {
Unix.set_fd_nonblocking (fd, true);
return fd;
} catch (GLib.Error e) {
assert_not_reached ();
}
}
}
public class FileDescriptor : Object, FileDescriptorBased {
public int handle;
public FileDescriptor (int handle) {
this.handle = handle;
}
~FileDescriptor () {
if (handle != -1)
Posix.close (handle);
}
public int steal () {
int result = handle;
handle = -1;
return result;
}
public int get_fd () {
return handle;
}
}
public StdioPipes? make_stdio_pipes (Stdio stdio, bool in_supported, out FileDescriptor? in_fd, out string? in_name,
out FileDescriptor? out_fd, out string? out_name, out FileDescriptor? err_fd, out string? err_name) throws Error {
if (stdio == PIPE) {
FileDescriptor? in_write = null;
FileDescriptor? out_read, err_read;
if (in_supported) {
make_stdio_pipe (out in_fd, out in_write, out in_name);
} else {
in_fd = null;
in_name = null;
}
make_stdio_pipe (out out_read, out out_fd, out out_name);
make_stdio_pipe (out err_read, out err_fd, out err_name);
return new StdioPipes (in_write, out_read, err_read);
} else {
in_fd = null;
in_name = null;
out_fd = null;
out_name = null;
err_fd = null;
err_name = null;
return null;
}
}
public void make_stdio_pipe (out FileDescriptor read, out FileDescriptor write, out string? name = null) throws Error {
#if HAVE_OPENPTY
int rfd = -1, wfd = -1;
char buf[Posix.Limits.PATH_MAX];
var res =
#if DARWIN
Darwin.XNU.openpty (out rfd, out wfd, buf, null, null)
#elif LINUX
Linux.openpty (out rfd, out wfd, buf, null, null)
#elif FREEBSD
FreeBSD.openpty (out rfd, out wfd, buf, null, null)
#endif
;
if (res == -1)
throw new Error.NOT_SUPPORTED ("Unable to open PTY: %s", strerror (errno));
name = (string) buf;
enable_close_on_exec (rfd);
enable_close_on_exec (wfd);
disable_sigpipe (rfd);
disable_sigpipe (wfd);
configure_terminal_attributes (rfd);
read = new FileDescriptor (rfd);
write = new FileDescriptor (wfd);
#else
name = null;
try {
int fds[2];
Unix.open_pipe (fds, Posix.FD_CLOEXEC);
read = new FileDescriptor (fds[0]);
write = new FileDescriptor (fds[1]);
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("Unable to open pipe: %s", e.message);
}
#endif
}
#if HAVE_OPENPTY
private void enable_close_on_exec (int fd) {
Posix.fcntl (fd, Posix.F_SETFD, Posix.fcntl (fd, Posix.F_GETFD) | Posix.FD_CLOEXEC);
}
private void disable_sigpipe (int fd) {
#if DARWIN
Posix.fcntl (fd, Darwin.XNU.F_SETNOSIGPIPE, true);
#endif
}
private void configure_terminal_attributes (int fd) {
var tios = Posix.termios ();
Posix.tcgetattr (fd, out tios);
tios.c_oflag &= ~Posix.ONLCR;
tios.c_cflag = (tios.c_cflag & Posix.CLOCAL) | Posix.CS8 | Posix.CREAD | Posix.HUPCL;
tios.c_lflag &= ~Posix.ECHO;
Posix.tcsetattr (fd, 0, tios);
}
#endif
}
+16
View File
@@ -57,6 +57,22 @@ namespace Frida {
push_scope (v);
}
public string[] list_members () throws Error {
var scope = peek_scope ();
if (scope.dict == null)
throw new Error.PROTOCOL ("Dictionary expected, but at %s", scope.val.print (true));
var members = new Gee.ArrayList<string> ();
foreach (var item in scope.val) {
string k;
Variant v;
item.get ("{sv}", out k, out v);
members.add (k);
}
return members.to_array ();
}
public bool has_member (string name) throws Error {
var scope = peek_scope ();
if (scope.dict == null)
+22
View File
@@ -0,0 +1,22 @@
namespace Frida {
public sealed class StdioPipes : Object {
public OutputStream input {
get;
construct;
}
public InputStream output {
get;
construct;
}
public InputStream error {
get;
construct;
}
public StdioPipes (OutputStream input, InputStream output, InputStream error) {
Object (input: input, output: output, error: error);
}
}
}
+10 -1
View File
@@ -403,6 +403,10 @@ else
endif
have_local_backend = get_option('local_backend').allowed()
have_simmy_backend = get_option('simmy_backend') \
.disable_auto_if(host_os != 'macos') \
.require(host_os == 'macos', error_message: 'Simmy backend is only available on macOS') \
.allowed()
have_fruity_backend = get_option('fruity_backend') \
.disable_auto_if(not host_docks_mobile_devices) \
.allowed()
@@ -491,6 +495,7 @@ build_tests = get_option('tests') \
.allowed()
foreach b : [['local', have_local_backend],
['simmy', have_simmy_backend],
['fruity', have_fruity_backend],
['droidy', have_droidy_backend],
['socket', have_socket_backend],
@@ -581,7 +586,7 @@ if lzfse_dep.found()
backend_reqs_private += 'lzfse'
endif
if host_os_family == 'linux' and cc.has_function('openpty', prefix: '#include <pty.h>')
if host_os_family in ['darwin', 'freebsd'] or (host_os_family == 'linux' and cc.has_function('openpty', prefix: '#include <pty.h>'))
vala_flags += '--define=HAVE_OPENPTY'
endif
@@ -653,6 +658,9 @@ endif
if host_os in ['ios', 'tvos']
backend_libs_private += ['-Wl,-framework,CoreGraphics', '-Wl,-framework,UIKit']
endif
if host_os_family == 'freebsd'
backend_libs_private += ['-lutil']
endif
if get_option('devkits').length() != 0
if get_option('default_library') != 'static'
@@ -786,6 +794,7 @@ endif
summary(
{
'local': have_local_backend,
'simmy': have_simmy_backend,
'fruity': have_fruity_backend,
'droidy': have_droidy_backend,
'socket': have_socket_backend,
+6
View File
@@ -10,6 +10,12 @@ option('local_backend',
description: 'Include backend for the local system',
)
option('simmy_backend',
type: 'feature',
value: 'auto',
description: 'Include the iDevice Simulator backend',
)
option('fruity_backend',
type: 'feature',
value: 'auto',
+1 -1
Submodule releng updated: c24becede0...800f2385c3.
+3 -1
View File
@@ -238,7 +238,7 @@ def emit_gir(api: ApiSpec, core_gir: str, base_gir: str, output_dir: Path) -> st
for elem in core_elements + base_elements:
if tag_name == "class":
for child in list(elem):
if (child.tag == CORE_TAG_IMPLEMENTS and child.get("name") == "FridaBase.AgentMessageSink") \
if (child.tag == CORE_TAG_IMPLEMENTS and child.get("name") in {"Frida.HostSessionHub", "FridaBase.AgentMessageSink"}) \
or child.tag == CORE_TAG_FIELD \
or child.get("name").startswith("_"):
elem.remove(child)
@@ -512,6 +512,8 @@ def function_is_public(name):
"parse_socket_address",
"negotiate_connection",
"check_kernel_version",
"make_stdio_pipes",
"make_stdio_pipe",
]
def parse_vala_object_types(source) -> List[ApiObjectType]:
+2 -1
View File
@@ -40,7 +40,8 @@ namespace Frida {
}
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
+13 -12
View File
@@ -845,8 +845,8 @@ namespace Frida {
construct;
}
public LaunchdAgent (DarwinHostSession host_session, Cancellable io_cancellable) {
Object (host_session: host_session, io_cancellable: io_cancellable);
public LaunchdAgent (HostSessionConnection connection, Cancellable io_cancellable) {
Object (connection: connection, io_cancellable: io_cancellable);
}
construct {
@@ -905,7 +905,7 @@ namespace Frida {
try {
if (path == XPC_PROXY_PATH) {
var agent = new XpcProxyAgent ((DarwinHostSession) host_session, identifier, pid);
var agent = new XpcProxyAgent (connection, identifier, pid);
yield agent.run_until_exec (io_cancellable);
}
@@ -921,7 +921,7 @@ namespace Frida {
try {
if (path == XPC_PROXY_PATH) {
var agent = new XpcProxyAgent ((DarwinHostSession) host_session, identifier, pid);
var agent = new XpcProxyAgent (connection, identifier, pid);
yield agent.run_until_exec (io_cancellable);
}
@@ -937,7 +937,8 @@ namespace Frida {
protected override async string? load_source (Cancellable? cancellable) throws Error, IOError {
string * raw_source = Frida.Data.Darwin.get_launchd_js_blob ().data;
return raw_source->replace ("@REPORT_CRASHES@", ((DarwinHostSession) host_session).report_crashes.to_string ());
return raw_source->replace ("@REPORT_CRASHES@",
((DarwinHostSession) connection.host_session).report_crashes.to_string ());
}
}
@@ -952,8 +953,8 @@ namespace Frida {
construct;
}
public XpcProxyAgent (DarwinHostSession host_session, string identifier, uint pid) {
Object (host_session: host_session, identifier: identifier, pid: pid);
public XpcProxyAgent (HostSessionConnection connection, string identifier, uint pid) {
Object (connection: connection, identifier: identifier, pid: pid);
}
construct {
@@ -965,7 +966,7 @@ namespace Frida {
public async void run_until_exec (Cancellable? cancellable) throws Error, IOError {
yield ensure_loaded (cancellable);
var helper = ((DarwinHostSession) host_session).helper;
var helper = ((DarwinHostSession) connection.host_session).helper;
yield helper.resume (pid, cancellable);
yield wait_for_unload (cancellable);
@@ -1002,10 +1003,10 @@ namespace Frida {
construct;
}
public ReportCrashAgent (DarwinHostSession host_session, uint pid, MappedAgentContainer mapped_agent_container,
public ReportCrashAgent (HostSessionConnection connection, uint pid, MappedAgentContainer mapped_agent_container,
Cancellable io_cancellable) {
Object (
host_session: host_session,
connection: connection,
pid: pid,
mapped_agent_container: mapped_agent_container,
io_cancellable: io_cancellable
@@ -1226,9 +1227,9 @@ namespace Frida {
construct;
}
public OSAnalyticsAgent (DarwinHostSession host_session, uint pid, Cancellable io_cancellable) {
public OSAnalyticsAgent (HostSessionConnection connection, uint pid, Cancellable io_cancellable) {
Object (
host_session: host_session,
connection: connection,
pid: pid,
io_cancellable: io_cancellable
);
+52 -93
View File
@@ -30,7 +30,6 @@
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include <util.h>
#ifndef _POSIX_SPAWN_DISABLE_ASLR
# define _POSIX_SPAWN_DISABLE_ASLR 0x0100
@@ -181,6 +180,7 @@ struct _FridaSpawnInstance
mach_vm_address_t dyld_data;
GumAddress modern_entry_address;
GumAddress sim_notify_address;
/* V4+ */
GumAddress notify_objc_init;
@@ -405,9 +405,6 @@ static void frida_spawn_instance_disable_nth_breakpoint (FridaSpawnInstance * se
static guint32 frida_spawn_instance_put_software_breakpoint (FridaSpawnInstance * self, GumAddress where, guint index);
static guint32 frida_spawn_instance_overwrite_arm64_instruction (FridaSpawnInstance * self, GumAddress address, guint32 new_instruction);
static void frida_make_pty (int fds[2]);
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_close (FridaInjectInstance * instance);
@@ -804,12 +801,17 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
posix_spawnattr_t attributes;
sigset_t signal_mask_set;
short flags;
int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
FridaFileDescriptor * in_fd = NULL;
FridaFileDescriptor * out_fd = NULL;
FridaFileDescriptor * err_fd = NULL;
GError * stdio_error = NULL;
FridaAslr aslr = FRIDA_ASLR_AUTO;
GVariant * aslr_value;
gchar * old_cwd = NULL;
int result, spawn_errno;
*pipes = NULL;
instance = frida_spawn_instance_new (self);
argv = frida_host_spawn_options_compute_argv (options, path, NULL);
@@ -823,33 +825,13 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
flags = POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_START_SUSPENDED;
switch (options->stdio)
{
case FRIDA_STDIO_INHERIT:
*pipes = NULL;
*pipes = frida_make_stdio_pipes (options->stdio, TRUE, &in_fd, NULL, &out_fd, NULL, &err_fd, NULL, &stdio_error);
if (stdio_error != NULL)
goto propagate_stdio_error;
posix_spawn_file_actions_adddup2 (&file_actions, 0, 0);
posix_spawn_file_actions_adddup2 (&file_actions, 1, 1);
posix_spawn_file_actions_adddup2 (&file_actions, 2, 2);
break;
case FRIDA_STDIO_PIPE:
frida_make_pty (stdin_pipe);
frida_make_pty (stdout_pipe);
frida_make_pty (stderr_pipe);
*pipes = frida_stdio_pipes_new (stdin_pipe[1], stdout_pipe[0], stderr_pipe[0]);
posix_spawn_file_actions_adddup2 (&file_actions, stdin_pipe[0], 0);
posix_spawn_file_actions_adddup2 (&file_actions, stdout_pipe[1], 1);
posix_spawn_file_actions_adddup2 (&file_actions, stderr_pipe[1], 2);
break;
default:
g_assert_not_reached ();
}
posix_spawn_file_actions_adddup2 (&file_actions, (in_fd != NULL) ? in_fd->handle : 0, 0);
posix_spawn_file_actions_adddup2 (&file_actions, (out_fd != NULL) ? out_fd->handle : 1, 1);
posix_spawn_file_actions_adddup2 (&file_actions, (err_fd != NULL) ? err_fd->handle : 2, 2);
aslr_value = g_hash_table_lookup (options->aux, "aslr");
if (aslr_value != NULL && !frida_parse_aslr_option (aslr_value, &aslr, error))
@@ -872,13 +854,6 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
if (old_cwd != NULL)
chdir (old_cwd);
if (options->stdio == FRIDA_STDIO_PIPE)
{
close (stdin_pipe[0]);
close (stdout_pipe[1]);
close (stderr_pipe[1]);
}
posix_spawnattr_destroy (&attributes);
posix_spawn_file_actions_destroy (&file_actions);
@@ -892,6 +867,11 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
goto beach;
propagate_stdio_error:
{
g_propagate_error (error, stdio_error);
goto early_failure;
}
chdir_failed:
{
g_set_error (error,
@@ -936,12 +916,18 @@ any_failure:
kill (instance->pid, SIGKILL);
frida_spawn_instance_close (instance);
g_clear_object (pipes);
pid = 0;
goto beach;
}
beach:
{
g_clear_object (&in_fd);
g_clear_object (&out_fd);
g_clear_object (&err_fd);
g_free (old_cwd);
g_strfreev (envp);
g_strfreev (argv);
@@ -1057,6 +1043,8 @@ frida_darwin_helper_backend_launch_using_fbs (NSString * identifier, NSURL * url
FridaSpringboardApi * api;
NSMutableDictionary * debug_options, * open_options;
FridaStdioPipes * pipes = NULL;
gchar * stdout_name = NULL;
gchar * stderr_name = NULL;
GError * error = NULL;
FridaAslr aslr = FRIDA_ASLR_AUTO;
GVariant * aslr_value;
@@ -1092,22 +1080,12 @@ frida_darwin_helper_backend_launch_using_fbs (NSString * identifier, NSURL * url
if (strlen (spawn_options->cwd) > 0)
goto cwd_not_supported;
pipes = frida_make_stdio_pipes (spawn_options->stdio, FALSE, NULL, NULL, NULL, &stdout_name, NULL, &stderr_name, &error);
if (error != NULL)
goto failure;
if (spawn_options->stdio == FRIDA_STDIO_PIPE)
{
gint stdout_master, stdout_slave, stderr_master, stderr_slave;
gchar stdout_name[PATH_MAX], stderr_name[PATH_MAX];
openpty (&stdout_master, &stdout_slave, stdout_name, NULL, NULL);
openpty (&stderr_master, &stderr_slave, stderr_name, NULL, NULL);
pipes = frida_stdio_pipes_new (-1, stdout_master, stderr_master);
frida_configure_terminal_attributes (stdout_master);
frida_configure_terminal_attributes (stderr_master);
frida_stdio_pipes_retain (pipes, stdout_slave);
frida_stdio_pipes_retain (pipes, stderr_slave);
chmod (stdout_name, 0666);
chmod (stderr_name, 0666);
@@ -1174,7 +1152,7 @@ frida_darwin_helper_backend_launch_using_fbs (NSString * identifier, NSURL * url
}
});
return;
goto beach;
envp_not_supported:
{
@@ -1195,6 +1173,16 @@ cwd_not_supported:
failure:
{
on_complete (NULL, error, on_complete_target);
g_clear_object (&pipes);
goto beach;
}
beach:
{
g_free (stdout_name);
g_free (stderr_name);
return;
}
}
@@ -1881,7 +1869,7 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
mach_msg_type_number_t state_count = GUM_DARWIN_THREAD_STATE_COUNT;
thread_state_flavor_t state_flavor = GUM_DARWIN_THREAD_STATE_FLAVOR;
GumAddress dyld_start, dyld_granularity, dyld_chunk, dyld_header;
GumAddress modern_entry_address, legacy_entry_address;
GumAddress modern_entry_address, legacy_entry_address, sim_notify_address;
const gchar * launch_with_closure_names[] = {
"__ZN4dyldL17launchWithClosureEPKN5dyld37closure13LaunchClosureEPK15DyldSharedCachePKNS0_11MachOLoadedEmiPPKcSD_SD_R11DiagnosticsPmSG_PbSH_",
"__ZN4dyldL17launchWithClosureEPKN5dyld312launch_cache13binary_format7ClosureEPK15DyldSharedCachePK11mach_headermiPPKcSE_SE_PmSF_",
@@ -2101,6 +2089,9 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
instance->ret_gadget |= 1;
}
sim_notify_address = gum_darwin_module_resolve_symbol_address (dyld, "__ZN5dyld4L29sim_notifyMonitorOfMainCalledEv");
instance->sim_notify_address = sim_notify_address;
instance->ret_state = FRIDA_RET_FROM_HELPER;
kr = frida_get_debug_state (child_thread, &instance->previous_debug_state, instance->cpu_type);
@@ -2112,6 +2103,8 @@ _frida_darwin_helper_backend_prepare_spawn_instance_for_injection (FridaDarwinHe
frida_spawn_instance_set_nth_breakpoint (instance, i++, legacy_entry_address, FRIDA_BREAKPOINT_REPEAT_ALWAYS);
if (modern_entry_address != 0)
frida_spawn_instance_set_nth_breakpoint (instance, i++, modern_entry_address, FRIDA_BREAKPOINT_REPEAT_ALWAYS);
if (sim_notify_address != 0)
frida_spawn_instance_set_nth_breakpoint (instance, i++, sim_notify_address, FRIDA_BREAKPOINT_REPEAT_ALWAYS);
if (instance->dyld_flavor == FRIDA_DYLD_V4_PLUS)
{
GumAddress restart_with_dyld_in_cache = gum_darwin_module_resolve_symbol_address (dyld,
@@ -3104,7 +3097,11 @@ frida_spawn_instance_handle_breakpoint (FridaSpawnInstance * self, FridaBreakpoi
if (self->breakpoint_phase == FRIDA_BREAKPOINT_DETECT_FLAVOR)
{
if (self->dyld_flavor == FRIDA_DYLD_V4_PLUS)
if (pc == self->sim_notify_address)
{
self->breakpoint_phase = FRIDA_BREAKPOINT_LIBSYSTEM_INITIALIZED;
}
else if (self->dyld_flavor == FRIDA_DYLD_V4_PLUS)
{
if (pc == self->modern_entry_address)
{
@@ -4065,44 +4062,6 @@ frida_spawn_instance_overwrite_arm64_instruction (FridaSpawnInstance * self, Gum
return original_instruction;
}
static void
frida_make_pty (int fds[2])
{
gboolean pipe_opened;
int i;
pipe_opened = openpty (&fds[0], &fds[1], NULL, NULL, NULL) != -1;
g_assert (pipe_opened);
for (i = 0; i != 2; i++)
{
const int fd = fds[i];
int res;
res = fcntl (fd, F_SETFD, fcntl (fd, F_GETFD) | FD_CLOEXEC);
g_assert (res == 0);
res = fcntl (fd, F_SETNOSIGPIPE, TRUE);
g_assert (res == 0);
}
frida_configure_terminal_attributes (fds[0]);
}
static void
frida_configure_terminal_attributes (gint fd)
{
struct termios tios;
tcgetattr (fd, &tios);
tios.c_oflag &= ~ONLCR;
tios.c_cflag = (tios.c_cflag & CLOCAL) | CS8 | CREAD | HUPCL;
tios.c_lflag &= ~ECHO;
tcsetattr (fd, 0, &tios);
}
static FridaInjectInstance *
frida_inject_instance_new (FridaDarwinHelperBackend * backend, guint id, guint pid)
{
+10 -62
View File
@@ -136,9 +136,9 @@ namespace Frida {
ChildWatch.add ((Pid) child_pid, on_child_dead);
if (pipes != null) {
stdin_streams[child_pid] = new UnixOutputStream (pipes.input, false);
process_next_output_from.begin (new UnixInputStream (pipes.output, false), child_pid, 1, pipes);
process_next_output_from.begin (new UnixInputStream (pipes.error, false), child_pid, 2, pipes);
stdin_streams[child_pid] = pipes.input;
process_next_output_from.begin (pipes.output, child_pid, 1, pipes);
process_next_output_from.begin (pipes.error, child_pid, 2, pipes);
}
return child_pid;
@@ -180,10 +180,8 @@ namespace Frida {
var pipes = pending.pipes;
if (pipes != null) {
pipes.clear_retained ();
process_next_output_from.begin (new UnixInputStream (pipes.output, false), pid, 1, pipes);
process_next_output_from.begin (new UnixInputStream (pipes.error, false), pid, 2, pipes);
process_next_output_from.begin (pipes.output, pid, 1, pipes);
process_next_output_from.begin (pipes.error, pid, 2, pipes);
}
}
@@ -439,8 +437,11 @@ namespace Frida {
private bool is_booting (uint task) throws Error {
Gum.Darwin.AllImageInfos infos;
if (!Gum.Darwin.query_all_image_infos (task, out infos))
throw new Error.PROCESS_NOT_FOUND ("Target process died unexpectedly");
try {
Gum.Darwin.query_all_image_infos (task, out infos);
} catch (Gum.Error e) {
throw new Error.PROCESS_NOT_FOUND ("%s", e.message);
}
return !infos.libsystem_initialized;
}
@@ -888,57 +889,4 @@ namespace Frida {
return false;
}
}
public sealed class StdioPipes : Object {
public int input {
get;
construct;
}
public int output {
get;
construct;
}
public int error {
get;
construct;
}
private int[] retained = {};
public StdioPipes (int input, int output, int error) {
Object (input: input, output: output, error: error);
}
construct {
try {
if (input != -1)
Unix.set_fd_nonblocking (input, true);
Unix.set_fd_nonblocking (output, true);
Unix.set_fd_nonblocking (error, true);
} catch (GLib.Error e) {
assert_not_reached ();
}
}
~StdioPipes () {
clear_retained ();
if (input != -1)
Posix.close (input);
Posix.close (output);
Posix.close (error);
}
public void clear_retained () {
foreach (var fd in retained)
Posix.close (fd);
retained = {};
}
public void retain (int fd) {
retained += fd;
}
}
}
+2 -1
View File
@@ -130,7 +130,8 @@ namespace Frida {
public async void close (Cancellable? cancellable) throws IOError {
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_ARGUMENT ("Already created");
+19 -37
View File
@@ -172,8 +172,6 @@ static gboolean frida_exec_instance_try_perform_transition (FridaExecInstance *
static void frida_exec_instance_suspend (FridaExecInstance * self);
static void frida_exec_instance_resume (FridaExecInstance * self);
static void frida_make_pipe (int fds[2]);
static FridaInjectInstance * frida_inject_instance_new (FridaBinjector * binjector, guint id, guint pid, const FridaRemoteApi * api,
const gchar * temp_path);
static void frida_inject_instance_recreate_fifo (FridaInjectInstance * self);
@@ -211,7 +209,10 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
{
FridaSpawnInstance * instance;
gchar ** argv, ** envp;
int stdin_pipe[2], stdout_pipe[2], stderr_pipe[2];
FridaFileDescriptor * in_fd = NULL;
FridaFileDescriptor * out_fd = NULL;
FridaFileDescriptor * err_fd = NULL;
GError * stdio_error = NULL;
gchar * old_cwd = NULL;
gboolean success;
const gchar * failed_operation;
@@ -221,24 +222,9 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
argv = frida_host_spawn_options_compute_argv (options, path, NULL);
envp = frida_host_spawn_options_compute_envp (options, NULL);
switch (options->stdio)
{
case FRIDA_STDIO_INHERIT:
*pipes = NULL;
break;
case FRIDA_STDIO_PIPE:
frida_make_pipe (stdin_pipe);
frida_make_pipe (stdout_pipe);
frida_make_pipe (stderr_pipe);
*pipes = frida_stdio_pipes_new (stdin_pipe[1], stdout_pipe[0], stderr_pipe[0]);
break;
default:
g_assert_not_reached ();
}
*pipes = frida_make_stdio_pipes (options->stdio, TRUE, &in_fd, NULL, &out_fd, NULL, &err_fd, NULL, &stdio_error);
if (stdio_error != NULL)
goto propagate_stdio_error;
if (strlen (options->cwd) > 0)
{
@@ -254,9 +240,9 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
if (options->stdio == FRIDA_STDIO_PIPE)
{
dup2 (stdin_pipe[0], 0);
dup2 (stdout_pipe[1], 1);
dup2 (stderr_pipe[1], 2);
dup2 (in_fd->handle, 0);
dup2 (out_fd->handle, 1);
dup2 (err_fd->handle, 2);
}
ptrace (PT_TRACE_ME, 0, NULL, 0);
@@ -273,13 +259,6 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
g_warning ("Failed to restore working directory");
}
if (options->stdio == FRIDA_STDIO_PIPE)
{
close (stdin_pipe[0]);
close (stdout_pipe[1]);
close (stderr_pipe[1]);
}
success = frida_wait_for_child_signal (instance->pid, SIGTRAP, NULL);
CHECK_OS_RESULT (success, !=, FALSE, "wait(SIGTRAP)");
@@ -290,6 +269,11 @@ _frida_binjector_do_spawn (FridaBinjector * self, const gchar * path, FridaHostS
goto beach;
propagate_stdio_error:
{
g_propagate_error (error, stdio_error);
goto failure;
}
chdir_failed:
{
g_set_error (error,
@@ -316,6 +300,10 @@ failure:
}
beach:
{
g_clear_object (&in_fd);
g_clear_object (&out_fd);
g_clear_object (&err_fd);
g_free (old_cwd);
g_strfreev (envp);
g_strfreev (argv);
@@ -736,12 +724,6 @@ frida_exec_instance_resume (FridaExecInstance * self)
ptrace (PT_DETACH, self->pid, NULL, 0);
}
static void
frida_make_pipe (int fds[2])
{
g_unix_open_pipe (fds, FD_CLOEXEC, NULL);
}
static FridaInjectInstance *
frida_inject_instance_new (FridaBinjector * binjector, guint id, guint pid, const FridaRemoteApi * api, const gchar * temp_path)
{
+3 -40
View File
@@ -68,9 +68,9 @@ namespace Frida {
monitor_child (child_pid);
if (pipes != null) {
stdin_streams[child_pid] = new UnixOutputStream (pipes.input, false);
process_next_output_from.begin (new UnixInputStream (pipes.output, false), child_pid, 1, pipes);
process_next_output_from.begin (new UnixInputStream (pipes.error, false), child_pid, 2, pipes);
stdin_streams[child_pid] = pipes.input;
process_next_output_from.begin (pipes.output, child_pid, 1, pipes);
process_next_output_from.begin (pipes.error, child_pid, 2, pipes);
}
return child_pid;
@@ -696,41 +696,4 @@ namespace Frida {
protected enum ProgressMessageType {
HELLO = 0xff
}
protected class StdioPipes : Object {
public int input {
get;
construct;
}
public int output {
get;
construct;
}
public int error {
get;
construct;
}
public StdioPipes (int input, int output, int error) {
Object (input: input, output: output, error: error);
}
construct {
try {
Unix.set_fd_nonblocking (input, true);
Unix.set_fd_nonblocking (output, true);
Unix.set_fd_nonblocking (error, true);
} catch (GLib.Error e) {
assert_not_reached ();
}
}
~StdioPipes () {
Posix.close (input);
Posix.close (output);
Posix.close (error);
}
}
}
+8 -2
View File
@@ -24,7 +24,7 @@ namespace Frida {
}
}
public sealed class DeviceManager : Object {
public sealed class DeviceManager : Object, HostSessionHub {
public signal void added (Device device);
public signal void removed (Device device);
public signal void changed ();
@@ -394,6 +394,12 @@ namespace Frida {
changed ();
}
private async HostSessionEntry resolve_host_session (string id, Cancellable? cancellable) throws Error, IOError {
var device = yield get_device_by_id (id, 0, cancellable);
var session = yield device.get_host_session (cancellable);
return new HostSessionEntry (device.provider, session);
}
private void check_open () throws Error {
if (stop_request != null)
throw new Error.INVALID_OPERATION ("Device manager is closed");
@@ -1326,7 +1332,7 @@ namespace Frida {
host_session_request = new Promise<HostSession> ();
try {
var session = yield provider.create (host_session_options, cancellable);
var session = yield provider.create (manager, host_session_options, cancellable);
attach_host_session (session);
current_host_session = session;
+2 -1
View File
@@ -80,7 +80,8 @@ namespace Frida {
yield Fruity.DTXConnection.close_all (device, cancellable);
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
+6 -2
View File
@@ -88,8 +88,12 @@ namespace Frida.Fruity {
request.set_string ("SystemBUID", system_buid);
var response = yield service.query (request, cancellable);
if (response.has ("Error"))
throw new LockdownError.PROTOCOL ("Unexpected response: %s", response.get_string ("Error"));
if (response.has ("Error")) {
var error = response.get_string ("Error");
if (error == "InvalidHostID")
yield unpair (cancellable);
throw new LockdownError.PROTOCOL ("Unexpected response: %s", error);
}
if (response.get_boolean ("EnableSessionSSL"))
service.stream = yield start_tls (service.stream, cancellable);
+55 -11
View File
@@ -47,6 +47,9 @@ namespace Frida {
}
private void add_nonlocal_backends () {
#if HAVE_SIMMY_BACKEND
add_backend (new SimmyHostSessionBackend ());
#endif
#if !IOS && !ANDROID && !TVOS
#if HAVE_FRUITY_BACKEND
add_backend (new FruityHostSessionBackend ());
@@ -161,7 +164,7 @@ namespace Frida {
get;
}
public abstract async HostSession create (HostSessionOptions? options = null,
public abstract async HostSession create (HostSessionHub hub, HostSessionOptions? options = null,
Cancellable? cancellable = null) throws Error, IOError;
public abstract async void destroy (HostSession session, Cancellable? cancellable = null) throws Error, IOError;
public signal void host_session_detached (HostSession session);
@@ -180,6 +183,42 @@ namespace Frida {
public abstract void unlink_service_session (HostSession host_session, ServiceSessionId id);
}
public interface HostSessionConnection : Object {
public abstract HostSession host_session {
get;
}
public abstract async AgentSession link_agent_session (AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable = null) throws Error, IOError;
public abstract void unlink_agent_session (AgentSessionId id);
}
public interface HostSessionHub : Object {
public abstract async HostSessionEntry resolve_host_session (string id, Cancellable? cancellable) throws Error, IOError;
}
public class NullHostSessionHub : Object, HostSessionHub {
public async HostSessionEntry resolve_host_session (string id, Cancellable? cancellable) throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Host session lookup not supported");
}
}
public class HostSessionEntry : Object {
public HostSessionProvider provider {
get;
construct;
}
public HostSession session {
get;
construct;
}
public HostSessionEntry (HostSessionProvider provider, HostSession session) {
Object (provider: provider, session: session);
}
}
public enum HostSessionProviderKind {
LOCAL,
REMOTE,
@@ -277,7 +316,8 @@ namespace Frida {
host_session = null;
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
@@ -332,7 +372,13 @@ namespace Frida {
}
}
public abstract class LocalHostSession : Object, HostSession, AgentController {
public abstract class LocalHostSession : Object, HostSession, HostSessionConnection, AgentController {
public HostSession host_session {
get {
return this;
}
}
private Gee.HashMap<uint, Cancellable> pending_establish_ops = new Gee.HashMap<uint, Cancellable> ();
private Gee.HashMap<uint, Future<AgentEntry>> agent_entries = new Gee.HashMap<uint, Future<AgentEntry>> ();
@@ -1423,7 +1469,7 @@ namespace Frida {
public abstract class InternalAgent : Object, AgentMessageSink, RpcPeer {
public signal void unloaded ();
public weak LocalHostSession host_session {
public weak HostSessionConnection connection {
get;
construct;
}
@@ -1447,11 +1493,7 @@ namespace Frida {
construct {
rpc_client = new RpcClient (this);
host_session.agent_session_detached.connect (on_agent_session_detached);
}
~InternalAgent () {
host_session.agent_session_detached.disconnect (on_agent_session_detached);
connection.host_session.agent_session_detached.connect (on_agent_session_detached);
}
public async void close (Cancellable? cancellable) throws IOError {
@@ -1463,6 +1505,8 @@ namespace Frida {
}
yield ensure_unloaded (cancellable);
connection.host_session.agent_session_detached.disconnect (on_agent_session_detached);
}
protected abstract async uint get_target_pid (Cancellable? cancellable) throws Error, IOError;
@@ -1516,9 +1560,9 @@ namespace Frida {
target_pid = yield get_target_pid (cancellable);
try {
session_id = yield host_session.attach (target_pid, attach_options, cancellable);
session_id = yield connection.host_session.attach (target_pid, attach_options, cancellable);
session = yield host_session.link_agent_session (session_id, (AgentMessageSink) this, cancellable);
session = yield connection.link_agent_session (session_id, (AgentMessageSink) this, cancellable);
string? source = yield load_source (cancellable);
if (source != null) {
+4 -4
View File
@@ -9,7 +9,7 @@
"version": "1.0.0",
"dependencies": {
"@frida/buffer": "^7.0.4",
"frida-java-bridge": "^7.0.8"
"frida-java-bridge": "^7.0.10"
},
"devDependencies": {
"esbuild": "^0.25.5",
@@ -534,9 +534,9 @@
"license": "MIT"
},
"node_modules/frida-java-bridge": {
"version": "7.0.8",
"resolved": "https://registry.npmjs.org/frida-java-bridge/-/frida-java-bridge-7.0.8.tgz",
"integrity": "sha512-EmqoTGzcDuCd+YFcjBJJPf7iVQCSbbj9fD0/TN8YYYwl5ZJtTqaCLTTsaC07o3bJaDVs/f3ks+Vu1bOw0S1PnQ==",
"version": "7.0.10",
"resolved": "https://registry.npmjs.org/frida-java-bridge/-/frida-java-bridge-7.0.10.tgz",
"integrity": "sha512-AtjgAHE7a2wV6M/8tZvjVqda0T8YGmSoXqkbD0r1Eq6XTFDKYbClKbZqMYHpI9OjxtkIx+xPqkHWWE3+0fDH7w==",
"license": "LGPL-2.0 WITH WxWindows-exception-3.1"
},
"node_modules/ieee754": {
+1 -1
View File
@@ -12,6 +12,6 @@
},
"dependencies": {
"@frida/buffer": "^7.0.4",
"frida-java-bridge": "^7.0.8"
"frida-java-bridge": "^7.0.10"
}
}
+9 -98
View File
@@ -36,21 +36,8 @@ namespace Frida {
string[] argv = options.compute_argv (path);
string[] envp = options.compute_envp ();
StdioPipes? pipes = null;
FileDescriptor? stdin_read = null, stdin_write = null;
FileDescriptor? stdout_read = null, stdout_write = null;
FileDescriptor? stderr_read = null, stderr_write = null;
switch (options.stdio) {
case INHERIT:
break;
case PIPE: {
make_pty (out stdin_read, out stdin_write);
make_pty (out stdout_read, out stdout_write);
make_pty (out stderr_read, out stderr_write);
pipes = new StdioPipes (stdin_write, stdout_read, stderr_read);
break;
}
}
FileDescriptor? in_fd, out_fd, err_fd;
StdioPipes? pipes = make_stdio_pipes (options.stdio, true, out in_fd, null, out out_fd, null, out err_fd, null);
string? old_cwd = null;
if (options.cwd.length > 0) {
@@ -68,10 +55,10 @@ namespace Frida {
if (pid == 0) {
Posix.setsid ();
if (options.stdio == PIPE) {
Posix.dup2 (stdin_read.handle, 0);
Posix.dup2 (stdout_write.handle, 1);
Posix.dup2 (stderr_write.handle, 2);
if (pipes != null) {
Posix.dup2 (in_fd.handle, 0);
Posix.dup2 (out_fd.handle, 1);
Posix.dup2 (err_fd.handle, 2);
}
if (_ptrace (TRACEME) == -1) {
@@ -637,9 +624,9 @@ namespace Frida {
monitor ();
if (pipes != null) {
stdin_stream = new UnixOutputStream (pipes.input.handle, false);
process_next_output_from.begin (new UnixInputStream (pipes.output.handle, false), 1);
process_next_output_from.begin (new UnixInputStream (pipes.error.handle, false), 2);
stdin_stream = pipes.input;
process_next_output_from.begin (pipes.output, 1);
process_next_output_from.begin (pipes.error, 2);
}
}
@@ -703,37 +690,6 @@ namespace Frida {
}
}
private sealed class StdioPipes : Object {
public FileDescriptor input {
get;
construct;
}
public FileDescriptor output {
get;
construct;
}
public FileDescriptor error {
get;
construct;
}
public StdioPipes (FileDescriptor input, FileDescriptor output, FileDescriptor error) {
Object (input: input, output: output, error: error);
}
construct {
try {
Unix.set_fd_nonblocking (input.handle, true);
Unix.set_fd_nonblocking (output.handle, true);
Unix.set_fd_nonblocking (error.handle, true);
} catch (GLib.Error e) {
assert_not_reached ();
}
}
}
private sealed class ExecTransitionSession : SeizeSession {
private ExecTransitionSession (uint pid) {
Object (pid: pid, on_init: SeizeSession.InitBehavior.CONTINUE);
@@ -3167,53 +3123,8 @@ namespace Frida {
return Linux.syscall (SysCall.tgkill, tgid, tid, sig);
}
public extern bool _syscall_satisfies (int syscall_id, LinuxSyscall mask);
private void make_pty (out FileDescriptor read, out FileDescriptor write) throws Error {
#if HAVE_OPENPTY
int rfd = -1, wfd = -1;
char name[Posix.Limits.PATH_MAX];
if (Linux.openpty (out rfd, out wfd, name, null, null) == -1)
throw new Error.NOT_SUPPORTED ("Unable to open PTY: %s", strerror (errno));
enable_close_on_exec (rfd);
enable_close_on_exec (wfd);
configure_terminal_attributes (rfd);
read = new FileDescriptor (rfd);
write = new FileDescriptor (wfd);
#else
try {
int fds[2];
Unix.open_pipe (fds, Posix.FD_CLOEXEC);
read = new FileDescriptor (fds[0]);
write = new FileDescriptor (fds[1]);
} catch (GLib.Error e) {
throw new Error.NOT_SUPPORTED ("Unable to open pipe: %s", e.message);
}
#endif
}
#if HAVE_OPENPTY
private void enable_close_on_exec (int fd) {
Posix.fcntl (fd, Posix.F_SETFD, Posix.fcntl (fd, Posix.F_GETFD) | Posix.FD_CLOEXEC);
}
private void configure_terminal_attributes (int fd) {
var tios = Posix.termios ();
Posix.tcgetattr (fd, out tios);
tios.c_oflag &= ~Posix.ONLCR;
tios.c_cflag = (tios.c_cflag & Posix.CLOCAL) | Posix.CS8 | Posix.CREAD | Posix.HUPCL;
tios.c_lflag &= ~Posix.ECHO;
Posix.tcsetattr (fd, 0, tios);
}
#endif
private sealed class ProcMapsSoEntry {
public uint64 base_address;
public string path;
+7 -7
View File
@@ -758,9 +758,9 @@ namespace Frida {
set;
}
public ZygoteAgent (LinuxHostSession host_session, uint pid, string name) {
public ZygoteAgent (HostSessionConnection connection, uint pid, string name) {
Object (
host_session: host_session,
connection: connection,
pid: pid,
name: name
);
@@ -768,7 +768,7 @@ namespace Frida {
public async void load (Cancellable? cancellable) throws Error, IOError {
#if ARM || ARM64
LinuxHelper helper = ((LinuxHostSession) host_session).helper;
LinuxHelper helper = ((LinuxHostSession) connection.host_session).helper;
yield helper.await_syscall (pid, POLL_LIKE, cancellable);
try {
#endif
@@ -798,9 +798,9 @@ namespace Frida {
private sealed class SystemServerAgent : InternalAgent {
private delegate void CompletionNotify ();
public SystemServerAgent (LinuxHostSession host_session) {
public SystemServerAgent (HostSessionConnection connection) {
Object (
host_session: host_session,
connection: connection,
#if HAVE_V8
script_runtime: ScriptRuntime.V8
#else
@@ -996,7 +996,7 @@ namespace Frida {
}
};
LinuxHelper helper = ((LinuxHostSession) host_session).helper;
LinuxHelper helper = ((LinuxHostSession) connection.host_session).helper;
foreach (var tid in thread_ids) {
bool safe_to_suspend = false;
string thread_name;
@@ -1050,7 +1050,7 @@ namespace Frida {
}
private void resume_threads (Gee.List<uint> thread_ids) {
LinuxHelper helper = ((LinuxHostSession) host_session).helper;
LinuxHelper helper = ((LinuxHostSession) connection.host_session).helper;
foreach (var tid in thread_ids)
helper.resume_syscall.begin (tid, null);
}
+28
View File
@@ -500,6 +500,34 @@ if have_local_backend
endif
endif
if have_simmy_backend
backend_sources += [
'simmy' / 'simmy-host-session.vala',
'simmy' / 'simmy-host-session-glue.m',
'simmy' / 'text-plist.vala',
]
simmy_data = custom_target('frida-data-simmy',
input: [
'simmy' / 'frida-simmy.resources',
'simmy' / 'agent' / 'springboard.js',
],
output: [
'frida-data-simmy.vapi',
'frida-data-simmy.h',
'frida-data-simmy.c',
'frida-data-simmy-blob' + resource_blob_extension,
],
command: [
resource_compiler_cmd_array,
'-c', '@INPUT0@',
'-o', meson.current_build_dir() / 'frida-data-simmy',
'@INPUT1@',
],
)
backend_sources += [simmy_data]
endif
if have_fruity_backend
backend_sources += [
'fruity' / 'fruity-host-session.vala',
+2 -1
View File
@@ -957,7 +957,8 @@ namespace Frida {
_icon = builder.end ();
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (channel != null)
throw new Error.INVALID_OPERATION ("Already created");
+131
View File
@@ -0,0 +1,131 @@
const SBS = bind({
module: '/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices',
cprefix: 'SBS',
functions: {
copyFrontmostApplicationDisplayIdentifier: ['pointer', [ ]],
copyLocalizedApplicationNameForDisplayIdentifier: ['pointer', ['pointer' ]],
copyIconImagePNGDataForDisplayIdentifier: ['pointer', ['pointer' ]],
processIDForDisplayIdentifier: ['bool', ['pointer', 'pointer']],
}
});
const CF = bind({
module: '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation',
cprefix: 'CF',
functions: {
retain: ['pointer', ['pointer' ]],
release: ['void', ['pointer' ]],
stringCreateWithCString: ['pointer', ['pointer', 'pointer', 'uint' ]],
stringGetCStringPtr: ['pointer', ['pointer', 'uint' ]],
stringGetLength: ['long', ['pointer' ]],
stringGetMaximumSizeForEncoding: ['long', ['long', 'uint' ]],
stringGetCString: ['bool', ['pointer', 'pointer', 'long', 'uint']],
dataGetLength: ['long', ['pointer' ]],
dataGetBytePtr: ['pointer', ['pointer' ]],
}
});
const kCFBooleanTrue = Module.getGlobalExportByName('kCFBooleanTrue').readPointer();
const kCFStringEncodingUTF8 = 0x08000100;
rpc.exports = {
getFrontmostApplication(scope) {
const idObj = SBS.copyFrontmostApplicationDisplayIdentifier();
if (idObj.isNull())
return null;
const id = cfStringToUtf8(idObj);
const nameObj = SBS.copyLocalizedApplicationNameForDisplayIdentifier(idObj);
const name = cfStringToUtf8(nameObj);
const pidBuf = Memory.alloc(4);
SBS.processIDForDisplayIdentifier(idObj, pidBuf);
const pid = pidBuf.readU32();
CF.release(nameObj);
CF.release(idObj);
if (pid === 0)
return null;
return [
id,
name,
pid,
(scope === 'full') ? fetchApplicationIcon(id) : null
];
},
fetchApplicationIcons(appIds) {
return appIds.map(fetchApplicationIcon);
},
};
function fetchApplicationIcon(appId) {
const idObj = CF.stringCreateWithCString(NULL, Memory.allocUtf8String(appId), kCFStringEncodingUTF8);
const pngObj = SBS.copyIconImagePNGDataForDisplayIdentifier(idObj);
CF.release(idObj);
const png = CF.dataGetBytePtr(pngObj).readByteArray(CF.dataGetLength(pngObj));
CF.release(pngObj);
return base64FromBytes(png);
}
Interceptor.attach(Module.getGlobalExportByName('SecTaskCopyValueForEntitlement'), {
onEnter(args) {
this.entitlement = cfStringToUtf8(args[1]);
},
onLeave(retval) {
if (this.entitlement === 'com.apple.springboard.iconState' && retval.isNull())
retval.replace(CF.retain(kCFBooleanTrue));
}
});
function bind({ module, cprefix, functions }) {
const mod = Process.getModuleByName(module);
const nfOpts = { exceptions: 'propagate' };
return Object.fromEntries(Object.entries(functions).map(([name, [retType, argTypes]]) => [
name,
new NativeFunction(mod.getExportByName(cprefix + name.charAt(0).toUpperCase() + name.slice(1)), retType, argTypes, nfOpts)
]));
}
function cfStringToUtf8(cfstr) {
const direct = CF.stringGetCStringPtr(cfstr, kCFStringEncodingUTF8);
if (!direct.isNull())
return direct.readUtf8String();
const len16 = CF.stringGetLength(cfstr);
const maxBytes = CF.stringGetMaximumSizeForEncoding(len16, kCFStringEncodingUTF8).valueOf() + 1;
const buf = Memory.alloc(maxBytes);
CF.stringGetCString(cfstr, buf, maxBytes, kCFStringEncodingUTF8);
return buf.readUtf8String();
}
function base64FromBytes(buf) {
const bytes = new Uint8Array(buf);
const enc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
let i = 0;
const len = bytes.length;
let out = '';
while (i < len) {
const c1 = bytes[i++];
const c2 = (i !== len) ? bytes[i++] : 0;
const c3 = (i !== len) ? bytes[i++] : 0;
const o1 = c1 >> 2;
const o2 = ((c1 & 0x03) << 4) | (c2 >> 4);
const o3 = ((c2 & 0x0f) << 2) | (c3 >> 6);
const o4 = c3 & 0x3f;
if (i - 1 > len)
out += enc[o1] + enc[o2] + '==';
else if (i > len)
out += enc[o1] + enc[o2] + enc[o3] + '=';
else
out += enc[o1] + enc[o2] + enc[o3] + enc[o4];
}
return out;
}
+2
View File
@@ -0,0 +1,2 @@
[resource-compiler]
namespace = Frida.Data.Simmy
+431
View File
@@ -0,0 +1,431 @@
#include "frida-core.h"
#include <dlfcn.h>
#import <Foundation/Foundation.h>
typedef struct _FridaSimmyContext FridaSimmyContext;
struct _FridaSimmyContext
{
dispatch_queue_t dispatch_queue;
void * core_simulator;
NSString * SimDeviceLaunchApplicationKeyStandardErrPath;
NSString * SimDeviceLaunchApplicationKeyStandardOutPath;
NSString * SimDeviceLaunchApplicationKeyTerminateRunningProcess;
NSString * SimDeviceLaunchApplicationKeyWaitForDebugger;
NSString * SimDeviceSpawnKeyArguments;
NSString * SimDeviceSpawnKeyStderr;
NSString * SimDeviceSpawnKeyStdin;
NSString * SimDeviceSpawnKeyStdout;
NSString * SimDeviceSpawnKeyWaitForDebugger;
FridaSimmyHostSessionBackendDeviceAddedFunc on_device_added;
gpointer on_device_added_target;
};
static void frida_simmy_context_destroy (FridaSimmyContext * self);
static void frida_simmy_context_emit_devices (FridaSimmyContext * self);
@interface SimRuntime : NSObject
@property (nonatomic, readonly, strong) NSString * identifier;
@property (nonatomic, readonly, strong) NSString * shortName;
@property (nonatomic, readonly, strong) NSString * versionString;
@property (nonatomic, readonly, strong) NSString * root;
@end
@interface SimDevice : NSObject
@property (nonatomic, readonly, strong) NSUUID * UDID;
@property (nonatomic, readonly, strong) NSString * name;
@property (nonatomic, readonly, strong) SimRuntime * runtime;
@property (nonatomic, readonly, strong) NSString * stateString;
- (NSString *)getenv:(NSString *)name
error:(NSError * _Nullable * _Nullable)error;
- (NSDictionary<NSString *, NSDictionary<NSString *, id> *> *)installedAppsWithError:(NSError * _Nullable * _Nullable)error;
- (void)launchApplicationAsyncWithID:(NSString *)identifier
options:(NSDictionary<NSString *, id> * _Nullable)options
completionQueue:(dispatch_queue_t)queue
completionHandler:(void (^) (NSError * error, int pid))handler;
- spawnAsyncWithPath:(NSString *)path
options:(NSDictionary<NSString *, id> * _Nullable)options
terminationQueue:(dispatch_queue_t)tq
terminationHandler:(void (^) (int status))th
completionQueue:(dispatch_queue_t)cq
completionHandler:(void (^) (NSError * error, int pid))ch;
@end
@interface SimDeviceSet : NSObject
@property (nonatomic, readonly, strong) NSArray<SimDevice *> *devices;
@end
@protocol SimServiceContextClass
+ (instancetype)serviceContextForDeveloperDir:(NSString *)dir
error:(NSError * _Nullable * _Nullable)error;
@end
@interface SimServiceContext : NSObject
- (SimDeviceSet *)defaultDeviceSetWithError:(NSError * _Nullable * _Nullable)error;
@end
void *
_frida_simmy_host_session_backend_start (FridaSimmyHostSessionBackendDeviceAddedFunc on_device_added, gpointer on_device_added_target,
FridaSimmyCompleteFunc on_complete, gpointer on_complete_target, GDestroyNotify on_complete_target_destroy_notify)
{
FridaSimmyContext * ctx;
ctx = g_slice_new0 (FridaSimmyContext);
ctx->dispatch_queue = dispatch_queue_create ("re.frida.simmy.queue", DISPATCH_QUEUE_SERIAL);
ctx->on_device_added = on_device_added;
ctx->on_device_added_target = on_device_added_target;
dispatch_async (ctx->dispatch_queue, ^
{
frida_simmy_context_emit_devices (ctx);
on_complete (on_complete_target);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
});
return ctx;
}
void
_frida_simmy_host_session_backend_stop (void * simmy_context, FridaSimmyCompleteFunc on_complete,
gpointer on_complete_target, GDestroyNotify on_complete_target_destroy_notify)
{
FridaSimmyContext * ctx = simmy_context;
dispatch_async (ctx->dispatch_queue, ^
{
frida_simmy_context_destroy (ctx);
g_slice_free (FridaSimmyContext, ctx);
on_complete (on_complete_target);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
});
}
static void
frida_simmy_context_destroy (FridaSimmyContext * self)
{
g_clear_pointer (&self->core_simulator, dlclose);
dispatch_release (self->dispatch_queue);
}
static void
frida_simmy_context_emit_devices (FridaSimmyContext * self)
{
void * xcselect_module;
bool (* xcselect_get_developer_dir_path) (char *, size_t, bool *, bool *, bool *);
char developer_dir[1024] = { 0, };
bool from_override, is_command_line_tools, from_fallback;
void * cs;
NSString ** str;
Class<SimServiceContextClass> SimServiceContextClass;
SimServiceContext * ctx;
SimDeviceSet * set;
xcselect_module = dlopen ("/usr/lib/libxcselect.dylib", RTLD_GLOBAL | RTLD_LAZY);
if (xcselect_module == NULL)
goto beach;
xcselect_get_developer_dir_path = dlsym (xcselect_module, "xcselect_get_developer_dir_path");
if (!xcselect_get_developer_dir_path (developer_dir, sizeof (developer_dir), &from_override, &is_command_line_tools, &from_fallback))
goto beach;
cs = dlopen ("/Library/Developer/PrivateFrameworks/CoreSimulator.framework/CoreSimulator", RTLD_GLOBAL | RTLD_LAZY);
if (cs == NULL)
goto beach;
self->core_simulator = cs;
#define FRIDA_ASSIGN_CS_CONSTANT(N) \
str = dlsym (cs, G_STRINGIFY (N)); \
g_assert (str != NULL); \
self->N = *str
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyStandardErrPath);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyStandardOutPath);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyTerminateRunningProcess);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceLaunchApplicationKeyWaitForDebugger);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyArguments);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyStderr);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyStdin);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyStdout);
FRIDA_ASSIGN_CS_CONSTANT (SimDeviceSpawnKeyWaitForDebugger);
#undef FRIDA_ASSIGN_CS_CONSTANT
SimServiceContextClass = NSClassFromString (@"SimServiceContext");
ctx = [SimServiceContextClass serviceContextForDeveloperDir:[NSString stringWithUTF8String:developer_dir] error:nil];
set = [ctx defaultDeviceSetWithError:nil];
for (SimDevice * device in set.devices)
{
FridaSimmyRuntime * runtime;
FridaSimmyDevice * d;
if (![device.stateString isEqualToString:@"Booted"])
continue;
runtime = frida_simmy_runtime_new (device.runtime);
d = frida_simmy_device_new ([device retain], device.UDID.UUIDString.UTF8String, device.name.UTF8String,
[device getenv:@"SIMULATOR_MODEL_IDENTIFIER" error:nil].UTF8String, runtime, self);
self->on_device_added (d, self->on_device_added_target);
g_object_unref (d);
g_object_unref (runtime);
}
beach:
if (xcselect_module != NULL)
dlclose (xcselect_module);
}
void
_frida_simmy_device_list_applications (FridaSimmyDevice * self, FridaSimmyDeviceListApplicationsCompleteFunc on_complete,
gpointer on_complete_target, GDestroyNotify on_complete_target_destroy_notify)
{
FridaSimmyContext * ctx = frida_simmy_device_get_simmy_context (self);
dispatch_async (ctx->dispatch_queue, ^
{
GeeArrayList * applications;
SimDevice * device;
applications = gee_array_list_new (FRIDA_SIMMY_TYPE_APPLICATION, g_object_ref, g_object_unref, NULL, NULL, NULL);
device = frida_simmy_device_get_handle (self);
NSDictionary<NSString *, NSDictionary<NSString *, id> *> * bundles = [device installedAppsWithError:nil];
for (NSString * identifier in bundles)
{
NSDictionary<NSString *, id> * bundle;
NSString * display_name;
FridaSimmyApplication * app;
bundle = bundles[identifier];
display_name = bundle[@"CFBundleDisplayName"];
app = frida_simmy_application_new (identifier.UTF8String, display_name.UTF8String);
gee_collection_add (GEE_COLLECTION (applications), app);
g_object_unref (app);
}
on_complete (GEE_LIST (applications), on_complete_target);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
g_object_unref (applications);
});
}
void
_frida_simmy_device_launch_application (FridaSimmyDevice * self, const gchar * identifier, FridaHostSpawnOptions * options,
FridaSimmyDeviceLaunchApplicationCompleteFunc on_complete, gpointer on_complete_target,
GDestroyNotify on_complete_target_destroy_notify)
{
SimDevice * device;
FridaSimmyContext * ctx;
__block FridaStdioPipes * pipes;
__block FridaFileDescriptor * out_fd, * err_fd;
gchar * out_name, * err_name;
GError * error = NULL;
__block GMainContext * main_context;
device = frida_simmy_device_get_handle (self);
ctx = frida_simmy_device_get_simmy_context (self);
pipes = frida_make_stdio_pipes (options->stdio, FALSE, NULL, NULL, &out_fd, &out_name, &err_fd, &err_name, &error);
if (error != NULL)
{
dispatch_async (ctx->dispatch_queue, ^
{
on_complete (error->message, NULL, on_complete_target);
g_error_free (error);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
});
return;
}
main_context = g_main_context_ref_thread_default ();
@autoreleasepool
{
NSMutableDictionary<NSString *, id> * launch_opts;
launch_opts = [@{
ctx->SimDeviceLaunchApplicationKeyWaitForDebugger: @YES,
ctx->SimDeviceLaunchApplicationKeyTerminateRunningProcess: @YES,
} mutableCopy];
if (options->stdio == FRIDA_STDIO_PIPE)
{
launch_opts[ctx->SimDeviceLaunchApplicationKeyStandardOutPath] = @(out_name);
launch_opts[ctx->SimDeviceLaunchApplicationKeyStandardErrPath] = @(err_name);
};
[device launchApplicationAsyncWithID:[NSString stringWithUTF8String:identifier]
options:launch_opts
completionQueue:ctx->dispatch_queue
completionHandler:
^(NSError * error, int pid)
{
FridaSimmySpawnedProcess * process = NULL;
if (error == nil)
process = frida_simmy_spawned_process_new (pid, pipes, main_context);
g_main_context_unref (main_context);
main_context = NULL;
g_clear_object (&pipes);
g_clear_object (&out_fd);
g_clear_object (&err_fd);
on_complete (error.localizedDescription.UTF8String, process, on_complete_target);
g_clear_object (&process);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
}];
}
g_free (out_name);
g_free (err_name);
}
void
_frida_simmy_device_spawn_program (FridaSimmyDevice * self, const gchar * program, FridaHostSpawnOptions * options,
FridaSimmyDeviceSpawnProgramCompleteFunc on_complete, gpointer on_complete_target,
GDestroyNotify on_complete_target_destroy_notify)
{
const FridaStdio stdio = options->stdio;
SimDevice * device;
FridaSimmyContext * ctx;
__block FridaStdioPipes * pipes;
__block FridaFileDescriptor * in_fd, * out_fd, * err_fd;
GError * error = NULL;
__block GMainContext * main_context;
device = frida_simmy_device_get_handle (self);
ctx = frida_simmy_device_get_simmy_context (self);
pipes = frida_make_stdio_pipes (options->stdio, TRUE, &in_fd, NULL, &out_fd, NULL, &err_fd, NULL, &error);
if (error != NULL)
{
dispatch_async (ctx->dispatch_queue, ^
{
on_complete (error->message, NULL, on_complete_target);
g_error_free (error);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
});
return;
}
main_context = g_main_context_ref_thread_default ();
@autoreleasepool
{
NSMutableDictionary<NSString *, id> * spawn_opts;
__block FridaSimmySpawnedProcess * process = NULL;
spawn_opts = [@{
ctx->SimDeviceSpawnKeyWaitForDebugger: @YES,
} mutableCopy];
if (options->has_argv)
{
NSMutableArray<NSString *> * args;
int i;
args = [NSMutableArray arrayWithCapacity:options->argv_length1];
for (i = 0; i != options->argv_length1; i++)
[args addObject:[NSString stringWithUTF8String:options->argv[i]]];
spawn_opts[ctx->SimDeviceSpawnKeyArguments] = args;
}
if (stdio == FRIDA_STDIO_PIPE)
{
spawn_opts[ctx->SimDeviceSpawnKeyStdin] = @(in_fd->handle);
spawn_opts[ctx->SimDeviceSpawnKeyStdout] = @(out_fd->handle);
spawn_opts[ctx->SimDeviceSpawnKeyStderr] = @(err_fd->handle);
};
[device spawnAsyncWithPath:[NSString stringWithUTF8String:program]
options:spawn_opts
terminationQueue:ctx->dispatch_queue
terminationHandler:
^(int status)
{
_frida_simmy_spawned_process_on_termination (process, status);
g_object_unref (process);
process = NULL;
}
completionQueue:ctx->dispatch_queue
completionHandler:
^(NSError * error, int pid)
{
if (error == nil)
process = frida_simmy_spawned_process_new (pid, pipes, main_context);
g_main_context_unref (main_context);
main_context = NULL;
g_clear_object (&pipes);
g_clear_object (&in_fd);
g_clear_object (&out_fd);
g_clear_object (&err_fd);
on_complete (error.localizedDescription.UTF8String, process, on_complete_target);
if (on_complete_target_destroy_notify != NULL)
on_complete_target_destroy_notify (on_complete_target);
}];
}
}
const gchar *
_frida_simmy_runtime_get_identifier (FridaSimmyRuntime * self)
{
return ((SimRuntime *) frida_simmy_runtime_get_handle (self)).identifier.UTF8String;
}
const gchar *
_frida_simmy_runtime_get_short_name (FridaSimmyRuntime * self)
{
return ((SimRuntime *) frida_simmy_runtime_get_handle (self)).shortName.UTF8String;
}
const gchar *
_frida_simmy_runtime_get_version_string (FridaSimmyRuntime * self)
{
return ((SimRuntime *) frida_simmy_runtime_get_handle (self)).versionString.UTF8String;
}
const gchar *
_frida_simmy_runtime_get_root (FridaSimmyRuntime * self)
{
return ((SimRuntime *) frida_simmy_runtime_get_handle (self)).root.UTF8String;
}
+898
View File
@@ -0,0 +1,898 @@
namespace Frida {
public sealed class SimmyHostSessionBackend : Object, HostSessionBackend {
private Gee.Map<string, SimmyHostSessionProvider> providers = new Gee.HashMap<string, SimmyHostSessionProvider> ();
private void * simmy_context;
private MainContext main_context;
private Cancellable io_cancellable = new Cancellable ();
construct {
main_context = MainContext.ref_thread_default ();
}
public async void start (Cancellable? cancellable) throws IOError {
simmy_context = _start (on_device_added, () => {
schedule_on_frida_thread (start.callback);
});
yield;
}
public async void stop (Cancellable? cancellable) throws IOError {
_stop (simmy_context, () => {
schedule_on_frida_thread (stop.callback);
});
yield;
simmy_context = null;
io_cancellable.cancel ();
foreach (var provider in providers.values) {
provider_unavailable (provider);
yield provider.close (cancellable);
}
providers.clear ();
}
private void on_device_added (Simmy.Device device) {
schedule_on_frida_thread (() => {
var prov = new SimmyHostSessionProvider (device);
providers[device.udid] = prov;
provider_available (prov);
return Source.REMOVE;
});
}
private void schedule_on_frida_thread (owned SourceFunc function) {
var source = new IdleSource ();
source.set_callback ((owned) function);
source.attach (main_context);
}
public extern static void * _start (DeviceAddedFunc on_device_added, owned Simmy.CompleteFunc on_complete);
public extern static void _stop (void * simmy_context, owned Simmy.CompleteFunc on_complete);
public delegate void DeviceAddedFunc (Simmy.Device device);
}
public sealed class SimmyHostSessionProvider : Object, HostSessionProvider {
public Simmy.Device device {
get;
construct;
}
public string id {
get { return device.udid; }
}
public string name {
get { return device.name; }
}
public Variant? icon {
get { return null; }
}
public HostSessionProviderKind kind {
get {
return HostSessionProviderKind.REMOTE;
}
}
private SimmyHostSession? host_session;
public SimmyHostSessionProvider (Simmy.Device device) {
Object (device: device);
}
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 (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
if (host_session != null)
throw new Error.INVALID_OPERATION ("Already created");
HostSessionEntry local_system = yield hub.resolve_host_session ("local", cancellable);
host_session = new SimmyHostSession (device, local_system);
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);
}
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 sealed class SimmyHostSession : Object, HostSession, HostSessionConnection {
public Simmy.Device device {
get;
construct;
}
public HostSessionEntry local_system {
get;
construct;
}
public HostSession host_session {
get {
return this;
}
}
private Gee.Map<uint, Simmy.SpawnedProcess> spawned_processes = new Gee.HashMap<uint, Simmy.SpawnedProcess> ();
private SpringboardAgent springboard_agent;
private Cancellable io_cancellable = new Cancellable ();
public SimmyHostSession (Simmy.Device device, HostSessionEntry local_system) {
Object (device: device, local_system: local_system);
}
construct {
var s = local_system.session;
s.process_crashed.connect (on_process_crashed);
s.agent_session_detached.connect (on_agent_session_detached);
s.uninjected.connect (on_uninjected);
springboard_agent = new SpringboardAgent (this);
springboard_agent.unloaded.connect (on_springboard_agent_unloaded);
}
public async void close (Cancellable? cancellable) throws IOError {
springboard_agent.unloaded.disconnect (on_springboard_agent_unloaded);
yield springboard_agent.close (cancellable);
io_cancellable.cancel ();
}
public async void ping (uint interval_seconds, Cancellable? cancellable) throws Error, IOError {
throw new Error.INVALID_OPERATION ("Only meant to be implemented by services");
}
public async HashTable<string, Variant> query_system_parameters (Cancellable? cancellable) throws Error, IOError {
var parameters = new HashTable<string, Variant> (str_hash, str_equal);
var runtime = device.runtime;
unowned string os_name = runtime.short_name;
var os = new HashTable<string, Variant> (str_hash, str_equal);
os["id"] = os_name.down ();
os["name"] = os_name.replace ("iOS", "iPhone OS");
os["version"] = runtime.version_string;
parameters["os"] = os;
parameters["platform"] = "darwin";
parameters["arch"] = "arm64";
var hardware = new HashTable<string, Variant> (str_hash, str_equal);
hardware["product"] = device.model_identifier;
parameters["hardware"] = hardware;
parameters["access"] = "full";
parameters["name"] = device.name;
parameters["udid"] = device.udid;
return parameters;
}
public async HostApplicationInfo get_frontmost_application (HashTable<string, Variant> options,
Cancellable? cancellable) throws Error, IOError {
var opts = FrontmostQueryOptions._deserialize (options);
try {
return yield springboard_agent.get_frontmost_application (opts, cancellable);
} catch (Error e) {
if (e is Error.PERMISSION_DENIED)
throw new Error.NOT_SUPPORTED ("Frontmost query is only supported with SIP disabled (for now)");
throw e;
}
}
public async HostApplicationInfo[] enumerate_applications (HashTable<string, Variant> options,
Cancellable? cancellable) throws Error, IOError {
var opts = ApplicationQueryOptions._deserialize (options);
var scope = opts.scope;
Future<Gee.List<Simmy.Application>> list_apps_request = device.list_applications ();
Future<Gee.List<LaunchdJob>> list_jobs_request = list_launchd_jobs (cancellable);
var selected_ids = new Gee.HashSet<string> ();
opts.enumerate_selected_identifiers (identifier => {
selected_ids.add (identifier);
});
bool include_all_apps = selected_ids.is_empty;
var apps = new Gee.ArrayList<Simmy.Application> ();
foreach (Simmy.Application app in yield list_apps_request.wait_async (cancellable)) {
unowned string id = app.identifier;
if (include_all_apps || id in selected_ids)
apps.add (app);
}
Gee.Map<string, Bytes>? icons = null;
if (scope == FULL) {
var app_ids = new Gee.ArrayList<string> ();
foreach (var app in apps)
app_ids.add (app.identifier);
try {
icons = yield springboard_agent.fetch_application_icons (app_ids, cancellable);
} catch (Error e) {
}
}
var pids = new Gee.HashMap<string, uint> ();
foreach (var job in yield list_jobs_request.wait_async (cancellable)) {
if (job.pid == 0)
continue;
unowned string label = job.label;
if (!label.has_prefix ("UIKitApplication:"))
continue;
string identifier = label[17:].split ("[", 2)[0];
pids[identifier] = job.pid;
}
var result = new HostApplicationInfo[0];
foreach (var app in apps) {
unowned string identifier = app.identifier;
var info = HostApplicationInfo (identifier, app.display_name, pids[identifier], make_parameters_dict ());
if (scope == FULL && icons != null)
add_app_icons (info.parameters, icons[identifier]);
result += info;
}
return result;
}
public async HostProcessInfo[] enumerate_processes (HashTable<string, Variant> options, Cancellable? cancellable)
throws Error, IOError {
var opts = ProcessQueryOptions._deserialize (options);
if (!opts.has_selected_pids ()) {
foreach (var job in yield list_launchd_jobs (cancellable).wait_async (cancellable)) {
if (job.pid != 0)
opts.select_pid (job.pid);
}
}
try {
return yield local_system.session.enumerate_processes (opts._serialize (), cancellable);
} catch (GLib.Error e) {
throw_api_error (e);
}
}
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async HostChildInfo[] enumerate_pending_children (Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async uint spawn (string program, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
Future<Simmy.SpawnedProcess> f;
if (program[0] == '/')
f = device.spawn_program (program, options);
else
f = device.launch_application (program, options);
var process = yield f.wait_async (cancellable);
uint pid = process.pid;
if (options.stdio == PIPE) {
spawned_processes[pid] = process;
process.terminated.connect (on_spawned_process_terminated);
process.output.connect (on_spawned_process_output);
}
return pid;
}
private void on_spawned_process_terminated (Simmy.SpawnedProcess process, int status) {
spawned_processes.unset (process.pid);
}
private void on_spawned_process_output (Simmy.SpawnedProcess process, int fd, uint8[] data) {
output (process.pid, fd, data);
if (data.length == 0)
spawned_processes.unset (process.pid);
}
public async void input (uint pid, uint8[] data, Cancellable? cancellable) throws Error, IOError {
var process = spawned_processes[pid];
if (process == null)
throw new Error.INVALID_ARGUMENT ("Invalid PID");
yield process.input (data, cancellable);
}
public async void resume (uint pid, Cancellable? cancellable) throws GLib.Error {
yield local_system.session.resume (pid, cancellable);
}
public async void kill (uint pid, Cancellable? cancellable) throws GLib.Error {
yield local_system.session.kill (pid, cancellable);
}
public async AgentSessionId attach (uint pid, HashTable<string, Variant> options,
Cancellable? cancellable) throws GLib.Error {
return yield local_system.session.attach (pid, options, cancellable);
}
public async void reattach (AgentSessionId id, Cancellable? cancellable) throws Error, IOError {
throw new Error.INVALID_OPERATION ("Only meant to be implemented by services");
}
public async AgentSession link_agent_session (AgentSessionId id, AgentMessageSink sink,
Cancellable? cancellable) throws Error, IOError {
return yield local_system.provider.link_agent_session (local_system.session, id, sink, cancellable);
}
public void unlink_agent_session (AgentSessionId id) {
local_system.provider.unlink_agent_session (local_system.session, id);
}
public async InjectorPayloadId inject_library_file (uint pid, string path, string entrypoint, string data,
Cancellable? cancellable) throws GLib.Error {
return yield local_system.session.inject_library_file (pid, path, entrypoint, data, cancellable);
}
public async InjectorPayloadId inject_library_blob (uint pid, uint8[] blob, string entrypoint, string data,
Cancellable? cancellable) throws GLib.Error {
return yield local_system.session.inject_library_blob (pid, blob, entrypoint, data, cancellable);
}
public async ChannelId open_channel (string address, Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public IOStream link_channel (ChannelId id) throws Error {
throw_not_supported ();
}
public void unlink_channel (ChannelId id) {
assert_not_reached ();
}
public async ServiceSessionId open_service (string address, Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public ServiceSession link_service_session (ServiceSessionId id) throws Error {
throw_not_supported ();
}
public void unlink_service_session (ServiceSessionId id) {
assert_not_reached ();
}
private void on_process_crashed (CrashInfo crash) {
process_crashed (crash);
}
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
agent_session_detached (id, reason, crash);
}
private void on_uninjected (InjectorPayloadId id) {
uninjected (id);
}
internal Future<Gee.List<LaunchdJob>> list_launchd_jobs (Cancellable? cancellable) {
var promise = new Promise<Gee.List<LaunchdJob>> ();
perform_list_launchd_jobs.begin (cancellable, promise);
return promise.future;
}
private async void perform_list_launchd_jobs (Cancellable? cancellable, Promise<Gee.List<LaunchdJob>> request) {
try {
RunResult res = yield run_and_capture_output ({
Path.build_filename (device.runtime.root, "bin", "launchctl"),
"list"
}, cancellable).wait_async (cancellable);
res.check ();
var jobs = new Gee.ArrayList<LaunchdJob> ();
string[] lines = res.standard_output.chomp ().split ("\n");
foreach (string line in lines[1:]) {
string[] tokens = line.split ("\t", 3);
if (tokens.length != 3)
throw new Error.PROTOCOL ("Unexpected launchctl output");
unowned string raw_pid = tokens[0];
unowned string label = tokens[2];
jobs.add (new LaunchdJob () {
pid = uint.parse (raw_pid),
label = label,
});
}
request.resolve (jobs);
} catch (GLib.Error e) {
request.reject (e);
}
}
internal class LaunchdJob {
public uint pid;
public string label;
}
private Future<RunResult> run_and_capture_output (string[] argv, Cancellable? cancellable) {
var promise = new Promise<RunResult> ();
perform_run_and_capture_output.begin (argv, cancellable, promise);
return promise.future;
}
private async void perform_run_and_capture_output (string[] argv, Cancellable? cancellable, Promise<RunResult> request) {
string[] argv_copy = argv;
var opts = HostSpawnOptions ();
opts.has_argv = true;
opts.argv = argv_copy;
opts.stdio = PIPE;
Simmy.SpawnedProcess process;
try {
process = yield device.spawn_program (argv_copy[0], opts).wait_async (cancellable);
} catch (GLib.Error e) {
request.reject (e);
return;
}
int status = -1;
var standard_output = new StringBuilder.sized (1024);
var standard_error = new StringBuilder.sized (1024);
process.terminated.connect ((s) => {
status = s;
perform_run_and_capture_output.callback ();
});
process.output.connect ((fd, data) => {
var buf = new char[data.length + 1];
Memory.copy (buf, data, data.length);
buf[data.length] = '\0';
unowned StringBuilder target = (fd == 1) ? standard_output : standard_error;
target.append ((string) buf);
});
process.resume ();
yield;
request.resolve (new RunResult (argv_copy) {
status = status,
standard_output = standard_output.str,
standard_error = standard_error.str,
});
}
private class RunResult {
public int status;
public string standard_output;
public string standard_error;
private string[] argv;
public RunResult (string[] argv) {
this.argv = argv;
}
public void check () throws Error {
if (status != 0) {
throw new Error.NOT_SUPPORTED ("Failed to run `%s`, it exited with status %d: %s",
string.joinv (" ", argv),
status,
standard_error.chomp ());
}
}
}
private void on_springboard_agent_unloaded (InternalAgent dead_agent) {
dead_agent.unloaded.disconnect (on_springboard_agent_unloaded);
springboard_agent = new SpringboardAgent (this);
springboard_agent.unloaded.connect (on_springboard_agent_unloaded);
}
[NoReturn]
private static void throw_not_supported () throws Error {
throw new Error.NOT_SUPPORTED ("Not yet supported by the Simmy backend");
}
}
private sealed class SpringboardAgent : InternalAgent {
public SpringboardAgent (HostSessionConnection connection) {
Object (connection: connection);
}
public async HostApplicationInfo get_frontmost_application (FrontmostQueryOptions options,
Cancellable? cancellable) throws Error, IOError {
var scope = options.scope;
var scope_node = new Json.Node.alloc ().init_string (scope.to_nick ());
Json.Node response = yield call ("getFrontmostApplication", new Json.Node[] { scope_node }, null, cancellable);
if (response.get_node_type () == NULL)
return HostApplicationInfo.empty ();
var item = response.get_array ();
var identifier = item.get_string_element (0);
var name = item.get_string_element (1);
var pid = (uint) item.get_int_element (2);
var info = HostApplicationInfo (identifier, name, pid, make_parameters_dict ());
if (scope == FULL)
add_app_icons (info.parameters, new Bytes.take (Base64.decode (item.get_string_element (3))));
return info;
}
public async Gee.Map<string, Bytes> fetch_application_icons (Gee.List<string> app_ids, Cancellable? cancellable)
throws Error, IOError {
var app_id_values = new Json.Array ();
foreach (var id in app_ids)
app_id_values.add_string_element (id);
var args = new Json.Node[] { new Json.Node.alloc ().init_array (app_id_values) };
Json.Node response = yield call ("fetchApplicationIcons", args, null, cancellable);
var icons = new Gee.HashMap<string, Bytes> ();
var reader = new Json.Reader (response);
int n = reader.count_elements ();
for (int i = 0; i != n; i++) {
reader.read_element (i);
if (!reader.get_null_value ())
icons[app_ids[i]] = new Bytes (Base64.decode (reader.get_string_value ()));
reader.end_element ();
}
return icons;
}
protected override async uint get_target_pid (Cancellable? cancellable) throws Error, IOError {
var host_session = (SimmyHostSession) connection.host_session;
foreach (var job in yield host_session.list_launchd_jobs (cancellable).wait_async (cancellable)) {
if (job.label == "com.apple.SpringBoard")
return job.pid;
}
throw new Error.NOT_SUPPORTED ("Unable to locate SpringBoard process");
}
protected override async string? load_source (Cancellable? cancellable) throws Error, IOError {
return (string) Frida.Data.Simmy.get_springboard_js_blob ().data;
}
}
private void add_app_icons (HashTable<string, Variant> parameters, Bytes png) {
var icons = new VariantBuilder (new VariantType.array (VariantType.VARDICT));
icons.open (VariantType.VARDICT);
icons.add ("{sv}", "format", new Variant.string ("png"));
icons.add ("{sv}", "image", Variant.new_from_data (new VariantType ("ay"), png.get_data (), true, png));
icons.close ();
parameters["icons"] = icons.end ();
}
[CCode (gir_namespace = "FridaSimmy", gir_version = "1.0")]
namespace Simmy {
public sealed class Device : Object {
public void * handle {
get;
construct;
}
public string udid {
get;
construct;
}
public string name {
get;
construct;
}
public string model_identifier {
get;
construct;
}
public Runtime runtime {
get;
construct;
}
public void * simmy_context {
get;
construct;
}
public Device (void * handle, string udid, string name, string model_identifier, Runtime runtime,
void * simmy_context) {
Object (
handle: handle,
udid: udid,
name: name,
model_identifier: model_identifier,
runtime: runtime,
simmy_context: simmy_context
);
}
public Future<Gee.List<Application>> list_applications () {
var promise = new Promise<Gee.List<Application>> ();
_list_applications (applications => {
promise.resolve (applications);
});
return promise.future;
}
public Future<SpawnedProcess> launch_application (string identifier, HostSpawnOptions options) {
var promise = new Promise<SpawnedProcess> ();
_launch_application (identifier, options, (error_message, process) => {
if (error_message != null)
promise.reject (new Error.NOT_SUPPORTED ("%s", error_message));
else
promise.resolve (process);
});
return promise.future;
}
public Future<SpawnedProcess> spawn_program (string program, HostSpawnOptions options) {
var promise = new Promise<SpawnedProcess> ();
_spawn_program (program, options, (error_message, process) => {
if (error_message != null)
promise.reject (new Error.NOT_SUPPORTED ("%s", error_message));
else
promise.resolve (process);
});
return promise.future;
}
public extern void _list_applications (owned ListApplicationsCompleteFunc on_complete);
public extern void _launch_application (string identifier, HostSpawnOptions options,
owned LaunchApplicationCompleteFunc on_complete);
public extern void _spawn_program (string program, HostSpawnOptions options,
owned SpawnProgramCompleteFunc on_complete);
public delegate void ListApplicationsCompleteFunc (Gee.List<Application> device);
public delegate void LaunchApplicationCompleteFunc (string? error_message, SpawnedProcess? process);
public delegate void SpawnProgramCompleteFunc (string? error_message, SpawnedProcess? process);
}
public sealed class Runtime : Object {
public void * handle {
get;
construct;
}
public string identifier {
get {
return _get_identifier ();
}
}
public string short_name {
get {
return _get_short_name ();
}
}
public string version_string {
get {
return _get_version_string ();
}
}
public string root {
get {
return _get_root ();
}
}
public Runtime (void * handle) {
Object (handle: handle);
}
public extern unowned string _get_identifier ();
public extern unowned string _get_short_name ();
public extern unowned string _get_version_string ();
public extern unowned string _get_root ();
}
public sealed class Application : Object {
public string identifier {
get;
construct;
}
public string display_name {
get;
construct;
}
public Application (string identifier, string display_name) {
Object (identifier: identifier, display_name: display_name);
}
}
public delegate void CompleteFunc ();
public sealed class SpawnedProcess : Object {
public signal void terminated (int status);
public signal void output (int fd, uint8[] data);
public uint pid {
get;
construct;
}
public StdioPipes? pipes {
get;
construct;
}
public MainContext main_context {
get;
construct;
}
private OutputStream? stdin_stream;
private Cancellable io_cancellable = new Cancellable ();
public SpawnedProcess (uint pid, StdioPipes? pipes, MainContext main_context) {
Object (
pid: pid,
pipes: pipes,
main_context: main_context
);
}
construct {
if (pipes != null) {
var source = new IdleSource ();
source.set_callback (() => {
start_processing_pipes ();
return Source.REMOVE;
});
source.attach (main_context);
}
}
private void start_processing_pipes () {
stdin_stream = pipes.input;
process_next_output_from.begin (pipes.output, 1);
process_next_output_from.begin (pipes.error, 2);
}
public void close () {
io_cancellable.cancel ();
}
public void resume () {
Posix.kill ((Posix.pid_t) pid, Posix.Signal.CONT);
}
public void _on_termination (int status) {
var source = new IdleSource ();
source.set_callback (() => {
if (stdin_stream != null) {
stdin_stream.close_async.begin ();
stdin_stream = null;
}
close ();
terminated (status);
return Source.REMOVE;
});
source.attach (main_context);
}
public async void input (uint8[] data, Cancellable? cancellable) throws Error, IOError {
if (stdin_stream == null)
throw new Error.NOT_SUPPORTED ("Unable to pass input to process spawned without piped stdio");
try {
yield stdin_stream.write_all_async (data, Priority.DEFAULT, cancellable, null);
} catch (GLib.Error e) {
throw new Error.TRANSPORT ("%s", e.message);
}
}
private async void process_next_output_from (InputStream stream, int fd) {
try {
var buf = new uint8[4096];
var n = yield stream.read_async (buf, Priority.DEFAULT, io_cancellable);
var data = buf[0:n];
output (fd, data);
if (n > 0)
process_next_output_from.begin (stream, fd);
} catch (GLib.Error e) {
stream.close_async.begin ();
if (!(e is IOError.CANCELLED))
output (fd, {});
}
}
}
}
}
+296
View File
@@ -0,0 +1,296 @@
[CCode (gir_namespace = "FridaSimmy", gir_version = "1.0")]
namespace Frida.TextPList {
public class Parser {
private Lexer lex;
private Token look;
public Parser (string text) {
lex = new Lexer (text);
}
public Variant parse () throws Error {
look = lex.next_token ();
Variant v = parse_value ();
if (look.type != EOF)
throw new Error.PROTOCOL ("Trailing content after root value");
return v;
}
private Variant parse_value () throws Error {
switch (look.type) {
case LBRACE: return parse_dict ();
case LPAREN: return parse_array ();
case NUMBER: return parse_number ();
case STRING: return parse_string ();
default:
throw new Error.PROTOCOL (@"Unexpected token %s".printf (look.type.to_string ()));
}
}
private Variant parse_dict () throws Error {
eat (LBRACE);
var b = new VariantBuilder (new VariantType ("a{sv}"));
while (look.type != RBRACE) {
if (look.type != STRING)
throw new Error.PROTOCOL ("Dictionary key must be STRING");
string key = look.text;
eat (STRING);
eat (EQUAL);
b.add ("{sv}", key, parse_value ());
if (look.type == SEMI)
eat (SEMI);
if (look.type == RBRACE)
break;
}
eat (RBRACE);
return b.end ();
}
private Variant parse_array () throws Error {
eat (LPAREN);
var b = new VariantBuilder (new VariantType ("av"));
if (look.type != RPAREN) {
b.add_value (parse_value ());
while (look.type == COMMA) {
eat (COMMA);
b.add_value (new Variant.variant (parse_value ()));
}
}
eat (RPAREN);
return b.end ();
}
private Variant parse_number () throws Error {
string raw = look.text;
eat (NUMBER);
if (raw.index_of_char ('.') != -1) {
double d;
if (!double.try_parse (raw, out d))
throw new Error.PROTOCOL (@"Bad double: %s".printf (raw));
return d;
} else {
int64 x;
if (!int64.try_parse (raw, out x))
throw new Error.PROTOCOL (@"Bad int: %s".printf (raw));
return x;
}
}
private Variant parse_string () throws Error {
string s = look.text;
eat (STRING);
return s;
}
private void eat (TokenType t) throws Error {
if (look.type != t)
throw new Error.PROTOCOL (@"Expected %s but found %s".printf (t.to_string (), look.type.to_string ()));
look = lex.next_token ();
}
}
private class Lexer {
private string s;
private int i = 0;
private int n;
public Lexer (string input) {
s = input;
n = s.length;
}
public Token next_token () throws Error {
skip_whitespace_and_comments ();
if (at_end ())
return new Token (EOF);
switch (peek ()) {
case '{': next (); return new Token (LBRACE);
case '}': next (); return new Token (RBRACE);
case '(': next (); return new Token (LPAREN);
case ')': next (); return new Token (RPAREN);
case '=': next (); return new Token (EQUAL);
case ';': next (); return new Token (SEMI);
case ',': next (); return new Token (COMMA);
case '"': return new Token (STRING, read_quoted ());
default:
var word = read_bare ();
if (word.length == 0)
throw new Error.PROTOCOL (@"Unexpected character '%c'".printf (peek ()));
return read_number_or_bareword (word);
}
}
private bool at_end () {
return i >= n;
}
private char peek () {
return s[i];
}
private char next () {
return s[i++];
}
private void skip_whitespace_and_comments () {
while (!at_end ()) {
if (is_whitespace (peek ())) {
next ();
continue;
}
if (peek () == '/' && i + 1 < n) {
if (s[i + 1] == '/') {
i += 2;
while (!at_end () && peek () != '\n')
next ();
continue;
}
if (s[i + 1] == '*') {
i += 2;
while (!at_end ()) {
if (peek () == '*' && i + 1 < n && s[i + 1] == '/') {
i += 2;
break;
}
next ();
}
continue;
}
}
break;
}
}
private static bool is_whitespace (char c) {
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
private string read_quoted () throws Error {
next ();
var sb = new StringBuilder ();
while (!at_end ()) {
char c = next ();
if (c == '\\') {
if (at_end ())
break;
char e = next ();
switch (e) {
case 'n':
sb.append_c ('\n');
break;
case 'r':
sb.append_c ('\r');
break;
case 't':
sb.append_c ('\t');
break;
case '"':
sb.append_c ('"');
break;
case '\\':
sb.append_c ('\\');
break;
default:
sb.append_c (e);
break;
}
} else if (c == '"') {
return sb.str;
} else {
sb.append_c (c);
}
}
throw new Error.PROTOCOL ("Unterminated quoted string");
}
private string read_bare () {
int start = i;
while (!at_end () && is_bare_char (peek ()))
next ();
return s.substring (start, i - start);
}
private static bool is_bare_char (char c) {
if (c.isalnum ())
return true;
const string extra = "._-/:$+@";
return extra.index_of_char (c) != -1;
}
private Token read_number_or_bareword (string word) {
bool numeric = true;
bool has_dot = false;
int len = word.length;
for (int k = 0; k != len; k++) {
char c = word[k];
if (k == 0 && (c == '+' || c == '-'))
continue;
if (c == '.') {
if (has_dot) {
numeric = false;
break;
}
has_dot = true;
continue;
}
if (!c.isdigit ()) {
numeric = false;
break;
}
}
if (numeric && word.length > 0 && !(word == "+" || word == "-"))
return new Token (NUMBER, word);
return new Token (STRING, word);
}
}
private class Token {
public TokenType type;
public string text;
public Token (TokenType t, string s = "") {
type = t;
text = s;
}
}
private enum TokenType {
LBRACE,
RBRACE,
LPAREN,
RPAREN,
EQUAL,
SEMI,
COMMA,
STRING,
NUMBER,
EOF,
}
}
+2 -1
View File
@@ -61,7 +61,8 @@ namespace Frida {
io_cancellable.cancel ();
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
string? raw_address = null;
TlsCertificate? certificate = null;
string? origin = null;
-21
View File
@@ -298,25 +298,4 @@ namespace Frida {
public extern void _do_resume ();
}
public sealed class StdioPipes : Object {
public OutputStream input {
get;
construct;
}
public InputStream output {
get;
construct;
}
public InputStream error {
get;
construct;
}
public StdioPipes (OutputStream input, InputStream output, InputStream error) {
Object (input: input, output: output, error: error);
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/frida-gum.git
revision = 17.3.2
revision = 17.4.3
depth = 1
[provide]
+15 -14
View File
@@ -470,7 +470,8 @@ namespace Frida.HostSessionTest {
get { return HostSessionProviderKind.LOCAL; }
}
public async HostSession create (HostSessionOptions? options, Cancellable? cancellable) throws Error, IOError {
public async HostSession create (HostSessionHub hub, HostSessionOptions? options, Cancellable? cancellable)
throws Error, IOError {
throw new Error.NOT_SUPPORTED ("Not implemented");
}
@@ -1536,7 +1537,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var applications = yield session.enumerate_applications (make_parameters_dict (), cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
@@ -1577,7 +1578,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -1776,7 +1777,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var applications = yield session.enumerate_applications (make_parameters_dict (), cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
@@ -1814,7 +1815,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -1919,7 +1920,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -2398,7 +2399,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var id = yield host_session.attach (pid, make_parameters_dict (), cancellable);
var session = yield prov.link_agent_session (host_session, id, h, cancellable);
@@ -2549,7 +2550,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
@@ -2576,7 +2577,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -3307,7 +3308,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
@@ -3333,7 +3334,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
uint pid = 0;
bool waiting = false;
@@ -3584,7 +3585,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
@@ -3617,7 +3618,7 @@ namespace Frida.HostSessionTest {
Cancellable? cancellable = null;
stdout.printf ("connecting to frida-server\n");
var host_session = yield prov.create (null, cancellable);
var host_session = yield prov.create (new NullHostSessionHub (), null, cancellable);
stdout.printf ("enumerating processes\n");
var processes = yield host_session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
@@ -4242,7 +4243,7 @@ namespace Frida.HostSessionTest {
try {
Cancellable? cancellable = null;
var session = yield prov.create (null, cancellable);
var session = yield prov.create (new NullHostSessionHub (), null, cancellable);
var processes = yield session.enumerate_processes (make_parameters_dict (), cancellable);
assert_true (processes.length > 0);
+14
View File
@@ -77,6 +77,18 @@ namespace Darwin.XNU {
[CCode (cheader_filename = "sys/sysctl.h")]
public int sysctlbyname (string name, void * oldp, size_t * oldlenp, void * newp = null, size_t newlen = 0);
[CCode (cheader_filename = "util.h")]
public int openpty (out int amaster, out int aslave, [CCode (array_length=false, array_null_terminated=true)] char[] name,
Posix.termios? termp, winsize? winp);
[CCode (cname = "struct winsize", has_type_id = false, cheader_filename = "sys/ttycom.h", destroy_function = "")]
public struct winsize {
public ushort ws_row;
public ushort ws_col;
public ushort ws_xpixel;
public ushort ws_ypixel;
}
[CCode (cname = "struct xinpgen", cheader_filename = "netinet/in_pcb.h")]
public struct InetPcbGeneration {
[CCode (cname = "xig_len")]
@@ -104,4 +116,6 @@ namespace Darwin.XNU {
[CCode (cheader_filename = "fcntl.h")]
public const int F_FULLFSYNC;
[CCode (cheader_filename = "fcntl.h")]
public const int F_SETNOSIGPIPE;
}
+14
View File
@@ -0,0 +1,14 @@
[CCode (lower_case_cprefix = "", gir_namespace = "FreeBSD", gir_version = "1.0")]
namespace FreeBSD {
[CCode (cheader_filename = "libutil.h")]
public int openpty (out int amaster, out int aslave, [CCode (array_length=false, array_null_terminated=true)] char[] name,
Posix.termios? termp, winsize? winp);
[CCode (cname = "struct winsize", has_type_id = false, cheader_filename = "sys/ttycom.h", destroy_function = "")]
public struct winsize {
public ushort ws_row;
public ushort ws_col;
public ushort ws_xpixel;
public ushort ws_ypixel;
}
}