Compare commits

..
67 Commits
Author SHA1 Message Date
Ole André Vadla Ravnås f67d1e81df Dump alive tasks every 30s in server and agent
Route the dump through GLib.info() using the new
g_task_describe_alive_tasks(), so the agent's dump reaches
logcat where its stdio is not visible. Bump the GLib wrap to
the branch holding that function.
2026-07-29 18:12:10 +02:00
Ole André Vadla Ravnås a068c704d1 Trace agent session detach notification path
Log every hop that agent_session_detached has to survive, from
the agent emitting closed, through frida-server's provider and
control channel, the droidy relay, and finally Device's pending
detach request. Each silent id-keyed drop now reports whether
the lookup succeeded, so a hanging detach() points straight at
the hop that swallowed the notification.
2026-07-29 17:47:25 +02:00
Ole André Vadla Ravnås 34ac3a0d50 server: Add output redirection option
Add -o/--output for redirecting stdout and stderr to a file,
so output survives daemonizing. Skip the stdin debug console
when daemonized, as stdin is then /dev/null.
2026-07-29 15:10:37 +02:00
Ole André Vadla Ravnås 1ea81bcd59 server: Add stdin-based task debug console 2026-07-28 14:47:05 +02:00
Ole André Vadla Ravnås 0e1a53ba7a subprojects: Prepare for release 2026-07-22 00:30:06 +02:00
Ole André Vadla Ravnås bae82ee212 subprojects: Bump outdated 2026-07-22 00:28:46 +02:00
Ole André Vadla Ravnås 5d714719bc subprojects: Prepare for release 2026-07-20 17:55:53 +02:00
Ole André Vadla Ravnås 1d68703132 subprojects: Bump outdated 2026-07-20 17:46:45 +02:00
Ole André Vadla Ravnås afba4699c1 subprojects: Prepare for release 2026-07-19 22:25:17 +02:00
Ole André Vadla Ravnås 8a3afe7c37 subprojects: Bump outdated 2026-07-19 22:24:20 +02:00
Ole André Vadla Ravnås 5ed252adad linux: Stop helper inheriting embedder stdio
For stdio=inherit, pass the host's streams to the helper with each
spawn; with that in place, redirect the helper's own 0/1/2 to
/dev/null at launch so it no longer holds the embedder's pipes.
2026-07-19 22:20:24 +02:00
Ole André Vadla Ravnås 195c6b0618 darwin: Stop helper inheriting embedder stdio
For stdio=inherit, pass the host's streams to the helper with each
spawn; with that in place, redirect the helper's own 0/1/2 to
/dev/null at launch so it no longer holds the embedder's pipes.
2026-07-19 22:20:24 +02:00
Ole André Vadla Ravnås 5934b352b3 subprojects: Prepare for release 2026-07-18 13:30:24 +02:00
Ole André Vadla Ravnås 62f0b50694 subprojects: Bump outdated 2026-07-18 13:29:03 +02:00
Ole André Vadla Ravnås 405fb10d1e submodules: Bump releng 2026-07-18 13:29:02 +02:00
Ole André Vadla Ravnås 07eb059d31 subprojects: Prepare for release 2026-07-17 23:28:11 +02:00
Ole André Vadla Ravnås aca4c8fd14 subprojects: Bump outdated 2026-07-17 23:07:26 +02:00
Ole André Vadla Ravnås 3e6668ef3b subprojects: Bump outdated 2026-07-17 20:48:01 +02:00
Ole André Vadla Ravnås b62cda4965 submodules: Bump releng 2026-07-17 20:48:00 +02:00
Ole André Vadla Ravnås e7d1a48dca barebone: Bump @types/frida-gum to 19.10.0 2026-07-17 18:26:09 +02:00
Ole André Vadla Ravnås 06f685320b compiler: Bump @types/frida-gum to 19.10.0 2026-07-17 18:26:09 +02:00
Ole André Vadla Ravnås b98ae95894 windows: Use gum_memory_allocate for worker code
Follow frida-gum dropping gum_alloc_n_pages()/gum_free_pages().
2026-07-17 15:06:50 +02:00
Ole André Vadla Ravnås a366b1cbf2 compat: Stop forwarding build-machine options
The compat build generates its own machine files, so forwarding the
parent's per-machine build options injected a foreign toolchain. In a
native compat build that produced a compiler-less build machine and
broke every native: true target.
2026-07-17 00:24:49 +02:00
Ole André Vadla Ravnås 206d98a2c5 subprojects: Bump outdated 2026-07-16 17:50:38 +02:00
Ole André Vadla Ravnås e1799ba509 submodules: Bump releng 2026-07-16 17:50:37 +02:00
Ole André Vadla Ravnås 53f3713e86 build: Add glib.wrap to resolve GLib up front
The GLib flavor decides whether payloads are built in-tree, which
frida-gum needs to know before it is configured. Without a wrap of its
own, frida-core could only obtain GLib from the frida-gum subproject, so
the flavor wasn't yet knowable and static cross builds failed to find
it. Shipping glib.wrap lets frida-core resolve GLib itself — the fork
bundles its build deps — so the flavor is known up front for every
build.
2026-07-16 16:43:07 +02:00
Ole André Vadla Ravnås 7b86da31a6 auth: Flush pending reply before closing channel
A rejected token kicked the authentication channel on an idle
source without flushing, so the INVALID_ARGUMENT reply raced
the teardown and clients could see a closed connection instead
of the error.
2026-07-16 15:36:20 +02:00
Ole André Vadla Ravnås 2309a88935 build: Base in-tree payloads on GLib flavor
Whether payloads are built in-tree depends on the GLib flavor, not the
library type: a shared build against Frida's static GLib still wants
them in-tree. Only a shared upstream GLib needs the out-of-tree agent.
2026-07-16 13:52:57 +02:00
Ole André Vadla Ravnås 91011dcdc3 build: Add native languages for cross builds
A cross build still builds the resource compiler for the build
machine, which needs its own C/C++/Vala compilers configured.
2026-07-16 13:52:34 +02:00
Ole André Vadla Ravnås 2f51f2e72c windows: Name helper services from the manager
A spawned helper derived its rendezvous pipe name from its own module
path, which only agrees with the manager when they are colocated (the
embedded layout). In the installed layout each helper lives in its own
arch directory, so the names diverged and they never met. Have the
manager mint one unique name and pass it to every service it spawns —
on the command line for standalone helpers, in the registered binary
path for SCM services — restoring both properties the shared temp dir
used to give: agreement and per-session uniqueness.
2026-07-16 13:52:14 +02:00
Ole André Vadla Ravnås 1355b05293 windows: Find arch helpers in installed layout
The sibling helper path was derived by swapping the filename suffix,
which only fits the embedded layout. Swap the arch token instead, so
it also finds …\<arch>\frida-helper.exe.
2026-07-16 13:24:43 +02:00
Ole André Vadla Ravnås ca736a3268 windows: Expand <arch> in installed helper path
In installed-assets builds the helper path kept the literal <arch>
placeholder, so spawning the cross-arch helper failed with 0x2. Expand
it to the native arch, like the Linux backend already does.
2026-07-16 13:24:21 +02:00
Ole André Vadla Ravnås c71b256bd8 spawn-gating: Fix unhandled-error warning
The droidy and fruity forwards let GLib.Error through raw so the caller can
spot UNKNOWN_METHOD, but declared only Error/IOError. Match the interface's
throws GLib.Error.
2026-07-15 23:58:51 +02:00
Ole André Vadla Ravnås dfc33cd512 spawn-gating: Make it fail-safe and scoped
17.8's eBPF spawn gater stops every execve and leans on the client to
resume each one, so a client that stops resuming wedges process creation
system-wide.

