Dobby框架使用

7.6k words

Dobby使用

0x01环境配置:

环境:wsl2

工具:无需额外自己安装

首先把Dobby整个项目克隆下来,但是原项目直接编译会爆缺少Cpu.h,我这里用的是老版本的,亲测没问题

正常流程:

1.下载源码

1
2
cd workspace
git clone https://github.com/jmpews/Dobby.git //我下的最新版没法直接编译,原因刚刚说了

2.编译

我这里是要在安卓中使用。参考compile.md

1
2
3
# prepare and download cmake/llvm/ndk
sh scripts/setup_linux_cross_compile.sh
python3 scripts/platform_builder.py --platform=android --arch=all --cmake_dir=$HOME/opt/cmake-3.25.2 --llvm_dir=$HOME/opt/llvm-15.0.6 --android_ndk_dir=$HOME/opt/ndk-r25b

执行第一个脚本之后它会自动下载特定版本的工具链,然后执行py脚本进行编译。编译的文件在./workspace/Dobby/build下,包含各个架构的.a.so文件

这里我们需要用到静态库(.a文件),和我们的so一起链接。

在Android Studio里新建native app项目,在/app/src/main/cpp/下创建Dobby文件夹,当然叫啥都随你,我习惯这样啊,接着把刚刚编译生成的各个架构的.a文件复制进去,接着改一下cmake就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cmake_minimum_required(VERSION 3.22.1)

project("fakelocation")

# 添加头文件路径(dobby.h 所在目录)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})

# 指定 libdobby.a 静态库路径
set(LIBDOBBY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/Dobby/${CMAKE_ANDROID_ARCH_ABI}/libdobby.a)

# 添加共享库目标
add_library(${CMAKE_PROJECT_NAME} SHARED
yuuki.cpp)

# 链接库:libdobby.a、android、log
target_link_libraries(${CMAKE_PROJECT_NAME}
${LIBDOBBY_PATH}
android
log)

然后需要把dobby项目里的dobby.h拷贝到自己的项目里,到这就差不多了,可以跑一个简单的demo试试看

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
63
#include <jni.h>
#include <android/log.h>
#include <dlfcn.h>
#include <stdio.h>
#include "dobby.h"

#define LOG_TAG "FakeLocation"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

// 原始函数:返回固定值
int get_number() {
LOGD("Original get_number called");
return 42;
}

// Hook 替换函数
int hooked_get_number() {
LOGD("Hooked get_number called");
return 100;
}

// JNI 接口:测试 Hook
extern "C" JNIEXPORT jstring JNICALL
Java_com_yuuki_fakelocation_MainActivity_stringFromJNI(JNIEnv* env, jobject /* this */) {
LOGD("Entering stringFromJNI");

// 调用原始函数
int original_result = get_number();
LOGD("Original get_number result: %d", original_result);

// 检查 Dobby 符号是否可用
void* dobby_hook = dlsym(RTLD_DEFAULT, "DobbyHook");
if (!dobby_hook) {
LOGE("Failed to find DobbyHook symbol");
return env->NewStringUTF("DobbyHook symbol not found");
}
LOGD("DobbyHook symbol found at %p", dobby_hook);

// 执行 Hook
void* orig_func = (void*)get_number;
void* hook_func = (void*)hooked_get_number;
LOGD("Attempting to hook get_number at %p with %p", orig_func, hook_func);
int result = DobbyHook(orig_func, hook_func, nullptr);
if (result == 0) {
LOGD("DobbyHook succeeded");
} else {
LOGE("DobbyHook failed with code: %d", result);
char error_msg[128];
snprintf(error_msg, sizeof(error_msg), "Hook failed: %d", result);
return env->NewStringUTF(error_msg);
}

// 调用 Hook 后的函数
int hooked_result = get_number();
LOGD("Hooked get_number result: %d", hooked_result);

// 返回结果
char buffer[128];
snprintf(buffer, sizeof(buffer), "Original: %d, Hooked: %d", original_result, hooked_result);
LOGD("Returning: %s", buffer);
return env->NewStringUTF(buffer);
}
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
package com.yuuki.fakelocation;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import com.yuuki.fakelocation.utils.Miscellaneous;

public class MainActivity extends Activity {

static {
System.loadLibrary("fakelocation");
}


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Miscellaneous.setImmersiveStatusBar(this);
setContentView(R.layout.activity_main);
View rootView = findViewById(R.id.main);
Miscellaneous.setViewTopPadding(rootView, this);

TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}

public native String stringFromJNI();

}

可以看看是否输出了预期的结果

