首页
社区
课程
招聘
[原创]Frida-gum 源代码速通笔记
发表于: 2023-8-15 19:43 15419

[原创]Frida-gum 源代码速通笔记

2023-8-15 19:43
15419

最近做一些逆向的时候卡住了,感觉自己对 frida 的了解过于浅薄了,由于自己对安全研究的一些坏习惯,因此不读一下 frida 的源码就理解不了它的实现原理,于是直接就拿起来开始看了。(尽管现在做的不是安全研究,但希望这种习惯能延续下去吧。)

不得不说,frida 的代码写的是真的赏心悦目,像我这样阅读代码的苦手都能大致通过语义理解原理,我只能说,非常有感觉!

Frida 的源代码目录结构按照模块进行区分,本文只选择其中几个笔者认为比较重要的部分进行分析:

出于完整性考虑,笔者也把其他比较重要的模块的介绍贴在这里。如有需要,读者可以自行去深入了解:

frida-python: Frida Python bindings
frida-node: Frida Node.js bindings
frida-qml: Frida Qml plugin
frida-swift: Frida Swift bindings
frida-tools: Frida CLI tools

本文中,笔者将按照自顶向下的方法去分析对应模块的功能实现。但本篇仅涉及到 Frida-gum部分,Frida-core将在另外一篇文章中介绍。

frida-gum 的实现结果是跨架构跨平台的,为此它抽象出了架构无关/平台无关/系统无关的 api 供用户使用。

该模块中有几个较为关心的子模块:

我们从测试样例开始:

样例分为了对部分代码或整体函数进行钩取两种,似乎没什么区别,不妨先从函数开始。

frida 从 interceptor_fixture_attach 函数开始去 hook 对应函数,向下跟进可以找到实现函数:

可以注意到,其中 on_enteron_leave 是可以由用户自行重载的。然后再从 gum_interceptor_attach 进入,该函数包括了布置 hook 并启动 hook 的任务:

笔者已经在上述代码的诸事中大致描述了关键部分的代码功能,在这里需要为此做一些额外说明。

所谓 inline hook 的工作原理是:将函数开头的指令替换为跳转指令,使得函数在执行时先跳转到 hook 到 on_entry 函数中,然后再从中返回执行原函数。

其中,用于从函数开头跳转到 on_entry 中的指令被称之为 跳板,而构造跳板首先需要获得 hook 函数在内存中的地址。这个操作在本文中不会详细介绍,若读者有兴趣了解原理可以自行阅读代码。

而监听器(Listener)的作用则是一个用于记录相关监视数据的结构体,对于已经被 hook 过的函数是不需要添加两个监听器的。

接下来我们跟入 gum_interceptor_instrument

在注释中,笔者提到了二级跳板和三级跳板,那么一级跳板是什么?

一级跳板其实就是函数开头的跳转指令,该指令将会让程序跳转到二级跳板中,而二级跳板会转入三级跳板,最后由三级跳板分发,选择用户提供的 on_entry 函数进行调用。

首先创建的是三级跳板,因此我们跟到 _gum_interceptor_backend_create 里看看它是如何实现的。该函数是平台相关的具体函数,由于笔者打算分析 arm64 下的实现,因此这里的源代码应为 frida-gum/gum/backend-arm64/guminterceptor-arm64.c:

跟入 gum_interceptor_backend_create_thunks

此处通过调用 gum_memory_patch_codegum_emit_thunks 的实现写入到 self->thunks 中,因此我们这里跟入 gum_emit_thunks

此处涉及到了具体的三级跳板的创建,分别由 gum_emit_enter_thunkgum_emit_leave_thunk 完成,这里笔者先从 gum_emit_enter_thunk 进行分析:

此处主要负责调用四级跳板 _gum_function_context_begin_invocation 并进行传参,跟入该函数:

注意,此处第三个参数 caller_ret_addr 代表的是被 hook 函数用于储存返回地址的内存地址,而第四个参数则是四级跳板返回时执行的下一个函数地址。

