iOS之Tweak

编写HOOK插件

创建工程

1
2
3
4
5
6
7
8
1. 终端输入nic.pl
2. Choose a Template (required): 10
3. Project Name (required): 工程名
4. Package Name [com.yourcompany.tututest]: 包名:com.xxx.xxx
5. Author/Maintainer Name [lk]: 作者
6. [iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: 要Hook的包名
7. [iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: 回车默认

工程文件简介

创建好的工程下共有4个文件

  • Makefile 编译文件

  • Tweak.x 编写HOOK文件

  • control 插件作者、版本信息

  • demo001.plist 设置要HOOK的Bundle ID

Makefile

指定工程用到的文件,框架,库等信息,将整个过程自动化

1
2
3
4
5
6
7
8
9
10
INSTALL_TARGET_PROCESSES = SpringBoard

include $(THEOS)/makefiles/common.mk

TWEAK_NAME = demo001

demo001_FILES = Tweak.x
demo001_CFLAGS = -fobjc-arc

include $(THEOS_MAKE_PATH)/tweak.mk
  1. INSTALL_TARGET_PROCESSES = SpringBoard 目标安装的项目
  2. include $(THEOS)/makefiles/common.mk 固定写法
  3. TWEAK_NAME = demo001 tweak的名称
  4. demo001_FILES = Tweak.x tweak源文件,多个文件以空格分隔
  5. include $(THEOS_MAKE_PATH)/tweak.mk 通过include命令指定不同的.mk文件

除了上面的信息格式,我们还可以添加以下信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
我们如何通过进行指定SDK?
TARGET = iPhone:latest:8.0

我们如何导入framework?
demo001_FRAMWORKS = UIKit

我们如何指定处理器架构
ARCHS = armv7 arm64

我么如何链接Mach-O对象
demo001_LDFLAGS = -lx
注意
-lx 代表链接libx.a或libx.dylib,即给x加上lib的前缀,以及.a或.dylib的后缀,可以根据自己的项目进行调整

Tweak.x

xm 中的x代表这个文件支持Logos语法

x 如果后缀名是单独的x,说明源文件支持Logos和C语法;

xm 如果后缀名是xm,说明源文件支持Logos和C/C++语法.

.xi 将首先作为objective-c进行预处理,然后Logos将处理结果,然后将进行编译。

.xmi 将首先作为objective-c ++进行预处理,然后Logos将处理结果,然后将进行编译。

demo001.plist

用于指定需要注入的目标文件的Bundle ID

control

指定deb包的一些信息,包括名字,描述,版本号等

编写HOOK代码

编辑tweak.x

1
2
3
4
5
6
7
8
9
10
//hook ViewController的test方法

%hook ViewController

- (void)test{

NSLog(@"🍺🍺🍺HOOK🍺🍺🍺");
}

%end

设置环境变量

1
2
export THEOS_DEVICE_IP = localhost
export THEOS_DEVICE_PORT = 2222

编译安装

使用iproxy转发手机22端口到本地2222

1
iproxy 2222 22

终端输入 make;make package install

1
2
make
make package install

Logos语法

1
http://iphonedevwiki.net/index.php/Logos
Logos语法 功能解释 事例
%hook 需要hook哪个类 %hook Classname
%end 代码块结束标记 %end
%group 分组 %group Groupname
%new 添加新方法 %new(signature)
%ctor 构造函数 %ctor { … }
%dtor 析构函数 %dtor { … }
%log 输出打印 %log; %log([(), …]);
%orig 保持原有方法 %orig;%orig(arg1, …);
%c 动态获取类 %c([+/-]Class);

1. %hook %end

指定需要hook的class,必须以%end结尾。

1
2
3
4
5
6
7
8
// hook SpringBoard类里面的_menuButtonDown函数,先打印一句话,再之子那个函数原始的操作
%hook SpringBorad
- (void)_menuButtonDown:(id)down
{
NSLog(@"111111");
%orig; // 调用原始的_menuButtonDown函数
}
%end

2.%group

该指令用于将%hook分组,便于代码管理及按条件初始化分组,必须以%end结尾。

一个%group可以包含多个%hook,所有不属于某个自定义group的%hook会被隐式归类到%group_ungrouped中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
%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);
}
}

