iOS-逆向15-HOOK原理《下》

《iOS底层原理文章汇总》
上一篇文章《iOS-逆向14-HOOK原理》介绍了符号绑定的过程,本文继续介绍Hook原理

image

1.fishhook如何通过符号找到非懒加载符号表中绑定的地址进行修改的呢,如何通过NSLog,一步一步找到对应的符号表中的地址???

fishhook修改懒加载符号表中符号绑定的地址,通过NSLog字符串告诉fishhook要修改的符号,fishhook通过字符串能找到符号表
首先知道要找的是Lazy Symbol Pointers中的符号NSLog,排在Lazy Symbol Pointers中的第一位,Indirect Symbols中的排序和Lazy Symbol Pointers中的排序一样


image

image

间接符号表Indirect Symbols和总表Symbols有关系,间接符号表中的00000F6(246)指明了符号NSLog在symbol中的角标


图片.png

image

String Table中的起始值0000D810+偏移值000000D8=0000D8E8,找到字符串常量,有一个下划线,指向_NSLog,由.隔开,前面一个是自定义的函数my_NSLog
图片.png

fishhook的查找流程:

I.通过NSLog符号在String table中查找字符串,从而找到NSLog在Symbols中的偏移值000000D8
image
II.此时通过Symbols中Data->000000D8找到NSLog在Symbols中的偏移值Offset为#246,从而找到在间接符号表Indirect Symbols中符号_NSLog的Data->000000F6(246)
image
III.找到_NSLog在Indirect Symbols中是第几个符号,在Lazy Symbols Pointers中就是第几个
image
IV.找到Lazy Symbols Pointers中的NSLog里面Data的值,修改Data进行符号重绑定
image

符号查找流程图

fishhook符号查找流程图

此过程在fishhook的github地址也有说明

image

2.符号:去符号和恢复符号,App中的符号已经被去掉了再上架,App被编译了,内存中可以通过地址访问,符号在MachO文件中占用体积大小,完全可以去除这些符号减少包的体积大小,一方面优化包的大小,一方面为了安全防破解。

I.符号的种类

间接符号必须保留
App:会去掉所有全局(给外界调用)符号,本地(static修饰)符号
动态库:全局符号要保留,供外部调用

II.Xcode默认去符号在Xcode->Build Settings->Strip Style

Strip Style有三个选项
All Symbols:全部符号去掉
Non-Global Symbols:全局符号以外的符号都去掉
Debugging Symbols:去掉调试符号


image

Deployment Postprocessing:为No只会在打包阶段去符号,为YES在编译阶段就去符号了


image

图片.png

查看MachO文件,Lazy Symbols Pointers中的间接符号不变,全局符号和本地符号都被去掉了
图片.png

image

image

若将Deployment Postprocessing:改为No只会在打包阶段去符号,则本地符号和全局符号还在


图片.png

去符号后,Xcode中的断点没生效


image

因为去符号了,断点没生效,NSLog不是全局符号和本地符号.
属于间接符号,为什么没有断住呢?这个断点并不是符号断点。

要想断住,下符号断点


图片.png

image

不知道函数名称的情况下,如何进行逆向分析呢?

I.动态调试时,使用OC调用方法objc_msgSend(),读取x0和x1获取self和_cmd

此时断点已经跳入NSLog中
如何将断点跳入objc_msgSend中呢
可以添加地址断点:
偏移地址+ASLR
实际地址0x10468de1c-ASLR0x0000000104688000 = 偏移地址0x5e1c

地址断点可以下为0x5elc+ASLR,即断住了objc_msgSend前面一句代码,若直接断符号objc_msgSend,代码中存在太多objc_msgSend不容易分辨跳出,容易乱套

image

0x0000000100895elc = ASLR0x0000000100890000+0x5e1c


image

II.恢复符号表:如果没有符号名称,在断点调试的时候比较麻烦,怎么去恢复这个符号呢?

全局符号和本地符号


图片.png

选择Strip Style模式为Debugging Symbols,去掉调试符号
查看MackO文件中的全局符号和本地符号


image

image

图片.png