稍微向下看看函数的实现:(省略部分)

这里不难理解:

此处只是先在栈中保存数据。

然后接下来会调用注册的 on_enter:

后续过程中:

可以看到此处会将 on_leave_trampoline 二级跳板写入到用于储存返回地址的内存中去。也就是说被 hook 函数在执行完毕以后会返回到 on_leave_trampoline

然后如果需要替换函数实现,那么就要把用于替换的实现代码地址写入当前函数的返回地址去,否则就将跳板注入进去。

因此在该函数结束后会根据这一步选择接下来是执行 function_ctx->replacement_function 还是 function_ctx->on_invoke_trampoline

前者就不难理解了,接下来就是调用我们自己实现的函数,并在返回的时候回到二级跳板。

我们看看后者的实现:

此处用于调用原本的函数。

这里我们留个疑问,先不管二级跳板 on_leave_trampoline 的实现是什么,现在再跟一下 leave_chunk

和前一个 chunk 的结构差不多,我们跟入 _gum_function_context_end_invocation :

接下来回到最开始我们跳过的地方,按照代码的顺序,其实在完成上述跳板设置以后才开始准备二级跳板:

该代码大致会实现如下结构:

到这一步其实流程就清晰很多了。

而如果用户没有注册 on_leave 函数,那么钩子的步骤将会减少很多。在 _gum_function_context_begin_invocation 中将不会修改真正的返回地址,并直接让 next_hop 设置为 on_invoke_trampoline ,此时程序将直接离开钩子,因为后续不会再进入到 leave_chunk 了。

在上文中可以发现,几个跳板的实现其实是走了固定的寄存器的。因此如果程序本身本来就要使用这两个寄存器进行工作的话,去 hook 那些函数会导致非预期的结果。这个地方可能需要注意一下吧,毕竟大多数时候使用 frida 感觉 hook 基本上都是透明的,容易忽略到这种级别的问题。

其实笔者没怎么用过这个功能,它所实现的 “代码跟踪” 能力其实和调试器差不多,而如果能使用调试器进行调试的话,大部分问题其实都能解决,而就算不能使用调试器,靠 frida 的 hook 也能解决不少问题了,这导致笔者基本上没用过它。

不过没用过不影响看看原理。因为 Stalker 是靠代码插桩的方式实现跟踪的,这和事情的 Interceptor 有点相似。

Stalker 的测试样例就比较多了,但我们只想对源代码的实现有所了解,因此不需要每个都看,选几个比较有意思的就行。这里笔者选了 TESTENTRY(call) 作为分析样例:

关键的实现在 invoke_flat 中,这里我们跟入:invoke_flat - invoke_flat_expecting_return_value

再跟入 test_arm64_stalker_fixture_follow_and_invoke

逻辑比较清晰,相当于将原本的代码注入到另外一片内存,然后对其进行插桩执行,并在插桩代码中记录覆盖率相关的信息。这里我们先从 gum_stalker_follow_me 开始,它是一个由汇编实现的函数:

此处对于 Apple 架构和其他架构选用了两个不同的函数,不过我在源代码中并没有找到 __gum_stalker_do_follow_me 的声明或实现,这里我们将就这用 _gum_stalker_do_follow_me 进行理解吧:

关键内容跟入 gum_event_sink_start ,里面是用于记录覆盖率信息的具体函数,分别有两套实现,一套是用 quickjs ,另外一套是 v8 的实现,细节这里笔者就不深究了,大致逻辑如图:

这部分内容也不是笔者关心的重点,但笔者找了一圈似乎没找到 arm64 下的实现,倒是有 x86 平台下的测试样例。因此本文也就不过多赘述了,大致原理就是设置内存页的读写权限,从而在读写监控页面的时候引发中断来监视内容。

实现的内容分了 Windows 平台和 posix 平台两种,如下代码为 posix 平台:

