美文网首页
16-Hook原理(三)InlineHook

16-Hook原理(三)InlineHook

作者: 深圳_你要的昵称 | 来源:发表于2021-05-23 00:15 被阅读0次

    前言

    一、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
    

    命令执行完成后,工程应该是这样👇

    1. 编译Xcode工程👇

    我们生成动态库DobbyX.framework👇

    2.2 项目演示

    Dobby的使用

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

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

    • 关闭当前⼯程DoddyDemoBitcode
    • 编译DobbyX.framework时,开启Bitcode

    Bitcode的设置👇

    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. 真机运行项目,点击屏幕👇

    sum函数HOOK成功!🍺🍺🍺🍺🍺

    三、Dobby Hook原理

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

    1. 在上述案例中的touchesBegan方法上设置断点👇
    1. 打开汇编,真机运行项目,点击屏幕,进入touchesBegan方法👇

    step into,进入sum函数👇

    我们发现👇

    • 拉伸栈空间的代码没有了
    • 前三句的代码被替换了
    1. 单步调试,向下执行3步。通过br x17指令,跳转到mySum函数👇

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

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

    1. 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函数的原始代码不会被执行,所以也不会恢复栈平衡

    2.1 Hook函数地址

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

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

    案例演示

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

    函数实现地址是 👉 0x104221bcc
    然后使用image list,找到主程序的基地址👇

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

    0x104221bcc - 0x10421c000 = 0x5BCC
    

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

    上图显而易见,对应的正是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函数偏移地址👇

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

    static uintptr_t sumP = 0x5B34 + 0x100000000;
    
    1. 真机运行项目,点击屏幕👇

    果然,Hook成功!🍺🍺🍺🍺🍺🍺

    2.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中的偏移地址👇

    计算出偏移地址 👉 0x5D68

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

    剥离符号后真机运行,通过暂停👇

    然后lldb中输入image list获取基地址👇

    计算出sum函数的地址 👉 0x1000dc000 + 0x5D68 = 0x1000E1D68

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

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

    FuncDemo.app进行代码注入

    1. 搭建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"
    
    1. 在项目根目录,创建App目录,将FuncDemo.app拷贝至App目录👇
    1. HookDemo主工程中添加target,就是注入的动态库,命名HOOK ,在HOOK动态库中,创建Inject
    1. HookDemo主工程中,拖入DobbyX.framework,勾选HookDemo👇
    1. HookDemo中,找到Embed Framewords,添加DobbyX.framework👇

    然后在Hook.framework Target配置👇

    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

    相关文章

      网友评论

          本文标题:16-Hook原理(三)InlineHook

          本文链接:https://www.haomeiwen.com/subject/iyxpjltx.html