目錄
一厂汗、Logos 簡(jiǎn)介
Logos語(yǔ)法其實(shí)是CydiaSubsuct框架提供的一組宏定義业崖。便于開(kāi)發(fā)者使用宏進(jìn)行HOOK操作野芒。語(yǔ)法簡(jiǎn)單,功能強(qiáng)大且穩(wěn)定双炕。
http://iphonedevwiki.net/index.php/Logos
Logos 語(yǔ)法
Logos語(yǔ)法分為三大類:
Block level
這一類型的指令會(huì)開(kāi)辟一個(gè)代碼塊狞悲,以%end結(jié)束。
%group妇斤、%hook摇锋、% subclass 丹拯、 %end
Top level
這個(gè)TopLevel指令不放在BlockLevel中。
%config荸恕、%hookf乖酬、%ctor、%dtor
Function level
這一塊的指令就放在方法中融求。
%init咬像、%class、 %c生宛、 %orig县昂、%log
常用語(yǔ)法
HOOK 某個(gè)類里面的某個(gè)方法
%hook ClassName
// 對(duì)象方法
- (void)instanceMethod {
}
// 類方法
+ (void)classMethod {
}
%end
為某個(gè)類添加新方法
%hook ClassName
// 添加對(duì)象方法
%new
- (void)newInstanceMethod {
}
// 添加類方法
%new
+ (void)newClassMethod {
}
%end
-
%group
用來(lái)將代碼分組。開(kāi)發(fā)中hook代碼會(huì)很多陷舅,這樣方便管理Logos代碼倒彰。
%group group1
%hook ClassName
%end
%end
%ctor {
NSString *version = [UIDevice currentDevice].systemVersion;
if (version.doubleValue >= 14.0) {
%init(group1);
}
// 如果有g(shù)roup未在某種條件下初始化就會(huì)報(bào)錯(cuò)
}
%ctor(constructor)
構(gòu)造函數(shù),用于確定加載那個(gè)組莱睁。和%init結(jié)合用%init
用來(lái)初始化某個(gè)組待讳。%log;
能夠輸出日志!! 輸出方法調(diào)用的詳細(xì)信息
%orig(original)
這個(gè)就是保持原有的方法實(shí)現(xiàn),如果原來(lái)的方法有返回值缩赛,那么%orig 就有返回值的耙箍。%new
給某個(gè)類添加方法,在%hook 和 %end 中使用酥馍。
%new
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
-
%c(ClassName)
類似getClass函數(shù)辩昆,獲得一個(gè)類對(duì)象。一般用于調(diào)用類方法旨袒。
二汁针、Logos 使用
- 創(chuàng)建目標(biāo)項(xiàng)目
Demo
創(chuàng)建Hook項(xiàng)目
HookDemo
(MonkeyApp),并安裝到手機(jī)上將目標(biāo)項(xiàng)目
Demo
的IPA放到Hook項(xiàng)目HookDemo
中的TargetApp
文件夾中
重新運(yùn)行
HookDemo
即可重簽注入修改HookDemoDylib.xm type
關(guān)于xm文件:xm表示支持OC砚尽、C/C++語(yǔ)法
2.1 HOOK loginBtnClick:方法
修改HookDemoDylib.xm文件中的代碼
#import <UIKit/UIKit.h>
// 下面兩行代碼self 調(diào)用showViewController: sender:方法需要
@interface ViewController : UIViewController
@end
%hook ViewController
- (void)loginBtnClick:(id)sender {
UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"HOOK成功施无!" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"確定" style:(UIAlertActionStyleCancel) handler:nil];
[alertVC addAction:cancel];
[self showViewController:alertVC sender:nil];
}
%new
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
%end
2.2 將要HOOK的類的頭信息導(dǎo)入HookDemoDylib.xm文件
- dump出目標(biāo)項(xiàng)目
Demo
的頭文件
class-dump -H Demo -o DemoHeaders/
導(dǎo)入頭后就能直接使用原APP中的屬性和方法
三、使用 Logos 為 WeChat 設(shè)置界面添加Cell
- 完成后的界面如下:
3.1 找到需要修改的控制器
- 使用Debug View Hierarchy看到設(shè)置界面的控制器為:
NewSettingViewController
- 使用Cycript也能查看當(dāng)前的控制器
3.2 分析界面中的數(shù)據(jù)源由當(dāng)前控制器管理還是在其他類中管理
- dump出目標(biāo)項(xiàng)目WeChat的頭文件
class-dump -H WeChat -o WeChatHeaders/
- 將
WeChatHeaders
拖到Sublime Text
中打開(kāi)必孤,Command + Shift + F搜索NewSettingViewController
可以看到NewSettingViewController
中沒(méi)有tableView相關(guān)的數(shù)據(jù)源和代理方法:
因此我們要修改設(shè)置界面的cell數(shù)量就只能HOOK NewSettingViewController
中的WCTableViewManager
猾骡。因?yàn)樵O(shè)置界面的TableView的DataSource就是WCTableViewManager
。
cy# #0x1156ad000.dataSource
#"<WCTableViewManager: 0x280c44270>"
cy#
由于
WCTableViewManager
在多個(gè)控制器中使用敷搪,因此HOOKWCTableViewManager
時(shí)還需要判斷當(dāng)前的控制器(通過(guò)響應(yīng)鏈條
找到當(dāng)前控制器:
[tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)])
-
Sublime Text
中Command + Shift + F搜索WCTableViewManager :
兴想。可以看到TableView的數(shù)據(jù)源和代理方法均在這里實(shí)現(xiàn):
3.3 實(shí)現(xiàn)修改界面的代碼
- CycriptDemoDylib.xm中的代碼如下:
// See http://iphonedevwiki.net/index.php/Logos
#import <UIKit/UIKit.h>
#define CJDefaults [NSUserDefaults standardUserDefaults]
#define CJSWITCHKEY @"CJSWITCHKEY"
#define CJTIMEKEY @"CJTIMEKEY"
// 關(guān)于界面
@interface WCTableViewManager
- (long long)numberOfSectionsInTableView:(id)arg1;
@end
@interface NewSettingViewController:UIViewController
@end
%hook WCTableViewManager
%new
- (void)cjtextFieldDidChangeValue:(NSNotification *)notification {
UITextField *sender = (UITextField *)[notification object];
[CJDefaults setValue:sender.text forKey:CJTIMEKEY];
[CJDefaults synchronize];
}
%new
- (void)cjswitchChang:(UISwitch *)switchView {
[CJDefaults setBool:switchView.isOn forKey:CJSWITCHKEY];
[CJDefaults synchronize];
[MSHookIvar <UITableView *>(self,"_tableView") reloadData];
}
- (void)scrollViewWillBeginDragging:(id)arg1 {
%orig;
[MSHookIvar <UITableView *>(self,"_tableView") endEditing:YES];
}
//返回高度
- (double)tableView:(UITableView *)tableView heightForRowAtIndexPath:(id)indexPath {
//定位設(shè)置界面,并且是最后一組
if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]
&& [indexPath section] == [self numberOfSectionsInTableView:tableView]-1){
return 44;
}
return %orig;
}
//每一個(gè)Cell
- (id)tableView:(UITableView *)tableView cellForRowAtIndexPath:(id)indexPath {
//定位設(shè)置界面,并且是最后一組
if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]
&& [indexPath section] == [self numberOfSectionsInTableView:tableView]-1) {
UITableViewCell * cell = nil;
if ([indexPath row] == 0) {
static NSString *swCell = @"SWCELL";
cell = [tableView dequeueReusableCellWithIdentifier:swCell];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:nil];
}
cell.textLabel.text = @"自動(dòng)搶紅包";
//搶紅包開(kāi)關(guān)!!
UISwitch *switchView = [[UISwitch alloc] init];
switchView.on = [CJDefaults boolForKey:CJSWITCHKEY];
[switchView addTarget:self action:@selector(cjswitchChang:) forControlEvents:(UIControlEventValueChanged)];
cell.accessoryView = switchView;
cell.imageView.image = [UIImage imageNamed:([CJDefaults boolForKey:CJSWITCHKEY] == 1) ? @"unlocked" : @"locked"];
} else if([indexPath row] == 1) {
static NSString * waitCell = @"waitCell";
cell = [tableView dequeueReusableCellWithIdentifier:waitCell];
if(!cell){
cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:nil];
}
cell.textLabel.text = @"等待時(shí)間(秒)";
UITextField * textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 150, 40)];
//監(jiān)聽(tīng)鍵盤輸入
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cjtextFieldDidChangeValue:) name:UITextFieldTextDidChangeNotification object:textField];
textField.text = [CJDefaults valueForKey:CJTIMEKEY];
textField.borderStyle = UITextBorderStyleRoundedRect;
cell.accessoryView = textField;
cell.imageView.image = [UIImage imageNamed:@"clock"];
}
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
return %orig;
}
//每組多少行
- (long long)tableView:(UITableView *)tableView numberOfRowsInSection:(long long)section {
//定位設(shè)置界面,并且是最后一個(gè)
if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]
&& section == [self numberOfSectionsInTableView:tableView]-1) {
return 2;
}
return %orig;
}
//多少組
- (long long)numberOfSectionsInTableView:(UITableView *)tableView {
if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]) {// 定位設(shè)置界面
// 在原來(lái)基礎(chǔ)上多搞一組
return %orig+1;
}
return %orig;
}
%end
%hook NewSettingViewController
%new
-(void)cjkeyboardWillShow:(NSNotification *)note {
UIView * view = self.view;
CGRect keyBoardRect = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
view.frame = CGRectMake(0, -keyBoardRect.size.height, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height );
}
%new
-(void)cjkeyboardWillHide:(NSNotification *)note {
UIView *view = self.view;
view.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
}
- (void)viewDidLoad {
%orig;
//監(jiān)聽(tīng)textField彈出和消失
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(cjkeyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(cjkeyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
%end
注意一:logos中獲取對(duì)象的成員變量一般有如下三種方法:
- 可以導(dǎo)入頭文件中屬性和成員變量的定義
- KVC
- MSHookIvar :UITableView *tableView = MSHookIvar<UITableView *>(self,"_tableView");
注意二:所有
%new
新添加的方法一定要加上自己的前綴赡勘,避免意外覆蓋了原APP已有的方法
注意三:可以在
CycriptDemoDylib.xm
的方法中添加斷點(diǎn)進(jìn)行調(diào)試
注意四:引用的圖片的方法:將ipa包解壓后添加需要的圖片并打包即可
zip –ry 輸出文件 輸入文件 將輸入文件壓縮為輸出文件
參考:iOS 逆向開(kāi)發(fā)12:iOS 應(yīng)用重簽名