这部分主要是做一个扫盲。细节可以参考 evilpan 大佬的文章,里面也大致介绍了 gum-js 的实现。

简单来说就说,V8 支持对 JavaScript 的动态解析,并能够将其抽象到 C 语言层面进行调用。

通过这种交互面板,就能给允许用户动态传入脚本进行执行了。之所以选择的是 JavaScript 而不是其他语言,就笔者估计来看,大致上有两个原因(以下内容为笔者的猜测,各位读者可以当看个乐子):

Turbofan 可参考本文:https://bbs.kanxue.com/thread-273791.htm

https://evilpan.com/2022/04/05/frida-internal/#stalker
https://zhuanlan.zhihu.com/p/603717118
https://o0xmuhe.github.io/2019/11/15/frida-gum%E4%BB%A3%E7%A0%81%E9%98%85%E8%AF%BB/#2-2-2-hook%E4%BB%8E0%E5%88%B01

TESTLIST_BEGIN (interceptor_arm64)
    TESTENTRY (attach_to_thunk_reading_lr)
    TESTENTRY (attach_to_function_reading_lr)
TESTLIST_END ()
TESTLIST_BEGIN (interceptor_arm64)
    TESTENTRY (attach_to_thunk_reading_lr)
    TESTENTRY (attach_to_function_reading_lr)
