目錄
一、Logos 簡介
Logos語法其實是CydiaSubsuct框架提供的一組宏定義。便于開發者使用宏進行HOOK操作。語法簡單,功能強大且穩定。
http://iphonedevwiki.net/index.php/Logos
Logos 語法
Logos語法分為三大類:
Block level
這一類型的指令會開辟一個代碼塊,以%end結束。
%group、%hook、% subclass 、 %end
Top level
這個TopLevel指令不放在BlockLevel中。
%config、%hookf、%ctor、%dtor
Function level
這一塊的指令就放在方法中。
%init、%class、 %c、 %orig、%log
常用語法
HOOK 某個類里面的某個方法
%hook ClassName
// 對象方法
- (void)instanceMethod {
}
// 類方法
+ (void)classMethod {
}
%end
為某個類添加新方法
%hook ClassName
// 添加對象方法
%new
- (void)newInstanceMethod {
}
// 添加類方法
%new
+ (void)newClassMethod {
}
%end
-
%group
用來將代碼分組。開發中hook代碼會很多,這樣方便管理Logos代碼。
%group group1
%hook ClassName
%end
%end
%ctor {
NSString *version = [UIDevice currentDevice].systemVersion;
if (version.doubleValue >= 14.0) {
%init(group1);
}
// 如果有group未在某種條件下初始化就會報錯
}
%ctor(constructor)
構造函數,用于確定加載那個組。和%init結合用%init
用來初始化某個組。%log;
能夠輸出日志!! 輸出方法調用的詳細信息
%orig(original)
這個就是保持原有的方法實現,如果原來的方法有返回值,那么%orig 就有返回值的。%new
給某個類添加方法,在%hook 和 %end 中使用。
%new
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self.view endEditing:YES];
}
-
%c(ClassName)
類似getClass函數,獲得一個類對象。一般用于調用類方法。
二、Logos 使用
- 創建目標項目
Demo
創建Hook項目
HookDemo
(MonkeyApp),并安裝到手機上將目標項目
Demo
的IPA放到Hook項目HookDemo
中的TargetApp
文件夾中
重新運行
HookDemo
即可重簽注入修改HookDemoDylib.xm type
關于xm文件:xm表示支持OC、C/C++語法
2.1 HOOK loginBtnClick:方法
修改HookDemoDylib.xm文件中的代碼
#import <UIKit/UIKit.h>
// 下面兩行代碼self 調用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的類的頭信息導入HookDemoDylib.xm文件
- dump出目標項目
Demo
的頭文件
class-dump -H Demo -o DemoHeaders/
導入頭后就能直接使用原APP中的屬性和方法
三、使用 Logos 為 WeChat 設置界面添加Cell
- 完成后的界面如下:
3.1 找到需要修改的控制器
- 使用Debug View Hierarchy看到設置界面的控制器為:
NewSettingViewController
- 使用Cycript也能查看當前的控制器
3.2 分析界面中的數據源由當前控制器管理還是在其他類中管理
- dump出目標項目WeChat的頭文件
class-dump -H WeChat -o WeChatHeaders/
- 將
WeChatHeaders
拖到Sublime Text
中打開,Command + Shift + F搜索NewSettingViewController
可以看到NewSettingViewController
中沒有tableView相關的數據源和代理方法:
因此我們要修改設置界面的cell數量就只能HOOK NewSettingViewController
中的WCTableViewManager
。因為設置界面的TableView的DataSource就是WCTableViewManager
。
cy# #0x1156ad000.dataSource
#"<WCTableViewManager: 0x280c44270>"
cy#
由于
WCTableViewManager
在多個控制器中使用,因此HOOKWCTableViewManager
時還需要判斷當前的控制器(通過響應鏈條
找到當前控制器:
[tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)])
-
Sublime Text
中Command + Shift + F搜索WCTableViewManager :
。可以看到TableView的數據源和代理方法均在這里實現:
3.3 實現修改界面的代碼
- CycriptDemoDylib.xm中的代碼如下:
// See http://iphonedevwiki.net/index.php/Logos
#import <UIKit/UIKit.h>
#define CJDefaults [NSUserDefaults standardUserDefaults]
#define CJSWITCHKEY @"CJSWITCHKEY"
#define CJTIMEKEY @"CJTIMEKEY"
// 關于界面
@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 {
//定位設置界面,并且是最后一組
if([tableView.nextResponder.nextResponder isKindOfClass:%c(NewSettingViewController)]
&& [indexPath section] == [self numberOfSectionsInTableView:tableView]-1){
return 44;
}
return %orig;
}
//每一個Cell
- (id)tableView:(UITableView *)tableView cellForRowAtIndexPath:(id)indexPath {
//定位設置界面,并且是最后一組
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 = @"自動搶紅包";
//搶紅包開關!!
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 = @"等待時間(秒)";
UITextField * textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 150, 40)];
//監聽鍵盤輸入
[[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 {
//定位設置界面,并且是最后一個
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)]) {// 定位設置界面
// 在原來基礎上多搞一組
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;
//監聽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中獲取對象的成員變量一般有如下三種方法:
- 可以導入頭文件中屬性和成員變量的定義
- KVC
- MSHookIvar :UITableView *tableView = MSHookIvar<UITableView *>(self,"_tableView");
注意二:所有
%new
新添加的方法一定要加上自己的前綴,避免意外覆蓋了原APP已有的方法
注意三:可以在
CycriptDemoDylib.xm
的方法中添加斷點進行調試
注意四:引用的圖片的方法:將ipa包解壓后添加需要的圖片并打包即可
zip –ry 輸出文件 輸入文件 將輸入文件壓縮為輸出文件
參考:iOS 逆向開發12:iOS 應用重簽名