使用 Cycript
的时候,安装了 Monkey Dev
, 用 Monkey Dev
新建的工程后,文件夹 xxxDylib
下有一个 Logos
的文件夹,该文件夹下有一个后缀为 .xm
的文件,.xm
文件中所使用的就是 Logos
语法。
如果点击 .xm
文件无法显示其中代码,Xcode
做如下更改即可。
1、什么是 Logos?
Logos
是一种语法,该语法其实是CydiaSubstruct
框架提供的一组宏定义。便于开发者使用宏进行 HOOK
操作。语法简单,功能强大且稳定。
2、Logos 语法说明
Logos的官网。
文件后缀名为 .xm
代表支持 Logos
语法、C
和 C++
,如果后缀名为 .x
代表支持 Logos
语法和 C
。
1、%hook
你要 Hook
哪个类。 %hook
后面接类名:%hook ViewController
以 %end
结束,中间放的是需要 Hook
的方法
%hook ViewController
- (void)hookMethod {
}
%end
2、%group
用于分组,比如在不同 iOS
系统版本上使用不同分组的代码,兼容性就会更好。
%group group1
%hook ViewController
- (void)hookMethod {
NSLog(@"第一组Hook");
}
%end
%end
%group group2
%hook ViewController
- (void)hookMethod {
NSLog(@"第二组Hook");
}
%end
%end
3、%init
初始化一个组。
%init(group1)
%init(group2)
4、%ctor
构造函数,如果有上述分组可以在这个方法里面去判断。
%ctor{
NSString *version = [UIDevice currentDevice].systemVersion;
if (version.floatValue >= 8.0) {
%init(group1)
} else {
%init(group2)
}
}
5、%dtor
析构函数。
%dtor {
//释放一些东西...
}
6、%log
输出方法的一些详细信息,就类似于 LLDB
的 frame variable
。
7、%orig
Hook
到一个方法后,保持原来的方法的调用,如果该方法有返回值,也能接收返回值。
8、%new
动态添加方法
%new
- (void)addMethod {
//一些方法实现...
}
%new
+ (void)classMethod {
//一些方法实现...
}
9、%c
如果是添加了一个类方法,就需要使用类对象是调用,%c(类名)
就可以获取类对象。
3、Logos 语法使用
Xcode
新建一个普通工程命名为 LogosLoginText
,然后在 ViewController.m
中写入如下代码:
//
// ViewController.m
// LogosLoginText
//
// Created by ABC on 2019/11/9.
// Copyright © 2019 ABC. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self postWithUID:@"ABC" pwd:@"123456"];
}
- (void) postWithUID:(NSString *)uid pwd:(NSString *)pwd {
if ([uid isEqualToString:@"ABC"] && [pwd isEqualToString:@"123456"]) {
//登录成功
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"登录成功" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:nil];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];
} else {
//登录失败
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"登录失败" message:nil preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:nil];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];
}
}
@end
上方代码就是简单的一个判断,目的是在使用 Logos
语法 Hook
的时候,改变了相应的值,会提示错误,以便于知道 Hook
到改方法并修改值成功了。
添加完了上述代码,真机编译一下就可以了,这里只是为了拿到生成的 LogosLoginText.app
包,以便于使用 Monkey Dev
重签而已,就相当于模拟其他 App
。
编译成功后找到 Xcode
工程里 Products
文件下的 LogosLoginText.app
,Show in Finder
拷贝出来。
新建 Monkey
工程,然后重签 LogosLoginText.app
成功后,使用 class-dump
导出LogosLoginText.MachO
的头文件,这样准备工作就完成了。
打开 LogosLoginText
的头文件,就是假装打开了其他 App
的头文件,然后分析一下:
假装看到了登录方法 - (void)postWithUID:(id)arg1 pwd:(id)arg2;
然后我们对这个方法进行 Hook
,然后在注入库 MonkeyLoginDylib.lib
的 MonkeyLoginDylib.xm
中写要注入的代码。
// See http://iphonedevwiki.net/index.php/Logos
#import
%hook ViewController
%hook ViewController
- (void)postWithUID:(id)arg1 pwd:(id)arg2 {
NSLog(@"Hook到了登录");
}
%end
然后点击屏幕,发现控制台打印了:Hook到了登录
,并没有弹出 AlertView
证明该方法被 Hook
成功了。
如果需要版本判断,就需要分组了。使用 %group
、 %init
和 % ctor
了。
%group group1
%hook ViewController
- (void)postWithUID:(id)arg1 pwd:(id)arg2 {
NSLog(@"第一组Hook到了登录");
}
%end
%end
%group group2
%hook ViewController
- (void)postWithUID:(id)arg1 pwd:(id)arg2 {
NSLog(@"第二组Hook到了登录");
}
%end
%end
%ctor{
NSString *version = [UIDevice currentDevice].systemVersion;
if (version.floatValue >= 8.0) {
%init(group1)
} else {
%init(group2)
}
}
注意:组一定会有的,如果没有写组,那么Logos 会默认生成一个组 _ungrouped
并自己调用构造函数和 init
方法创建。
尝试输出一下当前方法的详细信息 %log
,添加后运行一下,查看结果发现,输出了参数详细的值和方法的指针地址。
%hook ViewController
- (void)postWithUID:(id)arg1 pwd:(id)arg2 {
%log;
NSLog(@"Hook到了登录");
}
%end
因为 Hook
了这个方法导致,该方法没有执行原有的,现在通过 %orig
保持原来的调用。添加完成后重新运行,点击屏幕显示了 AlertView
。
%hook ViewController
- (void)postWithUID:(id)arg1 pwd:(id)arg2 {
%log;
%orig;
NSLog(@"Hook到了登录");
}
%end
因为 - (void)postWithUID:(id)arg1 pwd:(id)arg2
是有参数的,所以可以通过 %orig
更改值:%orig(@"AAA",@"111111")
,再出重新运行就发现,AlertView
提示登录失败了。
%hook ViewController
- (void)postWithUID:(id)arg1 pwd:(id)arg2 {
%log;
%orig(@"AAA",@"11111");
NSLog(@"Hook到了登录");
}
%end
现在给 - (void)postWithUID:(id)arg1 pwd:(id)arg2
需要调用一个动态添加的方法 testMethod
就需要用到 %new
了。
当使用 [self testMethod]
会提示找不到 ViewController
,在逆向中如果找不到这个类,只需要将这个类和新添加的方法声明一下就可以,仅仅是为了让编译器编译能通过。
如果使用原来类里方法过多,可以把 class-dump
导出的头文件直接拖入 Logos
文件夹下,在这个头文件里声明一下动态添加的方法名称。
代码如下:
@interface ViewController
- (void)testMethod;
@end
%hook ViewController
%new
- (void)testMethod {
NSLog(@"调用了我动态添加的方法");
}
- (void)postWithUID:(id)arg1 pwd:(id)arg2 {
%log;
%orig(@"AAA",@"11111");
NSLog(@"Hook到了登录");
[self testMethod];
}
%end
重启运行,点击屏幕,控制台就打印了刚才添加方法中的 NSLog
。
上方的代码使用的是实例方法,需要添加一个类方法,然后可以使用 %c(类名)
直接调用类方法。
@interface ViewController
- (void)testMethod;
@end
%hook ViewController
%new
- (void)testMethod {
NSLog(@"调用了我动态添加的方法");
}
%new
+ (void)classMethod {
NSLog(@"调用了我动态添加的类方法");
}
- (void)postWithUID:(id)arg1 pwd:(id)arg2 {
%log;
%orig(@"AAA",@"11111");
NSLog(@"Hook到了登录");
[self testMethod];
[%c(ViewController) classMethod];
}
%end
以上就是 Logos
的介绍和基本用法了。