TESTLIST_END ()
TESTCASE (attach_to_function_reading_lr)
{
  const gsize code_size_in_pages = 1;
  gsize code_size;
  GumEmitLrFuncContext ctx;
   
  code_size = code_size_in_pages * gum_query_page_size ();
  ctx.code = gum_alloc_n_pages (code_size_in_pages, GUM_PAGE_RW);
  ctx.run = NULL;
  ctx.func = NULL;
  ctx.caller_lr = 0;
   
  gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_func, &ctx);
   
  g_assert_cmphex (ctx.run (), ==, ctx.caller_lr);
   
  interceptor_fixture_attach (fixture, 0, ctx.func, '>', '<');
  g_assert_cmphex (ctx.run (), !=, ctx.caller_lr);
  g_assert_cmpstr (fixture->result->str, ==, "><");
   
  interceptor_fixture_detach (fixture, 0);
  gum_free_pages (ctx.code);
}
TESTCASE (attach_to_function_reading_lr)
{
  const gsize code_size_in_pages = 1;
  gsize code_size;
  GumEmitLrFuncContext ctx;
   
  code_size = code_size_in_pages * gum_query_page_size ();
  ctx.code = gum_alloc_n_pages (code_size_in_pages, GUM_PAGE_RW);
  ctx.run = NULL;
  ctx.func = NULL;
  ctx.caller_lr = 0;
   
  gum_memory_patch_code (ctx.code, code_size, gum_emit_lr_func, &ctx);
   
  g_assert_cmphex (ctx.run (), ==, ctx.caller_lr);
   
  interceptor_fixture_attach (fixture, 0, ctx.func, '>', '<');
  g_assert_cmphex (ctx.run (), !=, ctx.caller_lr);
  g_assert_cmpstr (fixture->result->str, ==, "><");
   
  interceptor_fixture_detach (fixture, 0);
  gum_free_pages (ctx.code);
}
static GumAttachReturn
interceptor_fixture_try_attach (InterceptorFixture * h,
                                guint listener_index,
                                gpointer test_func,
                                gchar enter_char,
                                gchar leave_char)
{
  GumAttachReturn result;
  Arm64ListenerContext * ctx;
 
  ctx = h->listener_context[listener_index];
  if (ctx != NULL)
  {
    arm64_listener_context_free (ctx);
    h->listener_context[listener_index] = NULL;
  }
 
  ctx = g_slice_new0 (Arm64ListenerContext);
 
  ctx->listener = test_callback_listener_new ();
  ctx->listener->on_enter =
      (TestCallbackListenerFunc) arm64_listener_context_on_enter;
  ctx->listener->on_leave =
      (TestCallbackListenerFunc) arm64_listener_context_on_leave;
  ctx->listener->user_data = ctx;
 
  ctx->fixture = h;
  ctx->enter_char = enter_char;
  ctx->leave_char = leave_char;
 
  result = gum_interceptor_attach (h->interceptor, test_func,
      GUM_INVOCATION_LISTENER (ctx->listener), NULL);
  if (result == GUM_ATTACH_OK)
  {
    h->listener_context[listener_index] = ctx;
  }
  else
  {
    arm64_listener_context_free (ctx);
  }
 
  return result;
}
static GumAttachReturn
interceptor_fixture_try_attach (InterceptorFixture * h,
                                guint listener_index,
                                gpointer test_func,
                                gchar enter_char,
                                gchar leave_char)
{
  GumAttachReturn result;
  Arm64ListenerContext * ctx;
 
  ctx = h->listener_context[listener_index];
  if (ctx != NULL)
  {
    arm64_listener_context_free (ctx);
    h->listener_context[listener_index] = NULL;
  }
 
  ctx = g_slice_new0 (Arm64ListenerContext);
 
  ctx->listener = test_callback_listener_new ();
  ctx->listener->on_enter =
      (TestCallbackListenerFunc) arm64_listener_context_on_enter;
  ctx->listener->on_leave =
      (TestCallbackListenerFunc) arm64_listener_context_on_leave;
  ctx->listener->user_data = ctx;
 
  ctx->fixture = h;
  ctx->enter_char = enter_char;
  ctx->leave_char = leave_char;
 
  result = gum_interceptor_attach (h->interceptor, test_func,
      GUM_INVOCATION_LISTENER (ctx->listener), NULL);
  if (result == GUM_ATTACH_OK)
  {
    h->listener_context[listener_index] = ctx;
  }
  else
  {
    arm64_listener_context_free (ctx);
  }
 
  return result;
}
GumAttachReturn
gum_interceptor_attach (GumInterceptor * self,
                        gpointer function_address,
                        GumInvocationListener * listener,
                        gpointer listener_function_data)
{
  GumAttachReturn result = GUM_ATTACH_OK;
  GumFunctionContext * function_ctx;
  GumInstrumentationError error;
 
  gum_interceptor_ignore_current_thread (self);
  GUM_INTERCEPTOR_LOCK (self);
  gum_interceptor_transaction_begin (&self->current_transaction);
  self->current_transaction.is_dirty = TRUE;
//1. 获得需要钩取的函数地址
  function_address = gum_interceptor_resolve (self, function_address);
 
//2. 此处用于构造跳板、布置 hook,并将该函数抽象为一个 ctx 结构体
//后续对该函数的引用都将使用 ctx 指代该函数
  function_ctx = gum_interceptor_instrument (self, GUM_INTERCEPTOR_TYPE_DEFAULT,
      function_address, &error);
 
  if (function_ctx == NULL)
    goto instrumentation_error;
//3. 添加监听器
  if (gum_function_context_has_listener (function_ctx, listener))
    goto already_attached;
 
  gum_function_context_add_listener (function_ctx, listener,
      listener_function_data);
 
  goto beach;
 
instrumentation_error:
  {
    switch (error)
    {
      case GUM_INSTRUMENTATION_ERROR_WRONG_SIGNATURE:
        result = GUM_ATTACH_WRONG_SIGNATURE;
        break;
      case GUM_INSTRUMENTATION_ERROR_POLICY_VIOLATION:
        result = GUM_ATTACH_POLICY_VIOLATION;
        break;
      case GUM_INSTRUMENTATION_ERROR_WRONG_TYPE:
        result = GUM_ATTACH_WRONG_TYPE;
        break;
      default:
        g_assert_not_reached ();
    }
    goto beach;
  }
already_attached:
  {
    result = GUM_ATTACH_ALREADY_ATTACHED;
    goto beach;
  }
beach:
  {
//4. 注入跳板
    gum_interceptor_transaction_end (&self->current_transaction);
    GUM_INTERCEPTOR_UNLOCK (self);
    gum_interceptor_unignore_current_thread (self);
 
    return result;
  }
}
GumAttachReturn
gum_interceptor_attach (GumInterceptor * self,
                        gpointer function_address,
                        GumInvocationListener * listener,
                        gpointer listener_function_data)
{
  GumAttachReturn result = GUM_ATTACH_OK;
  GumFunctionContext * function_ctx;
  GumInstrumentationError error;
 
  gum_interceptor_ignore_current_thread (self);
  GUM_INTERCEPTOR_LOCK (self);
  gum_interceptor_transaction_begin (&self->current_transaction);
  self->current_transaction.is_dirty = TRUE;
//1. 获得需要钩取的函数地址
  function_address = gum_interceptor_resolve (self, function_address);
 
//2. 此处用于构造跳板、布置 hook,并将该函数抽象为一个 ctx 结构体
//后续对该函数的引用都将使用 ctx 指代该函数
  function_ctx = gum_interceptor_instrument (self, GUM_INTERCEPTOR_TYPE_DEFAULT,
      function_address, &error);
 
  if (function_ctx == NULL)
    goto instrumentation_error;
//3. 添加监听器
  if (gum_function_context_has_listener (function_ctx, listener))
    goto already_attached;
 
  gum_function_context_add_listener (function_ctx, listener,
      listener_function_data);
 
  goto beach;
 
instrumentation_error:
  {
    switch (error)
    {
      case GUM_INSTRUMENTATION_ERROR_WRONG_SIGNATURE:
        result = GUM_ATTACH_WRONG_SIGNATURE;
        break;
      case GUM_INSTRUMENTATION_ERROR_POLICY_VIOLATION:
        result = GUM_ATTACH_POLICY_VIOLATION;
        break;
      case GUM_INSTRUMENTATION_ERROR_WRONG_TYPE:
        result = GUM_ATTACH_WRONG_TYPE;
        break;
      default:
        g_assert_not_reached ();
    }
    goto beach;
  }
already_attached:
  {
    result = GUM_ATTACH_ALREADY_ATTACHED;
    goto beach;
  }
beach:
  {
//4. 注入跳板
    gum_interceptor_transaction_end (&self->current_transaction);
    GUM_INTERCEPTOR_UNLOCK (self);
    gum_interceptor_unignore_current_thread (self);
 
    return result;
  }
}
static GumFunctionContext *
gum_interceptor_instrument (GumInterceptor * self,
                            GumInterceptorType type,
                            gpointer function_address,
                            GumInstrumentationError * error)
{
  GumFunctionContext * ctx;
 
  *error = GUM_INSTRUMENTATION_ERROR_NONE;
//1. 获得需要 hook 的函数对象
//该对象在第一次调用 gum_interceptor_instrument 进行初始化入表
//此处由于第一次调用的缘故,在表里查询不到 ctx
//因此会继续往下器创建该 ctx
  ctx = (GumFunctionContext *) g_hash_table_lookup (self->function_by_address,
      function_address);
 
  if (ctx != NULL)
  {
    if (ctx->type != type)
    {
      *error = GUM_INSTRUMENTATION_ERROR_WRONG_TYPE;
      return NULL;
    }
    return ctx;
  }
 
  if (self->backend == NULL)
  {
//2. 构造三级跳板
    self->backend =
        _gum_interceptor_backend_create (&self->mutex, &self->allocator);
  }
//3. 初始化 ctx
  ctx = gum_function_context_new (self, function_address, type);
 
  if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_REQUIRED)
  {
    if (!_gum_interceptor_backend_claim_grafted_trampoline (self->backend, ctx))
      goto policy_violation;
  }
  else
  {
//4. 构造二级跳板
    if (!_gum_interceptor_backend_create_trampoline (self->backend, ctx))
      goto wrong_signature;
  }