Add a shared SpawnGatingWatchdog that cancels gating and resumes
everything once a caught process sits unresumed too long, wired into all
four suspending gaters (Linux eBPF and RoboLauncher, iOS/tvOS
FruitController, macOS DTrace). Re-send SIGCONT until the task is seen
running, to beat the bpf_send_signal(SIGSTOP) vs SIGCONT race. Report it
via a new spawn_gating_disabled (reason) HostSession signal bubbled to
Device.

Make the system-wide gater opt-in: enable_spawn_gating takes a
SpawnGatingScope (DEFAULT, APPLICATIONS, PROCESSES), arming eBPF only for
PROCESSES. The scope rides a new enable_spawn_gating_with_options; the
plain call stays for wire compatibility and only DEFAULT falls back to
it, so an explicit scope fails loudly against an older Frida.

Also populate ControlService's pending_spawn so the approver model and
resume-on-disable work again.
2026-07-15 23:32:17 +02:00
Håvard Sørbø ca4c90eab1 fruity: Protect segments before applying fixups
The upload injector maps the agent module r-x, streams in its
segments, applies threaded and chained fixups, and only afterwards
restores each segment's protection.

The fixup passes write the rebased and authenticated pointers
directly into the module (e.g. into __DATA_CONST). Because the data
segments are still mapped r-x at that point, on arm64e the first
authenticated-rebase store faults with KERN_PROTECTION_FAILURE and
the injection is aborted with "Invocation of 0x... crashed".

