mirror of
https://github.com/frida/frida-core.git
synced 2026-11-08 08:05:58 -10:00
Compare commits
56
Commits
17.14.1
...
debug/tasks
No files matched your search
+5
-1
@@ -624,7 +624,11 @@ def option_should_be_forwarded(k: "OptionKey",
|
||||
return is_for_us or is_for_child
|
||||
|
||||
if coredata.CoreData.is_per_machine_option(k):
|
||||
return k.machine is coredata.MachineChoice.BUILD
|
||||
# The compat build generates its own machine files, so forwarding the
|
||||
# parent's build-machine options would inject a foreign toolchain. In a
|
||||
# native compat build that conjures a compiler-less build machine and
|
||||
# breaks any native: true target.
|
||||
return False
|
||||
|
||||
if k.is_builtin():
|
||||
if k.name in {"buildtype", "genvslite"}:
|
||||
|
||||
@@ -292,6 +292,8 @@ namespace Frida.Agent {
|
||||
|
||||
start.begin ((owned) padder);
|
||||
|
||||
start_alive_task_dumper ();
|
||||
|
||||
main_loop.run ();
|
||||
|
||||
main_context.pop_thread_default ();
|
||||
@@ -362,6 +364,16 @@ namespace Frida.Agent {
|
||||
padder = null;
|
||||
}
|
||||
|
||||
// TEMPORARY DEBUG HACK: dump alive GTasks every 30 seconds.
|
||||
private void start_alive_task_dumper () {
|
||||
var source = new TimeoutSource.seconds (30);
|
||||
source.set_callback (() => {
|
||||
GLib.info ("%s", g_task_describe_alive_tasks ());
|
||||
return Source.CONTINUE;
|
||||
});
|
||||
source.attach (main_context);
|
||||
}
|
||||
|
||||
private void keep_running_eternalized () {
|
||||
agent_gthread = new Thread<bool> ("frida-eternal-agent", () => {
|
||||
var ignore_scope = new ThreadIgnoreScope (FRIDA_THREAD);
|
||||
@@ -924,6 +936,8 @@ namespace Frida.Agent {
|
||||
private void on_session_closed (BaseAgentSession base_session) {
|
||||
LiveAgentSession session = (LiveAgentSession) base_session;
|
||||
|
||||
GLib.info ("detach-trace: Agent emitting provider closed id=%s", session.id.handle);
|
||||
|
||||
closed (session.id);
|
||||
|
||||
unregister_session (session);
|
||||
@@ -1724,4 +1738,8 @@ namespace Frida.Agent {
|
||||
return repeat;
|
||||
});
|
||||
}
|
||||
|
||||
// TEMPORARY DEBUG HACK: only present when GLib is built with G_ENABLE_DEBUG.
|
||||
[CCode (cname = "g_task_describe_alive_tasks")]
|
||||
private extern string g_task_describe_alive_tasks ();
|
||||
}
|
||||
@@ -12,6 +12,8 @@ namespace Frida {
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public abstract async void enable_spawn_gating (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void disable_spawn_gating (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async HostChildInfo[] enumerate_pending_children (Cancellable? cancellable) throws GLib.Error;
|
||||
@@ -31,6 +33,7 @@ namespace Frida {
|
||||
|
||||
public abstract async ServiceSessionId open_service (string address, Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public signal void spawn_gating_disabled (SpawnGatingDisabledReason reason);
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
public signal void child_added (HostChildInfo info);
|
||||
@@ -358,6 +361,8 @@ namespace Frida {
|
||||
|
||||
private async void teardown_peer_connection_and_emit_closed () {
|
||||
schedule_on_frida_thread (() => {
|
||||
GLib.info ("detach-trace: AgentMessageTransmitter %p emitting closed, nice_agent=%p",
|
||||
this, nice_agent);
|
||||
if (nice_agent != null)
|
||||
close_nice_resources_and_emit_closed.begin ();
|
||||
else
|
||||
@@ -595,6 +600,7 @@ namespace Frida {
|
||||
|
||||
private async void teardown_peer_connection_and_emit_closed () {
|
||||
schedule_on_frida_thread (() => {
|
||||
GLib.info ("detach-trace: AgentMessageTransmitter %p emitting closed", this);
|
||||
closed ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
@@ -903,6 +909,11 @@ namespace Frida {
|
||||
throw_not_authorized ();
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_authorized ();
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_authorized ();
|
||||
}
|
||||
@@ -982,6 +993,46 @@ namespace Frida {
|
||||
throw new Error.PERMISSION_DENIED ("Not authorized, authentication required");
|
||||
}
|
||||
|
||||
// Fires `expired` when a gated process goes unresumed too long, so the backend can cancel
|
||||
// gating instead of letting a stalled client wedge process creation.
|
||||
public sealed class SpawnGatingWatchdog : Object {
|
||||
public signal void expired ();
|
||||
|
||||
public uint timeout_msec {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private Gee.Map<uint, Source> deadlines = new Gee.HashMap<uint, Source> ();
|
||||
|
||||
// Default deadline: under the OS's own spawn-start timeouts (Android's is 10s).
|
||||
public SpawnGatingWatchdog (uint timeout_msec = 8000) {
|
||||
Object (timeout_msec: timeout_msec);
|
||||
}
|
||||
|
||||
public void arm (uint pid) {
|
||||
var deadline = new TimeoutSource (timeout_msec);
|
||||
deadline.set_callback (() => {
|
||||
expired ();
|
||||
return Source.REMOVE;
|
||||
});
|
||||
deadline.attach (MainContext.get_thread_default ());
|
||||
deadlines[pid] = deadline;
|
||||
}
|
||||
|
||||
public void cancel (uint pid) {
|
||||
Source? deadline;
|
||||
if (deadlines.unset (pid, out deadline))
|
||||
deadline.destroy ();
|
||||
}
|
||||
|
||||
public void clear () {
|
||||
foreach (var deadline in deadlines.values)
|
||||
deadline.destroy ();
|
||||
deadlines.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The realm an agent should run in.
|
||||
*/
|
||||
@@ -1104,6 +1155,29 @@ namespace Frida {
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Why spawn gating was disabled on a {@link Device}.
|
||||
*/
|
||||
public enum SpawnGatingDisabledReason {
|
||||
/**
|
||||
* A client asked to disable spawn gating.
|
||||
*/
|
||||
APPLICATION_REQUESTED = 1,
|
||||
/**
|
||||
* A caught process went unresumed long enough that the gater cancelled gating to
|
||||
* avoid stalling process creation.
|
||||
*/
|
||||
WATCHDOG_TIMEOUT;
|
||||
|
||||
public static SpawnGatingDisabledReason from_nick (string nick) throws Error {
|
||||
return Marshal.enum_from_nick<SpawnGatingDisabledReason> (nick);
|
||||
}
|
||||
|
||||
public string to_nick () {
|
||||
return Marshal.enum_to_nick<SpawnGatingDisabledReason> (this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Why a {@link Session} was detached from its target.
|
||||
*/
|
||||
@@ -1484,6 +1558,67 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for {@link Device.enable_spawn_gating}.
|
||||
*/
|
||||
public sealed class SpawnGatingOptions : Object {
|
||||
/**
|
||||
* Which processes to gate. Anything other than DEFAULT requires a Frida new enough to
|
||||
* support scoping, and fails rather than silently gating something broader than asked.
|
||||
*/
|
||||
public SpawnGatingScope scope {
|
||||
get;
|
||||
set;
|
||||
default = DEFAULT;
|
||||
}
|
||||
|
||||
public HashTable<string, Variant> _serialize () {
|
||||
var dict = make_parameters_dict ();
|
||||
|
||||
if (scope != DEFAULT)
|
||||
dict["scope"] = new Variant.string (scope.to_nick ());
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public static SpawnGatingOptions _deserialize (HashTable<string, Variant> dict) throws Error {
|
||||
var options = new SpawnGatingOptions ();
|
||||
|
||||
Variant? scope = dict["scope"];
|
||||
if (scope != null) {
|
||||
if (!scope.is_of_type (VariantType.STRING))
|
||||
throw new Error.INVALID_ARGUMENT ("The 'scope' option must be a string");
|
||||
options.scope = SpawnGatingScope.from_nick (scope.get_string ());
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SpawnGatingScope {
|
||||
/**
|
||||
* Leave the choice to the implementation. Sent as a plain enable_spawn_gating call, so
|
||||
* it still works against a Frida too old to know about scopes.
|
||||
*/
|
||||
DEFAULT,
|
||||
/**
|
||||
* Application launches only.
|
||||
*/
|
||||
APPLICATIONS,
|
||||
/**
|
||||
* Every process created on the system. More potent but far more intrusive.
|
||||
*/
|
||||
PROCESSES;
|
||||
|
||||
public static SpawnGatingScope from_nick (string nick) throws Error {
|
||||
return Marshal.enum_from_nick<SpawnGatingScope> (nick);
|
||||
}
|
||||
|
||||
public string to_nick () {
|
||||
return Marshal.enum_to_nick<SpawnGatingScope> (this);
|
||||
}
|
||||
}
|
||||
|
||||
public struct HostSpawnOptions {
|
||||
public bool has_argv;
|
||||
public string[] argv;
|
||||
|
||||
@@ -1810,6 +1810,11 @@ namespace Frida.Gadget {
|
||||
throw new Error.NOT_SUPPORTED ("Not possible when embedded");
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not possible when embedded");
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not possible when embedded");
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
GLib.info ("detach-trace: BaseAgentSession.close() id=%s close_request=%p", id.handle, close_request);
|
||||
|
||||
while (close_request != null) {
|
||||
try {
|
||||
yield close_request.future.wait_async (cancellable);
|
||||
@@ -85,6 +87,8 @@ namespace Frida {
|
||||
|
||||
yield transmitter.close (cancellable);
|
||||
|
||||
GLib.info ("detach-trace: BaseAgentSession.close() done id=%s", id.handle);
|
||||
|
||||
close_request.resolve (true);
|
||||
}
|
||||
|
||||
@@ -276,6 +280,8 @@ namespace Frida {
|
||||
transmitter.new_candidates.disconnect (on_transmitter_new_candidates);
|
||||
transmitter.candidate_gathering_done.disconnect (on_transmitter_candidate_gathering_done);
|
||||
|
||||
GLib.info ("detach-trace: BaseAgentSession emitting closed id=%s", id.handle);
|
||||
|
||||
closed ();
|
||||
}
|
||||
|
||||
|
||||
+25
-20
@@ -130,6 +130,10 @@ if host_os_family == 'darwin'
|
||||
add_languages('objc', 'objcpp', native: false)
|
||||
endif
|
||||
|
||||
if meson.is_cross_build()
|
||||
add_languages('c', 'cpp', 'vala', native: true)
|
||||
endif
|
||||
|
||||
python = import('python').find_installation()
|
||||
|
||||
if cc.get_argument_syntax() == 'msvc'
|
||||
@@ -329,18 +333,6 @@ if get_option('b_sanitize') == 'address'
|
||||
cdata.set('HAVE_ASAN', 1)
|
||||
endif
|
||||
|
||||
have_in_tree_payloads = get_option('default_library') != 'shared'
|
||||
gum_dep = dependency('frida-gum-1.0', default_options: [
|
||||
'default_library=static',
|
||||
f'frida_version=@frida_version@',
|
||||
'gumjs=' + (have_in_tree_payloads ? 'enabled' : 'disabled'),
|
||||
'inspector=enabled',
|
||||
])
|
||||
if have_in_tree_payloads
|
||||
gumjs_dep = dependency('frida-gumjs-1.0', default_options: ['default_library=static'])
|
||||
endif
|
||||
gumjs_inspector_dep = dependency('frida-gumjs-inspector-1.0', default_options: ['default_library=static'])
|
||||
|
||||
glib_version_req = '>=2.74'
|
||||
glib_options = [
|
||||
'default_library=static',
|
||||
@@ -359,14 +351,6 @@ if optimize_for_prod
|
||||
]
|
||||
endif
|
||||
|
||||
gee_options = [
|
||||
'default_library=static',
|
||||
'disable-introspection=true',
|
||||
]
|
||||
if optimize_for_prod
|
||||
gee_options += 'disable-internal-asserts=true'
|
||||
endif
|
||||
|
||||
glib_dep = dependency('glib-2.0', version: glib_version_req, default_options: glib_options)
|
||||
if glib_dep.type_name() == 'internal'
|
||||
have_shared_glib = get_option('default_library') != 'static'
|
||||
@@ -385,6 +369,27 @@ if have_frida_glib
|
||||
cdata.set('HAVE_FRIDA_GLIB', 1)
|
||||
vala_flags += '--define=HAVE_FRIDA_GLIB'
|
||||
endif
|
||||
|
||||
have_in_tree_payloads = not have_shared_glib
|
||||
gum_dep = dependency('frida-gum-1.0', default_options: [
|
||||
'default_library=static',
|
||||
f'frida_version=@frida_version@',
|
||||
'gumjs=' + (have_in_tree_payloads ? 'enabled' : 'disabled'),
|
||||
'inspector=enabled',
|
||||
])
|
||||
if have_in_tree_payloads
|
||||
gumjs_dep = dependency('frida-gumjs-1.0', default_options: ['default_library=static'])
|
||||
endif
|
||||
gumjs_inspector_dep = dependency('frida-gumjs-inspector-1.0', default_options: ['default_library=static'])
|
||||
|
||||
gee_options = [
|
||||
'default_library=static',
|
||||
'disable-introspection=true',
|
||||
]
|
||||
if optimize_for_prod
|
||||
gee_options += 'disable-internal-asserts=true'
|
||||
endif
|
||||
|
||||
gobject_dep = dependency('gobject-2.0')
|
||||
gmodule_dep = dependency('gmodule-2.0')
|
||||
gio_dep = dependency('gio-2.0')
|
||||
|
||||
+1
-1
Submodule releng updated: db122dd1f0...abb8238970.
+87
-4
@@ -12,6 +12,7 @@ namespace Frida.Server {
|
||||
private static string? directory = null;
|
||||
#if !WINDOWS && !TVOS
|
||||
private static bool daemonize = false;
|
||||
private static string? output_path = null;
|
||||
#endif
|
||||
private static string? softener_flavor_str = null;
|
||||
private static bool enable_preload = true;
|
||||
@@ -42,6 +43,7 @@ namespace Frida.Server {
|
||||
{ "directory", 'd', 0, OptionArg.STRING, ref directory, "Store binaries in DIRECTORY", "DIRECTORY" },
|
||||
#if !WINDOWS && !TVOS
|
||||
{ "daemonize", 'D', 0, OptionArg.NONE, ref daemonize, "Detach and become a daemon", null },
|
||||
{ "output", 'o', 0, OptionArg.FILENAME, ref output_path, "Redirect output to FILE", "FILE" },
|
||||
#endif
|
||||
{ "policy-softener", 0, 0, OptionArg.STRING, ref softener_flavor_str, "Select policy softener", "system|internal" },
|
||||
{ "disable-preload", 'P', OptionFlags.REVERSE, OptionArg.NONE, ref enable_preload, "Disable preload optimization", null },
|
||||
@@ -78,6 +80,13 @@ namespace Frida.Server {
|
||||
|
||||
Environment.set_verbose_logging_enabled (verbose);
|
||||
|
||||
#if !WINDOWS && !TVOS
|
||||
if (output_path != null && !daemonize) {
|
||||
if (!redirect_output_to (output_path))
|
||||
return 6;
|
||||
}
|
||||
#endif
|
||||
|
||||
EndpointParameters endpoint_params;
|
||||
try {
|
||||
endpoint_params = new EndpointParameters (listen_address, 0, parse_certificate (certpath), origin,
|
||||
@@ -156,12 +165,15 @@ namespace Frida.Server {
|
||||
Posix.setsid ();
|
||||
|
||||
var null_in = Posix.open ("/dev/null", Posix.O_RDONLY);
|
||||
var null_out = Posix.open ("/dev/null", Posix.O_WRONLY);
|
||||
Posix.dup2 (null_in, Posix.STDIN_FILENO);
|
||||
Posix.dup2 (null_out, Posix.STDOUT_FILENO);
|
||||
Posix.dup2 (null_out, Posix.STDERR_FILENO);
|
||||
Posix.close (null_in);
|
||||
Posix.close (null_out);
|
||||
|
||||
if (output_path == null || !redirect_output_to (output_path)) {
|
||||
var null_out = Posix.open ("/dev/null", Posix.O_WRONLY);
|
||||
Posix.dup2 (null_out, Posix.STDOUT_FILENO);
|
||||
Posix.dup2 (null_out, Posix.STDERR_FILENO);
|
||||
Posix.close (null_out);
|
||||
}
|
||||
}
|
||||
|
||||
var status = new uint8[1];
|
||||
@@ -218,6 +230,22 @@ namespace Frida.Server {
|
||||
return application.run ();
|
||||
}
|
||||
|
||||
#if !WINDOWS && !TVOS
|
||||
private static bool redirect_output_to (string path) {
|
||||
int fd = Posix.open (path, Posix.O_WRONLY | Posix.O_CREAT | Posix.O_APPEND, 0644);
|
||||
if (fd == -1) {
|
||||
printerr ("Unable to open “%s”: %s\n", path, Posix.strerror (Posix.errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
Posix.dup2 (fd, Posix.STDOUT_FILENO);
|
||||
Posix.dup2 (fd, Posix.STDERR_FILENO);
|
||||
Posix.close (fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Environment {
|
||||
public extern void init ();
|
||||
public extern void set_verbose_logging_enabled (bool enabled);
|
||||
@@ -274,6 +302,15 @@ namespace Frida.Server {
|
||||
return false;
|
||||
});
|
||||
|
||||
#if !WINDOWS && !TVOS
|
||||
if (!daemonize)
|
||||
process_commands.begin ();
|
||||
#elif !WINDOWS
|
||||
process_commands.begin ();
|
||||
#endif
|
||||
|
||||
start_alive_task_dumper ();
|
||||
|
||||
exit_code = 0;
|
||||
|
||||
loop.run ();
|
||||
@@ -281,6 +318,45 @@ namespace Frida.Server {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
#if !WINDOWS
|
||||
// TEMPORARY DEBUG HACK: type “t” + Enter to dump alive GTasks.
|
||||
private async void process_commands () {
|
||||
var input = new DataInputStream (new UnixInputStream (Posix.STDIN_FILENO, false));
|
||||
|
||||
while (true) {
|
||||
string? line;
|
||||
try {
|
||||
line = yield input.read_line_async (Priority.DEFAULT, io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
return;
|
||||
}
|
||||
if (line == null)
|
||||
return;
|
||||
|
||||
switch (line.strip ()) {
|
||||
case "t":
|
||||
g_task_print_alive_tasks ();
|
||||
break;
|
||||
case "":
|
||||
break;
|
||||
default:
|
||||
printerr ("Unknown command; try “t”\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// TEMPORARY DEBUG HACK: dump alive GTasks every 30 seconds.
|
||||
private void start_alive_task_dumper () {
|
||||
var source = new TimeoutSource.seconds (30);
|
||||
source.set_callback (() => {
|
||||
GLib.info ("%s", g_task_describe_alive_tasks ());
|
||||
return Source.CONTINUE;
|
||||
});
|
||||
source.attach (MainContext.default ());
|
||||
}
|
||||
|
||||
private async void start () {
|
||||
try {
|
||||
if (device_id != null && device_id != "local") {
|
||||
@@ -355,4 +431,11 @@ namespace Frida.Server {
|
||||
|
||||
return new TlsCertificate.from_file (path);
|
||||
}
|
||||
|
||||
// TEMPORARY DEBUG HACK: only present when GLib is built with G_ENABLE_DEBUG.
|
||||
[CCode (cname = "g_task_print_alive_tasks")]
|
||||
private extern void g_task_print_alive_tasks ();
|
||||
|
||||
[CCode (cname = "g_task_describe_alive_tasks")]
|
||||
private extern string g_task_describe_alive_tasks ();
|
||||
}
|
||||
@@ -661,6 +661,7 @@ def parse_api(frida_version, frida_version_components, api_version, toplevel_cod
|
||||
enum_types = []
|
||||
|
||||
base_public_types = {
|
||||
"SpawnGatingOptions": "ProcessMatchOptions",
|
||||
"FrontmostQueryOptions": "SpawnOptions",
|
||||
"ApplicationQueryOptions": "FrontmostQueryOptions",
|
||||
"ProcessQueryOptions": "ApplicationQueryOptions",
|
||||
|
||||
@@ -286,6 +286,11 @@ namespace Frida {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
Generated
+4
-4
@@ -8,7 +8,7 @@
|
||||
"name": "frida-barebone-script-runtime",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@types/frida-gum": "^19.6.0",
|
||||
"@types/frida-gum": "^19.10.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
@@ -439,9 +439,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "19.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.6.0.tgz",
|
||||
"integrity": "sha512-wEcTiUfIxtSkMXilYkaRnHtASCtzF+X1Ei8ygQg33/HfTZ+IiMNvykhHJvGZ8GjLqu/5cVt+ILZxTyEHw67pnA==",
|
||||
"version": "19.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.10.0.tgz",
|
||||
"integrity": "sha512-GBKAzwmamtNPBXEcIve1yJ6BB9ZgyW9al3+f06S+lDAvennOx9GfFQ9RDY9kP5nJrkiiRHLzgbDFjQ/+g+iw1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/frida-gum": "^19.6.0",
|
||||
"@types/frida-gum": "^19.10.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
|
||||
Generated
+4
-4
@@ -33,7 +33,7 @@
|
||||
"@frida/url": "^1.0.2",
|
||||
"@frida/util": "^1.0.3",
|
||||
"@frida/vm": "^2.0.0",
|
||||
"@types/frida-gum": "^19.6.0",
|
||||
"@types/frida-gum": "^19.10.0",
|
||||
"@types/node": "18.19.103",
|
||||
"frida-fs": "7.0.0"
|
||||
}
|
||||
@@ -248,9 +248,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "19.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.6.0.tgz",
|
||||
"integrity": "sha512-wEcTiUfIxtSkMXilYkaRnHtASCtzF+X1Ei8ygQg33/HfTZ+IiMNvykhHJvGZ8GjLqu/5cVt+ILZxTyEHw67pnA==",
|
||||
"version": "19.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.10.0.tgz",
|
||||
"integrity": "sha512-GBKAzwmamtNPBXEcIve1yJ6BB9ZgyW9al3+f06S+lDAvennOx9GfFQ9RDY9kP5nJrkiiRHLzgbDFjQ/+g+iw1Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@frida/url": "^1.0.2",
|
||||
"@frida/util": "^1.0.3",
|
||||
"@frida/vm": "^2.0.0",
|
||||
"@types/frida-gum": "^19.6.0",
|
||||
"@types/frida-gum": "^19.10.0",
|
||||
"@types/node": "18.19.103",
|
||||
"frida-fs": "7.0.0"
|
||||
}
|
||||
|
||||
+59
-18
@@ -122,6 +122,7 @@ namespace Frida {
|
||||
|
||||
private void assign_session (HostSession session, HostSessionProvider provider) {
|
||||
host_session = session;
|
||||
host_session.spawn_gating_disabled.connect (on_spawn_gating_disabled);
|
||||
host_session.spawn_added.connect (notify_spawn_added);
|
||||
host_session.child_added.connect (notify_child_added);
|
||||
host_session.child_removed.connect (notify_child_removed);
|
||||
@@ -389,18 +390,30 @@ namespace Frida {
|
||||
public void kick_authentication_channel (AuthenticationChannel channel) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
channel.connection.close.begin (io_cancellable);
|
||||
perform_kick.begin (channel);
|
||||
return false;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
|
||||
private async void perform_kick (AuthenticationChannel channel) {
|
||||
try {
|
||||
yield channel.connection.flush (io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
try {
|
||||
yield channel.connection.close (io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
public async void teardown_control_channel (ControlChannel channel) {
|
||||
yield parent.teardown_control_channel (channel);
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (ControlChannel requester) throws GLib.Error {
|
||||
yield parent.enable_spawn_gating (requester);
|
||||
public async void enable_spawn_gating (HashTable<string, Variant> options, ControlChannel requester)
|
||||
throws GLib.Error {
|
||||
yield parent.enable_spawn_gating (options, requester);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (ControlChannel requester) throws GLib.Error {
|
||||
@@ -538,20 +551,27 @@ namespace Frida {
|
||||
return channels.iterator ();
|
||||
}
|
||||
|
||||
private async void enable_spawn_gating (ControlChannel requester) throws GLib.Error {
|
||||
private async void enable_spawn_gating (HashTable<string, Variant> options, ControlChannel requester)
|
||||
throws GLib.Error {
|
||||
bool is_first = spawn_gaters.is_empty;
|
||||
spawn_gaters.add (requester);
|
||||
foreach (var spawn in pending_spawn.values)
|
||||
spawn.pending_approvers.add (requester);
|
||||
|
||||
if (is_first)
|
||||
yield host_session.enable_spawn_gating (io_cancellable);
|
||||
yield host_session.enable_spawn_gating_with_options (options, io_cancellable);
|
||||
}
|
||||
|
||||
private async void disable_spawn_gating (ControlChannel requester) throws GLib.Error {
|
||||
if (spawn_gaters.remove (requester)) {
|
||||
foreach (uint pid in pending_spawn.keys.to_array ())
|
||||
host_session.resume.begin (pid, io_cancellable);
|
||||
foreach (uint pid in pending_spawn.keys.to_array ()) {
|
||||
PendingSpawn spawn = pending_spawn[pid];
|
||||
var approvers = spawn.pending_approvers;
|
||||
if (approvers.remove (requester) && approvers.is_empty) {
|
||||
forget_pending_spawn (pid, spawn);
|
||||
host_session.resume.begin (pid, io_cancellable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spawn_gaters.is_empty)
|
||||
@@ -576,11 +596,8 @@ namespace Frida {
|
||||
var approvers = spawn.pending_approvers;
|
||||
approvers.remove (requester);
|
||||
if (approvers.is_empty) {
|
||||
pending_spawn.unset (pid);
|
||||
|
||||
forget_pending_spawn (pid, spawn);
|
||||
yield host_session.resume (pid, io_cancellable);
|
||||
|
||||
notify_spawn_removed (spawn.info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -687,11 +704,29 @@ namespace Frida {
|
||||
return id;
|
||||
}
|
||||
|
||||
// Backend disabled gating and already resumed everything; drop our mirror, tell the gaters.
|
||||
private void on_spawn_gating_disabled (SpawnGatingDisabledReason reason) {
|
||||
foreach (var spawn in pending_spawn.values.to_array ())
|
||||
notify_spawn_removed (spawn.info);
|
||||
pending_spawn.clear ();
|
||||
|
||||
foreach (ControlChannel channel in spawn_gaters)
|
||||
channel.spawn_gating_disabled (reason);
|
||||
spawn_gaters.clear ();
|
||||
}
|
||||
|
||||
private void notify_spawn_added (HostSpawnInfo info) {
|
||||
pending_spawn[info.pid] = new PendingSpawn (info.pid, info.identifier, spawn_gaters.iterator ());
|
||||
|
||||
foreach (ControlChannel channel in spawn_gaters)
|
||||
channel.spawn_added (info);
|
||||
}
|
||||
|
||||
private void forget_pending_spawn (uint pid, PendingSpawn spawn) {
|
||||
pending_spawn.unset (pid);
|
||||
notify_spawn_removed (spawn.info);
|
||||
}
|
||||
|
||||
private void notify_spawn_removed (HostSpawnInfo info) {
|
||||
foreach (ControlChannel channel in spawn_gaters)
|
||||
channel.spawn_removed (info);
|
||||
@@ -727,12 +762,13 @@ namespace Frida {
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
AgentSessionEntry entry;
|
||||
if (agent_sessions.unset (id, out entry)) {
|
||||
ControlChannel? controller = entry.controller;
|
||||
if (controller != null) {
|
||||
controller.agent_sessions.remove (id);
|
||||
controller.agent_session_detached (id, reason, crash);
|
||||
}
|
||||
bool entry_found = agent_sessions.unset (id, out entry);
|
||||
ControlChannel? controller = entry_found ? entry.controller : null;
|
||||
GLib.info ("detach-trace: ControlService session detached id=%s reason=%d entry_found=%s controller=%p",
|
||||
id.handle, reason, entry_found.to_string (), controller);
|
||||
if (controller != null) {
|
||||
controller.agent_sessions.remove (id);
|
||||
controller.agent_session_detached (id, reason, crash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -926,7 +962,12 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws GLib.Error {
|
||||
yield parent.enable_spawn_gating (this);
|
||||
yield parent.enable_spawn_gating (make_parameters_dict (), this);
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
yield parent.enable_spawn_gating (options, this);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws GLib.Error {
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace Frida {
|
||||
|
||||
construct {
|
||||
helper.output.connect (on_output);
|
||||
helper.gating_cancelled.connect (on_spawn_gating_cancelled);
|
||||
helper.spawn_added.connect (on_spawn_added);
|
||||
helper.spawn_removed.connect (on_spawn_removed);
|
||||
|
||||
@@ -80,6 +81,7 @@ namespace Frida {
|
||||
|
||||
#if IOS || TVOS
|
||||
fruit_controller = new FruitController (this, io_cancellable);
|
||||
fruit_controller.gating_cancelled.connect (on_spawn_gating_cancelled);
|
||||
fruit_controller.spawn_added.connect (on_spawn_added);
|
||||
fruit_controller.spawn_removed.connect (on_spawn_removed);
|
||||
fruit_controller.process_crashed.connect (on_process_crashed);
|
||||
@@ -91,6 +93,7 @@ namespace Frida {
|
||||
|
||||
#if IOS || TVOS
|
||||
yield fruit_controller.close (cancellable);
|
||||
fruit_controller.gating_cancelled.disconnect (on_spawn_gating_cancelled);
|
||||
fruit_controller.spawn_added.disconnect (on_spawn_added);
|
||||
fruit_controller.spawn_removed.disconnect (on_spawn_removed);
|
||||
fruit_controller.process_crashed.disconnect (on_process_crashed);
|
||||
@@ -113,6 +116,7 @@ namespace Frida {
|
||||
|
||||
yield helper.close (cancellable);
|
||||
helper.output.disconnect (on_output);
|
||||
helper.gating_cancelled.disconnect (on_spawn_gating_cancelled);
|
||||
helper.spawn_added.disconnect (on_spawn_added);
|
||||
helper.spawn_removed.disconnect (on_spawn_removed);
|
||||
|
||||
@@ -182,11 +186,17 @@ namespace Frida {
|
||||
return yield process_enumerator.enumerate_processes (ProcessQueryOptions._deserialize (options));
|
||||
}
|
||||
|
||||
public override async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
public override async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var scope = SpawnGatingOptions._deserialize (options).scope;
|
||||
#if IOS || TVOS
|
||||
if (scope == PROCESSES)
|
||||
throw new Error.NOT_SUPPORTED (
|
||||
"Process-level gating is unavailable on this OS; use the 'applications' scope");
|
||||
|
||||
yield fruit_controller.enable_spawn_gating (cancellable);
|
||||
#else
|
||||
yield helper.enable_spawn_gating (cancellable);
|
||||
yield helper.enable_spawn_gating (scope, cancellable);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -196,6 +206,8 @@ namespace Frida {
|
||||
#else
|
||||
yield helper.disable_spawn_gating (cancellable);
|
||||
#endif
|
||||
|
||||
spawn_gating_disabled (APPLICATION_REQUESTED);
|
||||
}
|
||||
|
||||
public override async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -218,7 +230,16 @@ namespace Frida {
|
||||
return yield fruit_controller.spawn (path, options, cancellable);
|
||||
#endif
|
||||
|
||||
return yield helper.spawn (path, options, cancellable);
|
||||
UnixInputStream? stdin_stream = null;
|
||||
UnixOutputStream? stdout_stream = null;
|
||||
UnixOutputStream? stderr_stream = null;
|
||||
if (options.stdio == INHERIT) {
|
||||
stdin_stream = new UnixInputStream (0, false);
|
||||
stdout_stream = new UnixOutputStream (1, false);
|
||||
stderr_stream = new UnixOutputStream (2, false);
|
||||
}
|
||||
|
||||
return yield helper.spawn (path, options, stdin_stream, stdout_stream, stderr_stream, cancellable);
|
||||
}
|
||||
|
||||
protected override async void await_exec_transition (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -278,6 +299,10 @@ namespace Frida {
|
||||
output (pid, fd, data);
|
||||
}
|
||||
|
||||
private void on_spawn_gating_cancelled () {
|
||||
spawn_gating_disabled (WATCHDOG_TIMEOUT);
|
||||
}
|
||||
|
||||
private void on_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
@@ -353,6 +378,7 @@ namespace Frida {
|
||||
|
||||
#if IOS || TVOS
|
||||
private sealed class FruitController : Object, MappedAgentContainer {
|
||||
public signal void gating_cancelled ();
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
public signal void process_crashed (CrashInfo crash);
|
||||
@@ -377,6 +403,7 @@ namespace Frida {
|
||||
private bool spawn_gating_enabled = false;
|
||||
private Gee.HashMap<string, Promise<uint>> spawn_requests = new Gee.HashMap<string, Promise<uint>> ();
|
||||
private Gee.HashMap<uint, HostSpawnInfo?> pending_spawn = new Gee.HashMap<uint, HostSpawnInfo?> ();
|
||||
private SpawnGatingWatchdog watchdog = new SpawnGatingWatchdog ();
|
||||
|
||||
private CrashReporterState crash_reporter_state;
|
||||
private Gee.HashMap<uint, ReportCrashAgent> crash_agents = new Gee.HashMap<uint, ReportCrashAgent> ();
|
||||
@@ -415,6 +442,8 @@ namespace Frida {
|
||||
|
||||
helper.process_resumed.connect (on_process_resumed);
|
||||
helper.process_killed.connect (on_process_killed);
|
||||
|
||||
watchdog.expired.connect (on_watchdog_expired);
|
||||
}
|
||||
|
||||
~FruitController () {
|
||||
@@ -448,6 +477,8 @@ namespace Frida {
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
spawn_gating_enabled = false;
|
||||
|
||||
watchdog.clear ();
|
||||
|
||||
yield launchd_agent.disable_spawn_gating (cancellable);
|
||||
|
||||
var pending = pending_spawn.values.to_array ();
|
||||
@@ -512,6 +543,7 @@ namespace Frida {
|
||||
HostSpawnInfo? info;
|
||||
if (!pending_spawn.unset (pid, out info))
|
||||
return false;
|
||||
watchdog.cancel (pid);
|
||||
spawn_removed (info);
|
||||
|
||||
yield helper.resume (pid, cancellable);
|
||||
@@ -644,6 +676,11 @@ namespace Frida {
|
||||
launchd_agent.claim_process.begin (pid, io_cancellable);
|
||||
}
|
||||
|
||||
private void on_watchdog_expired () {
|
||||
disable_spawn_gating.begin (io_cancellable);
|
||||
gating_cancelled ();
|
||||
}
|
||||
|
||||
private async void handle_spawn (HostSpawnInfo info) {
|
||||
try {
|
||||
var pid = info.pid;
|
||||
@@ -662,6 +699,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
pending_spawn[pid] = info;
|
||||
watchdog.arm (pid);
|
||||
spawn_added (info);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <gio/gunixinputstream.h>
|
||||
#include <gio/gunixoutputstream.h>
|
||||
#include <glib-unix.h>
|
||||
#include <spawn.h>
|
||||
#include <stdio.h>
|
||||
@@ -793,7 +795,8 @@ _frida_darwin_helper_backend_schedule_on_dispatch_queue (FridaDarwinHelperBacken
|
||||
|
||||
guint
|
||||
_frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar * path,
|
||||
FridaHostSpawnOptions * options, FridaStdioPipes ** pipes, GError ** error)
|
||||
FridaHostSpawnOptions * options, GUnixInputStream * stdin_stream, GUnixOutputStream * stdout_stream,
|
||||
GUnixOutputStream * stderr_stream, FridaStdioPipes ** pipes, GError ** error)
|
||||
{
|
||||
pid_t pid;
|
||||
FridaSpawnInstance * instance;
|
||||
@@ -830,11 +833,17 @@ _frida_darwin_helper_backend_spawn (FridaDarwinHelperBackend * self, const gchar
|
||||
if (stdio_error != NULL)
|
||||
goto propagate_stdio_error;
|
||||
|
||||
if (in_fd != NULL)
|
||||
if (stdin_stream != NULL)
|
||||
posix_spawn_file_actions_adddup2 (&file_actions, g_unix_input_stream_get_fd (stdin_stream), 0);
|
||||
else if (in_fd != NULL)
|
||||
posix_spawn_file_actions_adddup2 (&file_actions, in_fd->handle, 0);
|
||||
if (out_fd != NULL)
|
||||
if (stdout_stream != NULL)
|
||||
posix_spawn_file_actions_adddup2 (&file_actions, g_unix_output_stream_get_fd (stdout_stream), 1);
|
||||
else if (out_fd != NULL)
|
||||
posix_spawn_file_actions_adddup2 (&file_actions, out_fd->handle, 1);
|
||||
if (err_fd != NULL)
|
||||
if (stderr_stream != NULL)
|
||||
posix_spawn_file_actions_adddup2 (&file_actions, g_unix_output_stream_get_fd (stderr_stream), 2);
|
||||
else if (err_fd != NULL)
|
||||
posix_spawn_file_actions_adddup2 (&file_actions, err_fd->handle, 2);
|
||||
|
||||
aslr_value = g_hash_table_lookup (options->aux, "aslr");
|
||||
@@ -2873,7 +2882,9 @@ frida_spawn_instance_on_server_recv (void * context)
|
||||
thread_state_flavor_t state_flavor = GUM_DARWIN_THREAD_STATE_FLAVOR;
|
||||
mach_msg_type_number_t state_count = GUM_DARWIN_THREAD_STATE_COUNT;
|
||||
GumDarwinUnifiedThreadState state;
|
||||
#if __has_feature (ptrauth_calls)
|
||||
gboolean pc_may_need_fixup_due_to_ptrauth_failure;
|
||||
#endif
|
||||
guint i, current_bp_index;
|
||||
FridaBreakpoint * breakpoint = NULL;
|
||||
gboolean carry_on, pc_changed;
|
||||
@@ -2909,8 +2920,6 @@ frida_spawn_instance_on_server_recv (void * context)
|
||||
__darwin_arm_thread_state64_set_pc_fptr (state.ts_64, GSIZE_TO_POINTER (gum_sign_code_address (ret_gadget)));
|
||||
}
|
||||
}
|
||||
#else
|
||||
pc_may_need_fixup_due_to_ptrauth_failure = FALSE;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_I386
|
||||
|
||||
@@ -49,6 +49,7 @@ namespace Frida {
|
||||
|
||||
dtrace_agent = DTraceAgent.try_open ();
|
||||
if (dtrace_agent != null) {
|
||||
dtrace_agent.gating_cancelled.connect (on_dtrace_agent_gating_cancelled);
|
||||
dtrace_agent.spawn_added.connect (on_dtrace_agent_spawn_added);
|
||||
dtrace_agent.spawn_removed.connect (on_dtrace_agent_spawn_removed);
|
||||
}
|
||||
@@ -97,6 +98,7 @@ namespace Frida {
|
||||
yield;
|
||||
|
||||
if (dtrace_agent != null) {
|
||||
dtrace_agent.gating_cancelled.disconnect (on_dtrace_agent_gating_cancelled);
|
||||
dtrace_agent.spawn_added.disconnect (on_dtrace_agent_spawn_added);
|
||||
dtrace_agent.spawn_removed.disconnect (on_dtrace_agent_spawn_removed);
|
||||
yield dtrace_agent.close (cancellable);
|
||||
@@ -107,8 +109,8 @@ namespace Frida {
|
||||
public async void preload (Cancellable? cancellable) throws Error, IOError {
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
get_dtrace_agent ().enable_spawn_gating ();
|
||||
public async void enable_spawn_gating (SpawnGatingScope scope, Cancellable? cancellable) throws Error, IOError {
|
||||
get_dtrace_agent ().enable_spawn_gating (scope);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -126,12 +128,13 @@ namespace Frida {
|
||||
return dtrace_agent;
|
||||
}
|
||||
|
||||
public async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, Cancellable? cancellable) throws Error, IOError {
|
||||
if (!FileUtils.test (path, EXISTS))
|
||||
throw new Error.EXECUTABLE_NOT_FOUND ("Unable to find executable at '%s'", path);
|
||||
|
||||
StdioPipes? pipes;
|
||||
var child_pid = _spawn (path, options, out pipes);
|
||||
var child_pid = _spawn (path, options, stdin_stream, stdout_stream, stderr_stream, out pipes);
|
||||
|
||||
ChildWatch.add ((Pid) child_pid, on_child_dead);
|
||||
|
||||
@@ -629,6 +632,10 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private void on_dtrace_agent_gating_cancelled () {
|
||||
gating_cancelled ();
|
||||
}
|
||||
|
||||
private void on_dtrace_agent_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
@@ -664,7 +671,8 @@ namespace Frida {
|
||||
protected extern void _destroy_dispatch_context ();
|
||||
protected extern void _schedule_on_dispatch_queue (DispatchWorker worker);
|
||||
|
||||
protected extern uint _spawn (string path, HostSpawnOptions options, out StdioPipes? pipes) throws Error;
|
||||
protected extern uint _spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, out StdioPipes? pipes) throws Error;
|
||||
protected extern static void _launch (string identifier, HostSpawnOptions options, LaunchCompletionHandler on_complete);
|
||||
protected extern static bool _is_suspended (uint task) throws Error;
|
||||
public extern static void resume_process (uint task) throws Error;
|
||||
@@ -688,15 +696,22 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public sealed class DTraceAgent : Object {
|
||||
public signal void gating_cancelled ();
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
|
||||
private Subprocess? dtrace;
|
||||
private DataInputStream input;
|
||||
private SpawnGatingScope scope;
|
||||
private Gee.HashMap<uint, HostSpawnInfo?> pending_spawn = new Gee.HashMap<uint, HostSpawnInfo?> ();
|
||||
private SpawnGatingWatchdog watchdog = new SpawnGatingWatchdog ();
|
||||
|
||||
private Cancellable io_cancellable = new Cancellable ();
|
||||
|
||||
// Present in every path inside an app bundle, so APPLICATIONS gates the whole app
|
||||
// (helpers, XPC services, ...), not just its entry point.
|
||||
private const string APP_BUNDLE_MARKER = ".app/";
|
||||
|
||||
public static DTraceAgent? try_open () {
|
||||
if (Posix.getuid () != 0)
|
||||
return null;
|
||||
@@ -704,6 +719,15 @@ namespace Frida {
|
||||
return new DTraceAgent ();
|
||||
}
|
||||
|
||||
construct {
|
||||
watchdog.expired.connect (on_watchdog_expired);
|
||||
}
|
||||
|
||||
private void on_watchdog_expired () {
|
||||
disable_spawn_gating.begin (io_cancellable);
|
||||
gating_cancelled ();
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
if (dtrace != null) {
|
||||
try {
|
||||
@@ -714,10 +738,12 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public void enable_spawn_gating () throws Error {
|
||||
public void enable_spawn_gating (SpawnGatingScope scope) throws Error {
|
||||
if (dtrace != null)
|
||||
throw new Error.INVALID_OPERATION ("Already enabled");
|
||||
|
||||
this.scope = scope;
|
||||
|
||||
string? predicate = Environment.get_variable ("FRIDA_DTRACE_PREDICATE");
|
||||
string predicate_clause;
|
||||
if (predicate != null)
|
||||
@@ -776,6 +802,7 @@ namespace Frida {
|
||||
spawn_removed (e.value);
|
||||
}
|
||||
pending_spawn.clear ();
|
||||
watchdog.clear ();
|
||||
}
|
||||
|
||||
public HostSpawnInfo[] enumerate_pending_spawn () {
|
||||
@@ -788,8 +815,10 @@ namespace Frida {
|
||||
|
||||
public void on_resume (uint pid) {
|
||||
HostSpawnInfo? info;
|
||||
if (pending_spawn.unset (pid, out info))
|
||||
if (pending_spawn.unset (pid, out info)) {
|
||||
watchdog.cancel (pid);
|
||||
spawn_removed (info);
|
||||
}
|
||||
}
|
||||
|
||||
private async void process_incoming_messages () {
|
||||
@@ -837,8 +866,14 @@ namespace Frida {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scope != PROCESSES && !path.contains (APP_BUNDLE_MARKER)) {
|
||||
DarwinHelperBackend.resume_without_validation (pid);
|
||||
continue;
|
||||
}
|
||||
|
||||
var info = HostSpawnInfo (pid, path);
|
||||
pending_spawn[pid] = info;
|
||||
watchdog.arm (pid);
|
||||
spawn_added (info);
|
||||
}
|
||||
} catch (IOError e) {
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Frida {
|
||||
public async void preload (Cancellable? cancellable) throws Error, IOError {
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void enable_spawn_gating (SpawnGatingScope scope, Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
@@ -35,7 +35,8 @@ namespace Frida {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
public async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,10 +56,10 @@ namespace Frida {
|
||||
yield obtain (cancellable);
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
public async void enable_spawn_gating (SpawnGatingScope scope, Cancellable? cancellable) throws Error, IOError {
|
||||
var helper = yield obtain (cancellable);
|
||||
try {
|
||||
yield helper.enable_spawn_gating (cancellable);
|
||||
yield helper.enable_spawn_gating (scope, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_helper_error (e);
|
||||
}
|
||||
@@ -83,9 +83,12 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, Cancellable? cancellable) throws Error, IOError {
|
||||
var helper = yield obtain (cancellable);
|
||||
try {
|
||||
if (stdin_stream != null)
|
||||
return yield helper.spawn_with_stdio (path, options, stdin_stream, stdout_stream, stderr_stream, cancellable);
|
||||
return yield helper.spawn (path, options, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_helper_error (e);
|
||||
@@ -340,6 +343,9 @@ namespace Frida {
|
||||
GLib.SpawnFlags flags =
|
||||
GLib.SpawnFlags.LEAVE_DESCRIPTORS_OPEN |
|
||||
GLib.SpawnFlags.DO_NOT_REAP_CHILD |
|
||||
GLib.SpawnFlags.STDOUT_TO_DEV_NULL |
|
||||
GLib.SpawnFlags.STDERR_TO_DEV_NULL |
|
||||
/* GLib.SpawnFlags.STDIN_FROM_DEV_NULL */ 2048 |
|
||||
/* GLib.SpawnFlags.CLOEXEC_PIPES */ 256;
|
||||
|
||||
GLib.Process.spawn_async (null, argv, null, flags, null, out pending_pid);
|
||||
@@ -420,6 +426,7 @@ namespace Frida {
|
||||
|
||||
proxy = pending_proxy;
|
||||
proxy.output.connect (on_output);
|
||||
proxy.gating_cancelled.connect (on_gating_cancelled);
|
||||
proxy.spawn_added.connect (on_spawn_added);
|
||||
proxy.spawn_removed.connect (on_spawn_removed);
|
||||
proxy.injected.connect (on_injected);
|
||||
@@ -439,6 +446,7 @@ namespace Frida {
|
||||
obtain_request = null;
|
||||
|
||||
proxy.output.disconnect (on_output);
|
||||
proxy.gating_cancelled.disconnect (on_gating_cancelled);
|
||||
proxy.spawn_added.disconnect (on_spawn_added);
|
||||
proxy.spawn_removed.disconnect (on_spawn_removed);
|
||||
proxy.injected.disconnect (on_injected);
|
||||
@@ -460,6 +468,10 @@ namespace Frida {
|
||||
output (pid, fd, data);
|
||||
}
|
||||
|
||||
private void on_gating_cancelled () {
|
||||
gating_cancelled ();
|
||||
}
|
||||
|
||||
private void on_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace Frida {
|
||||
construct {
|
||||
backend.idle.connect (on_backend_idle);
|
||||
backend.output.connect (on_backend_output);
|
||||
backend.gating_cancelled.connect (on_backend_gating_cancelled);
|
||||
backend.spawn_added.connect (on_backend_spawn_added);
|
||||
backend.spawn_removed.connect (on_backend_spawn_removed);
|
||||
backend.injected.connect (on_backend_injected);
|
||||
@@ -93,6 +94,7 @@ namespace Frida {
|
||||
}
|
||||
backend.idle.disconnect (on_backend_idle);
|
||||
backend.output.disconnect (on_backend_output);
|
||||
backend.gating_cancelled.disconnect (on_backend_gating_cancelled);
|
||||
backend.spawn_added.disconnect (on_backend_spawn_added);
|
||||
backend.spawn_removed.disconnect (on_backend_spawn_removed);
|
||||
backend.injected.disconnect (on_backend_injected);
|
||||
@@ -147,8 +149,8 @@ namespace Frida {
|
||||
shutdown.begin ();
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
yield backend.enable_spawn_gating (cancellable);
|
||||
public async void enable_spawn_gating (SpawnGatingScope scope, Cancellable? cancellable) throws Error, IOError {
|
||||
yield backend.enable_spawn_gating (scope, cancellable);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -160,7 +162,12 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
return yield backend.spawn (path, options, cancellable);
|
||||
return yield backend.spawn (path, options, null, null, null, cancellable);
|
||||
}
|
||||
|
||||
public async uint spawn_with_stdio (string path, HostSpawnOptions options, UnixInputStream stdin_stream,
|
||||
UnixOutputStream stdout_stream, UnixOutputStream stderr_stream, Cancellable? cancellable) throws Error, IOError {
|
||||
return yield backend.spawn (path, options, stdin_stream, stdout_stream, stderr_stream, cancellable);
|
||||
}
|
||||
|
||||
public async void launch (string identifier, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -237,6 +244,10 @@ namespace Frida {
|
||||
output (pid, fd, data);
|
||||
}
|
||||
|
||||
private void on_backend_gating_cancelled () {
|
||||
gating_cancelled ();
|
||||
}
|
||||
|
||||
private void on_backend_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
namespace Frida {
|
||||
public interface DarwinHelper : Object {
|
||||
public signal void output (uint pid, int fd, uint8[] data);
|
||||
public signal void gating_cancelled ();
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
public signal void injected (uint id, uint pid, bool has_mapped_module, DarwinModuleDetails mapped_module);
|
||||
@@ -16,10 +17,11 @@ namespace Frida {
|
||||
|
||||
public abstract async void preload (Cancellable? cancellable) throws Error, IOError;
|
||||
|
||||
public abstract async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void enable_spawn_gating (SpawnGatingScope scope, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async uint spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void launch (string identifier, HostSpawnOptions options,
|
||||
Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void notify_launch_completed (string identifier, uint pid,
|
||||
@@ -49,6 +51,7 @@ namespace Frida {
|
||||
[DBus (name = "re.frida.Helper")]
|
||||
public interface DarwinRemoteHelper : Object {
|
||||
public signal void output (uint pid, int fd, uint8[] data);
|
||||
public signal void gating_cancelled ();
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
public signal void injected (uint id, uint pid, bool has_mapped_module, DarwinModuleDetails mapped_module);
|
||||
@@ -58,10 +61,12 @@ namespace Frida {
|
||||
|
||||
public abstract async void stop (Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public abstract async void enable_spawn_gating (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void enable_spawn_gating (SpawnGatingScope scope, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void disable_spawn_gating (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async HostSpawnInfo[] enumerate_pending_spawn (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async uint spawn_with_stdio (string path, HostSpawnOptions options, UnixInputStream stdin_stream,
|
||||
UnixOutputStream stdout_stream, UnixOutputStream stderr_stream, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void launch (string identifier, HostSpawnOptions options, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void notify_launch_completed (string identifier, uint pid,
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
@@ -521,6 +521,13 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
// Forward the error untranslated so the caller can spot UNKNOWN_METHOD from an older remote.
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
var server = yield get_remote_server (cancellable);
|
||||
yield server.session.enable_spawn_gating_with_options (options, cancellable);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
var server = yield get_remote_server (cancellable);
|
||||
try {
|
||||
@@ -920,6 +927,7 @@ namespace Frida {
|
||||
server.connection.on_closed.connect (on_remote_connection_closed);
|
||||
|
||||
var session = server.session;
|
||||
session.spawn_gating_disabled.connect (on_remote_spawn_gating_disabled);
|
||||
session.spawn_added.connect (on_remote_spawn_added);
|
||||
session.spawn_removed.connect (on_remote_spawn_removed);
|
||||
session.child_added.connect (on_remote_child_added);
|
||||
@@ -934,6 +942,7 @@ namespace Frida {
|
||||
server.connection.on_closed.disconnect (on_remote_connection_closed);
|
||||
|
||||
var session = server.session;
|
||||
session.spawn_gating_disabled.disconnect (on_remote_spawn_gating_disabled);
|
||||
session.spawn_added.disconnect (on_remote_spawn_added);
|
||||
session.spawn_removed.disconnect (on_remote_spawn_removed);
|
||||
session.child_added.disconnect (on_remote_child_added);
|
||||
@@ -954,6 +963,10 @@ namespace Frida {
|
||||
on_remote_agent_session_detached (remote_id, CONNECTION_TERMINATED, no_crash);
|
||||
}
|
||||
|
||||
private void on_remote_spawn_gating_disabled (SpawnGatingDisabledReason reason) {
|
||||
spawn_gating_disabled (reason);
|
||||
}
|
||||
|
||||
private void on_remote_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
@@ -980,7 +993,11 @@ namespace Frida {
|
||||
|
||||
private void on_remote_agent_session_detached (AgentSessionId remote_id, SessionDetachReason reason, CrashInfo crash) {
|
||||
AgentSessionId? local_id;
|
||||
if (!remote_agent_sessions.unset (remote_id, out local_id))
|
||||
bool mapping_found = remote_agent_sessions.unset (remote_id, out local_id);
|
||||
GLib.info ("detach-trace: Droidy remote session detached remote_id=%s reason=%d mapping_found=%s local_id=%s",
|
||||
remote_id.handle, reason, mapping_found.to_string (),
|
||||
mapping_found ? local_id.handle : "-");
|
||||
if (!mapping_found)
|
||||
return;
|
||||
|
||||
bool agent_session_found = agent_sessions.unset (local_id);
|
||||
|
||||
@@ -77,7 +77,8 @@ namespace Frida {
|
||||
return yield process_enumerator.enumerate_processes (ProcessQueryOptions._deserialize (options));
|
||||
}
|
||||
|
||||
public override async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
public override async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
|
||||
}
|
||||
|
||||
|
||||
+53
-6
@@ -619,6 +619,14 @@ namespace Frida {
|
||||
* to processes.
|
||||
*/
|
||||
public sealed class Device : Object {
|
||||
/**
|
||||
* Emitted when spawn gating is disabled, either by request or because the host
|
||||
* cancelled it, e.g. a caught process was left unresumed long enough to risk
|
||||
* stalling process creation.
|
||||
*
|
||||
* @param reason why gating was disabled
|
||||
*/
|
||||
public signal void spawn_gating_disabled (SpawnGatingDisabledReason reason);
|
||||
/**
|
||||
* Emitted when a process is spawned while spawn gating is enabled.
|
||||
*
|
||||
@@ -1162,26 +1170,46 @@ namespace Frida {
|
||||
/**
|
||||
* Enables spawn gating, suspending every newly spawned process until it
|
||||
* is explicitly resumed.
|
||||
*
|
||||
* @param options optional settings, e.g. how much to gate
|
||||
*/
|
||||
public async void enable_spawn_gating (Cancellable? cancellable = null) throws Error, IOError {
|
||||
public async void enable_spawn_gating (SpawnGatingOptions? options = null,
|
||||
Cancellable? cancellable = null) throws Error, IOError {
|
||||
check_open ();
|
||||
|
||||
var scope = (options != null) ? options.scope : SpawnGatingScope.DEFAULT;
|
||||
var raw_options = (options != null) ? options._serialize () : make_parameters_dict ();
|
||||
|
||||
var host_session = yield get_host_session (cancellable);
|
||||
|
||||
try {
|
||||
yield host_session.enable_spawn_gating (cancellable);
|
||||
yield host_session.enable_spawn_gating_with_options (raw_options, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
DBusError.strip_remote_error (e);
|
||||
if (scope != DEFAULT || !(e is DBusError.UNKNOWN_METHOD))
|
||||
throw_dbus_error (e);
|
||||
|
||||
// Older remote without the scoped call; fall back to the plain one (that's DEFAULT).
|
||||
try {
|
||||
yield host_session.enable_spawn_gating (cancellable);
|
||||
} catch (GLib.Error legacy_error) {
|
||||
throw_dbus_error (legacy_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void enable_spawn_gating_sync (Cancellable? cancellable = null) throws Error, IOError {
|
||||
create<EnableSpawnGatingTask> ().execute (cancellable);
|
||||
public void enable_spawn_gating_sync (SpawnGatingOptions? options = null,
|
||||
Cancellable? cancellable = null) throws Error, IOError {
|
||||
var task = create<EnableSpawnGatingTask> ();
|
||||
task.options = options;
|
||||
task.execute (cancellable);
|
||||
}
|
||||
|
||||
private class EnableSpawnGatingTask : DeviceTask<void> {
|
||||
public SpawnGatingOptions? options;
|
||||
|
||||
protected override async void perform_operation () throws Error, IOError {
|
||||
yield parent.enable_spawn_gating (cancellable);
|
||||
yield parent.enable_spawn_gating (options, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1750,6 +1778,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void attach_host_session (HostSession session) {
|
||||
session.spawn_gating_disabled.connect (on_spawn_gating_disabled);
|
||||
session.spawn_added.connect (on_spawn_added);
|
||||
session.spawn_removed.connect (on_spawn_removed);
|
||||
session.child_added.connect (on_child_added);
|
||||
@@ -1760,6 +1789,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void detach_host_session (HostSession session) {
|
||||
session.spawn_gating_disabled.disconnect (on_spawn_gating_disabled);
|
||||
session.spawn_added.disconnect (on_spawn_added);
|
||||
session.spawn_removed.disconnect (on_spawn_removed);
|
||||
session.child_added.disconnect (on_child_added);
|
||||
@@ -1855,6 +1885,9 @@ namespace Frida {
|
||||
break;
|
||||
}
|
||||
}
|
||||
GLib.info ("detach-trace: Device._release_session id=%s may_block=%s",
|
||||
(session_id != null) ? session_id.handle : "-", may_block.to_string ());
|
||||
|
||||
if (session_id == null)
|
||||
return;
|
||||
|
||||
@@ -1870,11 +1903,16 @@ namespace Frida {
|
||||
} catch (Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
GLib.info ("detach-trace: Device._release_session resumed id=%s", session_id.handle);
|
||||
}
|
||||
}
|
||||
|
||||
private void on_agent_session_detached (AgentSessionId id, SessionDetachReason reason, CrashInfo crash) {
|
||||
var session = agent_sessions[id];
|
||||
GLib.info ("detach-trace: Device session detached id=%s reason=%d session=%p pending=%s",
|
||||
id.handle, reason, session, pending_detach_requests.has_key (id).to_string ());
|
||||
|
||||
if (session != null)
|
||||
session._on_detached (reason, crash);
|
||||
|
||||
@@ -1885,6 +1923,10 @@ namespace Frida {
|
||||
agent_sessions.unset (id);
|
||||
}
|
||||
|
||||
private void on_spawn_gating_disabled (SpawnGatingDisabledReason reason) {
|
||||
spawn_gating_disabled (reason);
|
||||
}
|
||||
|
||||
private void on_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (Spawn.from_info (info));
|
||||
}
|
||||
@@ -3803,15 +3845,20 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private async void close_session_and_peer_connection (Cancellable? cancellable) throws IOError {
|
||||
GLib.info ("detach-trace: Session closing remote id=%s", id.handle);
|
||||
|
||||
try {
|
||||
yield active_session.close (cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
GLib.info ("detach-trace: Session remote close failed id=%s error=%s", id.handle, e.message);
|
||||
if (e is IOError.CANCELLED) {
|
||||
discard_peer_connection ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GLib.info ("detach-trace: Session remote close returned id=%s", id.handle);
|
||||
|
||||
yield teardown_peer_connection (cancellable);
|
||||
}
|
||||
|
||||
|
||||
@@ -742,6 +742,13 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
// Forward the error untranslated so the caller can spot UNKNOWN_METHOD from an older remote.
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws GLib.Error {
|
||||
var server = yield get_remote_server (cancellable);
|
||||
yield server.session.enable_spawn_gating_with_options (options, cancellable);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
var server = yield get_remote_server (cancellable);
|
||||
try {
|
||||
@@ -1426,6 +1433,7 @@ namespace Frida {
|
||||
server.connection.on_closed.connect (on_remote_connection_closed);
|
||||
|
||||
var session = server.session;
|
||||
session.spawn_gating_disabled.connect (on_remote_spawn_gating_disabled);
|
||||
session.spawn_added.connect (on_remote_spawn_added);
|
||||
session.spawn_removed.connect (on_remote_spawn_removed);
|
||||
session.child_added.connect (on_remote_child_added);
|
||||
@@ -1440,6 +1448,7 @@ namespace Frida {
|
||||
server.connection.on_closed.disconnect (on_remote_connection_closed);
|
||||
|
||||
var session = server.session;
|
||||
session.spawn_gating_disabled.disconnect (on_remote_spawn_gating_disabled);
|
||||
session.spawn_added.disconnect (on_remote_spawn_added);
|
||||
session.spawn_removed.disconnect (on_remote_spawn_removed);
|
||||
session.child_added.disconnect (on_remote_child_added);
|
||||
@@ -1460,6 +1469,10 @@ namespace Frida {
|
||||
on_remote_agent_session_detached (remote_id, CONNECTION_TERMINATED, no_crash);
|
||||
}
|
||||
|
||||
private void on_remote_spawn_gating_disabled (SpawnGatingDisabledReason reason) {
|
||||
spawn_gating_disabled (reason);
|
||||
}
|
||||
|
||||
private void on_remote_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
|
||||
+13
-13
@@ -507,6 +507,19 @@ namespace Frida.Fruity.Injector {
|
||||
yield output.write_all_async (bytes.get_data (), io_priority, cancellable, out bytes_written);
|
||||
}
|
||||
|
||||
foreach (unowned Gum.DarwinSegment segment in module.segments.data) {
|
||||
uint64 address = segment.vm_address + slide;
|
||||
|
||||
var protect_command = lldb.make_buffer_builder ()
|
||||
.append_uint8 (UploadCommandType.PROTECT)
|
||||
.append_uint64 (address)
|
||||
.append_uint32 ((uint32) segment.vm_size)
|
||||
.append_uint32 ((uint32) segment.protection)
|
||||
.build ();
|
||||
yield output.write_all_async (protect_command.get_data (), io_priority, cancellable,
|
||||
out bytes_written);
|
||||
}
|
||||
|
||||
if (!threaded_items.is_empty) {
|
||||
var command = lldb.make_buffer_builder ()
|
||||
.append_uint8 (UploadCommandType.APPLY_THREADED)
|
||||
@@ -538,19 +551,6 @@ namespace Frida.Fruity.Injector {
|
||||
out bytes_written);
|
||||
}
|
||||
|
||||
foreach (unowned Gum.DarwinSegment segment in module.segments.data) {
|
||||
uint64 address = segment.vm_address + slide;
|
||||
|
||||
var protect_command = lldb.make_buffer_builder ()
|
||||
.append_uint8 (UploadCommandType.PROTECT)
|
||||
.append_uint64 (address)
|
||||
.append_uint32 ((uint32) segment.vm_size)
|
||||
.append_uint32 ((uint32) segment.protection)
|
||||
.build ();
|
||||
yield output.write_all_async (protect_command.get_data (), io_priority, cancellable,
|
||||
out bytes_written);
|
||||
}
|
||||
|
||||
var construct_commands = lldb.make_buffer_builder ();
|
||||
module.enumerate_init_pointers (ptrs => {
|
||||
construct_commands
|
||||
|
||||
@@ -484,7 +484,12 @@ namespace Frida {
|
||||
public abstract async HostProcessInfo[] enumerate_processes (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError;
|
||||
|
||||
public abstract async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError;
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
yield enable_spawn_gating_with_options (make_parameters_dict (), cancellable);
|
||||
}
|
||||
|
||||
public abstract async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError;
|
||||
|
||||
public abstract async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError;
|
||||
|
||||
@@ -866,6 +871,8 @@ namespace Frida {
|
||||
|
||||
private void on_agent_session_provider_closed (AgentSessionId id) {
|
||||
var closed_after_opening = agent_sessions.unset (id);
|
||||
GLib.info ("detach-trace: BaseDBusHostSession provider closed id=%s closed_after_opening=%s",
|
||||
id.handle, closed_after_opening.to_string ());
|
||||
if (!closed_after_opening)
|
||||
return;
|
||||
var reason = SessionDetachReason.APPLICATION_REQUESTED;
|
||||
@@ -968,7 +975,10 @@ namespace Frida {
|
||||
|
||||
var crash_info = (crash != null) ? crash : CrashInfo.empty ();
|
||||
foreach (var id in entry.sessions) {
|
||||
if (agent_sessions.unset (id))
|
||||
bool still_registered = agent_sessions.unset (id);
|
||||
GLib.info ("detach-trace: BaseDBusHostSession teardown id=%s reason=%d still_registered=%s",
|
||||
id.handle, reason, still_registered.to_string ());
|
||||
if (still_registered)
|
||||
agent_session_detached (id, reason, crash_info);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ namespace Frida {
|
||||
p.close ();
|
||||
}
|
||||
|
||||
public async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, Cancellable? cancellable) throws Error, IOError {
|
||||
if (!FileUtils.test (path, EXISTS))
|
||||
throw new Error.EXECUTABLE_NOT_FOUND ("Unable to find executable at '%s'", path);
|
||||
|
||||
@@ -56,7 +57,11 @@ namespace Frida {
|
||||
if (pid == 0) {
|
||||
Posix.setsid ();
|
||||
|
||||
if (pipes != null) {
|
||||
if (stdin_stream != null) {
|
||||
Posix.dup2 (stdin_stream.get_fd (), 0);
|
||||
Posix.dup2 (stdout_stream.get_fd (), 1);
|
||||
Posix.dup2 (stderr_stream.get_fd (), 2);
|
||||
} else if (pipes != null) {
|
||||
Posix.dup2 (in_fd.handle, 0);
|
||||
Posix.dup2 (out_fd.handle, 1);
|
||||
Posix.dup2 (err_fd.handle, 2);
|
||||
|
||||
@@ -54,9 +54,10 @@ namespace Frida {
|
||||
return _resource_store;
|
||||
}
|
||||
|
||||
public async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, Cancellable? cancellable) throws Error, IOError {
|
||||
var helper = yield obtain_for_path (path, cancellable);
|
||||
return yield helper.spawn (path, options, cancellable);
|
||||
return yield helper.spawn (path, options, stdin_stream, stdout_stream, stderr_stream, cancellable);
|
||||
}
|
||||
|
||||
public async void prepare_exec_transition (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -379,7 +380,12 @@ namespace Frida {
|
||||
} catch (Error e) {
|
||||
string[] argv = { helper_file.path, socket_address };
|
||||
|
||||
GLib.SpawnFlags flags = GLib.SpawnFlags.LEAVE_DESCRIPTORS_OPEN | /* GLib.SpawnFlags.CLOEXEC_PIPES */ 256;
|
||||
GLib.SpawnFlags flags =
|
||||
GLib.SpawnFlags.LEAVE_DESCRIPTORS_OPEN |
|
||||
GLib.SpawnFlags.STDOUT_TO_DEV_NULL |
|
||||
GLib.SpawnFlags.STDERR_TO_DEV_NULL |
|
||||
/* GLib.SpawnFlags.STDIN_FROM_DEV_NULL */ 2048 |
|
||||
/* GLib.SpawnFlags.CLOEXEC_PIPES */ 256;
|
||||
GLib.Process.spawn_async (null, argv, envp, flags, null, out pending_pid);
|
||||
}
|
||||
|
||||
@@ -498,8 +504,11 @@ namespace Frida {
|
||||
proxy.uninjected.disconnect (on_uninjected);
|
||||
}
|
||||
|
||||
public async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
public async uint spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, Cancellable? cancellable) throws Error, IOError {
|
||||
try {
|
||||
if (stdin_stream != null)
|
||||
return yield proxy.spawn_with_stdio (path, options, stdin_stream, stdout_stream, stderr_stream, cancellable);
|
||||
return yield proxy.spawn (path, options, cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
throw_dbus_error (e);
|
||||
|
||||
@@ -121,7 +121,12 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError {
|
||||
return yield backend.spawn (path, options, cancellable);
|
||||
return yield backend.spawn (path, options, null, null, null, cancellable);
|
||||
}
|
||||
|
||||
public async uint spawn_with_stdio (string path, HostSpawnOptions options, UnixInputStream stdin_stream,
|
||||
UnixOutputStream stdout_stream, UnixOutputStream stderr_stream, Cancellable? cancellable) throws Error, IOError {
|
||||
return yield backend.spawn (path, options, stdin_stream, stdout_stream, stderr_stream, cancellable);
|
||||
}
|
||||
|
||||
public async void prepare_exec_transition (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
|
||||
@@ -5,7 +5,8 @@ namespace Frida {
|
||||
|
||||
public abstract async void close (Cancellable? cancellable) throws IOError;
|
||||
|
||||
public abstract async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async uint spawn (string path, HostSpawnOptions options, UnixInputStream? stdin_stream,
|
||||
UnixOutputStream? stdout_stream, UnixOutputStream? stderr_stream, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void prepare_exec_transition (uint pid, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void await_exec_transition (uint pid, Cancellable? cancellable) throws Error, IOError;
|
||||
public abstract async void cancel_exec_transition (uint pid, Cancellable? cancellable) throws Error, IOError;
|
||||
@@ -50,6 +51,8 @@ namespace Frida {
|
||||
public abstract async void stop (Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public abstract async uint spawn (string path, HostSpawnOptions options, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async uint spawn_with_stdio (string path, HostSpawnOptions options, UnixInputStream stdin_stream,
|
||||
UnixOutputStream stdout_stream, UnixOutputStream stderr_stream, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void prepare_exec_transition (uint pid, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void await_exec_transition (uint pid, Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract async void cancel_exec_transition (uint pid, Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace Frida {
|
||||
|
||||
#if ANDROID
|
||||
robo_launcher = new RoboLauncher (this, io_cancellable);
|
||||
robo_launcher.gating_cancelled.connect (on_spawn_gating_cancelled);
|
||||
robo_launcher.spawn_added.connect (on_spawn_added);
|
||||
robo_launcher.spawn_removed.connect (on_spawn_removed);
|
||||
|
||||
@@ -97,6 +98,7 @@ namespace Frida {
|
||||
public override async void close (Cancellable? cancellable) throws IOError {
|
||||
#if ANDROID
|
||||
yield robo_launcher.close (cancellable);
|
||||
robo_launcher.gating_cancelled.disconnect (on_spawn_gating_cancelled);
|
||||
robo_launcher.spawn_added.disconnect (on_spawn_added);
|
||||
robo_launcher.spawn_removed.disconnect (on_spawn_removed);
|
||||
|
||||
@@ -110,6 +112,7 @@ namespace Frida {
|
||||
#endif
|
||||
|
||||
if (spawn_gater != null) {
|
||||
spawn_gater.gating_cancelled.disconnect (on_spawn_gating_cancelled);
|
||||
spawn_gater.spawn_added.disconnect (on_spawn_added);
|
||||
spawn_gater.spawn_removed.disconnect (on_spawn_removed);
|
||||
spawn_gater.stop ();
|
||||
@@ -276,25 +279,28 @@ namespace Frida {
|
||||
#endif
|
||||
}
|
||||
|
||||
public override async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
public override async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
var helper_process = helper as LinuxHelperProcess;
|
||||
if (helper_process != null)
|
||||
yield helper_process.preload (cancellable);
|
||||
|
||||
var gater = ensure_spawn_gater ();
|
||||
if (gater.state == STOPPED) {
|
||||
#if ANDROID
|
||||
try {
|
||||
// RoboLauncher gates app launches; only add the system-wide eBPF gater for PROCESSES.
|
||||
// Let a start failure propagate — the caller opted in, so don't silently downgrade.
|
||||
if (SpawnGatingOptions._deserialize (options).scope == PROCESSES) {
|
||||
var gater = ensure_spawn_gater ();
|
||||
if (gater.state == STOPPED)
|
||||
gater.start ();
|
||||
} catch (Error e) {
|
||||
}
|
||||
#else
|
||||
gater.start ();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
yield robo_launcher.enable_spawn_gating (cancellable);
|
||||
#else
|
||||
// No portable way to tell a GUI app from any other execve here, so both scopes map to
|
||||
// the same system-wide gater.
|
||||
var gater = ensure_spawn_gater ();
|
||||
if (gater.state == STOPPED)
|
||||
gater.start ();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -304,11 +310,14 @@ namespace Frida {
|
||||
#if ANDROID
|
||||
yield robo_launcher.disable_spawn_gating (cancellable);
|
||||
#endif
|
||||
|
||||
spawn_gating_disabled (APPLICATION_REQUESTED);
|
||||
}
|
||||
|
||||
private SpawnGater ensure_spawn_gater () {
|
||||
if (spawn_gater == null) {
|
||||
spawn_gater = new SpawnGater (helper);
|
||||
spawn_gater.gating_cancelled.connect (on_spawn_gating_cancelled);
|
||||
spawn_gater.spawn_added.connect (on_spawn_added);
|
||||
spawn_gater.spawn_removed.connect (on_spawn_removed);
|
||||
}
|
||||
@@ -338,7 +347,16 @@ namespace Frida {
|
||||
return yield robo_launcher.spawn (program, options, cancellable);
|
||||
#endif
|
||||
|
||||
return yield helper.spawn (program, options, cancellable);
|
||||
UnixInputStream? stdin_stream = null;
|
||||
UnixOutputStream? stdout_stream = null;
|
||||
UnixOutputStream? stderr_stream = null;
|
||||
if (options.stdio == INHERIT) {
|
||||
stdin_stream = new UnixInputStream (0, false);
|
||||
stdout_stream = new UnixOutputStream (1, false);
|
||||
stderr_stream = new UnixOutputStream (2, false);
|
||||
}
|
||||
|
||||
return yield helper.spawn (program, options, stdin_stream, stdout_stream, stderr_stream, cancellable);
|
||||
}
|
||||
|
||||
protected override async void prepare_exec_transition (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -506,6 +524,10 @@ namespace Frida {
|
||||
}
|
||||
#endif
|
||||
|
||||
private void on_spawn_gating_cancelled () {
|
||||
spawn_gating_disabled (WATCHDOG_TIMEOUT);
|
||||
}
|
||||
|
||||
private void on_spawn_added (HostSpawnInfo info) {
|
||||
spawn_added (info);
|
||||
}
|
||||
@@ -1344,6 +1366,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private sealed class RoboLauncher : Object {
|
||||
public signal void gating_cancelled ();
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
|
||||
@@ -1367,6 +1390,7 @@ namespace Frida {
|
||||
private bool spawn_gating_enabled = false;
|
||||
private Gee.HashMap<string, Promise<uint>> spawn_requests = new Gee.HashMap<string, Promise<uint>> ();
|
||||
private Gee.HashMap<uint, HostSpawnInfo?> pending_spawn = new Gee.HashMap<uint, HostSpawnInfo?> ();
|
||||
private SpawnGatingWatchdog watchdog = new SpawnGatingWatchdog ();
|
||||
|
||||
private delegate void CompletionNotify (GLib.Error? error);
|
||||
|
||||
@@ -1379,11 +1403,22 @@ namespace Frida {
|
||||
);
|
||||
}
|
||||
|
||||
construct {
|
||||
watchdog.expired.connect (on_watchdog_expired);
|
||||
}
|
||||
|
||||
private void on_watchdog_expired () {
|
||||
disable_spawn_gating.begin (io_cancellable);
|
||||
gating_cancelled ();
|
||||
}
|
||||
|
||||
public async void preload (Cancellable? cancellable) throws Error, IOError {
|
||||
yield ensure_loaded (cancellable);
|
||||
}
|
||||
|
||||
public async void close (Cancellable? cancellable) throws IOError {
|
||||
watchdog.clear ();
|
||||
|
||||
if (ensure_request != null) {
|
||||
try {
|
||||
yield ensure_loaded (cancellable);
|
||||
@@ -1422,6 +1457,8 @@ namespace Frida {
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
spawn_gating_enabled = false;
|
||||
|
||||
watchdog.clear ();
|
||||
|
||||
var pending = pending_spawn.values.to_array ();
|
||||
pending_spawn.clear ();
|
||||
foreach (var spawn in pending) {
|
||||
@@ -1508,8 +1545,10 @@ namespace Frida {
|
||||
connection.resume.begin (io_cancellable);
|
||||
|
||||
HostSpawnInfo? info;
|
||||
if (pending_spawn.unset (pid, out info))
|
||||
if (pending_spawn.unset (pid, out info)) {
|
||||
watchdog.cancel (pid);
|
||||
spawn_removed (info);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1598,10 +1637,21 @@ namespace Frida {
|
||||
|
||||
on_complete (null);
|
||||
} catch (GLib.Error e) {
|
||||
if (is_transient_usap_patch_failure (name, e)) {
|
||||
on_complete (null);
|
||||
return;
|
||||
}
|
||||
|
||||
on_complete (e);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool is_transient_usap_patch_failure (string process_name, GLib.Error error) {
|
||||
return process_name.has_prefix ("usap") &&
|
||||
error is Error.NOT_SUPPORTED &&
|
||||
error.message == "Unable to locate android.os.Process.setArgV0() slot; please file a bug";
|
||||
}
|
||||
|
||||
private async void inject_zymbiote (uint pid, Cancellable? cancellable) throws Error, IOError {
|
||||
var prep = yield prepare_zymbiote_injection (pid, cancellable);
|
||||
|
||||
@@ -1709,15 +1759,14 @@ namespace Frida {
|
||||
|
||||
foreach (var m in maps) {
|
||||
unowned string path = m.path;
|
||||
if (path.has_suffix ("/libstagefright.so") && m.readable && m.executable) {
|
||||
if (payload_base == 0) {
|
||||
payload_base = m.end - Gum.query_page_size ();
|
||||
payload_original_protection = Posix.PROT_READ | Posix.PROT_EXEC;
|
||||
if (m.writable)
|
||||
payload_original_protection |= Posix.PROT_WRITE;
|
||||
payload_path = path;
|
||||
payload_file_offset = m.file_offset;
|
||||
}
|
||||
if (payload_base == 0 && m.executable &&
|
||||
(path.has_suffix ("/libstagefright.so") || path.has_suffix ("/libmedia.so"))) {
|
||||
payload_base = m.end - Gum.query_page_size ();
|
||||
payload_original_protection = Posix.PROT_READ | Posix.PROT_EXEC;
|
||||
if (m.writable)
|
||||
payload_original_protection |= Posix.PROT_WRITE;
|
||||
payload_path = path;
|
||||
payload_file_offset = m.file_offset;
|
||||
} else if (path.has_suffix ("/libc.so")) {
|
||||
if (libc_path == null)
|
||||
libc_path = path;
|
||||
@@ -2080,6 +2129,7 @@ namespace Frida {
|
||||
} else if (spawn_gating_enabled) {
|
||||
var spawn_info = HostSpawnInfo (hello.pid, hello.process_name);
|
||||
pending_spawn[hello.pid] = spawn_info;
|
||||
watchdog.arm (hello.pid);
|
||||
spawn_added (spawn_info);
|
||||
needs_resume = true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Frida {
|
||||
public sealed class SpawnGater : Object {
|
||||
public signal void gating_cancelled ();
|
||||
public signal void spawn_added (HostSpawnInfo info);
|
||||
public signal void spawn_removed (HostSpawnInfo info);
|
||||
|
||||
@@ -22,6 +23,7 @@ namespace Frida {
|
||||
private State _state = STOPPED;
|
||||
|
||||
private Gee.Map<uint, HostSpawnInfo?> pending_spawn = new Gee.HashMap<uint, HostSpawnInfo?> ();
|
||||
private SpawnGatingWatchdog watchdog = new SpawnGatingWatchdog ();
|
||||
|
||||
private BpfRingbufReader? events_reader;
|
||||
private IOChannel? events_channel;
|
||||
@@ -32,16 +34,30 @@ namespace Frida {
|
||||
private const size_t RINGBUF_SIZE = 1U << 22;
|
||||
private const size_t MAX_FILENAME = 256;
|
||||
|
||||
// bpf_send_signal(SIGSTOP) races our SIGCONT: one that lands first is dropped, so re-send
|
||||
// until the task is observed running.
|
||||
private const uint RESUME_MAX_ATTEMPTS = 20;
|
||||
private const uint RESUME_RETRY_MSEC = 25;
|
||||
|
||||
public SpawnGater (LinuxHelper helper) {
|
||||
Object (helper: helper);
|
||||
}
|
||||
|
||||
construct {
|
||||
watchdog.expired.connect (on_watchdog_expired);
|
||||
}
|
||||
|
||||
protected override void dispose () {
|
||||
stop ();
|
||||
|
||||
base.dispose ();
|
||||
}
|
||||
|
||||
private void on_watchdog_expired () {
|
||||
stop ();
|
||||
gating_cancelled ();
|
||||
}
|
||||
|
||||
public void start () throws Error {
|
||||
assert (state == STOPPED);
|
||||
|
||||
@@ -86,6 +102,7 @@ namespace Frida {
|
||||
perform_resume.begin (spawn.pid);
|
||||
}
|
||||
pending_spawn.clear ();
|
||||
watchdog.clear ();
|
||||
|
||||
events_channel = null;
|
||||
events_reader = null;
|
||||
@@ -105,6 +122,7 @@ namespace Frida {
|
||||
HostSpawnInfo? spawn;
|
||||
if (!pending_spawn.unset (pid, out spawn))
|
||||
return false;
|
||||
watchdog.cancel (pid);
|
||||
spawn_removed (spawn);
|
||||
perform_resume.begin (pid);
|
||||
return true;
|
||||
@@ -115,10 +133,37 @@ namespace Frida {
|
||||
yield helper.resume (pid, null);
|
||||
} catch (GLib.Error e) {
|
||||
if (e is Error.INVALID_ARGUMENT)
|
||||
Posix.kill ((Posix.pid_t) pid, Posix.Signal.CONT);
|
||||
yield resume_external (pid);
|
||||
}
|
||||
}
|
||||
|
||||
private async void resume_external (uint pid) {
|
||||
for (uint attempt = 0; attempt != RESUME_MAX_ATTEMPTS; attempt++) {
|
||||
if (Posix.kill ((Posix.pid_t) pid, Posix.Signal.CONT) != 0)
|
||||
return;
|
||||
if (!process_is_stopped (pid))
|
||||
return;
|
||||
|
||||
var source = new TimeoutSource (RESUME_RETRY_MSEC);
|
||||
source.set_callback (resume_external.callback);
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
yield;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool process_is_stopped (uint pid) {
|
||||
string contents;
|
||||
try {
|
||||
FileUtils.get_contents ("/proc/%u/stat".printf (pid), out contents);
|
||||
} catch (FileError e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// State is the char two past the final ')' (comm can itself contain ')').
|
||||
char state = contents[contents.last_index_of_char (')') + 2];
|
||||
return state == 'T' || state == 't';
|
||||
}
|
||||
|
||||
private void process_pending_events () {
|
||||
events_reader.drain (payload => {
|
||||
assert (payload.length == sizeof (ExecveEvent));
|
||||
@@ -130,6 +175,7 @@ namespace Frida {
|
||||
private void handle_event (ExecveEvent * e) {
|
||||
var info = HostSpawnInfo (e->pid, (string) e->command);
|
||||
pending_spawn[e->pid] = info;
|
||||
watchdog.arm (e->pid);
|
||||
spawn_added (info);
|
||||
}
|
||||
|
||||
|
||||
+19
-3
@@ -609,12 +609,23 @@ namespace Frida {
|
||||
private void kick_authentication_channel (AuthenticationChannel channel) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback (() => {
|
||||
channel.connection.close.begin (io_cancellable);
|
||||
perform_kick.begin (channel);
|
||||
return false;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
|
||||
private async void perform_kick (AuthenticationChannel channel) {
|
||||
try {
|
||||
yield channel.connection.flush (io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
try {
|
||||
yield channel.connection.close (io_cancellable);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private async Peer setup_authorized_peer (uint connection_id, DBusConnection connection,
|
||||
EndpointParameters parameters) throws GLib.Error {
|
||||
Peer peer;
|
||||
@@ -787,7 +798,7 @@ namespace Frida {
|
||||
return filtered_apps;
|
||||
}
|
||||
|
||||
private void enable_spawn_gating (ControlChannel requester) {
|
||||
private void enable_spawn_gating (HashTable<string, Variant> options, ControlChannel requester) {
|
||||
spawn_gaters.add (requester);
|
||||
foreach (PendingSpawn spawn in pending_spawn.values) {
|
||||
bool requester_has_access = find_node_accessible_by (requester, spawn.info.pid) != null;
|
||||
@@ -1409,7 +1420,12 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
parent.enable_spawn_gating (this);
|
||||
parent.enable_spawn_gating (make_parameters_dict (), this);
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
parent.enable_spawn_gating (options, this);
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
|
||||
@@ -74,7 +74,8 @@ namespace Frida {
|
||||
return yield process_enumerator.enumerate_processes (ProcessQueryOptions._deserialize (options));
|
||||
}
|
||||
|
||||
public override async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
public override async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
|
||||
}
|
||||
|
||||
|
||||
@@ -328,6 +328,11 @@ namespace Frida {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
public async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
public async void disable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
throw_not_supported ();
|
||||
}
|
||||
|
||||
@@ -312,7 +312,10 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
|
||||
|
||||
gum_init ();
|
||||
|
||||
code = gum_alloc_n_pages (1, GUM_PAGE_RWX); /* Executable so debugger can be used to inspect code */
|
||||
page_size = gum_query_page_size ();
|
||||
|
||||
/* Executable so debugger can be used to inspect code */
|
||||
code = gum_memory_allocate (NULL, page_size, page_size, GUM_PAGE_RWX);
|
||||
code_size = frida_remote_worker_context_emit_payload (rwc, code);
|
||||
|
||||
memset (rwc, 0, sizeof (FridaRemoteWorkerContext));
|
||||
@@ -327,7 +330,6 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
|
||||
StringCbCopyA (rwc->entrypoint_name, sizeof (rwc->entrypoint_name), details->entrypoint_name);
|
||||
StringCbCopyA (rwc->entrypoint_data, sizeof (rwc->entrypoint_data), details->entrypoint_data);
|
||||
|
||||
page_size = gum_query_page_size ();
|
||||
g_assert (code_size <= page_size);
|
||||
|
||||
alloc_size = page_size + sizeof (FridaRemoteWorkerContext);
|
||||
@@ -345,7 +347,8 @@ frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjection
|
||||
if (!VirtualProtectEx (details->process_handle, rwc->entrypoint, page_size, PAGE_EXECUTE_READ, &old_protect))
|
||||
goto virtual_protect_ex_failed;
|
||||
|
||||
gum_free_pages (code);
|
||||
gum_memory_free (code, page_size);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
@@ -387,7 +390,8 @@ virtual_protect_ex_failed:
|
||||
error_common:
|
||||
{
|
||||
frida_remote_worker_context_destroy (rwc, details);
|
||||
gum_free_pages (code);
|
||||
gum_memory_free (code, page_size);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +398,21 @@ namespace Frida {
|
||||
assert_not_reached ();
|
||||
}
|
||||
#else
|
||||
native_helper_path = Frida.helper_path;
|
||||
string native_arch;
|
||||
switch (Gum.Windows.query_native_cpu_type ()) {
|
||||
case IA32:
|
||||
native_arch = "x86";
|
||||
break;
|
||||
case AMD64:
|
||||
native_arch = "x86_64";
|
||||
break;
|
||||
case ARM64:
|
||||
native_arch = "arm64";
|
||||
break;
|
||||
default:
|
||||
assert_not_reached ();
|
||||
}
|
||||
native_helper_path = PathTemplate (Frida.helper_path).expand (native_arch);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,12 @@ frida_helper_manager_stop_services (void * context)
|
||||
frida_service_context_free (self);
|
||||
}
|
||||
|
||||
char *
|
||||
frida_helper_service_make_svcname_base (void)
|
||||
{
|
||||
return g_strdup_printf ("frida-%lu-", GetCurrentProcessId ());
|
||||
}
|
||||
|
||||
char *
|
||||
frida_helper_service_derive_basename (void)
|
||||
{
|
||||
@@ -138,17 +144,17 @@ char *
|
||||
frida_helper_service_derive_filename_for_suffix (const char * suffix)
|
||||
{
|
||||
WCHAR filename_utf16[MAX_PATH + 1] = { 0, };
|
||||
gchar * name, * tail, * tmp;
|
||||
glong len;
|
||||
gchar * name, * native_arch, * tmp;
|
||||
|
||||
GetModuleFileNameW (NULL, filename_utf16, MAX_PATH);
|
||||
|
||||
name = g_utf16_to_utf8 (filename_utf16, -1, NULL, &len, NULL);
|
||||
tail = strrchr (name, '-');
|
||||
if (tail != NULL)
|
||||
name = g_utf16_to_utf8 (filename_utf16, -1, NULL, NULL, NULL);
|
||||
|
||||
native_arch = g_strrstr (name, FRIDA_HELPER_SERVICE_ARCH);
|
||||
if (native_arch != NULL)
|
||||
{
|
||||
tail[1] = '\0';
|
||||
tmp = g_strconcat (name, suffix, ".exe", NULL);
|
||||
*native_arch = '\0';
|
||||
tmp = g_strconcat (name, suffix, native_arch + strlen (FRIDA_HELPER_SERVICE_ARCH), NULL);
|
||||
g_free (name);
|
||||
name = tmp;
|
||||
}
|
||||
@@ -172,18 +178,6 @@ frida_helper_service_derive_svcname_for_self (void)
|
||||
return name;
|
||||
}
|
||||
|
||||
char *
|
||||
frida_helper_service_derive_svcname_for_suffix (const char * suffix)
|
||||
{
|
||||
gchar * basename, * name;
|
||||
|
||||
basename = frida_helper_service_derive_basename ();
|
||||
name = g_strconcat (basename, suffix, NULL);
|
||||
g_free (basename);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
void
|
||||
frida_managed_helper_service_enter_dispatcher_and_main_loop (void)
|
||||
{
|
||||
@@ -449,8 +443,9 @@ frida_register_service (FridaServiceContext * self, const gchar * suffix)
|
||||
WCHAR * servicename;
|
||||
gchar * displayname_utf8;
|
||||
WCHAR * displayname;
|
||||
gchar * filename_utf8;
|
||||
WCHAR * filename;
|
||||
gchar * appname_utf8;
|
||||
gchar * cmdline_utf8;
|
||||
WCHAR * cmdline;
|
||||
|
||||
servicename_utf8 = g_strconcat (self->service_basename, suffix, NULL);
|
||||
servicename = g_utf8_to_utf16 (servicename_utf8, -1, NULL, NULL, NULL);
|
||||
@@ -458,8 +453,9 @@ frida_register_service (FridaServiceContext * self, const gchar * suffix)
|
||||
displayname_utf8 = g_strdup_printf ("Frida %s helper (%s)", suffix, servicename_utf8);
|
||||
displayname = g_utf8_to_utf16 (displayname_utf8, -1, NULL, NULL, NULL);
|
||||
|
||||
filename_utf8 = frida_helper_service_derive_filename_for_suffix (suffix);
|
||||
filename = g_utf8_to_utf16 (filename_utf8, -1, NULL, NULL, NULL);
|
||||
appname_utf8 = frida_helper_service_derive_filename_for_suffix (suffix);
|
||||
cmdline_utf8 = g_strconcat ("\"", appname_utf8, "\" SERVICE \"", servicename_utf8, "\"", NULL);
|
||||
cmdline = g_utf8_to_utf16 (cmdline_utf8, -1, NULL, NULL, NULL);
|
||||
|
||||
handle = CreateServiceW (self->scm,
|
||||
servicename,
|
||||
@@ -468,15 +464,16 @@ frida_register_service (FridaServiceContext * self, const gchar * suffix)
|
||||
SERVICE_WIN32_OWN_PROCESS,
|
||||
SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
filename,
|
||||
cmdline,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
g_free (filename);
|
||||
g_free (filename_utf8);
|
||||
g_free (cmdline);
|
||||
g_free (cmdline_utf8);
|
||||
g_free (appname_utf8);
|
||||
|
||||
g_free (displayname);
|
||||
g_free (displayname_utf8);
|
||||
@@ -635,17 +632,17 @@ frida_spawn_standalone_service (FridaServiceContext * self, const gchar * suffix
|
||||
HANDLE handle = NULL;
|
||||
gchar * appname_utf8;
|
||||
WCHAR * appname;
|
||||
gchar * svcname_utf8;
|
||||
gchar * cmdline_utf8;
|
||||
WCHAR * cmdline;
|
||||
STARTUPINFOW si = { 0, };
|
||||
PROCESS_INFORMATION pi = { 0, };
|
||||
|
||||
(void) self;
|
||||
|
||||
appname_utf8 = frida_helper_service_derive_filename_for_suffix (suffix);
|
||||
appname = (WCHAR *) g_utf8_to_utf16 (appname_utf8, -1, NULL, NULL, NULL);
|
||||
|
||||
cmdline_utf8 = g_strconcat ("\"", appname_utf8, "\" STANDALONE", NULL);
|
||||
svcname_utf8 = g_strconcat (self->service_basename, suffix, NULL);
|
||||
cmdline_utf8 = g_strconcat ("\"", appname_utf8, "\" STANDALONE \"", svcname_utf8, "\"", NULL);
|
||||
cmdline = (WCHAR *) g_utf8_to_utf16 (cmdline_utf8, -1, NULL, NULL, NULL);
|
||||
|
||||
si.cb = sizeof (si);
|
||||
@@ -658,6 +655,7 @@ frida_spawn_standalone_service (FridaServiceContext * self, const gchar * suffix
|
||||
|
||||
g_free (cmdline);
|
||||
g_free (cmdline_utf8);
|
||||
g_free (svcname_utf8);
|
||||
|
||||
g_free (appname);
|
||||
g_free (appname_utf8);
|
||||
|
||||
@@ -9,10 +9,10 @@ G_GNUC_INTERNAL void * frida_helper_manager_start_services (const char * service
|
||||
FridaPrivilegeLevel level);
|
||||
G_GNUC_INTERNAL void frida_helper_manager_stop_services (void * context);
|
||||
|
||||
G_GNUC_INTERNAL char * frida_helper_service_make_svcname_base (void);
|
||||
G_GNUC_INTERNAL char * frida_helper_service_derive_basename (void);
|
||||
G_GNUC_INTERNAL char * frida_helper_service_derive_filename_for_suffix (const char * suffix);
|
||||
G_GNUC_INTERNAL char * frida_helper_service_derive_svcname_for_self (void);
|
||||
G_GNUC_INTERNAL char * frida_helper_service_derive_svcname_for_suffix (const char * suffix);
|
||||
|
||||
G_GNUC_INTERNAL void frida_managed_helper_service_enter_dispatcher_and_main_loop (void);
|
||||
|
||||
|
||||
@@ -28,11 +28,13 @@ namespace Frida {
|
||||
return manager.run ();
|
||||
}
|
||||
|
||||
string? svcname = (args.length > 2) ? args[2] : null;
|
||||
|
||||
HelperService service;
|
||||
if (mode == HelperMode.STANDALONE)
|
||||
service = new StandaloneHelperService ();
|
||||
service = new StandaloneHelperService (svcname);
|
||||
else
|
||||
service = new ManagedHelperService ();
|
||||
service = new ManagedHelperService (svcname);
|
||||
service.run ();
|
||||
|
||||
return 0;
|
||||
@@ -104,12 +106,14 @@ namespace Frida {
|
||||
archs.add ("x86_64");
|
||||
archs.add ("x86");
|
||||
|
||||
string basename = HelperService.make_svcname_base ();
|
||||
|
||||
foreach (string arch in archs) {
|
||||
var helper = new ServiceConnection (HelperService.derive_svcname_for_suffix (arch));
|
||||
var helper = new ServiceConnection (basename + arch);
|
||||
helpers.add (helper);
|
||||
}
|
||||
|
||||
context = start_services (HelperService.derive_basename (), archs.to_array (), level);
|
||||
context = start_services (basename, archs.to_array (), level);
|
||||
|
||||
foreach (var helper in helpers) {
|
||||
yield helper.open ();
|
||||
@@ -219,6 +223,11 @@ namespace Frida {
|
||||
construct;
|
||||
}
|
||||
|
||||
public string? svcname {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private DBusConnection connection;
|
||||
private uint registration_id;
|
||||
|
||||
@@ -240,7 +249,7 @@ namespace Frida {
|
||||
|
||||
private async void start () {
|
||||
try {
|
||||
var stream_request = Pipe.open ("pipe:role=client,name=" + derive_svcname_for_self (), null);
|
||||
var stream_request = Pipe.open ("pipe:role=client,name=" + (svcname ?? derive_svcname_for_self ()), null);
|
||||
var stream = yield stream_request.wait_async (null);
|
||||
|
||||
connection = yield new DBusConnection (stream, null, DELAY_MESSAGE_PROCESSING);
|
||||
@@ -297,16 +306,16 @@ namespace Frida {
|
||||
uninjected (id);
|
||||
}
|
||||
|
||||
public extern static string make_svcname_base ();
|
||||
public extern static string derive_basename ();
|
||||
public extern static string derive_svcname_for_self ();
|
||||
public extern static string derive_svcname_for_suffix (string suffix);
|
||||
}
|
||||
|
||||
private sealed class StandaloneHelperService : HelperService {
|
||||
private MainLoop loop;
|
||||
|
||||
public StandaloneHelperService () {
|
||||
Object (level: PrivilegeLevel.NORMAL);
|
||||
public StandaloneHelperService (string? svcname) {
|
||||
Object (level: PrivilegeLevel.NORMAL, svcname: svcname);
|
||||
}
|
||||
|
||||
public override void run () {
|
||||
@@ -323,8 +332,8 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private sealed class ManagedHelperService : HelperService {
|
||||
public ManagedHelperService () {
|
||||
Object (level: PrivilegeLevel.ELEVATED);
|
||||
public ManagedHelperService (string? svcname) {
|
||||
Object (level: PrivilegeLevel.ELEVATED, svcname: svcname);
|
||||
}
|
||||
|
||||
public override void run () {
|
||||
|
||||
@@ -164,7 +164,8 @@ namespace Frida {
|
||||
return yield process_enumerator.enumerate_processes (ProcessQueryOptions._deserialize (options));
|
||||
}
|
||||
|
||||
public override async void enable_spawn_gating (Cancellable? cancellable) throws Error, IOError {
|
||||
public override async void enable_spawn_gating_with_options (HashTable<string, Variant> options,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
throw new Error.NOT_SUPPORTED ("Not yet supported on this OS");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 17.14.1
|
||||
revision = 17.16.4
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/glib.git
|
||||
revision = 90a8b77b3d4cdf0482eb398fb2000dcceaeb15f0
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
dependency_names = glib-2.0, gobject-2.0, gthread-2.0, gmodule-2.0, gio-2.0, gio-unix-2.0, gio-win32-2.0
|
||||
|
||||
Reference in new issue
Block a user