//5. 函数入表,表示已经完成基本操作
  g_hash_table_insert (self->function_by_address, function_address, ctx);
//6. 添加任务,设置回调函数 gum_interceptor_activate 用于激活跳板
  gum_interceptor_transaction_schedule_update (&self->current_transaction, ctx,
      gum_interceptor_activate);
 
  return ctx;
 
policy_violation:
  {
    *error = GUM_INSTRUMENTATION_ERROR_POLICY_VIOLATION;
    goto propagate_error;
  }
wrong_signature:
  {
    *error = GUM_INSTRUMENTATION_ERROR_WRONG_SIGNATURE;
    goto propagate_error;
  }
propagate_error:
  {
    gum_function_context_finalize (ctx);
 
    return NULL;
  }
}
static GumFunctionContext *
gum_interceptor_instrument (GumInterceptor * self,
                            GumInterceptorType type,
                            gpointer function_address,
                            GumInstrumentationError * error)
{
  GumFunctionContext * ctx;
 
  *error = GUM_INSTRUMENTATION_ERROR_NONE;
//1. 获得需要 hook 的函数对象
//该对象在第一次调用 gum_interceptor_instrument 进行初始化入表
//此处由于第一次调用的缘故,在表里查询不到 ctx
//因此会继续往下器创建该 ctx
  ctx = (GumFunctionContext *) g_hash_table_lookup (self->function_by_address,
      function_address);
 
  if (ctx != NULL)
  {
    if (ctx->type != type)
    {
      *error = GUM_INSTRUMENTATION_ERROR_WRONG_TYPE;
      return NULL;
    }
    return ctx;
  }
 
  if (self->backend == NULL)
  {
//2. 构造三级跳板
    self->backend =
        _gum_interceptor_backend_create (&self->mutex, &self->allocator);
  }
//3. 初始化 ctx
  ctx = gum_function_context_new (self, function_address, type);
 
  if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_REQUIRED)
  {
    if (!_gum_interceptor_backend_claim_grafted_trampoline (self->backend, ctx))
      goto policy_violation;
  }
  else
  {
//4. 构造二级跳板
    if (!_gum_interceptor_backend_create_trampoline (self->backend, ctx))
      goto wrong_signature;
  }
