所谓InlineHook(内联钩⼦),就是直接修改⽬标函数的头部代码。让它跳转到⾃定义函数中执⾏代码,从⽽达到Hook的⽬的。这种Hook技术⼀般用于静态语⾔
Dobby框架
Dobby是一个全平台的InlineHook框架,详情可查看 官方文档编译
Dobby将代码
clone下来
git clone https://github.com/jmpews/Dobby.git --depth=1由于
Dobby是跨平台框架,所以项⽬并不是⼀个Xcode⼯程,需要使⽤cmake将⼯程编译成为Xcode⼯程进⼊
Dobby⽬录,创建⼀个⽂件夹,然后cmake编译⼯程
cd Dobby && mkdir build_for_ios_arm64 && cd build_for_ios_arm64cmake .. -G Xcode \-DCMAKE_TOOLCHAIN_FILE=cmake/ios.toolchain.cmake \-DPLATFORM=OS64 -DARCHS="arm64" -DCMAKE_SYSTEM_PROCESSOR=arm64 \-DENABLE_BITCODE=0 -DENABLE_ARC=0 -DENABLE_VISIBILITY=1 -DDEPLOYMENT_TARGET=9.3 \-DDynamicBinaryInstrument=ON -DNearBranch=ON -DPlugin.SymbolResolver=ON -DPlugin.Darwin.HideLibrary=ON -DPlugin.Darwin.ObjectiveC=ON编译完成后,会⽣成⼀个
Xcode⼯程
编译
Xcode⼯程,⽣成Framework
导⼊
DobbyX.framework到⼯程,如果遇到Bitcode问题,两种解决方式
- 关闭当前⼯程的
Bitcode- 编译
DobbyX.framework时,开启Bitcode
DobbyX.framework拷⻉问题将
Framework库⾸次拖⼊⼯程,Xcode不会⾃动帮你拷⻉。运⾏时会发现Framework没有打包进⼊App包,造成DYLD加载时找不到库的错误来到
Xcode中的Build Phases,点击+,选择New Copy Files Phase
在
Copy Files中,将Destination选择Frameworks
点击
+,选择DobbyX.framework,点击Add
Dobby的核心的函数
int DobbyHook(void *address, void *replace_call, void **origin_call);
address:需要HOOK的函数地址replace_call:新函数地址origin_call:保留原始函数的指针的地址案例1
Dobby的使用搭建
InlineDemo项目,拖入DobbyX.framework,解决拷⻉问题打开
ViewController.m文件,写入以下代码:定义将要被
HOOK的静态函数
int sum(int a,int b){return a + b;}定义函数指针,⽤于保存被替换函数的地址
static int (*sum_p)(int a,int b);定义新函数,⽤此函数替换将要
HOOK的函数,该函数的返回值及参数必须⼀致
int mySum(int a,int b) {NSLog(@"Sum:%d,🍺🍺🍺🍺🍺",sum_p(a,b));return a - b;}在
viewDidLoad方法中,调用DobbyHook进行函数的Hook```
- (void)viewDidLoad { [super viewDidLoad];
DobbyHook((void )sum, mySum, (void )&sum_p);
}
> 在`touchesBegan`中,调用`sum`函数>
-(void)touchesBegan:(NSSet
> 真机运行项目,点击屏幕,输入以下内容:>
InlineDemo[9140:1691629] Sum:30,🍺🍺🍺🍺🍺 InlineDemo[9140:1691629] Sum:-10
> `sum`函数`HOOK`成功,先输出原始函数的执行结果,再输出替换函数的执行结果#####HOOK原理> 案例1:> 通过汇编代码,查看`HOOK`原理> 上述案例中,在`touchesBegan`方法上设置断点<br />> 真机运行项目,点击屏幕,进入`touchesBegan`方法<br />> 单步调试,向下执行`1`步。进入`sum`函数<br />> - 前三句代码被替换> - 拉伸栈空间的代码没有了> 单步调试,向下执行`3`步。通过`br x17`指令,跳转到`mySum`函数<br />> 进入`mySum`函数,通过`blr x8`指令,跳转到指定地址上执行代码<br />> 该代码中,通过`br x17`指令,回到`sum`函数<br />> - 这里出现了拉伸栈空间的代码> 进入`sum`函数,执行原始代码逻辑<br />> - 在`sum`函数的结尾,恢复栈平衡> 当`sum`函数执行`ret`指令,返回`mySum`函数,执行后续代码<br />> 静态函数的`HOOK`,并没有在原始函数中增加代码,而是将拉伸栈空间的三句代码进行了替换> 当调用原始函数,才会拉伸栈平衡。然后在原始函数的代码中,恢复栈平衡> 案例2:> `mySum`函数中,不调用原始函数> 在`mySum`函数中,注释原始函数的调用<br />> 真机运行项目,进入`sum`函数。代码并没有发生变化<br />> 进入`mySum`函数,跳转到指定地址的代码没有了<br />> 当`mySum`函数执行`ret`指令,直接返回到`touchesBegan`> 此时`sum`函数的原始代码都不会被执行<br />> 这种情况,不会拉伸栈空间,`sum`函数的原始代码不会被执行,所以也不会恢复栈平衡#####HOOK函数地址> 在逆向开发中,三方应用会剥离符号表,我们无法获得符号名称,所以`HOOK`的一定是地址> 应用每次启动时,`ASLR`偏移地址都不一样,所以不能直接`HOOK`地址> 正确的做法:先找到函数在`MachO`中的偏移地址,加上`PAGEZERO`的`0x100000000`,再加上本次启动的`ASLR`偏移地址> 案例1> 延用上述案例,找到`sum`函数的实现地址> 查看汇编代码,找到`sum`函数的调用<br />> - 函数实现地址:`0x1022edd48`> 使用`image list`函数,找到主程序的基地址<br />> - 基地址:`0x1022e8000`> 使用`函数实现地址 - 主程序基地址`,计算函数在`MachO`中的偏移地址>
e -f x — 0x1022edd48-0x1022e8000
$1 = 0x5d48
> - 偏移地址:`0x5d48`> 在`MachO`文件中,查看偏移地址<br />> - 对应的正是`sum`函数的汇编代码> - 和断点时看到的汇编代码有些区别,因为`Dobby`在运行时,`Hook`函数会替换汇编代码> 案例2:> 对函数地址进行`HOOK`> 打开`ViewController.m`文件,写入以下代码:>
import “ViewController.h”
import
import
@implementation ViewController
int sum(int a,int b){ return a + b; }
static uintptr_t sumP = 0x5d48 + 0x100000000;
(void)viewDidLoad { [super viewDidLoad];
sumP += _dyld_get_image_vmaddr_slide(0); DobbyHook((void )sumP, mySum, (void )&sum_p); }
static int (*sum_p)(int a,int b);
int mySum(int a,int b) { NSLog(@”Sum:%d,🍺🍺🍺🍺🍺”,sum_p(a,b)); return a - b; }
-(void)touchesBegan:(NSSet
@end
> 这里有一个小问题,因为案例是在自己的项目中`HOOK`地址,所以对项目的代码进行了修改,这样会造成`sum`函数的实现地址发生改变<br />> - `sum`函数的偏移地址,从之前的`0x5d48`变为`0x5d08`> 打开`ViewController.m`文件,修改代码:>
static uintptr_t sumP = 0x5d08 + 0x100000000;
> 真机运行项目,点击屏幕,`HOOK`成功>
InlineDemo[9883:1880364] Sum:30,🍺🍺🍺🍺🍺 InlineDemo[9883:1880364] Sum:-10
> 在代码不修改的情况下,地址不会改变。所以在逆向开发中,分析第三方应用,不会出现这种问题#####Dobby注入应用> 案例1:> 搭建被`HOOK`的应用> 创建`FuncDemo`项目> 打开`ViewController.m`文件,写入以下代码:>
import “ViewController.h”
@implementation ViewController
- (void)viewDidLoad { [super viewDidLoad]; }
-(void)touchesBegan:(NSSet
int sum(int a,int b){ return a + b; }
@end
> 真机运行项目,使用`函数实现地址 - 主程序基地址`,计算出`sum`函数在`MachO`中的偏移地址:`0x5F04`> 为了`HOOK`的场景更加真实,剥离除了间接符号之外的全部符号<br />> 剥离符号后,验证`sum`函数的实现地址是否改变> 真机运行项目,使用`image list`获得主程序基地址> 通过暂停,进入`lldb`。通过`主程序基地址 + 0x5F04`,得到`sum`函数地址<br />> 对地址设置断点,成功找到函数,说明`sum`函数的实现地址没有改变<br />> 案例2:> 修改重签名脚本,改为支持`.app`格式> 定义变量>
工程文件所在的目录
TEMP_PATH=”${SRCROOT}/Temp”
资源文件夹,我们提前在工程目录下新建一个APP文件夹,里面放ipa包
ASSETS_PATH=”${SRCROOT}/APP”
拿到临时的APP的路径
TEMP_APP_PATH=$(set — “${ASSETS_PATH}/“*.app;echo “$1”)
> 将`.app`拷贝进入工程>
TARGET_APP_PATH=”$BUILT_PRODUCTS_DIR/$TARGET_NAME.app”
rm -rf $TARGET_APP_PATH mkdir -p $TARGET_APP_PATH cp -rf $TEMP_APP_PATH/ $TARGET_APP_PATH
> 删除`Extention`和`Watch`>
rm -rf “$TARGET_APP_PATH/PlugIns” rm -rf “$TARGET_APP_PATH/Watch”
> 更新`info.plist`文件中的`CFBundleIdentifier`>
/usr/libexec/PlistBuddy -c “Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER” “$TARGET_APP_PATH/Info.plist”
> 拿到`MachO`文件的路径,给`MachO`文件上执行权限>
APP_BINARY=plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<
chmod +x “$TARGET_APP_PATH/$APP_BINARY”
> 重签名第三方`Frameworks`>
TARGET_APP_FRAMEWORKS_PATH=”$TARGET_APP_PATH/Frameworks” if [ -d “$TARGET_APP_FRAMEWORKS_PATH” ]; then for FRAMEWORK in “$TARGET_APP_FRAMEWORKS_PATH/“* do
> 签名>
/usr/bin/codesign —force —sign “$EXPANDED_CODE_SIGN_IDENTITY” “$FRAMEWORK” done fi
> 注入>
./yololib “$TARGET_APP_PATH/$APP_BINARY” “Frameworks/Hook.framework/Hook”
> 案例3:> 对`FuncDemo.app`进行`HOOK`> 搭建`HookDemo`项目> 将`yololib`、`appSign.sh`、`DobbyX.framework`,拷贝到项目根目录> 在项目根目录,创建`App`目录> 将`FuncDemo.app`拷贝至`App`目录> 创建`target`,添加注入的动态库,命名`HOOK`> 在`HOOK`动态库中,创建`Inject`类> 在`HookDemo`主项目中,拖入`DobbyX.framework`,勾选`HookDemo`和`HOOK`<br />> 在`HookDemo`中,找到`Embed Framewords`,添加`DobbyX.framework`<br />> 打开`Inject.m`文件,写入以下代码:>
import “Inject.h”
import
import
@implementation Inject
static uintptr_t sumP = 0x5F04 + 0x100000000;
+(void)load{ sumP += _dyld_get_image_vmaddr_slide(0); DobbyHook((void )sumP, mySum, (void )&sum_p); }
static int (*sum_p)(int a,int b);
int mySum(int a,int b) { NSLog(@”Sum:%d,🍺🍺🍺🍺🍺”,sum_p(a,b)); return a - b; }
@end
> 真机运行项目,点击屏幕,`HOOK`成功>
FuncDemo[11452:2162229] Sum:25,🍺🍺🍺🍺🍺 FuncDemo[11452:2162229] Sum:-5 ```
总结
Dobby
Dobby原理:运行时对目标函数的汇编代码替换,修改的是内存中MachO的代码段Dobby替换汇编代码时,对原始函数的调用,会影响栈的拉伸和平衡- 在真实
HOOK场景中,我们拿不到符号名称,只能对地址进行HOOKHOOK地址时,需要加上PAGEZERO和ASLR






