Logos语法,其实是CydiaSubstruct框架提供的一组宏定义。便于开发者使用宏进行HOOK操作。语法简单,功能强大且稳定。

基本使用

案例1:

搭建App项目

打开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

  1. > 编译项目,准备好`Demo.app`文件
  2. > 案例2
  3. > 找到将要`HOOK`的关键代码
  4. > 先使用`class-dump`工具,导出`App`的头文件,从中找到关键代码
  5. > `Demo.app``MachO`文件,拷贝到`class-dump`的同级目录
  6. > 使用`class-dump`指令,导出头文件
  7. >

./class-dump -H Demo -o ./header/

  1. > `ViewController.h`文件中,可以找到关键代码
  2. >

import

@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
  2. > 搭建`MokeyDev`项目,对`App`项目进行`HOOK`
  3. > 使用`MokeyDev`的好处:
  4. > - 重签名
  5. > - 代码注入
  6. > - 注入`HOOK`框架
  7. > `案例1`生成的`Demo.app`,拷贝到`MokeyDev`项目的`TargetApp`
  8. > `MokeyDemoDylib`中的`Logos`目录下,修改`.xm`文件的打开方式<br />
  9. ![](https://upload-images.jianshu.io/upload_images/9297953-56bf0661a6ff37bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  10. > 使用`Logos`语法,对`ViewController`中的`loginBtnClick:`方法进行`HOOK`
  11. > 打开`MokeyDemoDylib.xm`文件,写入以下代码:
  12. >

import

@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

  1. > 明确继承于`UIViewController`,否则`self`下的方法无法调用<br />
  2. ![](https://upload-images.jianshu.io/upload_images/9297953-d9660492c251760c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  3. > 使用`Logos`语法,和日常开发的逻辑很相似,并不需要加入`HOOK`相关的额外代码
  4. > 最终`.xm`文件中的`Logos`语法,会被转为`.mm`文件中的代码,然后参与编译<br />
  5. ![](https://upload-images.jianshu.io/upload_images/9297953-31449db95eaf439f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  6. #####语法学习
  7. > `Logos`指令分为三个等级:
  8. > - `Block level`:块等级
  9. > - `Top level`:顶部等级
  10. > - `Function level`:函数等级
  11. > #####Block level
  12. > 块等级,必须以`%end`结尾,并且不应存在于函数或方法中
  13. > `%group`
  14. >

%group Groupname

  1. > `%group`用于条件初始化,代码兼容性
  2. > `%group`不能在另一个`%group`块内,未分组的代码都在隐式`_ungrouped`组中
  3. > 如果没有其他分组,自动初始化`_ungrouped`
  4. > `%group`必须配合`%ctor`使用,如果存在多个分组,每个分组都必须使用`%init`指令,手动对其进行初始化
  5. > 示例:
  6. >

%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); } }

  1. > `%hook`
  2. >

%hook Classname

  1. > 为指定类定义一个`HOOK`块,可放在`%group`块内
  2. > 示例:
  3. >

%hook ViewController

  • (void)loginBtnClick:(id)arg1 { NSLog(@”🍺🍺🍺🍺🍺”); %orig; // 原始方法的调用 } %end ```

%new

  1. %new
  2. %new(signature)

HOOK的类添加新方法,signature是新方法的OC类型编码,如果省略,将生成一个

%new必须在%hook块内

示例:

  1. %hook ViewController
  2. %new
  3. - (void)my_NSLog {
  4. NSLog(@"🍺🍺🍺🍺🍺");
  5. }
  6. %end
  7. %end

%subclass

  1. %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]); }

  1. > `%property`
  2. >

%property (nonatomic|assign|retain|copy|weak|strong|getter|setter) Type name;

  1. > 在类中添加新的属性,可添加到`%subclass`创建的新类中,也可以添加到现有类中
  2. > 必须在`%subclass``%hook`块内
  3. > `%end`
  4. >

%end

  1. > 用于标记`group/hook/subclass`的块结束
  2. > #####Top level
  3. > 顶部等级,指令不应存在于`group/hook/subclass`块中
  4. > `%config`
  5. >

%config(Key=Value);

  1. > 设置`Logos`配置项<br />
  2. ![](https://upload-images.jianshu.io/upload_images/9297953-a8b9f50e3c301226.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  3. > `%hookf`
  4. >

%hookf(rtype, symbolName, args…) { … }

  1. > 对指定函数进行`HOOK`,如果函数名称作为字符串传递,则将动态查找该函数
  2. > 示例1
  3. >

// 原始函数的定义 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; // 原始方法的调用 }

  1. > 示例2
  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); … }

  1. >

%hookf(CFBooleanRef, “_MGGetBoolAnswer”, CFStringRef string) { if (CFEqual(string, CFSTR(“StarkCapability”))) { return kCFBooleanTrue; } return %orig; }

  1. > `%ctor`
  2. >

%ctor { … }

  1. > 生成匿名构造函数
  2. > `%dtor`
  3. >

%dtor { … }

  1. > 生成匿名析构函数
  2. > #####Function level
  3. > 函数等级,仅存在于函数或方法中
  4. > `%init`
  5. >

%init; %init([=, …]); %init(Group[, [+|-]=, …]);

  1. > 初始化分组或默认分组
  2. > 示例:
  3. >

%hook SomeClass -(id)init { return %orig; } %end

%ctor { %init(SomeClass=objc_getClass(“class with spaces in the name”)); }

  1. > `%c`
  2. >

%c([+|-]Class)

  1. > 传入实例对象名或类名
  2. > `%orig`
  3. >

%orig %orig(arg1, …)

  1. > 原始方法的调用,不能用于`%new`
  2. > `%log`
  3. >

%log; %log([(), …]); ```

输出日志

总结

Logos

  • Logos语法是CydiaSubstruct框架提供的一组宏定义
  • 详情可查看:官方文档

常用语法

  • %hook%end勾住某个类,在一个代码块中直接写需要勾住的方法
  • %group%end用于分组,配合%ctor构造函数使用,每个组都必须%init
  • %log输出方法的相信信息,包含调用者、方法名、参数
  • %orig调用原始方法,可传递参数,可接收返回值
  • %c类似getClass函数,获取一个类对象
  • %new添加某个方法

xm文件

  • xm代表支持OCC/C++语法
  • xm文件不参与执行
  • 编译该文件,需要导入头文件,以便编译通过