1,fishhook的执行原理
C语言函数通常是静态的,编译之后,从汇编代码变成了内存地址。iOS系统实现了一个动态缓存库技术,一些公共的系统库放进内存中的某个地方,当某个iOS项目启动后,machO文件会在Data段创建一个指针,dyld动态将machO中Data段中这个指针指向外部函数,这里的指针指向内部函数的调用,指向外部函数的地址,而这个指针也就是我们通常说的符号;这也是为什么fishhook中函数名为rebind_symbols(重新绑定符号),实际上是修改这个指针指向外部函数的地址,这也就是为什么修改不了内部函数和自定义函数,只能修改machO外部函数(在符号表中能找到的函数)。由于苹果实现了ASLR技术(不了解ASLR,看这篇逆向学习笔记8——ASLR),所以这些动态缓存库函数在APP项目的内存地址不确定,每次启动APP的时候都会有相应的变化,这是C语言的动态表现。
下载fishhook
-
1.1交换方法
准备如下代码
#import "ViewController.h"
#import "fishhook.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"123");//之所以这里打印下,因为NSLog是懒加载列表,如果不添加,内存中无法找到NSLog内存地址
struct rebinding nslogBind;
nslogBind.name = "NSLog";
nslogBind.replacement = myNSLog;
nslogBind.replaced = (void *)&old_nslog;
struct rebinding rebs[] = {nslogBind};
rebind_symbols(rebs, 1);
}
static void (*old_nslog)(NSString *format, ...);
void myNSLog(NSString *format, ...){
NSString * str = [NSString stringWithFormat:@"%@\n😆哈哈😆",format];
old_nslog(str);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"hello fish hook");
}
先运行一下,点击屏幕,打印结果如下:
2018-05-15 16:44:01.289126+0800 FishHook[5266:1450255] hello fish hook
😆哈哈😆
很明显,根据结果可以知道,fishhook交换方法成功了。
-
1.2验证fishhook绑定
接下来,我们探索下fishHook的流程,在rebind_symbols(rebs, 1);
加一个断点。从machO的懒加载列表中拿出NSLog
的地址偏移值,如下图
然后通过lldb拿到
ASLR
,通过下面操作,拿到内存中的NSLog函数。断点单步走,过掉rebind_symbols(rebs, 1);
,也就是让fishHook交换成功后,再重新打印,地址内存偏移0x8018
的值,如下图
根据结果,很明显看出,fishhook确实修改了符号绑定。
-
1.3验证fishhook无法修改自定义函数
代码如下
- (void)viewDidLoad {
[super viewDidLoad];
struct rebinding rebing;
rebing.name= "func";
rebing.replacement = newFunc;
rebing.replaced = (void *)&newFuncP;
struct rebinding rebs[] = {rebing};
rebind_symbols(rebs, 1);
}
static void (*newFuncP)(void);
void newFunc(){
NSLog(@"这是新的newFunc 函数");
}
void func(){
NSLog(@"这是自定义func 函数");
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
func();
}
点击屏幕,打印结果如下:
2018-05-16 10:08:09.350558+0800 FishHook[6722:1744594] 这是自定义func 函数
很明显,这里方法没有替换成功。也说明fishhook无法修改自定义函数。
2,利用fishhook防护
-
2.1 防护代码
新建一个antiHookDemo,并在target添加一个xxxFramework,在xxxFramework导入fishhook
,创建一个HookManager类,添加如下代码
+(void)load{
Method oldMethod = class_getInstanceMethod(objc_getClass("ViewController"), @selector(clickLeftBtn:));
Method newMethod = class_getInstanceMethod(self, @selector(clickLeftBtn:));
method_exchangeImplementations(newMethod, oldMethod);
struct rebinding exchangebinding;
exchangebinding.name = "method_exchangeImplementations";
exchangebinding.replacement = myExchange;
exchangebinding.replaced = (void *)&exchangeP;
struct rebinding getIMPbinding;
getIMPbinding.name = "method_getImplementation";
getIMPbinding.replacement = myExchange;
getIMPbinding.replaced = (void *)&getIMP;
struct rebinding setIMPbinding;
setIMPbinding.name = "method_setImplementation";
setIMPbinding.replacement = myExchange;
setIMPbinding.replaced = (void *)&setIMP;
struct rebinding rebs[] = {exchangebinding,getIMPbinding,setIMPbinding};
rebind_symbols(rebs, 3);
}
static void (*exchangeP)(Method _Nonnull m1, Method _Nonnull m2);
IMP _Nonnull (*setIMP)(Method _Nonnull m, IMP _Nonnull imp);
IMP _Nonnull (*getIMP)(Method _Nonnull m);
void myExchange(Method _Nonnull m1, Method _Nonnull m2){
NSLog(@"外部hook 不给过");
//这里可以做些让程序崩溃的事情
exit(0);//闪退,只要发现你hook我,我就闪退
}
-(void)clickLeftBtn:(id)sender{
NSLog(@"原始hook打印");
}
viewController中代码如下
- (IBAction)clickLeftBtn:(id)sender {
NSLog(@"点击按钮1");
}
- (IBAction)clickRightBtn:(id)sender {
NSLog(@"点击按钮2");
}
-
2.2Hook代码
新建一个hookDemo,拿到刚刚创建的antiHookDemo的app,创建一个xxframework,添加如下代码
+(void)load
{
Method oldMethod = class_getInstanceMethod(objc_getClass("ViewController"), @selector(clickRightBtn:));
Method newMethod = class_getInstanceMethod(self, @selector(hookMethod));
method_exchangeImplementations(oldMethod, newMethod);
}
-(void)hookMethod{
NSLog(@"我hook到你了");
}
利用重签和代码注入方法直接运行到真机,程序闪退,打印结果为
2018-05-16 10:28:09.350558+0800 hookDemo[6722:1744594] 外部hook 不给过
从打印结果出来和程序闪退可以看出防护成功了😁。
(不了解重签的童鞋,逆向学习笔记9——代码重签名和逆向学习笔记10——代码注入)