IOS 之FishHook原理及例子
一、HOOK概述
? ? HOOK,中文為“鉤子”或“掛鉤”,在ios逆向中是指改變程序的運行流程的一種技術,通過hook可以讓別人的程序執行自己的代碼邏輯,在逆向中經常使用。所以就來看看HOOK的原理吧!
上圖很常見的微信搶紅包,hook原理就相當于程序本來收到紅包消息用戶應該點擊紅包之后點擊“搶”,才能領紅包,而通過HOOK既可以執行自己的代碼,用戶不需要點擊自動執行搶紅包代碼,這就是HOOK常見的應用。
1.ios中HOOK技術的幾種方式?
1、Method Swizzle
? ? 利用OC的Runtime特性,動態改變SEL(方法編號)和IMP(方法的實現)的對應關系,達到OC方法調用流程改變的目的,主要用在OC方法中。
2、fishhook
? ? 他是facebook提供的一個動態修改鏈接mach-o文件的工具。利用MachO文件加載原理,通過修改懶加載和非懶加載兩個表指針達到C函數HOOK的目的。
3、Cydia Substrate
? ? Cydia Substrate 原名為 Mobile Substrate,主要針對OC方法、C函數以及函數地址進行hook操作,他可以用于iOS,也可用于android 。官網:www.cydiasubstrate.com
?
Method Swizzle :
? ? 在OC中,SEL和IMP的關系好比標題和頁碼、接口和實現之間的關系,一個是標題,一個是對應的實現。Runtime提供了交換兩個SEL和IMP對應關系的函數:method_exchangeImplememtations(Method m1,Method m2) 這個函數交換兩個SEL和IMP之間的關系的技術,叫Method Swizzle(方法欺騙)。
例子:
//
// ViewController.m
// Demo
//
// Created by yrl on 2019/7/26.
// Copyright ? 2019 apple. All rights reserved.
//#import "ViewController.h"
#import <objc/runtime.h>@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];NSURL *url = [NSURL URLWithString:@"https://study.163.com/courses-search?keyword=邏輯教育"];//出現中文應進行編碼轉換,否則會為空NSLog(@"%@",url);}@end
運行結果:?
?
?我們可以用stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]轉碼
利用OC的Runtime進行方法交換,例子:
//
// NSURL+HK_URL.m
// Demo
//
// Created by yrl on 2019/7/26.
// Copyright ? 2019 apple. All rights reserved.
//#import "NSURL+HK_URL.h"
#import <objc/runtime.h>@implementation NSURL (HK_URL)
+(void)load{//類方法Method URLWithStr = class_getClassMethod(self, @selector(URLWithString:));//系統的Method HK_URLWithStr = class_getClassMethod(self, @selector(HK_URLWithString:));//自己的//changemethod_exchangeImplementations(URLWithStr, HK_URLWithStr);
}+(nullable instancetype)HK_URLWithString:(NSString *)URLString{NSURL * url = [NSURL HK_URLWithString:URLString];//應用HK_URLWithString系,URLWithString已經交換了,會造成死遞歸統if(url == nil){//如果為空則進行編碼轉換URLString = [URLString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];}url = [NSURL HK_URLWithString:URLString];//將URLString賦值給UK_URLWithStringreturn url;
}@end
實現了HOOK 了NSURL類的URLWithString方法,在類方法load中交換了系統的URLWithString和自己定義的HK_URLWithString,在調用URLWithString時會調用HK_URLWithString,實現HOOK,將中文編碼,再運行程序:
可看到中文已經被編碼了,說明我們已經改變了程序的執行流程。?
Cydia Substrate
很強大的一個框架?
1.MoblieHooker
? ? 他有一系列的宏和函數,底層調用的objc的runtime和fishhook來替換系統或者目標應用,其中有兩個函數:
MSHookMessageEx 主要用于Objective-C方法 void MSHookMessageEx(Class class ,SEL selector ,IMP replacement ,IMP result)第一個參數是哪個類,第二個參數是哪個編號(方法),第三個新方法的實現,第四個參數舊方法的實現
MSHookFunction 主要用于C/C++函數 void MSHookFunction(voidfunction, void * replacement, void ** p_original) Logos語法的%HOOK就是對這個函數做了一層封裝。
2.MobileLoader
? ? 用于加載第三方dylib在運行的應用程序中。啟動MobileLoader會根據規則把指定的第三方動態庫加載進去,第三方動態庫也就是我們寫的破解程序。
3.Safe Mode
? ?破解程序本質是dylib,寄生在別人的進程里,系統進程一旦出錯,可能導致整個系統崩潰,Cydia Substrate提供安全模式,在安全模式下,所有的第三方dylib都會被禁止,便查錯與修復。
FishHook
? ? 不是一個框架,是一個工具,工具代碼可以在github上下載:https:/github.com/facebook/fishhook.git,勾不住自己定義的函數,只能勾系統函數。
其中只有一個.c和.h文件,代碼不長,有興趣可以看看源碼,代碼有很多關于MachO文件的東西,先了解一下MachO的API
fishhook.h就倆函數一個結構體:
例子:點擊屏幕觸發NSLog函數轉到myNSLog
將fishhook.c? ?fishhokk.h拖進項目
//
// ViewController.m
// fishhook
//
// Created by yrl on 2019/7/26.
// Copyright ? 2019 apple. All rights reserved.
//#import "ViewController.h"
#import "fishhook.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];NSLog(@"123");//NSLog屬于懶加載,調用才能看到//實現交換//定義rebinding結構體struct rebinding nslog;nslog.name = "NSLog";nslog.replacement = myNSLog;nslog.replaced = (void **)&sys_nslog;//定義結構體數組struct rebinding rebs[1] = {nslog};/* 用來重新綁定符號* arg1 存放rebinding結構體的數組* arg2 數組長度*/rebind_symbols(rebs, 1);//兩個參數 結構體數組、數組長度printf("修改完畢!!");
}
//-------------更改系統NSLOG函數-------------
//函數指針,用來保存原來的函數地址
static void(*sys_nslog)(NSString *format,...);
//定義一個新的函數
void myNSLog(NSString *format, ...){format = [format stringByAppendingString:@"\n勾上了!!"];sys_nslog(format);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{//屏幕觸發事件NSLog(@"點擊屏幕!!");
}@end
運行函數,點擊屏幕:
二、FishHook的原理探究
? ? 仿照上面的工程再寫一個代碼,這回我們用來勾自己定義的newFunc()函數,當點擊屏幕時,func(str)應該被newFunc HOOK,轉而執行newFunc函數邏輯:
//
// ViewController.m
// fishhook2
//
// Created by yrl on 2019/7/26.
// Copyright ? 2019 apple. All rights reserved.
//#import "ViewController.h"
#import "fishhook.h"
@interface ViewController ()@end@implementation ViewControllervoid func(const char *str){NSLog(@"%s",str);
}- (void)viewDidLoad {[super viewDidLoad];//交換rebind_symbols((struct rebinding[1]){{"func",newRunc,(void **)&funcP}}, 1);}
//原始指針
static void (*funcP)(const char * str);
//新方法
void newRunc(const char * str){NSLog(@"勾住了");funcP(str);
}-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{func("hello");
}@end
運行后發現并沒有按照預想的來:
FishHook原理:
git上有介紹(英文):
dyld?binds lazy and non-lazy symbols by updating pointers in particular sections of the?__DATA?segment of a Mach-O binary.?fishhook?re-binds these symbols by determining the locations to update for each of the symbol names passed to?rebind_symbols?and then writing out the corresponding replacements.
For a given image, the?__DATA?segment may contain two sections that are relevant for dynamic symbol bindings:?__nl_symbol_ptr?and?__la_symbol_ptr.?__nl_symbol_ptr?is an array of pointers to non-lazily bound data (these are bound at the time a library is loaded) and?__la_symbol_ptr?is an array of pointers to imported functions that is generally filled by a routine called?dyld_stub_binder?during the first call to that symbol (it's also possible to tell?dyld?to bind these at launch). In order to find the name of the symbol that corresponds to a particular location in one of these sections, we have to jump through several layers of indirection. For the two relevant sections, the section headers (struct sections from?<mach-o/loader.h>) provide an offset (in the?reserved1?field) into what is known as the indirect symbol table. The indirect symbol table, which is located in the?__LINKEDIT?segment of the binary, is just an array of indexes into the symbol table (also in?__LINKEDIT) whose order is identical to that of the pointers in the non-lazy and lazy symbol sections. So, given?struct section nl_symbol_ptr, the corresponding index in the symbol table of the first address in that section is?indirect_symbol_table[nl_symbol_ptr->reserved1]. The symbol table itself is an array of?struct nlists (see?<mach-o/nlist.h>), and each?nlist?contains an index into the string table in?__LINKEDITwhich where the actual symbol names are stored. So, for each pointer?__nl_symbol_ptr?and?__la_symbol_ptr, we are able to find the corresponding symbol and then the corresponding string to compare against the requested symbol names, and if there is a match, we replace the pointer in the section with the replacement.
可執行文件MachO怎么進入內存的?是通過DYLD(動態鏈接器)加載進內存的,通過lldb(調試工具)的image list可以看到加載MachO文件的同時還加載了哪些庫,期間涉及到了ASLR(地址空間布局隨機化),每次加載的內存地址的偏移都不一樣。
拿上面的例子說,自己定義的func函數和NSLog函數在內存中的地址不一樣,位置也不一樣,func在MachO文件中,NSLog在系統動態庫(dylib共享的動態鏈接庫)中,那么func里面調用NSLog要找到NSLog的地址,MachO文件怎么找?怎么知道的?在程序啟動之前都不知道,不同手機的NSLog地址也是不一樣的,這就是DYLD的工作了,如圖:
所有的可執行文件都是由它加載的,dyld加載了MachO,一旦加載了MachO文件,蘋果采用了一種技術叫PIC(位置獨立代碼),當程序MachO調用其外部的函數的時候,比如說NSLog,他就會在DATA段創建一個指針,八個字節,用來放外部函數的地址,在調用之前這里是什么是不知道的,當func調用系統動態庫中NSLog時就通過dyld鏈接dylib找到NSLog函數的真實地址,寫入到DATA段,dyld就會綁定一個MachO里面的函數(符號),也就是寫到data段里的符號,所以這就是為什么fishhook的交換函數叫rebind_sybomls(綁定符號),也就是說這個函數只適用于系統的動態連接庫中的函數(系統C級別函數),因為只有系統函數在MachO調用的時候會有符號寫入data段,所以這就是為什么去HOOK自己定義的函數不起作用,因為自己定義的函數MachO運行的時候不需要去通過dyld獲取函數地址,自然就HOOK不到。
具體驗證:上一個fishhook例子
? ? 將例子build一下,提取fishhook.app里面的可執行文件,用MachOview打開,看看其二進制文件格式:我們就看DATA段
可以看到NSLog的地址0x100003028(現在這個值沒用,運行的時候dyld才給它賦值) 偏移0x3028,運行時保存NSLog的真實地址,而這個地址在MachO文件偏移的0x3028處,我們找一下,調試在此處下斷點:
通過lldb查看 輸入image list
我們查看NSLog文件地址(MachO文件地址記得后面加偏移才是NSLog地址):x 0x10b729000+0x3028
這個是NSLog地址(小端序)0x010bac20de,查看此處的匯編代碼:dis -s?0x010bac20de
往下走一步ni 再查看原來NSLog的地址:
?此處變為myNSLog了,可見,hook成功。
通過符號找字符
在MachO文件找字符,在Lazy Symbol Pointers表中NSLog在第一位,與之對應的有一個表Dynamic Symbol Table下的Indirect Symbols表
對比一下兩個表一一對應,Indirect Symbols指向了下一個Symbols表的第0x79個表項:
?
?他中的數據指向了String Table 的index下標:
?位置在0x4f1c+0x9b =0x4fb7,找到這個位置:
對應的就是NSLog的字符,每一個字符都是以“_”以“.”結束。
這個過程就是開頭那段英文所說的意思,官方給的圖:
Lazy Symbol和Indirect Symbol Table對應,Lazy Symbol的0x1061處的表項指向了?Indirect Symbol Table的對應0x1061處的表項,而Indirect Symbol Table0x1061處指向的是Symbol Table的16343處,Symbol Table的16343處指向的是String Table的70026處的字符,可以看到字符為"_close.",他用的是close()函數,而我們用的是NSLog函數。
后記:
? ? MachO文件很重要,在做防護,檢測app是否被修改,被注入,是否在越獄環境中,都需要不斷檢測MachO文件的字段是否被修改,如果MachO被修改說明app被修改了,那么app就會做出相應的防護措施,比如微信掃臉支付等功能禁用等,所以我們要了解MachO文件。
總結
以上是生活随笔為你收集整理的IOS 之FishHook原理及例子的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线段树+树状数组
- 下一篇: 201409-5 拼图