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相关代码链接