13

frida-gum代码阅读笔记

 3 years ago
source link: https://o0xmuhe.github.io/2019/11/15/frida-gum%E4%BB%A3%E7%A0%81%E9%98%85%E8%AF%BB/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
a year ago学习记录26 minutes lesen (Über 3899 Worte)

frida-gum代码阅读笔记

0x00 : 前言与预备知识

frida : frida是一个优秀的跨平台Dynamic instrumentation toolkit,具体可以看官网介绍

GObject对象系统

GObject这个比较重要,因为frida框架底层的hook框架Frida-gum是纯c写的,为了实现一些面向对象的编程,使用了Gobject。

注:本篇主要是看interceptor这种hook方式,针对函数头,之后会有一篇针对Stalker 模式的分析。

0x01 : 项目构架

直接拉下来的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
╭─muhe@muheMacBookPro ~/Code/frida ‹master*›
╰─$ l
total 137608
drwxr-xr-x 29 muhe staff 928B Nov 13 17:22 .
drwxr-xr-x 90 muhe staff 2.8K Nov 5 16:44 ..
drwxr-xr-x 15 muhe staff 480B Nov 15 18:23 .git
-rw-r--r-- 1 muhe staff 383B Jan 21 2019 .gitignore
-rw-r--r-- 1 muhe staff 886B Jan 21 2019 .gitmodules
drwxr-xr-x 3 muhe staff 96B Nov 13 17:22 .vscode
-rw-r--r-- 1 muhe staff 2.4K Jan 21 2019 COPYING
-rw-r--r-- 1 muhe staff 1.2K Nov 7 18:11 Makefile
-rw-r--r-- 1 muhe staff 28K Nov 7 18:11 Makefile.linux.mk
-rw-r--r-- 1 muhe staff 28K Nov 7 18:11 Makefile.macos.mk
-rw-r--r-- 1 muhe staff 21K Nov 7 18:11 Makefile.sdk.mk
-rw-r--r-- 1 muhe staff 84K Apr 29 2019 Makefile.toolchain.mk
-rw-r--r-- 1 muhe staff 1.7K Nov 7 18:11 README.md
drwxr-xr-x 10 muhe staff 320B Nov 11 14:59 build
drwxr-xr-x 61 muhe staff 1.9K Jan 21 2019 capstone
-rw-r--r-- 1 muhe staff 1.0K Nov 7 18:11 config.mk
drwxr-xr-x 9 muhe staff 288B Jan 21 2019 frida-clr
drwxr-xr-x 21 muhe staff 672B Jan 21 2019 frida-core
drwxr-xr-x 20 muhe staff 640B Nov 11 17:37 frida-gum
drwxr-xr-x 15 muhe staff 480B Jan 21 2019 frida-node
drwxr-xr-x 20 muhe staff 640B Jan 21 2019 frida-python
drwxr-xr-x 27 muhe staff 864B Jan 21 2019 frida-qml
drwxr-xr-x 10 muhe staff 320B Jan 21 2019 frida-swift
drwxr-xr-x 12 muhe staff 384B Jan 21 2019 frida-tools
-rw-r--r-- 1 muhe staff 25K Nov 7 18:11 frida.sln
-rw-r--r-- 1 muhe staff 9.0K Nov 11 14:43 frida.srctrlbm
-rw-r--r-- 1 muhe staff 67M Nov 11 14:43 frida.srctrldb
-rw-r--r-- 1 muhe staff 6.1K Nov 11 14:35 frida.srctrlprj
drwxr-xr-x 47 muhe staff 1.5K Nov 7 18:11 releng

frida-gum是底层hook框架,跨平台;

frida-python , frida-node啥的是 bindings,暂时不管,不理解原理看也看不懂;

capstone 牛逼的反汇编框架,frida-gum中用到了,用于指令的读;

releng 编译相关的;

frida-core server/agent相关;

frida-tools 一些工具,比如frida-ps啥的。

重点是frida-gum ,这是理解这个框架的基础。

0x02 : 阅读frida-gum (x86为例)

frida-gum 注释并不多,甚至可以说几乎没,好在他代码写得好,构架合理代码规范好,所以阅读起来多读几遍,总会看懂的。

2.1. 构架