选择Strip Style模式为Non-Global Symbols,去掉所有非全局符号即去掉所有本地符号


image

选择Strip Style模式为All Symbols,去掉所有全局符号和本地符号,只保留间接符号


image

符号表里面已经没有符号了,怎么恢复符号呢?类名列表,方法名列表,方法类型依然存在于MachO文件中,这些是去不掉的,去掉后无法找到方法,凭什么通过字符串找到类NSClassFromString(@"ViewController"),存在classname类名,methodtype方法签名,methodname方法名称


图片.png

类名


图片.png

方法签名
图片.png

方法名


image

通过类名,方法签名,方法名可以创造出符号表,去符号是为了减少包的体积和MachO文件的大小
通过工具restore-symbol恢复符号./restore-symbol symbolDemo -o symbolDemo2 生成恢复了符号的symbolDemo2,通过MachView查看
image

图片.png

image

恢复建立在运行时基础上,动态派发的能恢复,动态派发的必定保留了类名,方法名,方法签名以及类名方法名IMP之间的关系,c函数就恢复不了了,静态的恢复不了了,swift动态派发的可以恢复,静态调用的和函数一样不能恢复,swift相对来说比动态语言更加安全
逆向时,恢复了符号的二进制MachO文件,重新替换老的MachO文件,重新签名,重新安装,重新运行,打断点时就能看到方法名称了

III.初探反Hook防护

1.利用fishhook做防护

破解Hook第三方的用OC MethodSwizzle方法,里面使用的是系统的函数和方法,若本地App利用fishhook将系统的MethodSwizzle方法hook了,外部想要Hook动态注入,就无法进行篡改了,达到了防护的目的

framework的load方法加载更快
注入的代码会在framwork的加载之后,主工程代码之前,防护的代码卸载

I.场景:外面hook按钮1,自己做防护,自己的hook内部要用

新建工程,创建一个framework,导入fishhook代码到framework中


image

image

图片.png

图片.png

显示包内容,拷贝出AntiHook到代码文件夹


image

A.新建一个工程HookDemo,去重签上面的App->AntiHook,试着去Hook它,看看是否还能成功
重签过程

1.appSign.sh脚本拷贝到根目录文件夹
2.重新打包AntiHook为.ipa文件,ip -ry WeChata.ipa Payload/,删掉Payload文件夹


图片.png

图片.png

3.运行拿到p12文件
4.添加脚本(过程已在前文详细描述过),运行成功


image

点击按钮1,调用按钮1,点击按钮2调用按钮2
image

B.再次进行framework的动态注入,看是否能改变按钮的调用

1.新建CloudHook动态库framework
2.添加Inject类

image

3.修改appSign.sh脚本,添加CloudHook动态注入库路径,拖入yololib可执行文件,重新运行
image

image

4.点击重签的App中的按钮,发现并没有打印HOOK成功!!!,起到了反防护的目的
图片.png

C.若本工程中要使用method_exchangeImplementations方法,则需要将fishhook捕获的exchangeP暴露出来给外界使用
1.暴露函数指针方法CF_EXPORT void (*exchangeP)(Method _Nonnull m1, Method _Nonnull m2)
图片.png

2.头文件暴露#import
image

3.HookMgr动态库public文件中暴露AntiHook.h
image

本工程的Hook很顺利,能顺利使用exchangeP函数指针执行method_exchangeImplementations方法进行方法交换
图片.png

将编译好的AntiHook包替换到HookDemo工程中,再次运行
AntiHook中的防护已生效,检测到了Hook,按钮1调用没有被拦截,本类中的按钮2调用被本类中的exchangeP拦截到
image

IV.此种防护方式很容易被破解,通过hopper查看framework可执行文件

图片.png

1.一旦把framework中的防护代码改了,就防护不了了,比如将字符串少写一个字母,就防护不了了
通过字符串常量很容易定位到防护代码,定位到后可以修改,将字符串常量修改了,就不会去hook这个函数了,一旦不会去修改了,就找不到了,就hook不到了,method_exchangeImplementations此字符串没法做混淆,必须要用此字符串才能使用fishhook,如果想要把字符串隐藏能用运算符,不能直接用变量
靠时间进行防护
2.靠运行时间,只要比防护代码执行的时间早,防护就没用了,可以通过对fishhook中的源码必要的系统函数进行符号断点断住或hook或将hook方法所在的load方法进行hook掉,将防护的代码不执行,就破解了防护

