iOS 逆向开发19:InlineHook(HOOK 自定义C函数)

iOS 逆向开发 文章汇总

目录

  • 一、InlineHook新秀Dobby框架
  • 二、利用纯地址进行HOOK
  • 三、将Dobby注入目标程序


一、InlineHook新秀Dobby框架

由于最近研究InlineHook(内联钩⼦),发现了⼀个不错的框架,Dobby(原名:HOOKZz)。这是⼀个全平台的InlineHook框架,它⽤起来就和fishhook⼀样,我们先看⼀下如何使⽤。

内联钩⼦:所谓InlineHook就是直接修改⽬标函数的头部代码。让它跳转到我们⾃定义的函数⾥⾯执⾏我们的代码,从⽽达到Hook的⽬的。这种Hook技术⼀般⽤在静态语⾔的HOOK上⾯

1.1 编译Dobby

⾸先我们将代码clone下来

#depth⽤于指定克隆深度,为1即表示只克隆最近⼀次commit.
git clone https://github.com/jmpews/Dobby.git --depth=1

注意由于这家伙是跨平台的,所以项⽬并不是⼀个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-DDOBBY_GENERATE_SHARED=OFF

编译完成后,会⽣成⼀个Xcode⼯程

接下来选择DobbyX Target、Bitcode设置为YES编译Xcode⼯程。⽣成我们的Framework

1.2 导⼊DobbyX.Framework

bitcode问题

我们新建⼀个⼯程开始使⽤Dobby。那么将Framework加⼊⼯程会有⼀个常⻅的问题,就是bitcode。
解决⽅案两种。1、解决Framework让他⽀持bitcode。2、本⼯程关闭bitcode。

拷⻉问题

Framework库⾸次拖⼊⼯程,Xcode不会⾃动帮你拷⻉,运⾏时你会发现库没有打包进⼊App包。造成DYLD加载时找不到库的错误。

此刻需要⼿动添加拷⻉

再次运⾏我们可以看到控制台的输出,说明引⼊成功了!

[*] [DobbySymbolResolver] resolve image: /usr/lib/system/libdyld.dylib
[*] Initialize DobbyInstrument => 0x102cc9548 => 0x102db0b60
[*] ================ DynamicBinaryInstrumentRouting Start ================
[*] Initialize assembler code buffer at 0x283c3cc20
[*] Initialize assembler code buffer at 0x283c3cc60

1.3 使⽤Dobby

引⼊成功之后,我们就可以开始玩⼀下了。⾸先我们来看看最关键的函数。

// replace function
/**
arg1:需要HOOK的函数地址
arg2:新函数地址
arg3:保留原始函数的指针的地址
*/
int DobbyHook(void *function_address, void *replace_call, void **origin_call);

这个就是⽤来HOOK我们⾃定义函数的,那么我们来使⽤⼀下。你会发现这个函数的使⽤和fishhook⾮常像!
接下来,我们可以写⼀个Demo。⽐如我们有⼀个⾃定义的sum函数,明显的加法运算。

int sum(int a,int b) {
    return a + b;
}

接下来,我们在ViewController的ViewDidLoad中输出

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"打印出:%d",sum(10, 20));
}

这个函数正常的执⾏结果应该是30。那么我们要在Load⽅法中去HOOK这个sum。

HOOK前的准备

⾸先我们要定义⼏个东⻄。

  • 函数指针,⽤于保存被替换函数的地址
//函数指针⽤于保留原来的执⾏流程
static int(*sum_p)(int a, int b);
  • 新函数(⽤这个函数替换你需要HOOK的函数,那么该函数的返回值以及参数要保持⼀致)
//新函数
int mySum(int a, int b){
    NSLog(@"原有的结果是:%d",sum_p(a,b));
    return a - b;
}
开始HOOK

使⽤DobbyHook来HOOK我们的函数。接下来,在Load⽅法中:

+ (void)load {
    //Hook sum
    DobbyHook(sum, mySum, (void *)&sum_p);
}
参数解析
  • arg1(sum):需要HOOK的函数的地址,函数名称就是函数指针
  • arg2(mySum):新函数的地址
  • arg3(sum_p):将原来的sum函数的地址存放到sum_p这个函数指针中(因为要给指针赋值,所以取指针的地址,so:⼆级指针)

运⾏结果:

原有的结果是:30
打印出:-10

顺利HOOK 自定义C函数


二、利用纯地址进行HOOK

结合上面次的Demo我们进⾏⼀点改进。在真实的逆向过程中,我们是没有函数符号的,因为符号表脱掉了。
接下来,我们通过纯地址的⽅式来HOOK函数。

2.1 通过LLDB调试获取Sum函数的⽂件偏移地址。

  • ⾸先在sum函数上下⼀个断点

  • 然后通过汇编显示确定函数地址,在Xcode顶部工具栏Debug设置中设置⼀下
    Debug --> Debug Workflow --> Always Show Disassembly(⼀直显示汇编)

  • 接下来通过LLDB指令找出主程序的⾸地址。得到 sum函数偏移地址 = sum函数地址 - 主程序⾸地址

我们可以⽤Mac⾃带的计算器算出偏移值,很⽅便(CMD+3 使⽤编程器)

2.2 在MachOView中查看到当前函数

我们打开MachOView,可以看到这⾥就是我们sum函数的开始位置。所以我们可以验证了sum函数的⽂件偏移地址就是0x5DA8。那么我们就⽤这个地址进⾏接下来的HOOK了。

2.3 HOOK前的准备

准备⼀个指针
注意:由于要⽅便我们计算,这⾥定义函数指针不要定义成为函数的结构。⽽是uintptr_t类型

//定义指针,表示sum函数的偏移地址!
static uintptr_t sumP = 0x100005DA8;

动态获取ASLR
⾸先导⼊头#import
然后使⽤函数_dyld_get_image_vmaddr_slide获取ASLR,我们在Load中就可以这样写。

+ (void)load {
    //获取ASLR,让sumP变成准确的地址
    //参数0代表imagelist中的主程序(⾃⼰)
    uintptr_t aslr = _dyld_get_image_vmaddr_slide(0);
    sumP += aslr;
    //Hook sum
    DobbyHook((void *)sumP, mySum, (void *)&sum_p);
}

但是这个时候,我们运⾏会发现HOOK失败,调用的任然是原来的sum函数?

2.4 为什么会出错?

原因很简单。我们刚才算出来的sum函数的⽂件偏移地址,在我们修改了这⾥的代码之后变了!所以只能再次去获取sum函数的偏移。(注意:HOOK别⼈应⽤时因为我们是动态库注⼊,所以不会修改⽬标应⽤的MachO,那么偏移值是固定的。没有这么麻烦)

注意:
这⾥有⼀个⼩技巧!因为修改代码之后,我们的sum函数有可能会没法断点调试(因为程序交换就崩溃),那么没关系,我们不做交换
1、将交换的代码注释起来
2、获取到最⼩改动的sum函数偏移地址。
3、再讲该执⾏的代码打开。⽣成最新的MachO。
4、在MachOView中找到那附近(往⾼地址⾛)最像函数起始位置的作为sum函数的偏移(⼀般都是)

这个时候,我们只需要将sum函数的偏移地址修改⼀下就可以了!

//定义指针,表示sum函数的偏移地址!
static uintptr_t sumP = 0x100005D6C;

运⾏!通过纯地址也能轻松搞定InlineHook了!这样离我们HOOK三⽅应⽤就更加接近了。


三、将Dobby注入目标程序

3.1 实现一个目标应用

简单的sum函数调用

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"10+15的结果是%d",sum(10,15));
}

int sum(int a, int b) {
    return  a + b;
}

编译完成之后,我们现在获取sum函数的准确地址,记录一下。

0x10229dfa4-0x0000000102298000 = 0x5FA4

接下来将该应用的符号脱掉。在Build Settings里面

重新编译运行后会发现符号断点来不了,这是一个明显的脱符号成功的标志。
那么这个时候我们可以通过内存断点来看看。运行APP,点击调试区域⏸,进入调试

使用sum内存偏移地址0x5FA4加上ASLR得到当前sum的内存地址,并在控制台添加内存断点

点击屏幕就会进入sum的函数内存断点

3.2 注入目标应用

接下来按照重签、注入的步骤进行代码注入。这里需要注意appSign.sh中APP的路径

TEMP_APP_PATH=$(set -- "${ASSETS_PATH}/"*.app;echo "$1")

重签成功后就可以调试了,接下来就是注入动态库了。创建HankHook动态库

添加DobbyX.framework

将DobbyX.framework拖入到项目并配置Other Linker Flags和Framework Search Paths、Library Search Paths

将appSign.sh中注入脚本的注释打开,并重新运行程序

#注入
./yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/HankHook.framework/HankHook"

Dobby 是在运行时进行HOOK的。当APP运行在内存中后对目标函数的汇编代码进行修改,修改的是内存中的MachO的代码段

InlineHook相关代码链接

你可能感兴趣的:(iOS 逆向开发19:InlineHook(HOOK 自定义C函数))