iOS Hook
HOOK 译为“钩子”或挂钩。在 iOS 逆向中指改变程序运行流程的一种技术。
iOS 中 hook 技术的几种方式
Method Swizzle
利用 OC 的 Runtime 特性,动态改变 SEL(方法编号)和 IMP(方法实现)的对应关系,达到 OC 方法调用流程改变的目的。主要用于 OC 方法。
fishhook
它是 Facebook 提供的一个动态修改链接 mach-O 文件的工具。利用 MachO 文件加载原理,通过修改懒加载和非懒加载两个表的指针达到 C 函数 Hook 的目的。
Cydia Substrate
Cydia Substrate 原名为 Mobile Substrate,它的主要作用是针对 OC 方法、C 函数以及函数地址进行 Hook 操作。当然它并不是仅仅针对 iOS 而设计的,安卓一样可以用。官方地址
一、Method Swizzle
在 OC 中,SEL 和 IMP 之间的关系,就好像一本书的“目录”。
SEL 是方法编号,就像“标题”一样,IMP 是方法实现的真实地址,就像“页码”一样。他们是一一对应的关系。
Runtime 提供了交换两个 SEL 和 IMP 对应关系的函数。
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
OBJC_AVAILABLE(10.5, 2.0, 9.0 1.0, 2.0);
通过这个函数交换两个 SEL 和 IMP 对应关系的技术,称之为 Method Swizzle(方法欺骗)。
使用场景:
- 埋点
- 防止程序崩溃
方法交换后要注意死递归
现象。死递归最终导致栈溢出,因为每次递归方法,会将方法压栈。
二、Cydia Substrate
Mobile Hooker
顾名思义用于 Hook。它定义一系列的宏和函数,底层调用 objc 的 runtime 和 fishhook 来替换系统或者目标应用的函数。
MSHookMessageEx
主要作用于 Objective-C 方法。
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
MSHookFunction
主要作用于 C 和 C++ 函数。
void MSHookFunction(void function, void * replacement, void** p_original)
Logos 语法 %hook 就是对此函数做了一层封装
MobileLoader
MobileLoader 用于加载第三方 dylib 在运行的应用程序中。启动时 MobileLoader 会根据规则把指定目录的第三方的动态库加载进去,第三方的动态库也就是我们写的破解程序。
safe mode
破解程序本质是 dylib,寄生在别的进程里。系统进程一旦出错,可能导致整个进程崩溃,崩溃后就会造成 iOS 瘫痪。所以 CydiaSubstrate 引入了安全模式,在安全模式下所有基于 CydiaSubstrate 的三方 dylib 都会被禁用,便于查错和修复。
三、fishHook
3.1 使用过程
- (void)viewDidLoad
{
[super viewDidLoad];
// 定义 rebinding 结构体
struct rebinding nslog;
nslog.name = "NSLog";
nslog.replacement = myNSLog;
nslog.replaced = (void **)&sys_nslog;
// 定义数组
struct rebinding rebs[1] = { nslog };
/** 用来重新绑定符号
参数 1:存放 rebinding 结构体的数组
参数 2:数组的长度
*/
rebind_symbols(rebs, 1);
}
// 函数指针,用来保存原始的函数地址
static void(* sys_nslog)(NSString * format, ...);
/// 定义一个新函数。
void myNSLog(NSString *format, ...)
{
format = [format stringByAppendingString:@"被 hook 了!"];
// 保留原始的调用
sys_nslog(format);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"NSLog ");
}
NSLog 被 hook 了!
3.2 fishhook 原理
PIC
Position Independent Code 位置代码独立。
为了使多个进程能够共享内存中的同一份代码拷贝,已达到节约内存资源的目的。
ASLR
Address Space Layout Randomization 地址空间布局随机化技术。
它将进程的某些内存空间地址进行随机化来增大入侵者预测目的地址的难度,从而降低进程被成功入侵的风险。因而每次启动程序时,程序在内存中的地址都不一样。
DYLD
Dynamic Loader 动态加载器。
在 Darwin/OS X 被叫做 dyld,它用来加载所有的 frameworks, dynamic libraries, and bundles (plug-ins)。
由于苹果的动态库遵循 PIC,存在于共享缓存库,同时采用了 ASLR 技术。每次重启手机后,动态库的地址都会随机偏移,需要通过基地址加偏移地址来获取代码的真实地址。
MachO 文件
MachO 文件是苹果系统的可执行文件。通过 DYLD 动态加载。
我们的应用程序为了能够获取动态库的真实地址,由 DYLD 动态加载 MachO 中的符号表,将符号表中的指针指向动态库地址,从而达到调用系统库的目的。
fishhook 正是通过 dyld 来修改符号表中指向动态库的指针来达到 hook 的目的。
MachO 文件中,__DATA 段中与动态符号绑定相关的有两个 section:__nl_symbol_ptr
和__la_symbol_ptr
。
- __nl_symbol_ptr 是一个非懒加载数据的指针数组。
- __la_symbol_ptr 在第一次调用前,dyld 会去通过 dyld_stub_binder 填充指针数组。在第一次调用之后才会真正链接到动态库的位置。
为了在 MachO 中找到想要 hook 的函数的名字,需要在几个间接的层间进行跳转。在这两个 section 的 section header 提供了一个 offset
用来指向对应的 section。
而 __la_symbol_ptr 指针数组的下标和 indirect_symbol_table 中的下标是一一对应的。
indirect_symbol_table 中的每个元素的 data 中保存着一个十六进制的数,这个数就是真正的符号表中的数组下标。比如 NSLog 这个函数的 Data 为 0x185,对应十进制 389,在symbol_table 中的第 389 位即可找到。
而 symbol_table 对应的元素中的 data 同样保存着一个十六进制的数,这个数是字符串表中的偏移地址。比如 NSLog 在 string_table 中的偏移为 0x000000C1,找到 string_table 的首地址,为 0x00009934,则该函数的字符串常量位于 0x9934 + 0xC1 = 0x99F5 的位置。
3.3 验证方法交换过程
注意:因为 NSLog 是懒加载的,所以在第一次使用之前,是没有确定位置的。
所以上面代码改成:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"First Use");
// 使用结构体的简便创建方式
struct rebinding rebs[1] = { { "NSLog", myNSLog, (void **)&sys_nslog } };
rebind_symbols(rebs, 1);
}
添加断点在
rebind_symbols(rebs, 1);
lldb 命令输出加载的模块的列表。第一个加载可执行文件,第二个加载 dyld。
(lldb) image list
[ 0] 069E0DBD-6F91-39FC-BEDD-5463147B622A 0x000000010461d000 /Users/dubinbin/Library/Developer/Xcode/DerivedData/fishhookDmeo-bxkdwcgogbunzidzmmsmdpjedffq/Build/Products/Debug-iphonesimulator/fishhookDmeo.app/fishhookDmeo
[ 1] CE635DB2-D47E-3C05-A0A3-6BD982E7E750 0x000000010bc55000 /usr/lib/dyld
[ 2] C3514384-926E-3813-BF0C-69FFC704E283 0x000000010462e000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
[ 3] E5391C7B-0161-33AF-A5A7-1E18DBF9041F 0x0000000104917000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/Foundation.framework/Foundation
[ 4] 177A61B3-9E02-3A09-9A98-C1C3C9AB7958 0x0000000104f3d000 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libobjc.A.dylib
...
由上可知 MachO 的地址是 0x000000010461d000,MachO 文件中 NSLog 的偏移为 0x50A0。读取指针:
(lldb) x 0x000000010461d000+0x50A0
0x1046220a0: 76 d2 9b 04 01 00 00 00 70 8c 9a 04 01 00 00 00 v.......p.......
0x1046220b0: 91 b7 80 08 01 00 00 00 72 05 62 04 01 00 00 00 ........r.b.....
查看汇编(注意:iOS 是小端序,地址从右往左读取,即为 0x00000001049bd276):
(lldb) dis -s 0x01049bd276
Foundation`NSLog:
0x1049bd276 <+0>: pushq %rbp
0x1049bd277 <+1>: movq %rsp, %rbp
0x1049bd27a <+4>: subq $0xd0, %rsp
0x1049bd281 <+11>: testb %al, %al
0x1049bd283 <+13>: je 0x1049bd2ab ; <+53>
0x1049bd285 <+15>: movaps %xmm0, -0xa0(%rbp)
0x1049bd28c <+22>: movaps %xmm1, -0x90(%rbp)
(lldb) p sys_nslog
(void (*)(NSString *, ...)) $1 = 0x0000000000000000
如果没有先执行一条 NSLog,这里输出是:
(lldb) dis -s 0x01049bd276
0x10d5c4460: pushq $0x1e
0x10d5c4465: jmp 0x10d5c4450
0x10d5c446a: pushq $0x2c
0x10d5c446f: jmp 0x10d5c4450
0x10d5c4474: pushq $0x152 ; imm = 0x152
0x10d5c4479: jmp 0x10d5c4450
这个位置还不是 NSlog。
替换方法实现后,重新读取 MachO 相同偏移位置的指针内容:
(lldb) x 0x000000010461d000+0x50A0
0x1046220a0: 50 de 61 04 01 00 00 00 70 8c 9a 04 01 00 00 00 P.a.....p.......
0x1046220b0: 91 b7 80 08 01 00 00 00 2b 51 42 07 01 00 00 00 ........+QB.....
(lldb) dis -s 0x010461de50
fishhookDmeo`myNSLog:
0x10461de50 <+0>: pushq %rbp
0x10461de51 <+1>: movq %rsp, %rbp
0x10461de54 <+4>: subq $0x30, %rsp
0x10461de58 <+8>: movq $0x0, -0x8(%rbp)
0x10461de60 <+16>: leaq -0x8(%rbp), %rax
0x10461de64 <+20>: movq %rdi, -0x10(%rbp)
0x10461de68 <+24>: movq %rax, %rdi
0x10461de6b <+27>: movq -0x10(%rbp), %rsi
(lldb) p sys_nslog
(void (*)(NSString *, ...)) $2 = 0x000000011c72780a
可以看到 sys_nslog 保存了交换前 NSLog 的地址
3.4 总结
fishhook 正是通过查找到需要 hook 的函数指针,然后替换成自己写的函数指针,从而实现 hook 系统函数的目的。
fishhook 不能 hook 自定义的函数,是因为自定义的函数不在 __la_symbol_ptr 中。
文章
iOS Hook的更多相关文章
- iOS hook原理
OC中的method其实是一个结构体 struct objc_method{ SEL method_name char *method_types IMP method_imp } SEL是方法名,I ...
- ios逆向工程
原 ios逆向工程-内部钩子(Method Swizzling) Method+Swizzling ios hook Method Swizzling(方法调配) 怎么说呢,先了解什么是钩子为什么 ...
- iOS开发,hook系统Objective-C的函数
我们都知道在windows下可以通过API轻松的hook很多消息,IOS也可以实现hook的功能. 建立一个 TestHookObject类 // // TestHookObject.m // Tes ...
- iOS App 无代码入侵的方法hook
继续Objective-C runtime的研究 最近公司项目在做用户行为分析 于是App端在某些页面切换,交互操作的时候需要给统计系统发送一条消息 在几十个Controller 的项目里,一个一个地 ...
- iOS开发中乱用hook可能导致灾难
今天有同事遇到问题,他重写viewDidAppear:方法,但是,代码并没有执行到.后来我发现,是另个一同事用了黑魔法搞的鬼,而且他本人并不知道这么做会产生影响.(本文中所有黑魔法指Swizzle) ...
- iOS安全—阻止tweak注入hook api
http://blog.csdn.net/zcrong/article/details/51617348 在Other Linker Flags中添加: -Wl,-sectcreate,__RESTR ...
- IOS Swizzle(hook)
/////////////////////////////////////////////////////////////////////////////////////////////////// ...
- iOS逆向工程之Theos
如果你对iOS逆向工程有所了解,那么你对Tweak并不陌生.那么由Tweak我们又会引出Theos, 那么什么是Theos呢,简单一句话,Theos是一个越狱开发工具包,Theos是越狱开发工具的首先 ...
- 【腾讯Bugly干货分享】移动App入侵与逆向破解技术-iOS篇
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e0acc896e9ebb6865f321 如果您有耐心看完这篇文章,您将懂 ...
随机推荐
- 性能测试之Mysql数据库调优
一.前言 性能调优前提:无监控不调优,对于mysql性能的监控前几天有文章提到过,有兴趣的朋友可以去看一下 二.Mysql性能指标及问题分析和定位 1.我们在监控图表中关注的性能指标大概有这么几个:C ...
- CMSampleBufferRef解析
CMTime:64位的value,32位的scale, media的时间格式 CMVideoFormatDesc:video的格式,包括宽高.颜色空间.编码格式.SPS.PPS CVPixelBuff ...
- FCC 成都社区·前端周刊 第 7 期
01. ES2016, 2017, 2018 中的新特性 文章介绍了 18 个 ECMAScript 2016,2017 和 2018 中新增加的特性,这些特性已被加入到 TC39 提案中.包括Arr ...
- DNA sequence HDU - 1560
DNA sequence Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- Cinemachine简介
先贴一下官方的Cinemachine文档Cinemachine Documentation 简介 使用 我们第一次使用Cinemachine时大概是这样一个流程: 在需要被控制的Camera上 ...
- Java8 内置的函数式接口
1.Java8 内置的四大核心函数式接口 (1)Consumer<T> : 消费型接口 void accept(T t); (2)Supplier<T> : 供 ...
- 单列集合List
1.Vector和ArrayList以及LinkedList区别和联系.应用场景 线程安全: Vector:如果创建Vector时没有指定容量,则默认容量为10,底层基于数组实现,线程是安全的,底层采 ...
- 为什么vue中的data用return返回呢?
不使用return包裹的数据会在项目的全局可见,会造成变量污染:使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件. 当一个组件被定义, data 必须声明为返回一个初始数据对象的函 ...
- git删除已经提交的包含敏感信息的文件(还没提交到远程仓库)
写好的代码已经提交了(但还没push到github),发现某个文件里包含密码.如果push的话,密码可就被公开了.如果在代码里改掉密码,再commit一次,也不行,历史提交记录还是会上传到github ...
- orale中pl/sql的数据类型总结
一.pl/sql的概念 pl/sql简单的说就是:sql语句+编程语言的特性. 二.pl/sql的优点 1.可以提高程序的运行效率,因为能够减少数据库的访问次数. 2.可以对复杂的业务逻辑进行封装 3 ...