//5. 函数入表,表示已经完成基本操作
  g_hash_table_insert (self->function_by_address, function_address, ctx);
//6. 添加任务,设置回调函数 gum_interceptor_activate 用于激活跳板
  gum_interceptor_transaction_schedule_update (&self->current_transaction, ctx,
      gum_interceptor_activate);
 
  return ctx;
 
policy_violation:
  {
    *error = GUM_INSTRUMENTATION_ERROR_POLICY_VIOLATION;
    goto propagate_error;
  }
wrong_signature:
  {
    *error = GUM_INSTRUMENTATION_ERROR_WRONG_SIGNATURE;
    goto propagate_error;
  }
propagate_error:
  {
    gum_function_context_finalize (ctx);
 
    return NULL;
  }
}
GumInterceptorBackend *
_gum_interceptor_backend_create (GRecMutex * mutex,
                                 GumCodeAllocator * allocator)
{
  GumInterceptorBackend * backend;
 
  backend = g_slice_new0 (GumInterceptorBackend);
  backend->mutex = mutex;
  backend->allocator = allocator;
 
  if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_OPTIONAL)
  {
    gum_arm64_writer_init (&backend->writer, NULL);
    gum_arm64_relocator_init (&backend->relocator, NULL, &backend->writer);
 
    gum_interceptor_backend_create_thunks (backend);
  }
 
  return backend;
}
GumInterceptorBackend *
_gum_interceptor_backend_create (GRecMutex * mutex,
                                 GumCodeAllocator * allocator)
{
  GumInterceptorBackend * backend;
 
  backend = g_slice_new0 (GumInterceptorBackend);
  backend->mutex = mutex;
  backend->allocator = allocator;
 
  if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_OPTIONAL)
  {
    gum_arm64_writer_init (&backend->writer, NULL);
    gum_arm64_relocator_init (&backend->relocator, NULL, &backend->writer);
 
    gum_interceptor_backend_create_thunks (backend);
  }
 
  return backend;
}
static void
gum_interceptor_backend_create_thunks (GumInterceptorBackend * self)
{
  gsize page_size, code_size;
 
  page_size = gum_query_page_size ();
  code_size = page_size;
 
  self->thunks = gum_memory_allocate (NULL, code_size, page_size, GUM_PAGE_RW);
  gum_memory_patch_code (self->thunks, 1024,
      (GumMemoryPatchApplyFunc) gum_emit_thunks, self);
}
static void
gum_interceptor_backend_create_thunks (GumInterceptorBackend * self)
{
  gsize page_size, code_size;
 
  page_size = gum_query_page_size ();
  code_size = page_size;
 
  self->thunks = gum_memory_allocate (NULL, code_size, page_size, GUM_PAGE_RW);
  gum_memory_patch_code (self->thunks, 1024,
      (GumMemoryPatchApplyFunc) gum_emit_thunks, self);
}
static void
gum_emit_thunks (gpointer mem,
                 GumInterceptorBackend * self)
{
  GumArm64Writer * aw = &self->writer;
 
  self->enter_thunk = self->thunks;
  gum_arm64_writer_reset (aw, mem);
  aw->pc = GUM_ADDRESS (self->enter_thunk);
  gum_emit_enter_thunk (aw);//1. 此处创建三级跳板 enter_thunk
  gum_arm64_writer_flush (aw);
 
  self->leave_thunk =
      (guint8 *) self->enter_thunk + gum_arm64_writer_offset (aw);
  gum_emit_leave_thunk (aw);//2. 此处创建三级跳板 leave_thunk
  gum_arm64_writer_flush (aw);
}
static void
gum_emit_thunks (gpointer mem,
                 GumInterceptorBackend * self)
{
  GumArm64Writer * aw = &self->writer;
 
  self->enter_thunk = self->thunks;
  gum_arm64_writer_reset (aw, mem);
  aw->pc = GUM_ADDRESS (self->enter_thunk);
  gum_emit_enter_thunk (aw);//1. 此处创建三级跳板 enter_thunk
  gum_arm64_writer_flush (aw);
 
  self->leave_thunk =
      (guint8 *) self->enter_thunk + gum_arm64_writer_offset (aw);
  gum_emit_leave_thunk (aw);//2. 此处创建三级跳板 leave_thunk
  gum_arm64_writer_flush (aw);
}
static void
gum_emit_enter_thunk (GumArm64Writer * aw)
{
  gum_emit_prolog (aw);//1. 保存上下文信息
 
//2. add x1,sp,0
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
//3. add x2,sp,G_STRUCT_OFFSET (GumCpuContext, lr)
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT + G_STRUCT_OFFSET (GumCpuContext, lr));
//4. add x3,sp,sizeof (GumCpuContext)
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X3, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
//5. call _gum_function_context_begin_invocation(x17,x1,x2,x3)
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_begin_invocation), 4,
      GUM_ARG_REGISTER, ARM64_REG_X17,
      GUM_ARG_REGISTER, ARM64_REG_X1,
      GUM_ARG_REGISTER, ARM64_REG_X2,
      GUM_ARG_REGISTER, ARM64_REG_X3);
 
  gum_emit_epilog (aw);
}
static void
gum_emit_enter_thunk (GumArm64Writer * aw)
{
  gum_emit_prolog (aw);//1. 保存上下文信息
 
//2. add x1,sp,0
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X1, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT);
//3. add x2,sp,G_STRUCT_OFFSET (GumCpuContext, lr)
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X2, ARM64_REG_SP,
      GUM_FRAME_OFFSET_CPU_CONTEXT + G_STRUCT_OFFSET (GumCpuContext, lr));
