Skip to content

feat(frida): expose Scope::Full + Process::get_parameters#238

Merged
s1341 merged 5 commits into
frida:mainfrom
jhasheng:feature/process-parameters
May 20, 2026
Merged

feat(frida): expose Scope::Full + Process::get_parameters#238
s1341 merged 5 commits into
frida:mainfrom
jhasheng:feature/process-parameters

Conversation

@jhasheng

Copy link
Copy Markdown
Contributor

What

Exposes two pieces of frida-core that the safe wrapper currently can't reach:

  1. FridaProcessQueryOptions scope. frida_device_enumerate_processes_sync takes a query-options arg that the wrapper hard-codes to null (≡ FRIDA_SCOPE_MINIMAL). Adds a Scope enum and Device::enumerate_processes_with_options(scope).
  2. frida_process_get_parameters returns a GHashTable<gchar*, GVariant*>. Process::get_parameters walks the hash and returns HashMap<String, Variant> (typical keys on Windows: ppid, path, user, started).

Existing Device::enumerate_processes() is unchanged — still Scope::Minimal, no breakage for current callers.

Why

Hooking Chromium-style multi-process apps (Chrome, Electron, Discord, WeChat/Weixin, VSCode, ...) needs to pick the main process out of N same-named entries. The info is in frida-core's parameter table (ppid), but Rust callers can't see it today.

Real use case:

let procs = device.enumerate_processes_with_options(Scope::Full);
let same: Vec<_> = procs.iter()
    .filter(|p| p.get_name() == "Weixin.exe")
    .collect();
let pids: HashSet<u32> = same.iter().map(|p| p.get_pid()).collect();
let main = same.iter().find(|p| {
    p.get_parameters().get("ppid")
        .and_then(|v| v.get_int())
        .map(|ppid| !pids.contains(&(ppid as u32)))
        .unwrap_or(false)
});

Side changes (required for the above to actually work)

  • Variant::from_ptr previously todo!()'d on unknown GVariant signatures. That crashes the moment frida hands you something the wrapper didn't anticipate — e.g. Windows packs process icons as "ay" byte arrays, so get_parameters() panics on the first one. Replaced with Variant::Unsupported(String) carrying the original sig; callers decide whether to skip or decode externally.
  • New numeric GVariant signatures. Previously only "x" (Int64) was decoded; added y n q i u t all widened to Int64 (i64 covers u32 / i32 / u64-up-to-i64::MAX comfortably). ppid arrives as "u", so this is a hard prerequisite for the new get_parameters to return anything useful.

Demo

A new example examples/core/get_process_parameters walks the API:

```text
$ cargo run -p get_process_parameters -- Weixin.exe
★ main pid= 15840 ppid= 12888 D:\Tencent\weixin_4.1.2.17\Weixin.exe
helper pid= 6964 ppid= 15840 D:\Tencent\weixin_4.1.2.17\Weixin.exe
helper pid= 15892 ppid= 15840 D:\Tencent\weixin_4.1.2.17\Weixin.exe
helper pid= 25400 ppid= 15840 D:\Tencent\weixin_4.1.2.17\Weixin.exe
```

Tested

Manually against Weixin 4.x on Windows 11. Round-trip works; ppid filter correctly picks the main process (PID 15840) out of 4 `Weixin.exe` instances where the other 3 are `--type=wxocr/wxplayer/wxutility` helpers.

Compatibility

  • No breaking changes. All existing public APIs keep their signatures.
  • New `Scope` and `Variant::Unsupported` are additive.
  • MSRV / dependency surface unchanged.

yw added 2 commits May 18, 2026 17:24
Adds the safe wrapper for two pieces of frida-core that were previously
unreachable from Rust:

  - frida_device_enumerate_processes_sync's FridaProcessQueryOptions arg
    (was passed null = Scope::Minimal). New Scope enum + new
    Device::enumerate_processes_with_options(Scope::Full) returns
    processes with their parameter table populated. enumerate_processes
    keeps the old behavior (Scope::Minimal).

  - frida_process_get_parameters returns a GHashTable<gchar*, GVariant*>.
    Process::get_parameters walks the hash via g_hash_table_iter_* and
    builds HashMap<String, Variant>. Common keys on Windows: ppid (u32),
    path (string), user (string), started (string).

Variant changes:

  - Adds numeric type signatures the previous version only had Int64 for:
    y/n/q/i/u/t are all widened to Int64 (i64 covers all of them; u64
    above i64::MAX wraps, but frida parameter values fit comfortably).

  - Replaces todo!() panic on unknown signatures with
    Variant::Unsupported(String) carrying the original GVariant sig.
    Frida on Windows packs process icons as "ay" byte arrays and there's
    no reason to crash on those — the caller decides to skip them.

Use case: distinguishing main from helper processes in Chromium-style
apps (Weixin.exe / Chrome / Electron / Discord). Filter by
get_parameters()["ppid"] != another same-name process's pid.
Walks `Device::enumerate_processes_with_options(Scope::Full)`, filters
by a user-supplied name, and prints pid / ppid / path for each match —
marking the entry whose parent PID is not another same-name process so
callers can pick the main process out of Chromium-style apps (Chrome,
Electron, Discord, VSCode, Weixin, ...).

Doubles as documentation for the new Scope + get_parameters APIs.
@s1341

s1341 commented May 19, 2026

Copy link
Copy Markdown
Contributor

Thanks! would it be possible to add a test so we can check this new functionality in CI?

Also, please address CI issues.

yw added 3 commits May 20, 2026 10:35
Two lines were broken across multiple lines but rustfmt prefers
them on a single line. CI runs `cargo fmt --all -- --check` and
was failing on these two hunks.
variant.rs in this branch reads ppid/uid/gid/etc. via the full set of
GVariant integer accessors (byte / int16 / uint16 / int32 / uint32 /
uint64), but the Linux cross-platform alias block only re-exported
boolean / int64 / string / type_string.

On Linux the frida-core devkit's frida-core.h #defines every glib
symbol with a `_frida_` prefix to avoid colliding with the system
glib at link time, so bindgen emits only the prefixed names. macOS
and Windows headers don't do that rename, which is why CI was only
red on the Linux job.

Add the 6 missing aliases so call sites in frida/src/variant.rs
resolve on all platforms without any per-platform cfg in the higher
crate.
…ant::Unsupported

Three #[test]s in frida/tests/process_parameters.rs, all running
against the test process itself (no device.attach needed):

  1. get_parameters_returns_data_under_full_scope
     Enumerates with Scope::Full, locates own pid, asserts the
     parameter map is non-empty and contains at least one String
     and one Int64 variant. Doesn't hard-code key names — Linux,
     macOS and Windows expose different keys.

  2. get_parameters_is_empty_under_minimal_scope
     Regression guard: the default-scope alias must NOT populate
     the parameter table. Catches anyone bloating the cheap
     enumerate_processes() path.

  3. variant_iteration_never_panics_on_unknown_signatures
     Core PR regression target. Variant::from_ptr previously
     todo!()'d on unknown GVariant signatures and crashed the
     whole caller — Windows packs process icons as 'ay' byte
     arrays so get_parameters() died on the first hit. This test
     enumerates everything and runs each accessor + Debug; a
     future panic regression breaks the test instead of users.

Tests serialize via a static Mutex<()> since frida-core is a
process-wide singleton. Locally verified on Windows and on Linux
through the docker rust:latest workflow.
@s1341

s1341 commented May 20, 2026

Copy link
Copy Markdown
Contributor

Thanks! Looks good.

@s1341 s1341 merged commit 85a9806 into frida:main May 20, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants