前言
logos
语法我们其实并不陌生,在之前的文章中早已经使用过了,例如15-Hook原理(二)反Hook防护 & MokeyDev
文章末尾,我们hook了ViewController
中的btnClick1
方法,使用的就是logos
语法。
logos
语法,其实是CydiaSubstruct
框架提供的一组宏定义
。便于开发者使用宏进行HOOK
操作。语法简单,功能强大且稳定。
一、基本使用
我们通过一个简单的案例演示一下,如何使用logos
语法进行Hook。
1.1 搭建Demo App
- 首先,创建一个App,命名为
Demo
。 - 打开
ViewController.m
,写入一下代码👇
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *uid;
@property (weak, nonatomic) IBOutlet UITextField *pwd;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction)loginBtnClick:(id)sender {
[self postUID:self.uid.text PWD:self.pwd.text];
}
-(void)postUID:(NSString *)uid PWD:(NSString *)pwd{
if ([uid isEqualToString:@"Zang"] && [pwd isEqualToString:@"123456"]) {
UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"登录成功" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleCancel) handler:nil];
[alertVC addAction:cancel];
[self showViewController:alertVC sender:nil];
}else{
UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"登录失败" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleCancel) handler:nil];
[alertVC addAction:cancel];
[self showViewController:alertVC sender:nil];
}
}
@end
- 编译项目,生成Demo.app文件。
1.2 dump Demo
我们将Demo
视作要Hook
的App,那么接下来就是找到要Hook
的对象。
- 先使用
class-dump
工具,导出App的头文件,从中找到关键代码。
- 将Demo.app的MachO文件,拷贝到class-dump的同级目录,使用
class-dump
指令,导出头文件👇
./class-dump -H Demo -o ./header/
- 在
ViewController.h
文件中,就可以找到关键代码👇
#import <UIKit/UIViewController.h>
@class UITextField;
@interface ViewController : UIViewController
{
UITextField *_uid;
UITextField *_pwd;
}
- (void).cxx_destruct;
@property(nonatomic) __weak UITextField *pwd; // @synthesize pwd=_pwd;
@property(nonatomic) __weak UITextField *uid; // @synthesize uid=_uid;
- (void)postUID:(id)arg1 PWD:(id)arg2;
- (void)loginBtnClick:(id)arg1;
- (void)viewDidLoad;
@end
1.3 对Demo App项目进行HOOK
接下来就是搭建MokeyDev
项目了,用来对Demo App
进行Hook。
- 将
Demo.app
拷贝到MokeyDev
项目的TargetApp
文件夹中 - 在MokeyDemoDylib中的Logos目录下,修改.xm文件的打开方式
- 使用
logos语法
,对ViewController
中的loginBtnClick:
方法进行hook
直接在MokeyDemoDylib.xm
文件,写入以下代码👇
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
%hook ViewController
- (void)loginBtnClick:(id)arg1 {
UIAlertController * alertVC = [UIAlertController alertControllerWithTitle:@"🍺🍺🍺🍺🍺" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleCancel) handler:nil];
[alertVC addAction:cancel];
[self showViewController:alertVC sender:nil];
}
%end
⚠️注意:
@interface ViewController : UIViewController
一定要声明,否则会报错👇
使用logos
语法,和日常开发的逻辑很相似,并不需要加入hook
相关的额外代码。最终.xm
文件中的logos
语法,会被转为.mm
文件中的代码,然后参与编译👇
二、logos语法
logos
指令分为三个等级👇
-
Block level
:块等级 -
Top level
:顶部等级 -
Function level
:函数等级
2.1 Block level
块等级,必须以%end
结尾,并且不应
存在于函数或方法
中。
%group
%group Groupname
-
%group
用于条件初始化,代码兼容性 -
%group
不能在另一个%group
块内,未分组的代码都在隐式_ungrouped
组中 - 如果没有其他分组,自动初始化
_ungrouped
组 -
%group
必须配合%ctor
使用,如果存在多个分组,每个分组都必须使用%init
指令,手动对其进行初始化
使用示例👇
%group iOS8
%hook IOS8_SPECIFIC_CLASS
// your code here
%end // end hook
%end // end group ios8
%group iOS9
%hook IOS9_SPECIFIC_CLASS
// your code here
%end // end hook
%end // end group ios9
%ctor {
if (kCFCoreFoundationVersionNumber > 1200) {
%init(iOS9);
} else {
%init(iOS8);
}
}
%hook
%hook Classname
为指定类
定义一个HOOK
块,可放在%group
块内
使用示例👇
%hook ViewController
- (void)loginBtnClick:(id)arg1 {
NSLog(@"🍺🍺🍺🍺🍺");
%orig; // 原始方法的调用
}
%end
%new
%new
%new(signature)
给HOOK
的类添加新方法
,signature
是新方法的OC类型
编码,如果省略,将生成一个
⚠️注意:
%new
必须在%hook
块内!
使用示例👇
%hook ViewController
%new
- (void)my_NSLog {
NSLog(@"🍺🍺🍺🍺🍺");
}
%end
%end
%subclass
%subclass Classname: Superclass <Protocol list>
- 创建
子类
,不支持ivars
。使用%new
添加新方法,实例化新类的对象,必须使用%c
操作符。 - 可以写在
%group
块内
使用示例👇
%subclass MyObject : NSObject
- (id)init {
self = %orig;
[self setSomeValue:@"value"];
return self;
}
//the following two new methods act as `@property (nonatomic, retain) id someValue;`
%new
- (id)someValue {
return objc_getAssociatedObject(self, @selector(someValue));
}
%new
- (void)setSomeValue:(id)value {
objc_setAssociatedObject(self, @selector(someValue), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
%end
%ctor {
MyObject *myObject = [[%c(MyObject) alloc] init];
NSLog(@"myObject: %@", [myObject someValue]);
}
%property
%property (nonatomic|assign|retain|copy|weak|strong|getter|setter) Type name;
- 在类中
添加新的属性
,可添加到%subclass
创建的新类
中,也可以添加到现有类
中 - 必须在
%subclass
或%hook
块内
%end
%end
用于标记group/hook/subclass
的块结束
。
2.2 Top level
顶部等级,指令不应
存在于group/hook/subclass
块中。
%config
%config(Key=Value);
设置Logos配置项
。有以下配置选项👇
%hookf
%hookf(rtype, symbolName, args...) { … }
对指定函数
进行HOOK
,如果函数名称作为字符串
传递,则将动态查找
该函数。
- 示例1
// 原始函数的定义
FILE *fopen(const char *path, const char *mode);
// 使用以下方式HOOK
%hookf(FILE *, fopen, const char *path, const char *mode) {
NSLog(@"Hey, we're hooking fopen to deny relative paths!");
if (path[0] != '/') {
return NULL;
}
return %orig; // 原始方法的调用
}
- 示例2
CFBooleanRef (*orig_MGGetBoolAnswer)(CFStringRef);
CFBooleanRef fixed_MGGetBoolAnswer(CFStringRef string)
{
if (CFEqual(string, CFSTR("StarkCapability"))) {
return kCFBooleanTrue;
}
return orig_MGGetBoolAnswer(string);
}
%ctor {
MSHookFunction(((void *)MSFindSymbol(NULL, "_MGGetBoolAnswer")), (void *)fixed_MGGetBoolAnswer, (void **)&orig_MGGetBoolAnswer);
...
}
%hookf(CFBooleanRef, "_MGGetBoolAnswer", CFStringRef string)
{
if (CFEqual(string, CFSTR("StarkCapability"))) {
return kCFBooleanTrue;
}
return %orig;
}
%ctor
%ctor { … }
生成匿名构造函数
。
%dtor
%dtor { … }
生成匿名析构函数
。
2.3 Function level
函数
等级,仅存在于函数或方法中
。
%init
%init;
%init([<class>=<expr>, …]);
%init(Group[, [+|-]<class>=<expr>, …]);
初始化分组
或默认分组
。
例如👇
%hook SomeClass
-(id)init {
return %orig;
}
%end
%ctor {
%init(SomeClass=objc_getClass("class with spaces in the name"));
}
%c
%c([+|-]Class)
传入实例对象名
或类名
。
%orig
%orig
%orig(arg1, …)
原始方法的调用,不能
用于%new
。
%log
%log;
%log([(<type>)<expr>, …]);
输出日志。
总结
-
Logos
-
Logos
语法是CydiaSubstruct
框架提供的一组宏定义 - 详情可查看:官方文档
-
-
语法
-
%hook
,%end
勾住某个类,在一个代码块中直接写需要勾住的方法 -
%group
,%end
用于分组,配合%ctor
构造函数使用,每个组都必须%init
-
%log
输出方法的相信信息,包含调用者、方法名、参数 -
%orig
调用原始方法,可传递参数,可接收返回值 -
%c
类似getClass
函数,获取一个类对象 -
%new
添加某个方法
-
-
xm
文件-
xm
代表支持OC
、C/C++
语法 -
xm
文件不参与执行 - 编译该文件,需要导入头文件,以便编译通过
-
网友评论