16-Hook原理(三)InlineHook
前言
一、InlineHook概述
inlineHook(内联钩子)
:所谓InlineHook
就是直接修改目标函数
的头部代码
,让它跳转
到我们自定义的函数
里面执行我们的代码,从而达到Hook
的目的。这种Hook技术一般用在静态语言
的HOOK
上面。
Inline Hook
就是在运行的流程中插入跳转指令
来抢夺运行流程的一个方法。大体分为三步????
将
原函数
的前 N 个字节搬运
到Hook 函数
的前 N 个字节;然后将
原函数
的前 N 个字节填充
跳转到 Hook 函数的指令;在
Hook
函数末尾几个字节填充
跳转回原函数 +N 的跳转指令;
之前的文章14-Hook原理(一)fishHook中也有介绍InlineHook
。
二、Dobby框架
Dobby
是一个全平台的InlineHook
框架,详情可查看 官方文档。
2.1 Dobby框架搭建
要学习Dobby
,当然是先大框架????
首先clone工程
#depth用于指定克隆深度,为1即表示只克隆最近一次commit. git clone https://github.com/jmpews/Dobby.git --depth=1 复制代码
由于
Dobby
是跨平台的,所以项目并不是一个Xcode工程
,要使用cmake
将这个工程编译成为Xcode
工程。相关指令如下????
// 进入`Dobby目录`,创建一个文件夹`build_for_ios_arm64` cd Dobby && mkdir build_for_ios_arm64 && cd build_for_ios_arm64 // 然后`cmake`编译生成XCode工程 cmake .. -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工程
????
我们生成动态库DobbyX.framework
????
2.2 项目演示
Dobby
的使用
新建一个工程
DoddyDemo
,导入DobbyX.framework
????
导⼊DobbyX.framework
到⼯程,如果遇到Bitcode
问题,两种解决方式????
关闭当前⼯程
DoddyDemo
的Bitcode
编译
DobbyX.framework
时,开启Bitcode
Bitcode
的设置????
打开
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; }复制代码
调用
DobbyHook
进行函数的Hook
????
int DobbyHook(void *address, void *replace_call, void **origin_call);复制代码
Dobby
的核心的函数????
参数名 | 释义 |
---|---|
address | 需要HOOK的函数地址 |
replace_call | 新函数地址 |
origin_call | 保留原始函数的指针的地址 |
在viewDidLoad
中进行Hook????
- (void)viewDidLoad { [super viewDidLoad]; DobbyHook((void *)sum, mySum, (void *)&sum_p); }复制代码
在
touchesBegan
中,调用sum函数
????
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"Sum:%d",sum(10, 20)); }复制代码
真机运行项目,点击屏幕????
sum函数
HOOK成功!????????????????????
三、Dobby Hook原理
接下来,我们来研究下Dobby
Hook的原理。
在上述案例中的
touchesBegan
方法上设置断点????
2. 打开汇编,真机运行项目,点击屏幕,进入touchesBegan
方法????
step into,进入sum函数????
我们发现????
拉伸栈空间
的代码没有了前三句
的代码被替换了
单步
调试,向下执行3步
。通过br x17
指令,跳转到mySum
函数????
看到第一句代码,就是拉伸栈空间
的代码。然后br x17
,会回到sum
函数????
接着就是执行完sum函数的逻辑,最后在结尾恢复栈平衡
。
当
sum函数
执行ret指令
,返回mySum函数
,执行后续代码????
综上所述????
静态函数
的HOOK,并没有在原始函数中增加代码,而是将拉伸栈空间的三句代码
进行了替换
当调用
原始函数
,才会拉伸栈平衡
。然后在原始函数
的代码中,恢复栈平衡
案例修改
我们再看修改下案例 ???? mySum
函数中,不调用
原始函数 ????
int mySum(int a,int b) { // NSLog(@"Sum:%d,????????????????????",sum_p(a,b)); return a - b; }复制代码
真机运行项目,查看sum函数
的汇编 ????
上图中,代码并没有变化。
接着进入mySum
函数????
跳转到指定地址的代码blr x18
没有了。
当mySum
函数执行ret指令
,直接返回到touchesBegan
????
此时sum
函数的原始代码都不会被执行
。这种情况????
不会
拉伸栈空间,sum
函数的原始代码不会
被执行,所以也不会恢复栈平衡
。
3.1 Hook函数地址
在逆向开发中,一般的应用App会剥离
符号表,所以我们无法获得符号名称
,那么就没法Hook方法名称,智能Hook地址
。
但是,应用每次启动时,ASLR
偏移地址都不一样
,所以不能直接
Hook固定的地址。正确的做法????
先找到函数在MachO中的
偏移地址
,加上PAGEZERO
的0x100000000
,再加上本次启动的ASLR偏移地址
案例演示
继续使用上述案例,找到sum
函数的地址????
函数实现地址是 ???? 0x104221bcc
。
然后使用image list
,找到主程序的基地址????
基地址是 ???? 0x10421c000
。
那么函数在MachO中的偏移地址 ???? 函数实现地址 - 主程序基地址 ????
0x104221bcc - 0x10421c000 = 0x5BCC复制代码
偏移地址是 ???? 0x5BCC
。在MachO文件中,查看该偏移地址????
上图显而易见,对应的正是sum函数
的汇编代码。
对函数地址
进行Hook
打开
ViewController.m
文件,写入以下代码 ????
#import "ViewController.h" #import <DobbyX/dobby.h> #import <mach-o/dyld.h> @implementation ViewController int sum(int a,int b){ return a + b; } static uintptr_t sumP = 0x5BCC + 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<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"Sum:%d",sum(10, 20)); } @end复制代码
⚠️注意:因为修改了代码,需要重新编译,所以,
sum
函数在Mach-O中的偏移地址
会发生改变
。
重新编译,查看Mach-O中的
sum函数
的偏移地址
????
偏移地址
是 ???? 0x5B34
,那么修改代码????
static uintptr_t sumP = 0x5B34 + 0x100000000;复制代码
真机运行项目,点击屏幕????
果然,Hook成功!????????????????????????
3.2 Dobby注入
最后,我们来看看使用Dobby
,如何代码注入?
创建
FuncDemo
项目,打开ViewController.m
文件,写入以下代码 ????
#import "ViewController.h" @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"Sum:%d",sum(10,15)); } int sum(int a,int b){ return a + b; } @end复制代码
真机运行项目,使用
函数实现地址 - 主程序基地址
,计算出sum函数
在MachO中的偏移地址
????
计算出偏移地址 ???? 0x5D68
⚠️注意:因为真实场景中会
剥离符号
,所以需要再build setting中设置????
剥离符号
后真机运行,通过暂停
????
然后lldb中输入image list获取基地址????
计算出sum函数
的地址 ???? 0x1000dc000 + 0x5D68 = 0x1000E1D68
。
对地址
0x1000E1D68
设置断点????
能断点成功,说明sum函数
的实现地址没有改变。
对FuncDemo.app
进行代码注入
搭建
HookDemo
项目,将yololib、appSign.sh、DobbyX.framework
,拷贝到项目根目录????
其中,脚本appSign.sh
????
# ${SRCROOT} 它是工程文件所在的目录 TEMP_PATH="${SRCROOT}/Temp" #资源文件夹,我们提前在工程目录下新建一个APP文件夹,里面放ipa包 ASSETS_PATH="${SRCROOT}/APP" # 拿到临时的APP的路径 TEMP_APP_PATH=$(set -- "${ASSETS_PATH}/"*.app;echo "$1") #目标ipa包路径 #TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa" #TEMP_APP_PATH=$(set -- "${ASSETS_PATH}/"*.app;echo "$1") #TEMP_APP_PATH="${ASSETS_PATH}/*.app" #清空Temp文件夹 #rm -rf "${SRCROOT}/Temp" #mkdir -p "${SRCROOT}/Temp" #---------------------------------------- # 1. 解压IPA到Temp下 #unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH" # 拿到解压的临时的APP的路径 #TEMP_APP_PATH=$(set -- "${ASSETS_PATH}/"*.app;echo "$1") #TEMP_APP_PATH=$(echo "${ASSETS_PATH}/"*.app) echo "TEMP_APP_PATH路径:$TEMP_APP_PATH" #echo "Tempapp路径:$TEMP_APP_PATH/" # echo "路径是:$TEMP_APP_PATH" #---------------------------------------- # 2. 将解压出来的.app拷贝进入工程下 # BUILT_PRODUCTS_DIR 工程生成的APP包的路径 # TARGET_NAME target名称 TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app" echo "app路径:$TARGET_APP_PATH" rm -rf $TARGET_APP_PATH mkdir -p $TARGET_APP_PATH cp -rf $TEMP_APP_PATH/ $TARGET_APP_PATH #---------------------------------------- # 3. 删除extension和WatchAPP.个人证书没法签名Extention rm -rf "$TARGET_APP_PATH/PlugIns" rm -rf "$TARGET_APP_PATH/Watch" #---------------------------------------- # 4. 更新info.plist文件 CFBundleIdentifier # 设置:"Set : KEY Value" "目标文件路径" /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist" #---------------------------------------- # 5. 给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" #---------------------------------------- # 6. 重签名第三方 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"复制代码
在项目根目录,创建
App目录
,将FuncDemo.app
拷贝至App目录????
3. 在HookDemo
主工程中添加target,就是注入的动态库,命名HOOK
,在HOOK
动态库中,创建Inject
类
4. 在HookDemo
主工程中,拖入DobbyX.framework
,勾选HookDemo
????
在
HookDemo
中,找到Embed Framewords
,添加DobbyX.framework
????
然后在Hook.framework
Target配置????
打开
Inject.m
文件,写入以下代码????
#import "Inject.h" #import <DobbyX/dobby.h> #import <mach-o/dyld.h> @implementation Inject static uintptr_t sumP = 0x5D68 + 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复制代码
真机运行项目,点击屏幕????
FuncDemo[11452:2162229] Sum:25,???????????????????? FuncDemo[11452:2162229] Sum:-5复制代码
总结
Dobby
原理 ???? 运行时对目标函数的汇编代码替换,修改的是内存中MachO的代码段
Dobby
替换汇编代码时,对原始函数的调用,会影响栈的拉伸和平衡
在真实Hook场景中,我们
拿不到符号名称
,只能对地址进行HOOKHook地址时,需要加上
PAGEZERO
和ASLR
作者:深圳_你要的昵称
链接:https://juejin.cn/post/7019473586251382797