阅读 121

16-Hook原理(三)InlineHook

前言

一、InlineHook概述

inlineHook(内联钩子):所谓InlineHook就是直接修改目标函数头部代码,让它跳转到我们自定义的函数里面执行我们的代码,从而达到Hook的目的。这种Hook技术一般用在静态语言HOOK上面。

Inline Hook 就是在运行的流程中插入跳转指令来抢夺运行流程的一个方法。大体分为三步????

  1. 原函数的前 N 个字节搬运Hook 函数的前 N 个字节;

  2. 然后将原函数的前 N 个字节填充跳转到 Hook 函数的指令;

  3. Hook 函数末尾几个字节填充跳转回原函数 +N 的跳转指令;

之前的文章14-Hook原理(一)fishHook中也有介绍InlineHook

二、Dobby框架

Dobby是一个全平台的InlineHook框架,详情可查看 官方文档。

2.1 Dobby框架搭建

要学习Dobby,当然是先大框架????

  1. 首先clone工程

#depth用于指定克隆深度,为1即表示只克隆最近一次commit.
git clone https://github.com/jmpews/Dobby.git --depth=1 
复制代码
  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复制代码

命令执行完成后,工程应该是这样????image.png

  1. 编译Xcode工程????

image.png

我们生成动态库DobbyX.framework????

image.png

2.2 项目演示

Dobby的使用

  1. 新建一个工程DoddyDemo,导入DobbyX.framework????

image.png

导⼊DobbyX.framework到⼯程,如果遇到Bitcode问题,两种解决方式????

  • 关闭当前⼯程DoddyDemoBitcode

  • 编译DobbyX.framework时,开启Bitcode

Bitcode的设置????

image.png

  1. 打开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;
}复制代码
  1. 调用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);    
}复制代码
  1. touchesBegan中,调用sum函数????

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
   NSLog(@"Sum:%d",sum(10, 20));
}复制代码
  1. 真机运行项目,点击屏幕????

image.pngsum函数HOOK成功!????????????????????

三、Dobby Hook原理

接下来,我们来研究下Dobby Hook的原理。

  1. 在上述案例中的touchesBegan方法上设置断点????

image.png2. 打开汇编,真机运行项目,点击屏幕,进入touchesBegan方法????

image.pngstep into,进入sum函数????

image.png

我们发现????

  • 拉伸栈空间的代码没有了

  • 前三句的代码被替换了

  1. 单步调试,向下执行3步。通过br x17指令,跳转到mySum函数????

image.png

image.png

image.png看到第一句代码,就是拉伸栈空间的代码。然后br x17,会回到sum函数????

image.png接着就是执行完sum函数的逻辑,最后在结尾恢复栈平衡

  1. sum函数执行ret指令,返回mySum函数,执行后续代码????

image.png

image.png综上所述????

  • 静态函数的HOOK,并没有在原始函数中增加代码,而是将拉伸栈空间的三句代码进行了替换

  • 当调用原始函数,才会拉伸栈平衡。然后在原始函数的代码中,恢复栈平衡

案例修改

我们再看修改下案例 ???? mySum函数中,不调用原始函数 ????

int mySum(int a,int b) {
//   NSLog(@"Sum:%d,????????????????????",sum_p(a,b));
   return a - b;
}复制代码

真机运行项目,查看sum函数的汇编 ????

image.png

上图中,代码并没有变化。

接着进入mySum函数????

image.png跳转到指定地址的代码blr x18没有了。

mySum函数执行ret指令,直接返回到touchesBegan ????

image.png此时sum函数的原始代码都不会被执行。这种情况????

不会拉伸栈空间,sum函数的原始代码不会被执行,所以也不会恢复栈平衡

3.1 Hook函数地址

在逆向开发中,一般的应用App会剥离符号表,所以我们无法获得符号名称,那么就没法Hook方法名称,智能Hook地址。 但是,应用每次启动时,ASLR偏移地址都不一样,所以不能直接Hook固定的地址。正确的做法????

先找到函数在MachO中的偏移地址,加上PAGEZERO0x100000000,再加上本次启动的ASLR偏移地址

案例演示

继续使用上述案例,找到sum函数的地址????

image.png函数实现地址是 ???? 0x104221bcc。 然后使用image list,找到主程序的基地址????

image.png

基地址是 ???? 0x10421c000。 那么函数在MachO中的偏移地址 ???? 函数实现地址 - 主程序基地址 ????

0x104221bcc - 0x10421c000 = 0x5BCC复制代码

偏移地址是 ???? 0x5BCC。在MachO文件中,查看该偏移地址????

image.png

上图显而易见,对应的正是sum函数的汇编代码。

函数地址进行Hook

  1. 打开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中的偏移地址会发生改变

  1. 重新编译,查看Mach-O中的sum函数偏移地址????

image.png

偏移地址是 ???? 0x5B34,那么修改代码????

static uintptr_t sumP = 0x5B34 + 0x100000000;复制代码
  1. 真机运行项目,点击屏幕????

image.png

果然,Hook成功!????????????????????????

3.2 Dobby注入

最后,我们来看看使用Dobby,如何代码注入?

  1. 创建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复制代码
  1. 真机运行项目,使用函数实现地址 - 主程序基地址,计算出sum函数在MachO中的偏移地址????

image.png计算出偏移地址 ???? 0x5D68

⚠️注意:因为真实场景中会剥离符号,所以需要再build setting中设置????

image.png剥离符号后真机运行,通过暂停????

image.png然后lldb中输入image list获取基地址????

image.png计算出sum函数的地址 ???? 0x1000dc000 + 0x5D68 = 0x1000E1D68

  1. 对地址0x1000E1D68设置断点????

image.png能断点成功,说明sum函数的实现地址没有改变。

FuncDemo.app进行代码注入

  1. 搭建HookDemo项目,将yololib、appSign.sh、DobbyX.framework,拷贝到项目根目录????

image.png其中,脚本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"复制代码
  1. 在项目根目录,创建App目录,将FuncDemo.app拷贝至App目录????

image.png3. 在HookDemo主工程中添加target,就是注入的动态库,命名HOOK ,在HOOK动态库中,创建Inject

image.png4. 在HookDemo主工程中,拖入DobbyX.framework,勾选HookDemo????

image.png

  1. HookDemo中,找到Embed Framewords,添加DobbyX.framework????

image.png然后在Hook.framework Target配置????

image.png

image.png

  1. 打开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复制代码
  1. 真机运行项目,点击屏幕????

FuncDemo[11452:2162229] Sum:25,????????????????????
FuncDemo[11452:2162229] Sum:-5复制代码

总结

  • Dobby原理 ???? 运行时对目标函数的汇编代码替换,修改的是内存中MachO的代码段

  • Dobby替换汇编代码时,对原始函数的调用,会影响栈的拉伸和平衡

  • 在真实Hook场景中,我们拿不到符号名称,只能对地址进行HOOK

  • Hook地址时,需要加上PAGEZEROASLR


作者:深圳_你要的昵称
链接:https://juejin.cn/post/7019473586251382797


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