问:能用方法交换,方法交换方法(method_exchangeImplementations)嘛?
因为方法交换exchange是函数,它不是方法,只能用fishhook交换方法交换方法(method_exchangeImplementations)。

这一种初探反防护的方式很容易被破解,很明显的防护手段很容易被攻克掉,介绍另外一种逆向工具Monkey

V.Monkey

根据官方文档进行安装

Xcode12会遇到一个安装错误xcode 12 Types.xcspec not found

执行如下命令再次安装即可

sudo ln -s /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/PrivatePlugIns/IDEOSXSupportCore.ideplugin/Contents/Resources /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications

在.zshrc中配置环境变量THEOS配置环境变量,配置成功后在终端执行nic.pl

#逆向相关的配置
export THEOS=/opt/theos
#写入环境变量
export PATH=/opt/MonkeyDev/bin:$THEOS/bin:$PATH
图片.png

安装成功后出现如下图标


image

Monkey进行重签名和代码注入

1.将微信8.0.2的ipa包放到TargetApp目录下,运行程序


图片.png

image

2.代码注入,将前文运行的antihook.ipa包放入TargetApp目录下
I.Logos目录下的MonkeyDemoDylib.xm选择为Objective-C++ Source源码,方便进行编辑
II.hook目标App中的点击事件,运行


图片.png
%hook ViewController
- (void)btnClick1:(id)sender {
    NSLog(@"大哥!哥么干掉你了!!!");
}
%end
image

点击按钮1,和按钮2,logos语法hook成功生效,检测到了MonkeyDemo在hook之后,但MonkeyDemo仍旧将btnClick1干掉了,防护没有防住


image

3.防护没有防住的原因,可能Monkey中进行hook的不是method_exchangeImplementations方法,是getImp和setImp方法,在防护的framework中继续hook防护getImp和setImp方法,看是否能防住Monkey中的hook


image

重新编译,将AntiHook包拖入到MonkeyDemo中,运行,检测到了3次Hook,分别是method_exchangeImplementations,method_setImplementation,method_getImplementation


image

再次点击按钮1,成功防住了Monkey的Hook,说明Monkey中的Hook用的是method_setImplementation和method_getImplementation方法
image

Monkey中通过libsubstrate.dylib进行Hook,写的logos语句均被此动态库读取,解析logos语句,logos语句对runtime代码进行了简化

%hook ViewController
- (void)btnClick1:(id)sender {
    NSLog(@"大哥!哥么干掉你了!!!");
}
%end

MonkeyDemoDylib.mm解析.xm文件中的logos语句,最后被libsubstrate.dylib动态库读取进行hook


图片.png

image

Monkey中的寻找回家的路,将AntiHook改为没有任何防护,再次替换TargetApp中的AntiHook包,重新运行,此时可以Hook到btnClick1,之前OC层面的methodSwizzle和exchangeMethod时,无法回到原来的代码或回到原来的代码时,要单独保存原来的代码,而用Monkey直接用%orig调用


image

image

图片.png

找回回家的路,执行原来的代码,%orig;相当于setImp后,执行getImp,编译期就已经有了,在MonkeyDemoDylib.mm文件中,被libsubstrate.dylib动态库读取进行hook


image

图片.png

Framework注入的代码会先执行,依赖于dyld源码中的哪一部分逻辑?

Dyld加载主程序的时候先加载insertDylid动态库,再是Framework动态库,再调用main函数,再实例化主程序------->objc中执行load方法------>c++构造方法,load顺序和ImageList有关,ImageList和链接顺序有关,insertDylid一个一个链接后,再链接系统的,再在ImageList中一个一个add添加后,执行load的时候,拿ImageList中的load循环执行,load方法看ImageList的顺序。

你可能感兴趣的:(iOS-逆向15-HOOK原理《下》)