但是通常我们写xposed模块不会这么写,因为这样你的hook是会影响自身的,所以我喜欢把hook的逻辑和自己jni的逻辑分别打包成两个so

0x02API介绍:

其实能用的Api都在dobby.h里写好了,我们来看看这个头文件里写了啥吧

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#ifndef dobby_h
#define dobby_h

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include <stdint.h>

typedef uintptr_t addr_t;
typedef uint32_t addr32_t;
typedef uint64_t addr64_t;

typedef void *dobby_dummy_func_t;
typedef void *asm_func_t;

#if defined(__arm__)
typedef struct {
uint32_t dummy_0;
uint32_t dummy_1;

uint32_t dummy_2;
uint32_t sp;

union {
uint32_t r[13];
struct {
uint32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12;
} regs;
} general;

uint32_t lr;
} DobbyRegisterContext;
#elif defined(__arm64__) || defined(__aarch64__)
#define ARM64_TMP_REG_NDX_0 17

typedef union _FPReg {
__int128_t q;
struct {
double d1;
double d2;
} d;
struct {
float f1;
float f2;
float f3;
float f4;
} f;
} FPReg;

// register context
typedef struct {
uint64_t dmmpy_0; // dummy placeholder
uint64_t sp;

uint64_t dmmpy_1; // dummy placeholder
union {
uint64_t x[29];
struct {
uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22,
x23, x24, x25, x26, x27, x28;
} regs;
} general;

uint64_t fp;
uint64_t lr;

union {
FPReg q[32];
struct {
FPReg q0, q1, q2, q3, q4, q5, q6, q7;
// [!!! READ ME !!!]
// for Arm64, can't access q8 - q31, unless you enable full floating-point register pack
FPReg q8, q9, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21, q22, q23, q24, q25, q26, q27, q28, q29,
q30, q31;
} regs;
} floating;
} DobbyRegisterContext;
#elif defined(_M_IX86) || defined(__i386__)
typedef struct _RegisterContext {
uint32_t dummy_0;
uint32_t esp;

uint32_t dummy_1;
uint32_t flags;

union {
struct {
uint32_t eax, ebx, ecx, edx, ebp, esp, edi, esi;
} regs;
} general;

} DobbyRegisterContext;
#elif defined(_M_X64) || defined(__x86_64__)
typedef struct {
uint64_t dummy_0;
uint64_t rsp;

union {
struct {
uint64_t rax, rbx, rcx, rdx, rbp, rsp, rdi, rsi, r8, r9, r10, r11, r12, r13, r14, r15;
} regs;
} general;

uint64_t dummy_1;
uint64_t flags;
} DobbyRegisterContext;
#endif

#define install_hook_name(name, fn_ret_t, fn_args_t...) \
static fn_ret_t fake_##name(fn_args_t); \
static fn_ret_t (*orig_##name)(fn_args_t); \
/* __attribute__((constructor)) */ static void install_hook_##name(void *sym_addr) { \
DobbyHook(sym_addr, (dobby_dummy_func_t)fake_##name, (dobby_dummy_func_t *)&orig_##name); \
return; \
} \
fn_ret_t fake_##name(fn_args_t)

// memory code patch
int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size);

// function inline hook
int DobbyHook(void *address, dobby_dummy_func_t replace_func, dobby_dummy_func_t *origin_func);

// dynamic binary instruction instrument
// for Arm64, can't access q8 - q31, unless enable full floating-point register pack
typedef void (*dobby_instrument_callback_t)(void *address, DobbyRegisterContext *ctx);
int DobbyInstrument(void *address, dobby_instrument_callback_t pre_handler);

// destroy and restore code patch
int DobbyDestroy(void *address);

const char *DobbyGetVersion();

// symbol resolver
void *DobbySymbolResolver(const char *image_name, const char *symbol_name);

// import table replace
int DobbyImportTableReplace(char *image_name, char *symbol_name, dobby_dummy_func_t fake_func,
dobby_dummy_func_t *orig_func);

// for arm, Arm64, try use b xxx instead of ldr absolute indirect branch
// for x86, x64, always use absolute indirect jump
void dobby_enable_near_branch_trampoline();
void dobby_disable_near_branch_trampoline();

#ifdef __cplusplus
}
#endif

#endif

常用的就底下那几个,而且dobby提供的DobbySymbolResolver只能查找导出符号,所以一般不用,通常都会用elf_utils去查找符号,而且DobbyHook使用之前需要先修改内存段权限,不加的话似乎会无限循环调用,可能只有我这个版本会。