//4. add x3,sp,sizeof (GumCpuContext)
  gum_arm64_writer_put_add_reg_reg_imm (aw, ARM64_REG_X3, ARM64_REG_SP,
      GUM_FRAME_OFFSET_NEXT_HOP);
//5. call _gum_function_context_begin_invocation(x17,x1,x2,x3)
  gum_arm64_writer_put_call_address_with_arguments (aw,
      GUM_ADDRESS (_gum_function_context_begin_invocation), 4,
      GUM_ARG_REGISTER, ARM64_REG_X17,
      GUM_ARG_REGISTER, ARM64_REG_X1,
      GUM_ARG_REGISTER, ARM64_REG_X2,
      GUM_ARG_REGISTER, ARM64_REG_X3);
 
  gum_emit_epilog (aw);
}
void
_gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
                                        GumCpuContext * cpu_context,
                                        gpointer * caller_ret_addr,
                                        gpointer * next_hop)
void
_gum_function_context_begin_invocation (GumFunctionContext * function_ctx,
                                        GumCpuContext * cpu_context,
                                        gpointer * caller_ret_addr,
                                        gpointer * next_hop)
//1. 如果替换了函数实现,或注册了 on_leave 则设置 will_trap_on_leave
  will_trap_on_leave = function_ctx->replacement_function != NULL ||
      (invoke_listeners && function_ctx->has_on_leave_listener);
  if (will_trap_on_leave)
  {
//2. 如果设置了 will_trap_on_leave,就需要保存原本的返回地址,这样在 on_leave 时能给正确返回
    stack_entry = gum_invocation_stack_push (stack, function_ctx,
        *caller_ret_addr);
    invocation_ctx = &stack_entry->invocation_context;
  }
  else if (invoke_listeners)
  {
//3. 如果没设置 will_trap_on_leave,但有注册 linsters,那么在这里把原本的函数地址保存到栈里
    stack_entry = gum_invocation_stack_push (stack, function_ctx,
        function_ctx->function_address);
    invocation_ctx = &stack_entry->invocation_context;
  }
