Dobby
Android逆向-Dobby
简介
Dobby是一个轻量的inlinehook库。
这里用到了两个分支,一个是原来的jmpews
,另一个是BeplnEx
fork出来的。一开始用jmpews
的总是报错还以为不行了,不过后面发现实际上似乎是静态链接的问题。
BepInEx/Dobby: Fork of jmpews/Dobby with stability edits for Windows (github.com)
环境搭建(jmpews)
环境
Ubuntu22
下载源码
1 | git clone https://github.com/jmpews/Dobby |
下载Release
Release latest · jmpews/Dobby (github.com)
编译源码
Dobby/docs/compile.md at master · jmpews/Dobby (github.com)
Linux
首先安装cmake-3.25.2
和cmake-3.25.2
。这两个可以自己安装,也可以用Dobby自己提供的脚本来安装。关于LLVM15的自行安装单独放在一个post里面。下面用脚本:
1 | # prepare and download cmake/llvm |
这样,它会在Ubuntu22上面安装cmake
,llvm
,以及ndk
。
然后执行以下脚本就能自动编译了:
1 | python3 scripts/platform_builder.py --platform=linux --arch=all --cmake_dir=$HOME/opt/cmake-3.25.2 --llvm_dir=$HOME/opt/llvm-15.0.6 |
当然,在我的具体情况中,LLVM15在下载解压的时候报错了,所以最后我还是自己编译了一份LLVM,然后将--llvm_dir
指向了自己的LLVM的build
文件夹。
Android⭐
在前面跑了脚本,安装好cmake
,llvm
和ndk
后,执行:
1 | 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 |
环境搭建(BepInEx)
下载源码
https://github.com/BepInEx/Dobby.git
下载Release
Release Version 1.0.5 · BepInEx/Dobby (github.com)
这里偷懒就不编译了,直接拿现成的。
使用
Linux x64
下面我们创建一个Demo代码文件test1.cpp
,假设里面写了一些需要用Dobby进行Hook的代码。
为了能够编译使用,文件结构大概如下:
1 | dobby.h |
反正就是要把so文件链接进来。
编译:
1 | g++ -o test1 test1.cpp -L ./ -ldobby |
然后执行前也要做一些操作:
首先是赋予权限:
1 | chmod 777 ./libdobby.so |
然后要添加这个so所在的路径,防止找不到:
1 | export LD_LIBRARY_PATH=/path/to/so:$LD_LIBRARY_PATH |
最后执行:
1 | ./test1 |
API(BeplnEx)
看它暴露了哪些API,其实只要看dobby.h
就好了。
jmpews
和BeplnEx
的dobby.h
有一些区别。现在学习后者的。
CodePatch⭐
1 | MemoryOperationError CodePatch(void *address, uint8_t *buffer, uint32_t buffer_size); |
修改一个指定地址的内存数据,用自己定义的Buffer
替换。可以用于Patch字节码。
RegisterContext
寄存器结构。
HookEntryInfo
记录了Hook入口点的数据。
DobbyHook⭐
1 | // hook and commit function |
Hook一个函数,并立刻挂钩。具体的使用例子如下:
1 | extern "C" { |
更细节的例子可以看install_hook_name
宏函数。
DobbyPrepare
1 | // prepare trampoline |
DobbyCommit
1 | // commit hook |
DobbyInstrument⭐(BeplnEx中没有被导出)
这个函数比DobbyHook
更细节一些,可以具体操作寄存器。
1 | // dynamic binary instrument for instruction |
例子:
1 |
|
DobbyDestroy
1 | // destory and restore hook |
DobbySymbolResolver⭐
解析符号字符串。
1 | // iterate symbol table and find symbol |
DobbyGlobalOffsetTableReplace
1 | // global offset table |
dobby_enable(disable)_near_branch_trampoline
1 | // [!!! READ ME !!!] |
dobby_register_image_load_callback
1 | // register linker load image callback |
API(jmpews)
install_hook_name
这是jmpews
的Dobby里面有的一个宏函数,挺好用的。
1 |
传入函数名,返回参数类型,传参类型。比如我要准备Hookint add(int a, int b)
函数,则我应该这样调用函数:
1 | install_hook_name(add, int, int a, int b) { |
这样能创建一个fake_add
函数指针,一个orig_add
函数指针,一个install_hook_add
函数。然后后面紧跟着fn_ret_t fake_##name(fn_args_t)
就是让人直接在后面套个大括号,直接实现fake_add
的实现逻辑。这样搞定后,我们模拟进行宏展开,即这个宏函数实际上让我们得到了:
1 | static int fake_add(int a, int b); |
因此这个宏函数很巧妙的帮我们把需要的一些函数和变量都定义好了,剩下的就是真正地去调用了,即:
1 | // 必须在main函数或其他执行点写这块代码 |
示例
DobbyHook替换函数
DobbyHook_replace.cpp
1 |
|
DobbyHook记录函数调用
DobbyHook_log.cpp
1 |
|
DobbyInstrument
DobbyInstrument_log.cpp
1 |
|
当然,这里似乎寄存器用错了,打印出来的值很怪异。不过确实有示范作用。
DobbySymbolResolver解析符号
这就是用来解析符号的,从一个符号字符串获得它代表的函数地址。不过要注意C与C++符号的区别。比如这里的"add"
就应该是C函数,在CPP文件里面我是用extern "C"
前缀声明的。C++符号更复杂,这里不描述。
1 | void * sym = DobbySymbolResolver(NULL, "add"); |
参考
BepInEx/Dobby: Fork of jmpews/Dobby with stability edits for Windows (github.com)
InlineHook新秀Dobby框架-腾讯云开发者社区-腾讯云 (tencent.com)
15.Inlinehook:Dobby - RyukieDev (gitbook.io)
Android Ptrace 绕过策略 :: HACKCATML (tistory.com)
Release Version 1.0.5 · BepInEx/Dobby (github.com)
Dobby/docs/get-started-android.md at master · BepInEx/Dobby (github.com)
- 本文作者: Taardis
- 本文链接: https://taardisaa.github.io/2023/09/25/Android逆向-Dobby/
- 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!