本文梳理了ios或osx中可以用于hook的框架及其使用,对于C/C++方法,进行了私有和系统方法的区分阐述,本文仅针对hook框架做讨论,对于实验中用到的注入、签名等不作阐述。
本文我们会对一个编写的测试mac app进行hook,其中mac app的主要代码如下:
#import "ViewController.h"
int cfunc(int x,int y,int z) {
return x + y + z;
}
@implementation ViewController
- (IBAction)button:(id)sender {
[self ocFunc];
NSLog(@"%@", [NSString stringWithFormat:@"%zu",_length]);
}
-(void)ocFunc{
NSLog(@"haha");
_length = cfunc(1, 2, 3);
}
- (void)viewDidLoad {
[super viewDidLoad];
_length = cfunc(1, 2, 3);
// Do any additional setup after loading the view.
}
接下来要对cfunc和ocFunc进行hook。
首先要来的自然是大名鼎鼎的Jay Freeman(saurik)写的CydiaSubstrate,iOS7越狱之前名为 MobileSubstrate(简称为MS或MS框架)。
MobileHooker组件主要提供了MSHookMessageEx和MSHookFunction两个函数针对不同语言的inline hook功能,其中MSHookMessageEx负责用来hook Objective-C函数,MSHookFunction负责用来hook C/C++函数。
原理:MSHookMessageEx对于ObjC函数采用的也是method swizzle的方法,主要是Objetive-C的runtime机制,可以在ObjC方法时动态采用class_replaceMethod等runtime函数替换其实现。
void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old);
其中第一个参数_class为要Hook的Objective-C函数的类名;第二个参数message为要Hook的Objective-C函数的message;第三个参数hook为hook后新的对应该message的执行逻辑,即替换后的函数地址;第四个参数old为对应该message的原函数的地址,若无需调用原函数则该参数可以设为NULL。
对ocFunc进行hook的动态库代码如下:
@class ViewController;
static void (*origin_ViewController_ocFunc)(id self,SEL _cmd);
static void new_ViewController_ocFunc(id self,SEL _cmd){
NSLog(@"oc func hook");
origin_ViewController_ocFunc(self,_cmd);
}
static void __attribute__((constructor)) initialize(void) {
// 初始化方法里进行替换
MSHookMessageEx(objc_getClass("ViewController"), @selector(ocFunc), (IMP)&new_ViewController_ocFunc, (IMP *)&origin_ViewController_ocFunc);
}
可以成功hook
原理:MSHookFunction对于C函数是在函数的开头修改了汇编指令,使其跳转到新的实现,执行完成后再返回执行原指令。
void MSHookFunction(void *symbol, void *hook, void **old);
其中第一个参数为所要Hook的函数地址,值得注意的是该地址不一定限于函数头,也可以是函数内部的任一代码地址;第二个参数为Hook后要替换的函数地址;第三个参数为指向Hook地址的指针,用来保存被Hook函数替换掉的汇编指令方便执行完自己的代码逻辑后能够继续执行原函数的逻辑,若不需要调用原函数,则此处可以设为“NULL”。
话不多说,上代码,来hook我们自己写的C方法
static int (*orig_cfunc)(int x,int y,int z);
int new_cfunc(int x,int y,int z){
NSLog(@"c func hook");
return orig_cfunc(x,y,z);
}
static void __attribute__((constructor)) initialize(void) {
MSHookFunction(MSFindSymbol(NULL,"_cfunc"), (void *)&new_cfunc, (void **)&orig_cfunc);
}
注意这里在调用**MSFindSymbol
**时传入的是**'_cfunc'
**
另外注意:
以上是hook私有的c方法,当然也可以hook系统的方法,比如官网的示例
void *(*oldConnect)(int, const sockaddr *, socklen_t);
void *newConnect(
int socket, const sockaddr *address, socklen_t length) {
if (address->sa_family == AF_INET) {
sockaddr_in *address_in = address;
if (address_in->sin_port == htons(6667)) {
sockaddr_in copy = *address_in;
address_in->sin_port = htons(7001);
return oldConnect(socket, ©, length);
}
}
return oldConnect(socket, address, length);
}
MSHookFunction(&connect, &newConnect, &oldConnect);
fishhook是Facebook提供的一个动态修改链接mach-O文件的工具,可以用来hook C/C++方法。
这里也尝试对cfunc进行了hook,发现并未生效,代码如下:
static int (*orig_cfunc)(int x,int y,int z);
int new_cfunc(int x,int y,int z){
NSLog(@"c func hook");
return (*orig_cfunc)(x,y,z);
}
static void __attribute__((constructor)) initialize(void) {
rebind_symbols((struct rebinding[1]){{"cfunc", new_cfunc, (void *)&orig_cfunc}}, 1);
}
结合fishhook的原理,可以知道fishhook是不能hook私有C方法的。
但是hook系统的方法还是很好使的,代码如下:
static void (*orig_NSLog)(NSString *format, ...);
void(new_NSLog)(NSString *format, ...) {
va_list args;
if(format) {
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
orig_NSLog(@"hook:%@", message);
va_end(args);
}
}
static void __attribute__((constructor)) initialize(void) {
rebind_symbols((struct rebinding[1]){{"NSLog", new_NSLog, (void *)&orig_NSLog}}, 1);
}
method swizzle的原理主要是Objetive-C的runtime机制,可以在ObjC方法时动态采用class_replaceMethod等runtime函数替换其实现。
由于是基于runtime的,所以C/C++方法是不生效的,仅针对Objective-C方法有效,swift中不是基于OC的对象也不会生效。
话不多说,直接上一个hook viewWillAppear方法的例子
#import <objc/runtime.h>
@implementation UIViewController (Swizzle)
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// Class class = object_getClass((id)self);
NSLog(@"%@",class);
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(my_viewWillAppear:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
} });
}
-(void)my_viewWillAppear:(BOOL)animated
{
[self my_viewWillAppear:animated];
NSLog(@"%@",[self class]);
}
@end
目前就针对这三种进行了实验和梳理,后续如有遇到其它再进行补充,也欢迎了解其他框架的大佬进行补充和指正。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。