3.%new

在%hook内部使用,给一个现有class添加新函数,功能与class_addMethod相同.

注:
Objective-C的category与class_addMethod的区别:

前者是静态的而后者是动态的。使用%new添加,而不需要向.h文件中添加函数声明,如果使用category,可能与遇到这样那样的错误.

1
2
3
4
5
6
7
%hook SpringBoard
%new
- (void)addNewMethod
{
NSLog(@"动态添加一个方法到SpringBoard");
}
%end

4.%ctor

tweak的constructor,完成初始化工作;如果不显示定义,Theos会自动生成一个%ctor,并在其中调用%init(_ungrouped)。

%ctor一般可以用来初始化%group,以及进行MSHookFunction等操作,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef KCFCoreFoundationVersionNumber_iOS_8_0
#define KCFCoreFoundationVersionNumber_iOS_8_0 1140.10
#endif

%ctor
{
%init;

if (KCFCoreFoundationVersionNumber >= KCFCoreFoundationVersionNumber_iOS_7_0 && KCFCoreFoundationVersionNumber > KCFCoreFoundationVersionNumber_iOS_8_0)
%init(iOS7Hook);
if (KCFCoreFoundationVersionNumber >= KCFCoreFoundationVersionNumber_iOS_8_0)
%init(iOS8Hook);
MSHookFunction((void *)&AudioServicesPlaySystemSound,(void *)&replaced_AudioServerPlaySystemSound,(void **)&orginal_AudioServicesPlaySystemSound);
}

5.%dtor

Generate an anonymous deconstructor (of default priority).

6.%log

该指令在%hook内部使用,将函数的类名、参数等信息写入syslog,可以%log([(),…..])的格式追加其他打印信息。

1
2
3
4
5
6
7
%hook SpringBorad
- (void)_menuButtonDown:(id)down
{
%log((NSString *)@"iosre",(NSString *)@"Debug");
%orig; // 调用原始的_menuButtonDown方法
}
%end

7.%orig

该指令在%hook内部使用,执行被hook的函数的原始代码;也可以用%orig更改原始函数的参数。

1
2
3
4
5
6
%hook SpringBorad
- (void)setCustomSubtitleText:(id)arg1 withColor: (id)arg2
{
%orig(@"change arg2",arg2);// 将arg2的参数修 改为"change arg2"
}
%end

8.%init

该指令用于初始化某个%group,必须在%hook或%ctor内调用;如果带参数,则初始化指定的group,如果不带参数,则初始化_ungrouped.

注: 切记,只有调用了%init,对应的%group才能起作用!

9.%c

该指令的作用等同于objc_getClass或NSClassFromString,即动态获取一个类的定义,在%hook或%ctor内使用 。

调用ViewController中的Method方法

1
[[[%c(ViewController) alloc ] init]Method]; 

日志打印

hook demo方法并添加打印入参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
#import "ViewController.h"

@interface ViewController ()
//点击事件
- (IBAction)compute;

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.

}

- (IBAction)compute {

[self demo:@"66"];

}

- (void)demo:(NSString *)str{
NSString *res = [str stringByAppendingString:@"🐂🐂🐂"];
NSLog(res);


}
@end

logify.pl生成.xm文件

1
2
//根据class-dump后的ViewController.h生成.x文件
logify.pl ViewController.h >Tweak.x

我们之关系程序中自己写的方法

1
2
3
4
5
6
7
8
//经过删减过后的Tweak.x文件

%hook ViewController

- (void)demo:(id)arg1 { %log; %orig; }

%end

将生成的Tweak.x文件替换theos生成的Tweak.x文件后编译安装即可

查看打印日志