#frida #injection #instrumentation #process

bin+lib hook-inject

Cross-platform process injection via Frida Core

1 unstable release

Uses new Rust 2024

0.1.0 Feb 21, 2026

#350 in Debugging

MIT/Apache

82KB
2K SLoC

Rust 1.5K SLoC // 0.0% comments C 338 SLoC // 0.0% comments Shell 49 SLoC

hook-inject

Cross-platform process injection API backed by Frida Core.

This crate provides a minimal Rust API for injecting a shared library into a running process or a process launched under injector control. Internally it builds and links against frida-core and calls into a tiny C shim. The injector uses Frida's helper-based backend by default; set HOOK_INJECT_INJECTOR=inprocess to avoid the helper process (at the cost of compatibility on some systems). On certain errors (e.g. permission denied on macOS) it falls back to device-based injection, which may spawn a helper process.

Quickstart

use hook_inject::{inject_process, Library, Process};

let process = Process::from_pid(1234)?;
let library = Library::from_path("/path/to/libagent.so")?;
let injected = inject_process(process, library)?;

injected.uninject()?;

Launch + inject:

use hook_inject::{inject_program, Library, Program, Stdio};

let mut program = Program::new("/usr/bin/true");
program.arg("--version");
let program = program.stdio(Stdio::Null);

let library = Library::from_path("/path/to/libagent.so")?;
let injected = inject_program(program, library)?;

injected.uninject()?;

Spawn suspended (manual resume):

use hook_inject::{spawn, Program};

let suspended = spawn(Program::new("/usr/bin/true"))?;
let _child = suspended.resume()?;

Spawn + inject with output capture:

use hook_inject::{inject_process, Library, Process, Program, Stdio};
use std::io::Read;
use std::process::Command;

let program = Program::new("/usr/bin/true").stdio(Stdio::Pipe);
let library = Library::from_path("/path/to/libagent.so")?;
let mut cmd: Command = program.into_command();
let mut child = cmd.spawn()?;
let process = unsafe { Process::from_pid_unchecked(child.id() as i32) };
let _injected = inject_process(process, library)?;

let mut stdout = String::new();
if let Some(out) = child.stdout.as_mut() {
    out.read_to_string(&mut stdout)?;
}

Inject from an in-memory blob:

use hook_inject::{inject_process, Library, Process};

let process = unsafe { Process::from_pid_unchecked(1234) };
let blob = Library::from_bytes(vec![1, 2, 3])?;
let injected = inject_process(process, blob)?;
injected.uninject()?;

Building agent libraries

Existing library path

use hook_inject::Library;

let lib = Library::from_path("/path/to/libagent.so")?;

Discover a Rust cdylib

use hook_inject::Library;

let lib = Library::from_crate("./agent-crate")?;

If the cdylib is missing, from_crate runs cargo build once and retries.

Dependencies

This crate downloads a prebuilt Frida Core devkit (headers + shared library) by default and links against it.

If you already have a prebuilt Frida Core devkit, you can skip the download by setting:

export FRIDA_CORE_DEVKIT_DIR=/path/to/frida-core-devkit

If you prefer to build a devkit from source, run:

./scripts/build_frida_core_devkit.sh
export FRIDA_CORE_DEVKIT_DIR=vendor/frida-core/build-hook-inject/src/devkit

The build script downloads into target/frida-devkit/<version>/<platform> by default. You can override its behavior with:

  • HOOK_INJECT_DEVKIT_VERSION (default 17.7.3)
  • HOOK_INJECT_DEVKIT_PLATFORM (e.g., linux-x86_64, macos-arm64)

macOS permissions

On macOS, Frida uses task_for_pid() under the hood. If your system denies this call, you will see PermissionDenied errors when attaching or injecting.

You can verify access with the included helper:

clang scripts/task_for_pid_test.c \
  -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk \
  -o /tmp/task_for_pid_test

/tmp/task_for_pid_test <pid>

If the test fails, ensure your user is allowed to debug (Developer Tools access) or run with elevated privileges.

You need the following tools and libraries installed:

  • pkg-config
  • glib-2.0
  • json-glib
  • libffi

If you build the devkit from source using scripts/build_frida_core_devkit.sh, you also need:

  • Meson + Ninja

The devkit download path uses curl + tar on Unix and PowerShell on Windows.

Environment overrides

  • HOOK_INJECT_INJECTOR=inprocess uses Frida's in-process injector instead of the default helper-based injector.

Common install commands:

  • macOS (Homebrew): brew install meson ninja pkg-config glib json-glib libffi
  • Ubuntu/Debian: sudo apt-get install -y meson ninja-build pkg-config libglib2.0-dev libjson-glib-dev libffi-dev

Testing

Full runtime build + tests

cargo test --workspace

Injection smoke test (Linux)

cargo test -p hook-inject --test inject_smoke -- --ignored

Notes

  • The runtime engine is Frida by default; there is no alternate selector.
  • On some platforms, process probing can fail with permission errors. In that case Process::from_pid will return Error::PermissionDenied instead of falsely reporting the process exists.

License

MIT OR Apache-2.0

Dependencies

~0.4–3.5MB
~68K SLoC