所谓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_arm64 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
⼯程
编译
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
*)touches withEvent:(UIEvent *)event { NSLog(@"Sum:%d",sum(10, 20)); } 真机运行项目,点击屏幕,输入以下内容:
InlineDemo[9140:1691629] Sum:30, InlineDemo[9140:1691629] Sum:-10
sum
函数HOOK
成功,先输出原始函数的执行结果,再输出替换函数的执行结果
HOOK原理
案例1:
通过汇编代码,查看
HOOK
原理上述案例中,在
touchesBegan
方法上设置断点
真机运行项目,点击屏幕,进入
touchesBegan
方法
单步调试,向下执行
1
步。进入sum
函数
- 前三句代码被替换
- 拉伸栈空间的代码没有了
单步调试,向下执行
3
步。通过br x17
指令,跳转到mySum
函数
进入
mySum
函数,通过blr x8
指令,跳转到指定地址上执行代码
该代码中,通过
br x17
指令,回到sum
函数
- 这里出现了拉伸栈空间的代码
进入
sum
函数,执行原始代码逻辑
- 在
sum
函数的结尾,恢复栈平衡当
sum
函数执行ret
指令,返回mySum
函数,执行后续代码
静态函数的
HOOK
,并没有在原始函数中增加代码,而是将拉伸栈空间的三句代码进行了替换当调用原始函数,才会拉伸栈平衡。然后在原始函数的代码中,恢复栈平衡
案例2:
mySum
函数中,不调用原始函数在
mySum
函数中,注释原始函数的调用
真机运行项目,进入
sum
函数。代码并没有发生变化
进入
mySum
函数,跳转到指定地址的代码没有了
当
mySum
函数执行ret
指令,直接返回到touchesBegan
此时
sum
函数的原始代码都不会被执行
这种情况,不会拉伸栈空间,
sum
函数的原始代码不会被执行,所以也不会恢复栈平衡
HOOK函数地址
在逆向开发中,三方应用会剥离符号表,我们无法获得符号名称,所以
HOOK
的一定是地址应用每次启动时,
ASLR
偏移地址都不一样,所以不能直接HOOK
地址正确的做法:先找到函数在
MachO
中的偏移地址,加上PAGEZERO
的0x100000000
,再加上本次启动的ASLR
偏移地址
案例1
延用上述案例,找到
sum
函数的实现地址查看汇编代码,找到
sum
函数的调用
- 函数实现地址:
0x1022edd48
使用
image list
函数,找到主程序的基地址
- 基地址:
0x1022e8000
使用
函数实现地址 - 主程序基地址
,计算函数在MachO
中的偏移地址e -f x -- 0x1022edd48-0x1022e8000 ------------------------- $1 = 0x5d48
- 偏移地址:
0x5d48
在
MachO
文件中,查看偏移地址
- 对应的正是
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 *)touches withEvent:(UIEvent *)event { NSLog(@"Sum:%d",sum(10, 20)); } @end 这里有一个小问题,因为案例是在自己的项目中
HOOK
地址,所以对项目的代码进行了修改,这样会造成sum
函数的实现地址发生改变
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
*)touches withEvent:(UIEvent *)event { NSLog(@"Sum:%d",sum(10,15)); } int sum(int a,int b){ return a + b; } @end 真机运行项目,使用
函数实现地址 - 主程序基地址
,计算出sum
函数在MachO
中的偏移地址:0x5F04
为了
HOOK
的场景更加真实,剥离除了间接符号之外的全部符号
剥离符号后,验证
sum
函数的实现地址是否改变真机运行项目,使用
image list
获得主程序基地址通过暂停,进入
lldb
。通过主程序基地址 + 0x5F04
,得到sum
函数地址
对地址设置断点,成功找到函数,说明
sum
函数的实现地址没有改变
案例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
在
HookDemo
中,找到Embed Framewords
,添加DobbyX.framework
打开
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
场景中,我们拿不到符号名称,只能对地址进行HOOK
HOOK
地址时,需要加上PAGEZERO
和ASLR