这个的框架的构架如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
total 200
drwxr-xr-x 20 muhe staff 640B Nov 11 17:37 .
drwxr-xr-x 29 muhe staff 928B Nov 13 17:22 ..
-rw-r--r-- 1 muhe staff 34B Jan 21 2019 .git
-rw-r--r-- 1 muhe staff 70B Jan 21 2019 .gitignore
drwxr-xr-x 3 muhe staff 96B Nov 11 17:37 .vscode
-rw-r--r-- 1 muhe staff 5.6K Jan 21 2019 COPYING
drwxr-xr-x 5 muhe staff 160B Jan 21 2019 bindings
-rw-r--r-- 1 muhe staff 2.1K Jan 21 2019 config.h.in
drwxr-xr-x 3 muhe staff 96B Jan 21 2019 ext
drwxr-xr-x 85 muhe staff 2.7K Jan 21 2019 gum
-rw-r--r-- 1 muhe staff 5.1K Jan 21 2019 gum-32.vcxproj
-rw-r--r-- 1 muhe staff 16K Jan 21 2019 gum-32.vcxproj.filters
-rw-r--r-- 1 muhe staff 5.1K Jan 21 2019 gum-64.vcxproj
-rw-r--r-- 1 muhe staff 16K Jan 21 2019 gum-64.vcxproj.filters
-rw-r--r-- 1 muhe staff 8.5K Jan 21 2019 gum-common.props
drwxr-xr-x 4 muhe staff 128B Jan 21 2019 libs
-rw-r--r-- 1 muhe staff 6.8K Jan 21 2019 meson.build
-rw-r--r-- 1 muhe staff 190B Jan 21 2019 meson_options.txt
drwxr-xr-x 28 muhe staff 896B Jan 21 2019 tests
drwxr-xr-x 7 muhe staff 224B Jan 21 2019 vapi

核心是在gum目录下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
gum
├── arch-arm
├── arch-arm64
├── arch-mips
├── arch-x86
├── backend-arm
├── backend-arm64
├── backend-darwin
├── backend-dbghelp
├── backend-elf
├── backend-libdwarf
├── backend-libunwind
├── backend-linux
├── backend-mips
├── backend-posix
├── backend-qnx
├── backend-windows
└── backend-x86
....// gum下其他文件

这里有必要说一下,frida-gum 为了实现跨平台,抽象出来 构架无关/平台无关/系统无关的api,比如一些内存操作,在frida-gum里可能就是gum_xxxxx,但是根据不同平台,调用到对应平台的api里去,正是做了很好的封装,上层代码才会看起来“平台无关”。

还有几个核心的对象,后面的代码里频繁提及:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct _GumInterceptor
{
GObject parent;

GRecMutex mutex;

GHashTable * function_by_address;

GumInterceptorBackend * backend;
GumCodeAllocator allocator;

volatile guint selected_thread_id;

GumInterceptorTransaction current_transaction;
};

从这个拦截器类索引出去的对象都需要好好注意,比如 GumInterceptorBackend , 最好可以生成一个uml图,阅读代码的时候对比着看。

2.2. 代码阅读

2.2.1 准备工作

面对比较大的代码,重要的是找到一个入口,从这个点开始读,我这里大概看了下单元测试的代码,发现基本是: 初始化,测试各种功能,清理,退出。

那么我的阅读思路就是 :

  1. 初始化部分
  2. 各种功能,比如 内存模块,指令读写模块,代码修复模块
  3. 清理 这部分大概过一下就行

这里我参考了 jmpews师傅的关于设计hook框架的文章,了解一个hook框架如何设计,分哪些模块,在阅读代码的时候能够有针对性一些。

  • 内存分配 模块
  • 指令写 模块
  • 指令读 模块
  • 指令修复 模块 relocator
  • 调度器 模块 enter_thunk部分实现

具体可以参考他的文章: 如何构建一款像 frida 一样的框架

2.2.2 hook从0到1

阅读顺序根据单元测试gum-test.c确定的,具体的可以看代码

gum_interceptor_obtain()