Apply each segment's protection before running the fixup passes, so
the data segments are writable when the pointers land. __TEXT is not
fixed up and stays r-x, so no rwx mapping is created, and the WRITE
command keeps populating the module through its own temporary
writable mapping regardless of ordering.
2026-07-15 19:26:27 +02:00
Ole André Vadla Ravnås 4a1bf993a8 barebone: Bump @types/frida-gum to 19.9.0 2026-07-15 00:20:34 +02:00
Ole André Vadla Ravnås 2ff51da2bc compiler: Bump @types/frida-gum to 19.9.0 2026-07-15 00:20:34 +02:00
Ole André Vadla Ravnås 4372e5f84f subprojects: Prepare for release 2026-07-13 12:55:47 +02:00
Ole André Vadla Ravnås c7661416e5 subprojects: Bump outdated 2026-07-13 12:54:36 +02:00
dezige131 e2bca40e4e android: Tolerate transient USAP patch misses (#1246) 2026-07-13 12:51:34 +02:00
Ole André Vadla Ravnås f31061e846 subprojects: Prepare for release 2026-07-06 19:05:09 +02:00
Ole André Vadla Ravnås f8f9c4386a subprojects: Bump outdated 2026-07-06 19:04:15 +02:00
Ole André Vadla Ravnås a0bd34ebf6 barebone: Bump @types/frida-gum to 19.8.0 2026-07-06 19:03:23 +02:00
Ole André Vadla Ravnås abaebf8a41 compiler: Bump @types/frida-gum to 19.8.0 2026-07-06 19:03:23 +02:00
Ole André Vadla Ravnås e0ec2bee62 subprojects: Prepare for release 2026-06-22 23:31:20 +02:00
Ole André Vadla Ravnås d64a1a934f subprojects: Bump outdated 2026-06-22 23:30:29 +02:00
Ole André Vadla Ravnås 12c052f409 subprojects: Prepare for release 2026-06-22 11:46:17 +02:00
Ole André Vadla Ravnås 6d4443ff26 subprojects: Bump outdated 2026-06-22 11:45:30 +02:00
wave-sky 1b540b96c2 linux: Fix payload base pick on Android 10
Spawning processes on Android 10 fails with a remote connection
error due to Android's XOM mechanism, which restricts
libstagefright.so to execute-only access. This makes the read
permission check in do_prepare_zymbiote_injection fail.

Fix this by:
- Drop the read permission check for target libraries.
- Expand the list of eligible media shared libraries.
- Force read-execute permissions to ensure payload execution.
2026-06-22 11:41:53 +02:00
Ole André Vadla Ravnås 9492205c6a subprojects: Prepare for release 2026-06-20 15:56:50 +02:00
Ole André Vadla Ravnås ff1ecbefd4 subprojects: Bump outdated 2026-06-20 15:55:12 +02:00
Ole André Vadla Ravnås 774db58809 darwin: Fix unused variable warning on non-arm64e 2026-06-19 12:39:22 +02:00
Ole André Vadla Ravnås c1eff69ceb subprojects: Prepare for release 2026-06-19 12:38:27 +02:00
Ole André Vadla Ravnås 99000093ae subprojects: Bump outdated 2026-06-19 11:55:46 +02:00
Ole André Vadla Ravnås 03ba50b50f barebone: Bump @types/frida-gum to 19.7.0 2026-06-18 20:06:01 +02:00
Ole André Vadla Ravnås b3c5a68471 compiler: Bump @types/frida-gum to 19.7.0 2026-06-18 20:05:47 +02:00
Ole André Vadla Ravnås 75802359f5 subprojects: Prepare for release 2026-06-17 12:42:26 +02:00
Ole André Vadla Ravnås a27cc42c15 subprojects: Bump outdated 2026-06-17 12:39:19 +02:00
Ole André Vadla Ravnås c55252ee98 subprojects: Bump outdated 2026-06-17 12:33:00 +02:00
Ole André Vadla Ravnås 12948e78b7 subprojects: Prepare for release 2026-06-16 17:30:17 +02:00
Ole André Vadla Ravnås 8e2e44126f Expose process argv at metadata scope
Make the argv parameter from enumerate_processes() available at
metadata scope, mirroring path, instead of restricting it to full
scope.
2026-06-16 17:30:17 +02:00
Ole André Vadla Ravnås 9ed8e08c26 subprojects: Bump outdated 2026-06-16 16:38:18 +02:00
Ole André Vadla Ravnås eafaf83cdc script: Add interrupt() and terminate()
Expose the new Gum.Script interrupt() and terminate() capabilities
across the agent session API:

  - Script.interrupt() aborts any JavaScript currently executing,
    leaving the script loaded and able to run again.
  - Script.terminate() interrupts execution and unloads the script,
    so one stuck in a long-running or infinite operation can still be
    torn down.

Plumbed through AgentSession, the payload script engine, and the
public Script class. Barebone sessions report these as not supported.
2026-06-16 16:26:56 +02:00
Ole André Vadla Ravnås ac26f8fd84 subprojects: Prepare for release 2026-06-15 19:09:24 +02:00
Ole André Vadla Ravnås 26c180f69c subprojects: Bump outdated 2026-06-15 19:08:33 +02:00
Ole André Vadla Ravnås a461e5d5ab api: Emit filename/line on synthesized docs 2026-06-15 19:00:40 +02:00
Ole André Vadla Ravnås dac42a81d4 Add argv to enumerated process parameters
Populate an "argv" parameter, under full scope, with the target's
command-line arguments. Backed by the PEB on Windows, KERN_PROCARGS2
on macOS, /proc/<pid>/cmdline on Linux, and KERN_PROC_ARGS on
FreeBSD. QNX has no documented source, so it is left out.

Cover it with a local test that spawns a labrat with a known argument
and verifies the round-trip.
2026-06-15 19:00:02 +02:00
51 changed files with 1253 additions and 200 deletions

No files matched your search

+5 -1
View File
@@ -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"}:
+18
View File
@@ -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 ();
}
+137
View File
@@ -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);
@@ -78,6 +81,8 @@ namespace Frida {
Cancellable? cancellable) throws GLib.Error;
public abstract async void destroy_script (AgentScriptId script_id, Cancellable? cancellable) throws GLib.Error;
public abstract async void load_script (AgentScriptId script_id, Cancellable? cancellable) throws GLib.Error;
public abstract async void interrupt_script (AgentScriptId script_id, Cancellable? cancellable) throws GLib.Error;
public abstract async void terminate_script (AgentScriptId script_id, Cancellable? cancellable) throws GLib.Error;
public abstract async void eternalize_script (AgentScriptId script_id, Cancellable? cancellable) throws GLib.Error;
public abstract async void enable_debugger (AgentScriptId script_id, Cancellable? cancellable) throws GLib.Error;
@@ -356,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
@@ -593,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;
});
@@ -901,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 ();
}
@@ -980,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.
*/
@@ -1102,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.
*/
@@ -1482,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;
+5
View File
@@ -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");
}
+18
View File
@@ -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);
}
@@ -179,6 +183,18 @@ namespace Frida {
yield script_engine.load_script (script_id);
}
public async void interrupt_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
check_open ();
script_engine.interrupt_script (script_id);
}
public async void terminate_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
check_open ();
yield script_engine.terminate_script (script_id);
}
public async void eternalize_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
check_open ();
@@ -264,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 ();
}
+18
View File
@@ -166,6 +166,14 @@ namespace Frida {
yield get_instance (script_id).load ();
}
public void interrupt_script (AgentScriptId script_id) throws Error {
get_instance (script_id).interrupt ();
}
public async void terminate_script (AgentScriptId script_id) throws Error {
yield get_instance (script_id).terminate ();
}
public Gum.Script eternalize_script (AgentScriptId script_id) throws Error {
var instance = get_instance (script_id);
@@ -354,6 +362,16 @@ namespace Frida {
load_request.resolve (true);
}
public void interrupt () {
script.interrupt ();
}
public async void terminate () {
script.interrupt ();
yield close ();
}
private Promise<bool> unload () {
var request = new Promise<bool> ();
+25 -20
View File
@@ -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
View File
@@ -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 ();
}
+31 -14
View File
@@ -282,6 +282,8 @@ class DocComment:
body: str
params: dict
returns: str
filename: str = "<generated>"
line: int = 0
@dataclass
class DocIndex:
@@ -296,22 +298,26 @@ DOC_METHOD_PATTERN = re.compile(r"public\s+\S.*?\b(\w+)\s*\(")
DOC_ENUM_MEMBER_PATTERN = re.compile(r"([A-Z][A-Z0-9_]*)\b")
def build_doc_index(src_dir: Path) -> DocIndex:
repo_root = src_dir.parent
sources = []
for name in TOPLEVEL_NAMES:
path = src_dir / name
if path.exists():
sources.append(path.read_text(encoding="utf-8"))
sources.append(path)
base_dir = src_dir.parent / "lib" / "base"
if base_dir.is_dir():
for path in sorted(base_dir.rglob("*.vala")):
sources.append(path.read_text(encoding="utf-8"))
sources.extend(sorted(base_dir.rglob("*.vala")))
index = DocIndex(types={}, members={}, enum_members={})
for source in sources:
_collect_docs_from_source(source, index)
for path in sources:
try:
filename = path.relative_to(repo_root).as_posix()
except ValueError:
filename = path.name
_collect_docs_from_source(path.read_text(encoding="utf-8"), index, filename)
return index
def _collect_docs_from_source(source: str, index: DocIndex):
def _collect_docs_from_source(source: str, index: DocIndex, filename: str):
lines = source.split("\n")
pending = None
current_type = None
@@ -323,11 +329,14 @@ def _collect_docs_from_source(source: str, index: DocIndex):
stripped = line.strip()
if stripped.startswith("/**"):
start_line = i + 1
block = [line]
while "*/" not in lines[i]:
i += 1
block.append(lines[i])
pending = _parse_doc_block(block)
pending.filename = filename
pending.line = start_line
i += 1
continue
@@ -508,13 +517,18 @@ def inject_documentation(merged_namespace: ET.Element, docs: DocIndex):
return "`" + target + "`"
return re.sub(r"\{@link\s+([^}]+)\}", repl, text)
def set_doc(elem: ET.Element, text: str):
def set_doc(elem: ET.Element, text: str, filename: str = "<generated>",
line: int = 0):
if not text:
return
if elem.find(doc_tag) is not None:
return
doc = ET.Element(doc_tag)
doc.set("xml:space", "preserve")
# gi-docgen accesses filename/line unconditionally, so always emit them,
# mirroring what g-ir-scanner produces for hand-written C.
doc.set("filename", filename)
doc.set("line", str(line))
doc.text = resolve_links(text)
elem.insert(0, doc)
@@ -529,17 +543,17 @@ def inject_documentation(merged_namespace: ET.Element, docs: DocIndex):
return comment
def apply_callable(elem: ET.Element, comment: DocComment):
set_doc(elem, comment.body)
set_doc(elem, comment.body, comment.filename, comment.line)
if comment.returns:
rv = elem.find(f"{{{CORE_NAMESPACE}}}return-value")
if rv is not None:
set_doc(rv, comment.returns)
set_doc(rv, comment.returns, comment.filename, comment.line)
params_elem = elem.find(f"{{{CORE_NAMESPACE}}}parameters")
if params_elem is not None and comment.params:
for param in params_elem.findall(f"{{{CORE_NAMESPACE}}}parameter"):
text = comment.params.get(param.get("name"))
if text:
set_doc(param, text)
set_doc(param, text, comment.filename, comment.line)
for type_elem in list(merged_namespace):
type_name = type_elem.get("name")
@@ -549,7 +563,8 @@ def inject_documentation(merged_namespace: ET.Element, docs: DocIndex):
type_comment = docs.types.get(type_name)
if type_comment is not None and tag in ("class", "interface", "enumeration"):
set_doc(type_elem, type_comment.body)
set_doc(type_elem, type_comment.body, type_comment.filename,
type_comment.line)
for child in list(type_elem):
child_name = child.get("name")
@@ -570,18 +585,19 @@ def inject_documentation(merged_namespace: ET.Element, docs: DocIndex):
vala_name = child_name.replace("-", "_")
comment = docs.members.get((type_name, vala_name))
if comment is not None:
set_doc(child, comment.body)
set_doc(child, comment.body, comment.filename, comment.line)
# The C API also exposes the property through getter/setter
# methods; give them the same description.
accessors = {"get_" + vala_name, "set_" + vala_name}
for sibling in type_elem:
if sibling.tag == f"{{{CORE_NAMESPACE}}}method" \
and sibling.get("name") in accessors:
set_doc(sibling, comment.body)
set_doc(sibling, comment.body, comment.filename,
comment.line)
elif child.tag == member_tag:
comment = docs.enum_members.get((type_name, child_name.upper()))
if comment is not None:
set_doc(child, comment.body)
set_doc(child, comment.body, comment.filename, comment.line)
def emit_vapi(api, output_dir):
with OutputFile(output_dir / f"frida-core-{api.version}.vapi") as output_vapi_file:
@@ -645,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",
+13
View File
@@ -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 ();
}
@@ -643,6 +648,14 @@ namespace Frida {
public abstract async void load_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError;
public async void interrupt_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async void terminate_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
public async void eternalize_script (AgentScriptId script_id, Cancellable? cancellable) throws Error, IOError {
throw_not_supported ();
}
+4 -4
View File
@@ -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"
},
+1 -1
View File
@@ -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"
}
+4 -4
View File
@@ -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": {
+1 -1
View File
@@ -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
View File
@@ -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 {
+41 -3
View File
@@ -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) {
}
+15 -6
View File
@@ -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
+42 -7
View File
@@ -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) {
+3 -2
View File
@@ -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 ();
}
+15 -3
View File
@@ -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);
}
+14 -3
View File
@@ -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);
}
+8 -3
View File
@@ -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;
+68
View File
@@ -5,6 +5,7 @@
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/sysctl.h>
@@ -98,6 +99,7 @@ extern int proc_pidpath (int pid, void * buffer, uint32_t buffer_size);
#endif
static void frida_add_process_metadata (GHashTable * parameters, const struct kinfo_proc * process);
static GVariant * frida_query_process_argv (guint pid);
static struct kinfo_proc * frida_system_query_kinfo_procs (guint * count);
static GVariant * frida_uid_to_name (uid_t uid);
@@ -614,7 +616,15 @@ frida_collect_process_info_from_kinfo (struct kinfo_proc * process, FridaEnumera
info.name = g_path_get_basename (path);
if (scope != FRIDA_SCOPE_MINIMAL)
{
GVariant * argv;
g_hash_table_insert (info.parameters, g_strdup ("path"), g_variant_ref_sink (g_variant_new_string (path)));
argv = frida_query_process_argv (info.pid);
if (argv != NULL)
g_hash_table_insert (info.parameters, g_strdup ("argv"), g_variant_ref_sink (argv));
}
}
if (still_alive)
@@ -822,6 +832,64 @@ frida_add_process_metadata (GHashTable * parameters, const struct kinfo_proc * p
g_date_time_unref (t0);
}
static GVariant *
frida_query_process_argv (guint pid)
{
GVariant * result = NULL;
int argmax;
size_t argmax_size = sizeof (argmax);
int argmax_mib[] = { CTL_KERN, KERN_ARGMAX };
gchar * buffer = NULL;
size_t buffer_size;
int args_mib[] = { CTL_KERN, KERN_PROCARGS2, (int) pid };
int argc;
const gchar * cursor, * end;
GVariantBuilder builder;
int i;
if (sysctl (argmax_mib, G_N_ELEMENTS (argmax_mib), &argmax, &argmax_size, NULL, 0) != 0)
goto beach;
buffer = g_malloc (argmax);
buffer_size = argmax;
if (sysctl (args_mib, G_N_ELEMENTS (args_mib), buffer, &buffer_size, NULL, 0) != 0)
goto beach;
if (buffer_size < sizeof (int))
goto beach;
memcpy (&argc, buffer, sizeof (int));
if (argc <= 0)
goto beach;
cursor = buffer + sizeof (int);
end = buffer + buffer_size;
while (cursor != end && *cursor != '\0')
cursor++;
while (cursor != end && *cursor == '\0')
cursor++;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
for (i = 0; i != argc && cursor != end; i++)
{
gsize arg_length = strnlen (cursor, end - cursor);
g_variant_builder_add_value (&builder, g_variant_new_take_string (g_strndup (cursor, arg_length)));
cursor += arg_length;
if (cursor != end)
cursor++;
}
result = g_variant_builder_end (&builder);
beach:
g_free (buffer);
return result;
}
static struct kinfo_proc *
frida_system_query_kinfo_procs (guint * count)
{
+18 -1
View File
@@ -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);
+2 -1
View File
@@ -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");
}
+54
View File
@@ -3,6 +3,7 @@
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <string.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
@@ -23,6 +24,7 @@ static void frida_add_process_metadata (GHashTable * parameters, const struct ki
static struct kinfo_proc * frida_system_query_kinfo_procs (guint * count);
static gboolean frida_system_query_proc_pathname (pid_t pid, gchar * path, gsize size);
static GVariant * frida_query_process_argv (guint pid);
static GVariant * frida_uid_to_name (uid_t uid);
void
@@ -116,7 +118,15 @@ frida_collect_process_info_from_kinfo (struct kinfo_proc * process, FridaEnumera
info.name = g_strdup (process->ki_comm);
if (scope != FRIDA_SCOPE_MINIMAL)
{
GVariant * argv;
g_hash_table_insert (info.parameters, g_strdup ("path"), g_variant_ref_sink (g_variant_new_string (path)));
argv = frida_query_process_argv (info.pid);
if (argv != NULL)
g_hash_table_insert (info.parameters, g_strdup ("argv"), g_variant_ref_sink (argv));
}
}
if (still_alive)
@@ -221,6 +231,50 @@ frida_system_query_proc_pathname (pid_t pid, gchar * path, gsize size)
return success;
}
static GVariant *
frida_query_process_argv (guint pid)
{
GVariant * result = NULL;
int mib[4];
gchar * buffer = NULL;
size_t size;
const gchar * cursor, * end;
GVariantBuilder builder;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ARGS;
mib[3] = pid;
size = 0;
if (sysctl (mib, G_N_ELEMENTS (mib), NULL, &size, NULL, 0) != 0 || size == 0)
goto beach;
buffer = g_malloc (size);
if (sysctl (mib, G_N_ELEMENTS (mib), buffer, &size, NULL, 0) != 0)
goto beach;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
cursor = buffer;
end = buffer + size;
while (cursor != end)
{
gsize arg_length = strnlen (cursor, end - cursor);
g_variant_builder_add_value (&builder, g_variant_new_take_string (g_strndup (cursor, arg_length)));
cursor += arg_length;
if (cursor != end)
cursor++;
}
result = g_variant_builder_end (&builder);
beach:
g_free (buffer);
return result;
}
static GVariant *
frida_uid_to_name (uid_t uid)
{
+103 -6
View File
@@ -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);
}
@@ -3906,6 +3953,30 @@ namespace Frida {
}
}
/**
* Interrupts any JavaScript currently executing in the script, leaving it
* loaded and able to run again. Does nothing if nothing is executing.
*/
public async void interrupt (Cancellable? cancellable = null) throws Error, IOError {
check_open ();
try {
yield session.active_session.interrupt_script (id, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
}
public void interrupt_sync (Cancellable? cancellable = null) throws Error, IOError {
create<InterruptTask> ().execute (cancellable);
}
private class InterruptTask : ScriptTask<void> {
protected override async void perform_operation () throws Error, IOError {
yield parent.interrupt (cancellable);
}
}
/**
* Unloads the script from the target process.
*/
@@ -3925,6 +3996,32 @@ namespace Frida {
}
}
/**
* Interrupts any JavaScript currently executing and unloads the script,
* even if it is stuck in a long-running or infinite operation.
*/
public async void terminate (Cancellable? cancellable = null) throws Error, IOError {
check_open ();
try {
yield session.active_session.terminate_script (id, cancellable);
yield _do_close (false, cancellable);
} catch (GLib.Error e) {
throw_dbus_error (e);
}
}
public void terminate_sync (Cancellable? cancellable = null) throws Error, IOError {
create<TerminateTask> ().execute (cancellable);
}
private class TerminateTask : ScriptTask<void> {
protected override async void perform_operation () throws Error, IOError {
yield parent.terminate (cancellable);
}
}
/**
* Eternalizes the script so it keeps running after the session is
* detached, instead of being unloaded.
+13
View File
@@ -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
View File
@@ -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
+12 -2
View File
@@ -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);
}
+7 -2
View File
@@ -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);
+13 -4
View File
@@ -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);
+6 -1
View File
@@ -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 {
+4 -1
View File
@@ -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;
+71 -21
View File
@@ -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;
}
+47 -1
View File
@@ -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);
}
+35 -1
View File
@@ -15,6 +15,7 @@ struct _FridaEnumerateProcessesOperation
};
static void frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op);
static GVariant * frida_parse_cmdline_argv (const gchar * cmdline_data, gsize cmdline_length);
static gboolean frida_is_directory_noexec (const gchar * directory);
static gchar * frida_get_application_directory (void);
static gboolean frida_add_process_metadata (GHashTable * parameters, const gchar * proc_entry_name);
@@ -86,6 +87,8 @@ frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op)
gchar * program_path = NULL;
gchar * cmdline_path = NULL;
gchar * cmdline_data = NULL;
gsize cmdline_length = 0;
GVariant * argv = NULL;
gchar * name = NULL;
proc_name = g_strdup_printf ("%u", pid);
@@ -100,10 +103,12 @@ frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op)
cmdline_path = g_build_filename ("/proc", proc_name, "cmdline", NULL);
g_file_get_contents (cmdline_path, &cmdline_data, NULL, NULL);
g_file_get_contents (cmdline_path, &cmdline_data, &cmdline_length, NULL);
if (cmdline_data == NULL)
goto beach;
argv = frida_parse_cmdline_argv (cmdline_data, cmdline_length);
if (g_str_has_prefix (cmdline_data, "/proc/"))
{
name = g_path_get_basename (program_path);
@@ -129,6 +134,9 @@ frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op)
g_hash_table_insert (info.parameters, g_strdup ("path"),
g_variant_ref_sink (g_variant_new_take_string (g_steal_pointer (&program_path))));
if (argv != NULL)
g_hash_table_insert (info.parameters, g_strdup ("argv"), g_variant_ref_sink (g_steal_pointer (&argv)));
still_alive = frida_add_process_metadata (info.parameters, proc_name);
}
@@ -138,6 +146,7 @@ frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op)
frida_host_process_info_destroy (&info);
beach:
g_clear_pointer (&argv, g_variant_unref);
g_free (name);
g_free (cmdline_data);
g_free (cmdline_path);
@@ -146,6 +155,31 @@ beach:
g_free (proc_name);
}
static GVariant *
frida_parse_cmdline_argv (const gchar * cmdline_data, gsize cmdline_length)
{
GVariantBuilder builder;
const gchar * cursor, * end;
if (cmdline_length == 0)
return NULL;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
cursor = cmdline_data;
end = cmdline_data + cmdline_length;
while (cursor != end)
{
gsize arg_length = strnlen (cursor, end - cursor);
g_variant_builder_add_value (&builder, g_variant_new_take_string (g_strndup (cursor, arg_length)));
cursor += arg_length;
if (cursor != end)
cursor++;
}
return g_variant_builder_end (&builder);
}
void
frida_system_kill (guint pid)
{
+19 -3
View File
@@ -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 {
+2 -1
View File
@@ -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");
}
+5
View File
@@ -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 ();
}
+8 -4
View File
@@ -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;
}
}
+15 -1
View File
@@ -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
}
+27 -29
View File
@@ -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);
+1 -1
View File
@@ -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);
+19 -10
View File
@@ -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 () {
+64
View File
@@ -3,7 +3,9 @@
#include "icon-helpers.h"
#include <psapi.h>
#include <shellapi.h>
#include <tlhelp32.h>
#include <winternl.h>
#define DRIVE_STRINGS_MAX_LENGTH (512)
@@ -22,6 +24,7 @@ static void frida_collect_process_info (guint pid, FridaEnumerateProcessesOperat
static gboolean frida_add_process_metadata (GHashTable * parameters, guint pid, HANDLE process, FridaEnumerateProcessesOperation * op);
static gboolean frida_get_process_filename (HANDLE process, WCHAR * name, DWORD name_capacity);
static GVariant * frida_query_process_argv (guint pid);
static GVariant * frida_get_process_user (HANDLE process);
static GVariant * frida_get_process_start_time (HANDLE process);
@@ -117,6 +120,8 @@ frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op)
if (op->scope != FRIDA_SCOPE_MINIMAL)
{
GVariant * argv;
g_hash_table_insert (info.parameters, g_strdup ("path"),
g_variant_ref_sink (g_variant_new_take_string (g_steal_pointer (&program_path))));
@@ -124,6 +129,10 @@ frida_collect_process_info (guint pid, FridaEnumerateProcessesOperation * op)
if (pid == op->frontmost_pid)
g_hash_table_insert (info.parameters, g_strdup ("frontmost"), g_variant_ref_sink (g_variant_new_boolean (TRUE)));
argv = frida_query_process_argv (pid);
if (argv != NULL)
g_hash_table_insert (info.parameters, g_strdup ("argv"), g_variant_ref_sink (argv));
}
if (op->scope == FRIDA_SCOPE_FULL)
@@ -254,6 +263,61 @@ frida_get_process_filename (HANDLE process, WCHAR * name, DWORD name_capacity)
return FALSE;
}
static GVariant *
frida_query_process_argv (guint pid)
{
GVariant * result = NULL;
HANDLE process;
HMODULE ntdll;
NTSTATUS (NTAPI * query_information_process) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
PROCESS_BASIC_INFORMATION basic_info;
PEB peb;
RTL_USER_PROCESS_PARAMETERS parameters;
WCHAR * command_line = NULL;
WCHAR ** argv = NULL;
int argc, i;
GVariantBuilder builder;
process = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (process == NULL)
goto beach;
ntdll = GetModuleHandleW (L"ntdll.dll");
query_information_process = (gpointer) GetProcAddress (ntdll, "NtQueryInformationProcess");
if (query_information_process (process, ProcessBasicInformation, &basic_info, sizeof (basic_info), NULL) != 0)
goto beach;
if (!ReadProcessMemory (process, basic_info.PebBaseAddress, &peb, sizeof (peb), NULL))
goto beach;
if (!ReadProcessMemory (process, peb.ProcessParameters, &parameters, sizeof (parameters), NULL))
goto beach;
command_line = g_malloc (parameters.CommandLine.Length + sizeof (WCHAR));
if (!ReadProcessMemory (process, parameters.CommandLine.Buffer, command_line, parameters.CommandLine.Length, NULL))
goto beach;
command_line[parameters.CommandLine.Length / sizeof (WCHAR)] = L'\0';
argv = CommandLineToArgvW (command_line, &argc);
if (argv == NULL)
goto beach;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
for (i = 0; i != argc; i++)
g_variant_builder_add_value (&builder, g_variant_new_take_string (g_utf16_to_utf8 (argv[i], -1, NULL, NULL, NULL)));
result = g_variant_builder_end (&builder);
beach:
if (argv != NULL)
LocalFree (argv);
g_free (command_line);
if (process != NULL)
CloseHandle (process);
return result;
}
static GVariant *
frida_get_process_user (HANDLE process)
{
+2 -1
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/frida/frida-gum.git
revision = 17.12.0
revision = 17.16.4
depth = 1
[provide]
+8
View File
@@ -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
+51
View File
@@ -107,6 +107,13 @@ namespace Frida.HostSessionTest {
#endif
#if HAVE_LOCAL_BACKEND
#if !QNX
GLib.Test.add_func ("/HostSession/process-argv-should-be-available", () => {
var h = new Harness ((h) => Local.process_argv_should_be_available.begin (h as Harness));
h.run ();
});
#endif
#if LINUX
GLib.Test.add_func ("/HostSession/Linux/backend", () => {
var h = new Harness ((h) => Linux.backend.begin (h as Harness));
@@ -1042,6 +1049,50 @@ namespace Frida.HostSessionTest {
namespace Local {
#if !QNX
private static async void process_argv_should_be_available (Harness h) {
try {
var device_manager = new DeviceManager ();
var device = yield device_manager.get_device_by_type (DeviceType.LOCAL);
var target_path = Frida.Test.Labrats.path_to_executable ("sleeper");
var marker = "--frida-argv-probe";
var target = Frida.Test.Process.start (target_path, new string[] { marker });
var options = new ProcessQueryOptions ();
options.scope = METADATA;
options.select_pid (target.id);
Process? match = null;
var timer = new Timer ();
while (match == null && timer.elapsed () < 5.0) {
var processes = yield device.enumerate_processes (options);
if (processes.size () > 0) {
match = processes.get (0);
} else {
Timeout.add (50, process_argv_should_be_available.callback);
yield;
}
}
assert_true (match != null);
var argv = match.parameters["argv"];
assert_true (argv != null);
assert_true (argv.n_children () == 2);
assert_true (argv.get_child_value (1).get_string () == marker);
target.kill ();
yield device_manager.close ();
} catch (GLib.Error e) {
printerr ("Oops: %s\n", e.message);
assert_not_reached ();
}
h.done ();
}
#endif
private static async void latency_should_be_nominal (Harness h) {
h.disable_timeout ();