準備工作
- 新建一個Demo 萌焰,類似登錄頁面总放,少許邏輯呈宇,導(dǎo)出 app 包 和 頭文件,以待備用局雄。
- 新建一個 MonkeyDev 項目甥啄,導(dǎo)入要 Hook 的 APP 包。
Cydia Substrate
Cydia Substrate 原名為 Mobile Substrate 炬搭,它的主要作用是針對OC方法蜈漓、C函數(shù)以及函數(shù)地址進行HOOK操作。當然它并不是僅僅針對iOS而設(shè)計的宫盔,安卓一樣可以用疆柔。官方地址:http://www.cydiasubstrate.com/
Cydia Substrate主要由3部分組成:
-
MobileHooker
MobileHooker顧名思義用于HOOK损肛。它定義一系列的宏和函數(shù),底層調(diào)用objc的runtime和fishhook來替換系統(tǒng)或者目標應(yīng)用的函數(shù).
其中有兩個函數(shù):- MSHookMessageEx 主要作用于Objective-C方法
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
- MSHookFunction 主要作用于C和C++函數(shù)
void MSHookFunction(voidfunction,void* replacement,void** p_original)
Logos語法的%hook 就是對此函數(shù)做了一層封裝
Cydia Substrate 底層使用的就是 Method Swizzle 和 fishhook。
Cydia substrate 注入進去之后渠啊,就可以用 Logos 語法 來寫 Logos 了。
其中 _02_LoginHookDemoDylib.xm
x : 代表 這個文件支持 Logos 語法
如果后綴為 單獨的 x : 說明 源文件支持 Logos 的 C語言 語法余爆。
xm 支持 **Logos ** 的 C斋配,C++,OC 語法寄悯。
- 文件擴展名
.X 將由Logos處理萤衰,然后進行預(yù)處理并編譯為objective-c。
.xm 將由Logos處理猜旬,然后進行預(yù)處理并編譯為objective-c ++腻菇。
.xi 將首先作為objective-c進行預(yù)處理,然后Logos將處理結(jié)果昔馋,然后將進行編譯筹吐。
.xmi 將首先作為objective-c ++進行預(yù)處理,然后Logos將處理結(jié)果秘遏,然后將進行編譯丘薛。
xi或xmi文件可以在#define宏中使用Logos指令。
- %group
- constructor 構(gòu)造函數(shù):多組 group 就需要 創(chuàng)建 constructor邦危。
// constructor 構(gòu)造函數(shù)
%ctor{
%init(group1)%init(group2)
}
沒創(chuàng)建一個group 就需要 init洋侨,前后的初始化順序很重要舍扰,后面會把前面的 group 覆蓋掉。
可以判斷加載不同的group
- %init 初始化
%ctor{
NSString *version = [UIDevice currentDevice].systemVersion;
if(version.doubleValue >= 11.4){
%init(group1)
}else{
%init(group2)
}
}
默認的會隱式加載
%ctor{
%init(_ungrouped)
}
如果不顯示定義希坚,Theos會自動生成一 個%ctor, 并在其中調(diào)用%init( ungrouped).
%ctor 一般可以用來初始化%group,以及進行 MSHookFunction 等操作边苹。
%dtor 析構(gòu)函數(shù),與 %ctor 對應(yīng)
%log 輸出裁僧,直接調(diào)用
%log;
002-LoginHookDemo/002-LoginHookDemoDylib/Logos/_02_LoginHookDemoDylib.xm:11?[m ?[0;30;46mDEBUG:?[m -[<ViewController: 0x105612cb0> btnClick:<UIButton: 0x1057156c0; frame = (164 318; 31 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x1c4229c40>>]
- %orig
可以用來接收返回的值并修改个束,也可以傳入?yún)?shù)。
- %new 給某個類添加方法
%new 直接添加聊疲,不需要 %end 結(jié)束茬底,另外 一個 %new 對應(yīng)一個方法。
如:
%new
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}
%new
+ (void)class_Methiod
{
NSLog(@"這是一個類方法");
}
上圖可以看出 MonkeyDev 原理也是注入的 動態(tài)庫获洲,可以在內(nèi)部新建你想創(chuàng)建的任何文件阱表,都會被打包進 動態(tài)庫中。
- %c() 根據(jù)類名獲取一個類
如下贡珊,這樣調(diào)用一樣的效果:
// 調(diào)用類方法
// 1.
// [self.class class_Methiod];
// 2.
// [NSClassFromString(@"ViewController") class_Methiod];
// 3.
[%c(ViewController) class_Methiod];
Logos 小練習(xí)
需求:在微信首頁左側(cè)添加一個按鈕實現(xiàn)和右側(cè)一樣的功能最爬。
- 準備
創(chuàng)建一個 MonkeyDev 項目,重簽名 WeChat门岔,編譯導(dǎo)出 WeChat 頭文件爱致。
class-dump -H WeChat -o /Users/username/Desktop/WeChatHeaders6.7.1
- 運行,使用 Xcode 調(diào)試 固歪,使用 Debug View Hierarchy 查看視圖層次結(jié)構(gòu)蒜鸡,可以看到:
可以看出添加按鈕相應(yīng)方法是在 NewMainFrameRightTopMenuBtn 類的 showRightTopMenuBtn 方法;
首頁:NewMainFrameViewController
- 編寫 Hook 代碼
%hook NewMainFrameViewController
- (void)viewDidLoad {
%orig;
NSLog(@"\n\n -----viewDidLoad------\n");
// UIButton *leftBtn = [UIButton buttonWithType:UIButtonTypeContactAdd];
// [leftBtn addTarget:self action:@selector(leftClick) forControlEvents:UIControlEventTouchUpInside];
// self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftBtn];
}
- (void)viewDidAppear:(BOOL)animated
{
%orig;
NSLog(@"\n\n ------ viewDidAppear --------\n\n");
UIButton *leftBtn = [UIButton buttonWithType:UIButtonTypeContactAdd];
[leftBtn addTarget:self action:@selector(leftClick) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:leftBtn];
}
%new
- (void)leftClick
{
NSLog(@"自定義按鈕響應(yīng)了@紊选7攴馈!");
}
- (UIBarButtonItem *)navigationItem
{
NSLog(@"\n\n------GetNavigationItem------\n\n");
return %orig;
}
%orig 調(diào)用原來的方法蒲讯,相當于犯法交換忘朝,調(diào)用自己
添加的按鈕:
- 接著查找,右側(cè)按鈕調(diào)用的方法判帮,
鏈接 Cycript 調(diào)試局嘁,
小問題:
這個折磨了我許久,因為 WeChat 的首頁一直在連接中晦墙,Cycript 連接不上悦昵,重新?lián)Q一個手機就 OK了。
- 修改 Hook 代碼
通過上面可以看出: 右側(cè)的彈窗按鈕調(diào)用的方法就是
[self.navigationItem.rightBarButtonItem.view showRightTopMenuBtn]
// 從內(nèi)存中能查到調(diào)用該方法:[self.navigationItem.rightBarButtonItem.view showRightTopMenuBtn]
self:代表NewMainFrameViewController控制器
[self.navigationItem.rightBarButtonItem.view showRightTopMenuBtn]
效果如圖:
- 修改Navigationbar標題:
- (void)viewDidAppear:(BOOL)animated
{
%orig;
NSLog(@"\n\n ------ viewDidAppear --------\n\n");
// 修改Navbar標題
UILabel *titleLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
titleLb.backgroundColor = [UIColor orangeColor];
titleLb.text = @"??????Superman??????";
self.navigationItem.titleView = titleLb;
}
- 修改Tabbar標題
// 修改Tabbar標題
NSArray *titlesArr = @[@"No1-6",@"No2-6",@"No3-6",@"No4-6"];
NSArray<UITabBarItem *> *tabbarsArr = self.tabBarController.tabBar.items;
for (int i =0; i < tabbarsArr.count; i++) {
UITabBarItem *item = [tabbarsArr objectAtIndex:i];
[item setTitle:[titlesArr objectAtIndex:i]];
}
修改你想修改的晌畅,能修改的但指!