这部分是 拦截器初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 初始化 interceptor 对象初始化
GumInterceptor *
gum_interceptor_obtain (void)
{
GumInterceptor * interceptor;

g_mutex_lock (&_gum_interceptor_lock);

if (_the_interceptor != NULL)
{
interceptor = GUM_INTERCEPTOR (g_object_ref (_the_interceptor));
}
else
{
_the_interceptor = g_object_new (GUM_TYPE_INTERCEPTOR, NULL);
g_object_weak_ref (G_OBJECT (_the_interceptor),
the_interceptor_weak_notify, NULL);

interceptor = _the_interceptor;
}

g_mutex_unlock (&_gum_interceptor_lock);

return interceptor;
}


static void
gum_interceptor_init (GumInterceptor * self)
{
g_rec_mutex_init (&self->mutex);

self->function_by_address = g_hash_table_new_full (NULL, NULL, NULL,
(GDestroyNotify) gum_function_context_destroy);

// 分配器初始化
gum_code_allocator_init (&self->allocator, GUM_INTERCEPTOR_CODE_SLICE_SIZE);

// 创建拦截器后端
self->backend = _gum_interceptor_backend_create (&self->allocator);

gum_interceptor_transaction_init (&self->current_transaction, self);
}

因为GObject的使用,gum_interceptor_init 这个构造函数,在 interceptor对象创建出来的时候触发。

重点看拦截器后端的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GumInterceptorBackend *
_gum_interceptor_backend_create (GumCodeAllocator * allocator)
{
GumInterceptorBackend * backend;

backend = g_slice_new (GumInterceptorBackend);
backend->allocator = allocator;

//初始化 codewriter和relocator
gum_x86_writer_init (&backend->writer, NULL);
gum_x86_relocator_init (&backend->relocator, NULL, &backend->writer);

// 创建 thunk
gum_interceptor_backend_create_thunks (backend);

return backend;
}

这里初始化的writerrelocator分别用于指令写和指令恢复。

thunks的初始化,这两个是用于调度执行,分别对应 进入hook和离开hook。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void
gum_interceptor_backend_create_thunks (GumInterceptorBackend * self)
{
GumX86Writer * cw = &self->writer;

self->enter_thunk = gum_code_allocator_alloc_slice (self->allocator);
gum_x86_writer_reset (cw, self->enter_thunk->data);
gum_emit_enter_thunk (cw);
gum_x86_writer_flush (cw);
g_assert_cmpuint (gum_x86_writer_offset (cw), <=, self->enter_thunk->size);

self->leave_thunk = gum_code_allocator_alloc_slice (self->allocator);
gum_x86_writer_reset (cw, self->leave_thunk->data);
gum_emit_leave_thunk (cw);
gum_x86_writer_flush (cw);
g_assert_cmpuint (gum_x86_writer_offset (cw), <=, self->leave_thunk->size);
}

因为原理类似,只举例enter_thunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void
gum_emit_enter_thunk (GumX86Writer * cw)
{
const gssize return_address_stack_displacement = 0;
// save ret addr
gum_emit_prolog (cw, return_address_stack_displacement);

// 构造自己的函数栈
gum_x86_writer_put_lea_reg_reg_offset (cw, GUM_REG_XSI,
GUM_REG_XBP, GUM_FRAME_OFFSET_CPU_CONTEXT);
gum_x86_writer_put_lea_reg_reg_offset (cw, GUM_REG_XDX,
GUM_REG_XBP, GUM_FRAME_OFFSET_TOP);
gum_x86_writer_put_lea_reg_reg_offset (cw, GUM_REG_XCX,
GUM_REG_XBP, GUM_FRAME_OFFSET_NEXT_HOP);

gum_x86_writer_put_call_address_with_aligned_arguments (cw, GUM_CALL_CAPI,
GUM_ADDRESS (_gum_function_context_begin_invocation), 4,
GUM_ARG_REGISTER, GUM_REG_XBX,
GUM_ARG_REGISTER, GUM_REG_XSI,
GUM_ARG_REGISTER, GUM_REG_XDX,
GUM_ARG_REGISTER, GUM_REG_XCX);

gum_emit_epilog (cw);
}
gum_interceptor_attach_listener
1
2
3
4
5
6
7
8
9
10
11
GumAttachReturn
gum_interceptor_attach_listener (GumInterceptor * self,
gpointer function_address,
GumInvocationListener * listener,
gpointer listener_function_data)
{

...


}
gum_interceptor_transaction_begingum_interceptor_instrument ✨

