Class
GumInterceptor
Description [src]
final class Gum.Interceptor : GObject.Object
{
/* No available fields */
}
Intercepts execution through inline hooking.
Three complementary mechanisms are offered:
- Attaching a
GumInvocationListenerto a function, to be notified right before it is entered and right after it returns, while leaving the original function in place. This is the classic enter/leave hook. - Probing a single point in the code with a listener that implements only
on_enter, typically one fromgum_make_probe_listener(). Because the target is an arbitrary address rather than a function entry, a probe can be placed in the middle of a function to observe execution reaching a specific instruction. A call listener given aNULLon_leaveis not equivalent: it still traps the return and counts towardgum_invocation_context_get_depth(). - Replacing a function outright with your own implementation. Your
replacement can still reach the original by calling the function’s own
address: the interceptor routes such a call to the original instead of
recursing back into the replacement. (The lighter
gum_interceptor_replace_fast()instead hands you a dedicated pointer for this.)
A batch of changes can be grouped into a transaction so that they are
activated as a unit, which is both faster and atomic from the target’s point
of view; see gum_interceptor_begin_transaction().
Attaching a listener
static void on_enter (GumInvocationContext * ic, gpointer user_data);
static void on_leave (GumInvocationContext * ic, gpointer user_data);
void
instrument (void)
{
g_autoptr(GumInterceptor) interceptor = gum_interceptor_obtain ();
GumInvocationListener * listener =
gum_make_call_listener (on_enter, on_leave, NULL, NULL);
gum_interceptor_begin_transaction (interceptor);
gum_interceptor_attach (interceptor,
GSIZE_TO_POINTER (gum_module_find_global_export_by_name ("open")),
listener, NULL);
gum_interceptor_end_transaction (interceptor);
}
Replacing a function
static int (* libc_open) (const char * path, int oflag, ...);
static int
replacement_open (const char * path, int oflag, ...)
{
g_printerr ("open(\"%s\")\n", path);
return libc_open (path, oflag); // reaches the original
}
void
instrument (void)
{
g_autoptr(GumInterceptor) interceptor = gum_interceptor_obtain ();
libc_open = GSIZE_TO_POINTER (
gum_module_find_global_export_by_name ("open"));
gum_interceptor_replace (interceptor, libc_open, replacement_open,
NULL, NULL);
}
Ahead-of-time instrumentation
Inline hooking normally rewrites code at runtime, but some platforms forbid
that: where code signing is strictly enforced (e.g. iOS), executable pages
cannot be patched on the fly. For these, trampolines can be grafted into a
Mach-O binary ahead of time with GumDarwinGrafter — exposed as the
gum-graft command-line tool — which reserves a trampoline at each code
offset you intend to hook.
At runtime gum_interceptor_attach() and
gum_interceptor_replace() then claim the matching grafted trampoline
instead of patching code, so interception works without writable code. If a
target has no grafted trampoline while code signing requires one, the attach
or replace fails with GUM_ATTACH_POLICY_VIOLATION /
GUM_REPLACE_POLICY_VIOLATION.
Functions
gum_interceptor_get_live_replacement_invocation
Returns the invocation context for the given replacement function, if currently active.
gum_interceptor_restore
Unwinds the calling thread’s invocation stack back to the depth recorded in
state, releasing any entries skipped by a non-local exit.
gum_interceptor_save
Records the calling thread’s current invocation depth into state, to be
restored later with gum_interceptor_restore(). Use this around a
non-local exit such as a longjmp() that would otherwise skip the
bookkeeping the interceptor does as intercepted calls return.
Instance methods
gum_interceptor_attach
Attaches listener so that it is notified right before target is entered and
right after it returns. The same listener may be attached to any number of
addresses, and multiple listeners may be attached to the same address. The
original code is left in place.
gum_interceptor_begin_transaction
Begins a transaction, deferring activation of any attach, replace and revert
operations until the matching gum_interceptor_end_transaction().
Batching changes this way is faster and lets a set of modifications be
applied as a unit. Transactions nest; only ending the outermost one applies
the changes.
gum_interceptor_detach
Detaches listener from every function it is currently attached to, undoing any gum_interceptor_attach() calls made with it. Functions left
without any listeners or replacement are restored to their original state.
gum_interceptor_end_transaction
Ends a transaction started with gum_interceptor_begin_transaction().
Ending the outermost transaction activates all changes made since it began.
gum_interceptor_flush
Completes any teardown still pending from earlier detaches and reverts. When a listener is detached or a replacement reverted the hook stops firing immediately, but the memory backing its instrumentation can only be released once no thread is left executing inside it, so that step is deferred. Call this to force a pass and learn whether it finished. Does nothing while a transaction is open.
gum_interceptor_flush_function
Like gum_interceptor_flush(), but reports specifically whether the
instrumentation for function_address is no longer in use, so its memory can
be reclaimed.
gum_interceptor_flush_listener
Like gum_interceptor_flush(), but reports specifically whether
listener is no longer referenced by any in-flight invocation, so it is safe
to release.
gum_interceptor_ignore_current_thread
Temporarily stops the calling thread’s calls into hooked code from triggering listeners. The typical use is to bracket work done internally by an injected payload — for example its own worker threads — so that a user’s hooks observe only the target process’s activity, not the payload’s own calls into the functions it has hooked.
gum_interceptor_ignore_other_threads
Restricts interception to the calling thread: invocations on all other
threads stop triggering listeners until
gum_interceptor_unignore_other_threads() is called.
gum_interceptor_is_locked
Checks whether the interceptor lock is currently held, e.g. to decide whether it is safe to make changes from a signal handler.
gum_interceptor_maybe_unignore_current_thread
Undoes one gum_interceptor_ignore_current_thread(), but only if the
calling thread is currently being ignored.
gum_interceptor_replace
Replaces function_address with replacement_function, so that any call to
it ends up in the replacement instead. The replacement can still reach the
original by calling function_address itself — the interceptor routes that
call to the original rather than recursing — or through original_function
if a pointer is more convenient.
gum_interceptor_replace_fast
Like gum_interceptor_replace(), but trades flexibility for speed by patching function_address to branch straight to replacement_function with
no trampoline in between. A trampoline is only involved if you ask for
original_function, which you must use to reach the original — unlike
gum_interceptor_replace(), calling function_address again would just
re-enter the replacement. A target replaced this way cannot also be attached
to; use gum_interceptor_replace() if you need that.
gum_interceptor_revert
Reverts a previous gum_interceptor_replace() of target, restoring the
original function. Has no effect if the function was not replaced.
gum_interceptor_set_default_options
Sets the instrumentation options applied when a subsequent attach or replace is given no options of its own.
gum_interceptor_unignore_current_thread
Undoes one gum_interceptor_ignore_current_thread() on the calling thread.
gum_interceptor_unignore_other_threads
Lifts a previous gum_interceptor_ignore_other_threads(), resuming
interception on all threads. Must be called from the same thread that
ignored the others.
Signals
Signals inherited from GObject (1)
GObject::notify
The notify signal is emitted on an object when one of its properties has its value set through g_object_set_property(), g_object_set(), et al.