//1. 如果替换了函数实现,或注册了 on_leave 则设置 will_trap_on_leave
  will_trap_on_leave = function_ctx->replacement_function != NULL ||
      (invoke_listeners && function_ctx->has_on_leave_listener);
  if (will_trap_on_leave)
  {
//2. 如果设置了 will_trap_on_leave,就需要保存原本的返回地址,这样在 on_leave 时能给正确返回
    stack_entry = gum_invocation_stack_push (stack, function_ctx,
        *caller_ret_addr);
    invocation_ctx = &stack_entry->invocation_context;
  }
  else if (invoke_listeners)
  {
//3. 如果没设置 will_trap_on_leave,但有注册 linsters,那么在这里把原本的函数地址保存到栈里
    stack_entry = gum_invocation_stack_push (stack, function_ctx,
        function_ctx->function_address);
    invocation_ctx = &stack_entry->invocation_context;
  }
if (listener_entry->listener_interface->on_enter != NULL)
{
  listener_entry->listener_interface->on_enter (
      listener_entry->listener_instance, invocation_ctx);
}
if (listener_entry->listener_interface->on_enter != NULL)
{
  listener_entry->listener_interface->on_enter (
      listener_entry->listener_instance, invocation_ctx);
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-8-15 19:43 被Tokameine编辑 ,原因:
收藏
免费 12
支持
分享
最新回复 (3)
雪    币: 14492
活跃值: (17493)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-8-18 09:42
0
雪    币: 1802
活跃值: (4000)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2023-10-17 11:00
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
想问下对于frida源码的分析,应该如何进行调试分析环境的部署?
2024-10-6 02:15
0
游客
登录 | 注册 方可回帖
返回
//