这里要说的是 function_address 就是要hook的目标函数,frida-gum把要hook的目标封装成了 GumFunctionContext对象,方便操作

1
2
3
function_address = gum_interceptor_resolve (self, function_address); // ?
// 创建跳板
function_ctx = gum_interceptor_instrument (self, function_address);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static GumFunctionContext *
gum_interceptor_instrument (GumInterceptor * self,
gpointer function_address)
{
GumFunctionContext * ctx;

// 要hook的函数,封装成了 GumFunctionContext,此时
// 根据 地址,得到与之对应的 GunFunctionContext对象
ctx = (GumFunctionContext *) g_hash_table_lookup (self->function_by_address,
function_address);
if (ctx != NULL)
return ctx;
// 如果获取到的是空的对象,必须初始化了才能使用
// 只写几个字断,分配内存/hook的函数地址/interceptor成员
ctx = gum_function_context_new (self, function_address);
if (ctx == NULL)
return NULL;
// 创建跳板
if (!_gum_interceptor_backend_create_trampoline (self->backend, ctx))
{
gum_function_context_finalize (ctx);
return NULL;
}

// 设置完成后, 添加到哈希表
// hash_table, key, value
// hook函数地址,GumFunctionContext对象对应, 方便查找
g_hash_table_insert (self->function_by_address, function_address, ctx);

// 当前 transaction 添加到 任务中, 设置回调 函数 gum_interceptor_activate 拦截器激活函数
gum_interceptor_transaction_schedule_prologue_write (
&self->current_transaction, ctx, gum_interceptor_activate);

return ctx;
}

这里贴一下跳板代码方便理解:

