一
从调试角度看Dobby的原理
注意:这里直接将Dobby源码作用三方库引入,目前Dobby最新Commit无法编译,需要切换到Commit:0932d69c320e786672361ab53825ba8f4245e9d3
#include <jni.h>
#include <string>
#include "Dobby/include/dobby.h"
#include "logging.h"
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_dobbytest1_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "no be hooked";
return env->NewStringUTF(hello.c_str());
}
static jstring (*orgin_Java_com_example_dobbytest1_MainActivity_stringFromJNI)(JNIEnv* env,jobject /* this */);
extern "C" JNIEXPORT jstring JNICALL
new_Java_com_example_dobbytest1_MainActivity_stringFromJNI(
JNIEnv* env,
jobject jobject1/* this */) {
LOGI("be hooked");
return orgin_Java_com_example_dobbytest1_MainActivity_stringFromJNI(env,jobject1);
}
__attribute__((constructor)) static void ctor() {
// 构造函数 静态插入hook调用
// 原函数名
// 新函数地址
// 旧函数地址
DobbyHook((void *) DobbySymbolResolver(NULL, "Java_com_example_dobbytest1_MainActivity_stringFromJNI"),
(void *) new_Java_com_example_dobbytest1_MainActivity_stringFromJNI,
(void **) &orgin_Java_com_example_dobbytest1_MainActivity_stringFromJNI
);
}
./lldb-server platform --listen '*:1234' --server
选择调试平台
(lldb) platform select remote-android
Platform: remote-android
Connected: no
连接lldb-server创建的端口
(lldb) platform connect connect://:1234
Platform: remote-android
Triple: aarch64-unknown-linux-android
OS Version: 30 (4.14.186-perf-g42f990859d35)
Hostname: localhost
Connected: yes
WorkingDir: /data/local/tmp
Kernel: #1 SMP PREEMPT Tue Mar 1 19:09:49 CST 2022
(lldb)
(lldb) br set -a 0x70ece54110
Breakpoint 1: address = 0x00000070ece54110
断点信息
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = breakpoint 1.1
frame #0: 0x00000070ece54110
-> 0x70ece54110: adrp x17, 0
0x70ece54114: add x17, x17, #0x290
0x70ece54118: br x17
0x70ece5411c: mrs x8, TPIDR_EL0
Target 0: (app_process64) stopped.
对比原始汇编代码
.text:000000000000B110 SUB SP, SP, #0x70
.text:000000000000B114 STP X29, X30, [SP,#0x60+var_s0]
.text:000000000000B118 ADD X29, SP, #0x60
.text:000000000000B11C MRS X8, #3, c13, c0, #2
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step over
frame #0: 0x00000070ece54114
-> 0x70ece54114: add x17, x17, #0x290
0x70ece54118: br x17
0x70ece5411c: mrs x8, TPIDR_EL0
0x70ece54120: ldr x8, [x8, #0x28]
Target 0: (app_process64) stopped.
(lldb) register read x17
x17 = 0x00000070ece54000
(lldb) n
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step over
frame #0: 0x00000070ece54118
-> 0x70ece54118: br x17
0x70ece5411c: mrs x8, TPIDR_EL0
0x70ece54120: ldr x8, [x8, #0x28]
0x70ece54124: stur x8, [x29, #-0x8]
Target 0: (app_process64) stopped.
(lldb) register read x17
x17 = 0x00000070ece54290
(lldb)
(lldb) br set -a 0x70ece542cc
Breakpoint 3: address = 0x00000070ece542cc
(lldb) c
Process 20161 resuming
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = breakpoint 3.1
frame #0: 0x00000070ece542cc
-> 0x70ece542cc: blr x8
0x70ece542d0: ldp x29, x30, [sp, #0x10]
0x70ece542d4: add sp, sp, #0x20
0x70ece542d8: ret
Target 0: (app_process64) stopped.
(lldb) s
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step into
frame #0: 0x0000007183933000
-> 0x7183933000: sub sp, sp, #0x70
0x7183933004: stp x29, x30, [sp, #0x60]
0x7183933008: add x29, sp, #0x60
0x718393300c: ldr x17, #0x8
Target 0: (app_process64) stopped.
(lldb) s
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step into
frame #0: 0x0000007183933004
-> 0x7183933004: stp x29, x30, [sp, #0x60]
0x7183933008: add x29, sp, #0x60
0x718393300c: ldr x17, #0x8
0x7183933010: br x17
Target 0: (app_process64) stopped.
(lldb) s
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step into
frame #0: 0x000000718393300c
-> 0x718393300c: ldr x17, #0x8
0x7183933010: br x17
0x7183933014: .long 0xece5411c ; unknown opcode
0x7183933018: udf #0x70
Target 0: (app_process64) stopped.
(lldb) register read x17
x17 = 0x0000007185f8e970 libc.so`pthread_mutex_unlock
(lldb) s
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step into
frame #0: 0x0000007183933010
-> 0x7183933010: br x17
0x7183933014: .long 0xece5411c ; unknown opcode
0x7183933018: udf #0x70
0x718393301c: udf #0x0
Target 0: (app_process64) stopped.
(lldb) register read x17
x17 = 0x00000070ece5411c
(lldb) s
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step into
frame #0: 0x00000070ece5411c
-> 0x70ece5411c: mrs x8, TPIDR_EL0
0x70ece54120: ldr x8, [x8, #0x28]
0x70ece54124: stur x8, [x29, #-0x8]
0x70ece54128: stur x0, [x29, #-0x28]
Target 0: (app_process64) stopped.
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = breakpoint 4.1
frame #0: 0x00000070ece54188
-> 0x70ece54188: ldr x0, [sp, #0x18]
0x70ece5418c: ldp x29, x30, [sp, #0x60]
0x70ece54190: add sp, sp, #0x70
0x70ece54194: ret
Target 0: (app_process64) stopped.
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step into
frame #0: 0x00000070ece54194
-> 0x70ece54194: ret
0x70ece54198: str x0, [sp, #0x28]
0x70ece5419c: mov w8, w1
0x70ece541a0: str w8, [sp, #0x24]
Target 0: (app_process64) stopped.
(lldb) s
Process 20161 stopped
* thread #1, name = 'mple.dobbytest1', stop reason = instruction step into
frame #0: 0x00000070ece542d0
-> 0x70ece542d0: ldp x29, x30, [sp, #0x10]
0x70ece542d4: add sp, sp, #0x20
0x70ece542d8: ret
0x70ece542dc: stp x29, x30, [sp, #-0x10]!
Target 0: (app_process64) stopped.
二
源码分析
// source/InterceptRouting/Routing/FunctionInlineHook/FunctionInlineHook.cc
PUBLIC int DobbyHook(void *address, dobby_dummy_func_t replace_func, dobby_dummy_func_t *origin_func) {
......
#if defined(ANDROID)
void *page_align_address = (void *)ALIGN_FLOOR(address, OSMemory::PageSize());
if (!OSMemory::SetPermission(page_align_address, OSMemory::PageSize(), kReadExecute)) {
return -1;
}
#endif
DEBUG_LOG("----- [DobbyHook:%p] -----", address);
// check if already register
auto entry = Interceptor::SharedInstance()->find((addr_t)address);
if (entry) {
ERROR_LOG("%p already been hooked.", address);
return -1;
}
entry = new InterceptEntry(kFunctionInlineHook, (addr_t)address);
auto *routing = new FunctionInlineHookRouting(entry, replace_func);
routing->Prepare();
routing->DispatchRouting();
// set origin func entry with as relocated instructions
if (origin_func) {
*origin_func = (dobby_dummy_func_t)entry->relocated_addr;
#if defined(__APPLE__) && defined(__arm64__)
*origin_func = pac_sign(*origin_func);
#endif
}
routing->Commit();
Interceptor::SharedInstance()->add(entry);
return 0;
}
// source/InterceptRouting/Routing/FunctionInlineHook/RoutingImpl.cc
void FunctionInlineHookRouting::DispatchRouting() {
BuildRouting();
// generate relocated code which size == trampoline size
GenerateRelocatedCode();
}
void FunctionInlineHookRouting::BuildRouting() {
SetTrampolineTarget((addr_t)replace_func);
// generate trampoline buffer, run before GenerateRelocatedCode
addr_t from = entry_->patched_addr;
#if defined(TARGET_ARCH_ARM)
if (entry_->thumb_mode)
from += 1;
#endif
addr_t to = GetTrampolineTarget();
GenerateTrampolineBuffer(from, to);
}
// source/InterceptRouting/InterceptRouting.cpp
bool InterceptRouting::GenerateTrampolineBuffer(addr_t src, addr_t dst) {
// if near branch trampoline plugin enabled
if (RoutingPluginManager::near_branch_trampoline) {
auto plugin = static_cast<RoutingPluginInterface *>(RoutingPluginManager::near_branch_trampoline);
if (plugin->GenerateTrampolineBuffer(this, src, dst) == false) {
DEBUG_LOG("Failed enable near branch trampoline plugin");
}
}
if (GetTrampolineBuffer() == nullptr) {
auto tramp_buffer = GenerateNormalTrampolineBuffer(src, dst);
SetTrampolineBuffer(tramp_buffer);
}
return true;
}
// source/TrampolineBridge/Trampoline/arm64/trampoline_arm64.cc
CodeBufferBase *GenerateNormalTrampolineBuffer(addr_t from, addr_t to) {
TurboAssembler turbo_assembler_((void *)from);
#define _ turbo_assembler_.
uint64_t distance = llabs((int64_t)(from - to));
uint64_t adrp_range = ((uint64_t)1 << (2 + 19 + 12 - 1));
if (distance < adrp_range) {
// adrp, add, br
_ AdrpAdd(TMP_REG_0, from, to);
_ br(TMP_REG_0);
DEBUG_LOG("[trampoline] use [adrp, add, br]");
} else {
// ldr, br, branch-address
CodeGen codegen(&turbo_assembler_);
codegen.LiteralLdrBranch((uint64_t)to);
DEBUG_LOG("[trampoline] use [ldr, br, #label]");
}
#undef _
// Bind all labels
turbo_assembler_.RelocBind();
auto result = turbo_assembler_.GetCodeBuffer()->Copy();
return result;
}
0x70ece54110: adrp x17, 0
0x70ece54114: add x17, x17, #0x290
0x70ece54118: br x17
第三行就是跳转指令。
// source/InterceptRouting/InterceptRouting.cpp
bool InterceptRouting::GenerateRelocatedCode() {
uint32_t tramp_size = GetTrampolineBuffer()->GetBufferSize();
origin_ = new CodeMemBlock(entry_->patched_addr, tramp_size);
relocated_ = new CodeMemBlock();
auto buffer = (void *)entry_->patched_addr;
#if defined(TARGET_ARCH_ARM)
if (entry_->thumb_mode) {
buffer = (void *)((addr_t)buffer + 1);
}
#endif
GenRelocateCodeAndBranch(buffer, origin_, relocated_);
if (relocated_->size == 0) {
ERROR_LOG("[insn relocate]] failed");
return false;
}
// set the relocated instruction address
entry_->relocated_addr = relocated_->addr;
// save original prologue
memcpy((void *)entry_->origin_insns, (void *)origin_->addr, origin_->size);
entry_->origin_insn_size = origin_->size;
// log
DEBUG_LOG("[insn relocate] origin %p - %d", origin_->addr, origin_->size);
log_hex_format((uint8_t *)origin_->addr, origin_->size);
DEBUG_LOG("[insn relocate] relocated %p - %d", relocated_->addr, relocated_->size);
log_hex_format((uint8_t *)relocated_->addr, relocated_->size);
return true;
}
// source/InstructionRelocation/arm64/InstructionRelocationARM64.cc
void GenRelocateCode(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) {
relo_ctx_t ctx = {0};
ctx.buffer = ctx.buffer_cursor = (uint8_t *)buffer;
ctx.buffer_size = origin->size;
ctx.src_vmaddr = (addr_t)origin->addr;
ctx.dst_vmaddr = (addr_t)relocated->addr;
ctx.origin = origin;
relo_relocate(&ctx, branch);
relocated->reset(ctx.relocated->addr, ctx.relocated->size);
}
void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated) {
GenRelocateCode(buffer, origin, relocated, true);
}
int relo_relocate(relo_ctx_t *ctx, bool branch) {
int relocated_insn_count = 0;
TurboAssembler turbo_assembler_(0);
#define _ turbo_assembler_.
auto relocated_buffer = turbo_assembler_.GetCodeBuffer();
......
// update origin
int new_origin_len = (addr_t)ctx->buffer_cursor - (addr_t)ctx->buffer;
ctx->origin->reset(ctx->origin->addr, new_origin_len);
// TODO: if last instr is unlink branch, ignore it
if (branch) {
CodeGen codegen(&turbo_assembler_);
codegen.LiteralLdrBranch(ctx->origin->addr + ctx->origin->size);
}
// Bind all labels
turbo_assembler_.RelocBind();
// Generate executable code
{
auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_);
ctx->relocated = code;
}
return 0;
}
// source/core/codegen/codegen-arm64.cc
void CodeGen::LiteralLdrBranch(uint64_t address) {
auto turbo_assembler_ = reinterpret_cast<TurboAssembler *>(this->assembler_);
#define _ turbo_assembler_->
auto label = RelocLabel::withData(address);
turbo_assembler_->AppendRelocLabel(label);
_ Ldr(TMP_REG_0, label);
_ br(TMP_REG_0);
#undef _
}
// source/InterceptRouting/InterceptRouting.cpp
void InterceptRouting::Active() {
auto ret = DobbyCodePatch((void *)entry_->patched_addr, trampoline_buffer_->GetBuffer(),
trampoline_buffer_->GetBufferSize());
if (ret == -1) {
ERROR_LOG("[intercept routing] active failed");
return;
}
DEBUG_LOG("[intercept routing] active");
}
// source/Backend/UserMode/ExecMemory/code-patch-tool-posix.cc
PUBLIC int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size) {
#if defined(__ANDROID__) || defined(__linux__)
int page_size = (int)sysconf(_SC_PAGESIZE);
uintptr_t patch_page = ALIGN_FLOOR(address, page_size);
uintptr_t patch_end_page = ALIGN_FLOOR((uintptr_t)address + buffer_size, page_size);
// change page permission as rwx
mprotect((void *)patch_page, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
if (patch_page != patch_end_page) {
mprotect((void *)patch_end_page, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
}
// patch buffer
memcpy(address, buffer, buffer_size);
// restore page permission
mprotect((void *)patch_page, page_size, PROT_READ | PROT_EXEC);
if (patch_page != patch_end_page) {
mprotect((void *)patch_end_page, page_size, PROT_READ | PROT_EXEC);
}
addr_t clear_start_ = (addr_t)address;
ClearCache((void *)clear_start_, (void *)(clear_start_ + buffer_size));
#endif
return 0;
}
参考
看雪ID:tcc0lin
https://bbs.kanxue.com/user-home-886627.htm
#
原文始发于微信公众号(看雪学苑):Dobby框架源码学习
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论