Skip to content

Commit

Permalink
darwin: Massage libunwind around Frida hooks (#515)
Browse files Browse the repository at this point in the history
The problem this change intends to solve is that, currently, with code
like this:

    NSURL * url = nil;
    @Try
    {
        [NSBundle bundleWithURL:url];
    }
    @catch (id err)
    {
        NSLog (@"No worries mate!");
    }

If there's any Frida hook, either via Interceptor or via NativeCallback
swizzling like ObjC.implement(), anywhere in the call stack between the
raise and the catch of the exception, libunwind fails to find the
exception handler. This leads to uncaught exceptions which wouldn't
happen without instrumentation.

When libunwind fails to unwind it usually means it can't resolve the
unwind info, and it can happen for two different reasons:

1. libunwind asks dyld, via _dyld_find_unwind_sections(), but if
   frida-agent is injected then dyld doesn't know about it and libunwind
   gives up on the first instruction pointer to any of frida-agent's
   code on the stack (as normally happens for swizzling-like hooks).
2. In case of Interceptor, for function-aware hooks, the original return
   address on the stack is replaced with the one needed to implement
   onLeave() which belongs to a runtime-generated code thunk, therefore
   missing its mapping to unwind info.

This solution addresses the two above problems directly, via the new
UnwindSitter component:

1. By hooking _dyld_find_unwind_sections() to provide the right unwind
   info for the "invader".
2. By hooking UnwindCursor::setInfoBasedOnIPRegister() to call
   gum_invocation_stack_translate() on the return address to restore the
   original one on the stack at unwind-time.

All of this can be disabled via the `unwind-sitter:off` frida-agent
option.
  • Loading branch information
mrmacete authored May 13, 2024
1 parent 05df3f7 commit ab28e33
Show file tree
Hide file tree
Showing 13 changed files with 809 additions and 0 deletions.
8 changes: 8 additions & 0 deletions lib/agent/agent.vala
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ namespace Frida.Agent {
private Cond transition_cond;
private SpawnMonitor? spawn_monitor;
private ThreadSuspendMonitor? thread_suspend_monitor;
private UnwindSitter? unwind_sitter;

private delegate void CompletionNotify ();

Expand Down Expand Up @@ -258,6 +259,7 @@ namespace Frida.Agent {
interceptor.begin_transaction ();

thread_suspend_monitor = null;
unwind_sitter = null;

invalidate_dbus_context ();

Expand Down Expand Up @@ -286,6 +288,7 @@ namespace Frida.Agent {
#endif
bool enable_exit_monitor = true;
bool enable_thread_suspend_monitor = true;
bool enable_unwind_sitter = true;
foreach (unowned string option in tokens[1:]) {
if (option == "eternal")
ensure_eternalized ();
Expand All @@ -297,6 +300,8 @@ namespace Frida.Agent {
enable_exit_monitor = false;
else if (option == "thread-suspend-monitor:off")
enable_thread_suspend_monitor = false;
else if (option == "unwind-sitter:off")
enable_unwind_sitter = false;
}

if (!enable_exceptor)
Expand All @@ -312,6 +317,9 @@ namespace Frida.Agent {
if (enable_thread_suspend_monitor)
thread_suspend_monitor = new ThreadSuspendMonitor (this);

if (enable_unwind_sitter)
unwind_sitter = new UnwindSitter (this);

this.interceptor = interceptor;
this.exceptor = Gum.Exceptor.obtain ();

Expand Down
2 changes: 2 additions & 0 deletions lib/gadget/gadget.vala
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,7 @@ namespace Frida.Gadget {

private ExitMonitor exit_monitor;
private ThreadSuspendMonitor thread_suspend_monitor;
private UnwindSitter unwind_sitter;

private Gum.ScriptBackend? qjs_backend;
private Gum.ScriptBackend? v8_backend;
Expand All @@ -852,6 +853,7 @@ namespace Frida.Gadget {
construct {
exit_monitor = new ExitMonitor (this, MainContext.default ());
thread_suspend_monitor = new ThreadSuspendMonitor (this);
unwind_sitter = new UnwindSitter (this);
}

public async void start () throws Error, IOError {
Expand Down
2 changes: 2 additions & 0 deletions lib/payload/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ payload_sources = [
'spawn-monitor-glue.c',
'thread-suspend-monitor.vala',
'thread-suspend-monitor-glue.c',
'unwind-sitter.vala',
'unwind-sitter-glue.c',
'exit-monitor.vala',
'cloak.vala',
'fd-guard.vala',
Expand Down
Loading

0 comments on commit ab28e33

Please sign in to comment.