1
2
3
4
5
6
7
8
9
10
00C30200  mov         al,byte ptr ds:[FF00C121h]  
00C30205 xor eax,0C30200h
00C3020A jmp 00C30000 // 跳到上面的 enter_thunk
00C3020F push dword ptr ds:[0C30200h]
00C30215 jmp 00C30100 // 跳到 leave_thunk
// 原函数修复的指令,7个字节
00C3021A push ebp
00C3021B mov ebp,esp
00C3021D cmp dword ptr [ebp+8],0
00C30221 jmp gum_test_target_function+7h (0D6FB97h) // 跳回原函数,因为写跳转用了7字节,所以+7
gum_interceptor_transaction_end
1
2
3
// 当前 transaction 添加到 任务中, 设置回调 函数 gum_interceptor_activate 拦截器激活函数
gum_interceptor_transaction_schedule_prologue_write (
&self->current_transaction, ctx, gum_interceptor_activate);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 拦截器激活
static void
gum_interceptor_activate (GumInterceptor * self,
GumFunctionContext * ctx,
gpointer prologue)
{
if (ctx->destroyed)
return;

g_assert (!ctx->activated);
ctx->activated = TRUE;

// 激活
_gum_interceptor_backend_activate_trampoline (self->backend, ctx,
prologue);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
_gum_interceptor_backend_activate_trampoline (GumInterceptorBackend * self,
GumFunctionContext * ctx,
gpointer prologue)
{
GumX86Writer * cw = &self->writer;
guint padding;

// 设置base
gum_x86_writer_reset (cw, prologue);
// 设置pc
cw->pc = GPOINTER_TO_SIZE (ctx->function_address);
// 写jmp, 跳转到 跳板中, 进入跳板这已经到hook里了
gum_x86_writer_put_jmp_address (cw, GUM_ADDRESS (ctx->on_enter_trampoline));
gum_x86_writer_flush (cw);
g_assert_cmpint (gum_x86_writer_offset (cw),
<=, GUM_INTERCEPTOR_REDIRECT_CODE_SIZE);

// 原本代码(hook点),剩余的地方nop补齐
padding = ctx->overwritten_prologue_len - gum_x86_writer_offset (cw);
for (; padding != 0; padding--)
gum_x86_writer_put_nop (cw);
gum_x86_writer_flush (cw);
}

2.2.3 执行流程

通过设置函数返回地址(__gum_function_context_begin/end_invocation),控制流程,这就是ROP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
原函数
----------------------------------------------------
跳板 02C80204
----------------------------------------------------
`enter_chunk` // 首先要保存现场, 构造栈帧,随后进入下一个函数 ⬇️
`__gum_function_context_begin_invocation` // 通过设置栈(ret addr)控制执行流程
----------------------------------------------------
replacement_function
----------------------------------------------------
跳板 02C8020F
----------------------------------------------------
`leave_chunk`
`__gum_function_context_end_invocation`
----------------------------------------------------
继续执行

0x03 : 调试分析帮助理解

这里调试了单元测试中写hook和函数替换的逻辑,过程如下:

_gum_interceptor_backend_create()

后端初始化,初始化两个thunk

enter_thunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
00C30000  pushfd  
00C30001 cld
00C30002 pushad
00C30003 lea esp,[esp-4]
00C3000A lea eax,[esp+2Ch]
00C30011 mov dword ptr [esp+10h],eax
00C30015 mov ebx,dword ptr [esp+28h]
00C30019 mov ebp,esp
00C3001B and esp,0FFFFFFF0h
00C30021 sub esp,200h
00C30027 fxsave [esp]
00C3002B lea esi,[ebp]
00C30031 lea edx,[ebp+2Ch]
00C30037 lea ecx,[ebp+28h]
00C3003D push ecx
00C3003E push edx
00C3003F push esi
00C30040 push ebx
00C30041 call __gum_function_context_begin_invocation (0CE8E1Fh)
00C30046 add esp,10h
00C30049 fxrstor [esp]
00C3004D mov esp,ebp
00C3004F lea esp,[esp+4]
00C30056 popad
00C30057 popfd
00C30058 ret

leave_thunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
00C30100  pushfd  
00C30101 cld
00C30102 pushad
00C30103 lea esp,[esp-4]
00C3010A lea eax,[esp+28h]
00C30111 mov dword ptr [esp+10h],eax
00C30115 mov ebx,dword ptr [esp+28h]
00C30119 mov ebp,esp
00C3011B and esp,0FFFFFFF0h
00C30121 sub esp,200h
00C30127 fxsave [esp]
00C3012B lea esi,[ebp]
00C30131 lea edx,[ebp+28h]
00C30137 sub esp,4
00C3013A push edx
00C3013B push esi
00C3013C push ebx
00C3013D call __gum_function_context_end_invocation (0CEAB1Bh)
00C30142 add esp,0Ch
00C30145 add esp,4
00C30148 fxrstor [esp]
00C3014C mov esp,ebp
00C3014E lea esp,[esp+4]
00C30155 popad
00C30156 popfd
00C30157 ret
1
2
3
4
5
6
7
8
// hook 构造
GumAttachReturn
gum_interceptor_attach (GumInterceptor * self,
gpointer function_address,
GumInvocationListener * listener,
gpointer listener_function_data)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
GumAttachReturn
gum_interceptor_attach (GumInterceptor * self,
gpointer function_address,
GumInvocationListener * listener,
gpointer listener_function_data)
{
GumAttachReturn result = GUM_ATTACH_OK;
GumFunctionContext * function_ctx;

if (gum_process_get_code_signing_policy () == GUM_CODE_SIGNING_REQUIRED)
goto policy_violation;

gum_interceptor_ignore_current_thread (self);
GUM_INTERCEPTOR_LOCK (self);
gum_interceptor_transaction_begin (&self->current_transaction);
self->current_transaction.is_dirty = TRUE;

// 获取hook目标函数的地址
function_address = gum_interceptor_resolve (self, function_address);

// 获取这个函数的 GumFunctionContext 对象
// 没有就新建一个
// 这里已经 准备好了跳板,写好了hook
// 添加任务,设置相对应的回调函数
function_ctx = gum_interceptor_instrument (self, function_address);
if (function_ctx == NULL)
goto wrong_signature;

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;

policy_violation:
{
return GUM_ATTACH_POLICY_VIOLATION;
}
wrong_signature:
{
result = GUM_ATTACH_WRONG_SIGNATURE;
goto beach;
}
already_attached:
{
result = GUM_ATTACH_ALREADY_ATTACHED;
goto beach;
}
beach:
{
// 到这里,基本没什么问题,hook什么都打好了
// 拦截器激活 跳板激活
// 这里把原函数开头改写
gum_interceptor_transaction_end (&self->current_transaction);
GUM_INTERCEPTOR_UNLOCK (self);
gum_interceptor_unignore_current_thread (self);

return result;
}
}

on_invoke_trampoline 跳板

1
2
3
4
5
6
7
8
9
10
00C30200  mov         al,byte ptr ds:[FF00C121h]  
00C30205 xor eax,0C30200h
00C3020A jmp 00C30000 // 跳到上面的 enter_thunk
00C3020F push dword ptr ds:[0C30200h]
00C30215 jmp 00C30100 // 跳到 leave_thunk
// 原函数修复的指令,7个字节
00C3021A push ebp
00C3021B mov ebp,esp
00C3021D cmp dword ptr [ebp+8],0
00C30221 jmp gum_test_target_function+7h (0D6FB97h) // 跳回原函数,因为写跳转用了7字节,所以+7

gum_interceptor_transaction_end (&self->current_transaction); 调用 gum_interceptor_activate() 然后_gum_interceptor_backend_activate_trampolie() 随后,目标函数开头被修改:

1
2
3
4
5
6
7
8
gpointer GUM_NOINLINE
gum_test_target_function (GString * str)
{
00D6FB90 jmp 00C30204
if (str != NULL)
00D6FB95 nop
00D6FB96 nop
00D6FB97 je gum_test_target_function+19h (0D6FBA9h)

直接跳转到 00C30204, 其实就是 跳板,因为反汇编的地址差了点,所以开始的指令不太一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
00C30204  push        dword ptr ds:[0C30200h]  
00C3020A jmp 00C30000
00C3020F push dword ptr ds:[0C30200h]
00C30215 jmp 00C30100
00C3021A push ebp
00C3021B mov ebp,esp
00C3021D cmp dword ptr [ebp+8],0
00C30221 jmp gum_test_target_function+7h (0D6FB97h)
00C30226 add byte ptr [eax],al
00C30228 add byte ptr [eax],al
00C3022A add byte ptr [eax],al
00C3022C add byte ptr [eax],al
00C3022E add byte ptr [eax],al

调用流程调试分析,这里分两个情况,是否存在``replacement_function`

首先是不存在,只是打个hook(根据 TESTCASE(attach_one);)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
call 原函数
----------------------------------------------------
原函数
----------------------------------------------------
跳板
----------------------------------------------------
`enter_chunk` // 首先要保存现场, 构造栈帧,随后进入下一个函数 ⬇️
`__gum_function_context_begin_invocation` // 通过设置栈(ret addr)控制执行流程
----------------------------------------------------
跳板+n (00C3021A) // 执行原函数的 修复的若干字节
----------------------------------------------------
原函数
----------------------------------------------------
`leave_chunk`
`__gum_function_context_end_invocation`
----------------------------------------------------
继续执行....

存在替换的函数(TESTCASE(replace_one);)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
原函数
----------------------------------------------------
跳板 02C80204
----------------------------------------------------
`enter_chunk` // 首先要保存现场, 构造栈帧,随后进入下一个函数 ⬇️
`__gum_function_context_begin_invocation` // 通过设置栈(ret addr)控制执行流程
----------------------------------------------------
replacement_function
----------------------------------------------------
跳板 02C8020F
----------------------------------------------------
`leave_chunk`
`__gum_function_context_end_invocation`
----------------------------------------------------
继续执行

replace_one 的跳板

1
2
3
4
5
6
7
8
9
02C80204  push        dword ptr ds:[2C80200h]  
02C8020A jmp 02C80000
02C8020F push dword ptr ds:[2C80200h]
02C80215 jmp 02C80100
02C8021A mov edi,edi
02C8021C push ebp
02C8021D mov ebp,esp
02C8021F jmp malloc+5h (01E5A7B5h)

0x04 : 结语

这个过程大概花了我一周 5天多的样子,挺难的个人感觉,需要捋清楚的话,配合调试会好很多,最开始我直接看的代码,看+做笔记,脑内debug,最后编译了工程,vs调试,清晰多了,还是建议边调试边看。

如果文中有任何问题,欢迎批评指正 : )

后面可能会在他基础上做点事情吧…这框架真牛逼 !

0x05 : 参考与引用

rida-gum源码解读

gobject c语言

如何构建一款像 frida 一样的框架


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK