前言
- 目的:iOS逆向开发中经常使用的Reveal、SSL Kill Switch 2等工具都是依附于设置而存在的,本文就是做一个这样的应用
- 环境:macOS10.14.6、Xcode11.3
- 适配:iOS11.0-iOS最新
新建
nic.pl
新建App1
2
3
4
5
6
7
8
9
10
11
12NIC 2.0 - New Instance Creator
------------------------------
...
[7.] iphone/preference_bundle_modern
...
Choose a Template (required): 7
Project Name (required): Test
Package Name [com.yourcompany.test]: com.test.www
Author/Maintainer Name [drag]: xxoo
[iphone/preference_bundle_modern] Class name prefix (three or more characters unique to this project) [XXX]:
Instantiating iphone/preference_bundle_modern in test/...
Done.创建后,目录结构如下图
1
2
3
4
5
6
7
8
9
10
11
12
$ tree
.
├── Makefile
├── Resources
│ ├── Info.plist
│ └── Root.plist
├── XXXRootListController.h
├── XXXRootListController.m
└── entry.plist
1 directory, 6 files更改Makefile配置
- 目前支持的架构有armv7 armv7s arm64 arm64e等
- 添加
ARCHS = armv7 armv7s arm64
表示支持 armv7 armv7s arm64 三种处理器架构 - 对于最新的A12处理器(iPhoneX以后)的设备,需要添加arm64e,否则生成的dylib文件不能正常加载
- 本文只适配64位架构设备,设置如下:
ARCHS = arm64 arm64e
- 添加
- 指定目标规范:
TARGET = iPhone:BaseSDK:DeploymentTarget
BaseSDK
代表编译用的SDK版本Deployment Target
是最低兼容的系统版本,- 本文初始设置
TARGET = iPhone:latest:11.0
- 目前支持的架构有armv7 armv7s arm64 arm64e等
编译
make
编译出现找不到私有库Preferences.framework的错误,Preferences.framework包含在PrivateFrameworks里面,本文采取的是下载整个sdk1
2
3
4
5
6
7
8
9
10
11
12$ make
> Making all for bundle Test…
==> Copying resource directories into the bundle wrapper…
==> Compiling XXXRootListController.m (arm64)…
==> Linking bundle Test (arm64)…
ld: warning: directory not found for option '-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk/System/Library/PrivateFrameworks'
ld: framework not found Preferences
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make[3]: *** [/Users/drag/Desktop/OCTest/test/.theos/obj/debug/arm64/Test.bundle/Test] Error 1
make[2]: *** [/Users/drag/Desktop/OCTest/test/.theos/obj/debug/arm64/Test.bundle/Test] Error 2
make[1]: *** [internal-bundle-all_] Error 2
make: *** [Test.all.bundle.variables] Error 2从
https://github.com/xybp888/iOS-SDKs
下载iPhoneOS13.0.sdk
放入theos/sdks。- 因为本人Xcode版本是11.3,所以下载iPhoneOS13.0.sdk,可以根据自己的Xcode版本选择合适的sdk
- 如果从github下载过慢,可以通过gitee中转一下。
Makefile里面更改
TARGET = iPhone:13.0:11.0
- 下载的sdk版本为13.0,BaseSDK是填入13.0
- 适配版本11.0,DeploymentTarget填入11.0
make
编译通过1
2
3
4
5
6
7
8
9
10
11
12make
Making all for bundle Test…
==> Copying resource directories into the bundle wrapper…
==> Compiling XXXRootListController.m (arm64)…
==> Linking bundle Test (arm64)…
==> Generating debug symbols for Test…
==> Compiling XXXRootListController.m (arm64e)…
==> Linking bundle Test (arm64e)…
==> Generating debug symbols for Test…
==> Merging bundle Test…
==> Signing Test…
`
打包
make package
打包报错,提示缺少control文件1
2
3
4
5
6
7make package
Making all for bundle Test…
==> Copying resource directories into the bundle wrapper…
make[2]: Nothing to be done for `internal-bundle-compile'.
Making stage for bundle Test…
==> Error: /Applications/Xcode.app/Contents/Developer/usr/bin/make package requires you to have a layout/ directory in the project root, containing the basic package structure, or a control file in the project root describing the package.
make: *** [internal-package] Error 1新建control文件,填入如下内容
1
2
3Package: com.test.www
Architecture: iphoneos-arm
Version: 0.0.1再次
make package
,成功,生成deb包1
2
3
4
5
6make package
Making all for bundle Test…
==> Copying resource directories into the bundle wrapper…
make[2]: Nothing to be done for `internal-bundle-compile'.
Making stage for bundle Test…
dm.pl: building package `com.test.www:iphoneos-arm' in `./packages/com.test.www_-2+debug_iphoneos-arm.deb
安装
- 执行
make install
进行安装,安装成功后可以去设置里面查看有没有Test,- 如果没有发现,是因为没有安装PreferenceLoader,需要在Cydia中安装PreferenceLoader
- 如果发现了,那是因为其它插件对PreferenceLoader有依赖,安装插件的时候就顺带安装了
- PreferenceLoader
- 是一个开源的基础依赖包,越狱插件的系统设置菜单是由它提供的,最好在
iphone/preference_bundle_modern
开发前进行安装 - 进入系统设置时,PreferenceLoader会从/Library/PreferenceLoader/Preferences/目录下解析符合规则的plist文件,并生成相应的控件动态添加到系统设置中
- 是一个开源的基础依赖包,越狱插件的系统设置菜单是由它提供的,最好在
实战一:构造SSL Kill Switch 2样式的App
最终效果图
构造过程
打开上面创建的项目,找到entry.plist文件。entry.plist描述了设置菜单项看到的内容,更改后的内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>entry</key>
<dict>
<key>bundle</key>
<string>Test</string>
<key>cell</key>
<string>PSLinkCell</string>
<key>detail</key>
<string>XXXRootListController</string>
<key>icon</key>
<string>test.png</string>
<key>isController</key>
<true/>
<key>label</key>
<string>Test</string>
</dict>
</dict>
</plist>- cell:cell类型,这里是PSLinkCell类型,表示是一个可以点击的控件,更多控件类型参考
- detail:XXXRootListController,表示点击后跳转到此控制器
- icon:该cell前面显示的图标,图标test.png需要存储在Resources文件夹下面。
- label:该cell前面显示的文字
当执行
make install
安装后,theos会做两件事情- 在/Library/PreferenceLoader/Preferences/文件夹下新建和项目同名的plist文件,比如Test.plist
- 把entry.plist内容拷贝一份存储到Test.plist,进入设置时会读取该plist文件,生成相应的cell
Resources文件夹:存放项目需要的资源文件,比如图片,比如构建控制器需要的plist文件
- Root.plist描述XXXRootListController的视图结构,更改里面相应的值,可以更改控制里面看到的内容
- 目前项目只有一个控制器,如果我们需要定义另外的控制器CustomListController,那么就需要新建一个custom.plist文件描述此控制器的内容。
- test.png,设置菜单项看到的图片资源
打开XXXRootListController控制器后发现其内容是由Root.plist构建。
1
2
3
4
5
6- (NSArray *)specifiers {
if (!_specifiers) {
_specifiers = [self loadSpecifiersFromPlistName:@"Root" target:self];
}
return _specifiers;
}更改
Resources/Root.plist
内容如下: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
32
33
34
35
36
37
38
39
40
41<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>禁用后可以绕过https验证。</string>
<key>footerText</key>
<string>温馨提示:违权必究</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>defaults</key>
<string>com.test.www</string>
<key>key</key>
<string>isDisCerVarify</string>
<key>label</key>
<string>禁用证书验证</string>
<key>default</key>
<false/>
</dict>
<dict>
<key>cell</key>
<string>PSEditTextCell</string>
<key>defaults</key>
<string>com.test.www</string>
<key>key</key>
<string>excludedIDs</string>
<key>label</key>
<string>排除BundleIDs:</string>
</dict>
</array>
<key>title</key>
<string>Test</string>
</dict>
</plist>- defaults 用户操作控件得到的值存储到/var/mobile/Library/Preferences/com.test.www.plist文件中
- key 在defaults中用于关联该cell的键值
- default 表示存储在
com.test.www.plist
中的默认值
整个构建过程到这里就结束了,执行
make && make package && make install
后,重新打开设置App就会看到如上图所示的内容
与tweak交互
上面只是做了UI方面的操作,没有实际的功能是没有任何用的。要想发挥作用,必须与tweak进行交互。
新建tweak,BundleID填写
com.apple.UIKit
表示可以拦截任何App,也就是所有App启动都会走Tweak.x中的%ctor {}
初始化函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16nic.pl
NIC 2.0 - New Instance Creator
------------------------------
...
[9.] iphone/tool_swift
[10.] iphone/tweak
[11.] iphone/xpc_service
...
Choose a Template (required): 10
Project Name (required): Tweak
Package Name [com.yourcompany.tweak]: com.tweak.www
Author/Maintainer Name [drag]:
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.apple.UIKit
[iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]:
Instantiating iphone/tweak in tweak/...
Done.Makefile文件增加以下内容
1
2TARGET = iPhone:13.0:11.0
ARCHS = arm64 arm64eTweak.x里面hook相应的方法来实现禁用证书的功能,核心代码里面调用shouldHookFromPreference()来判断某个App是否需要hook
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20static BOOL shouldHookFromPreference() {
BOOL shouldHook = NO;
NSMutableDictionary *plist = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.test.www.plist"];
if (!plist) {
NSLog(@"Preference file not found.");
return shouldHook;
}
shouldHook = [[plist objectForKey:@"isDisCerVarify"] boolValue]; // Root.plist里面填写的key
// 查看过滤的bundleid
NSString *excludedBundleIdsString = [plist objectForKey:@"excludedIDs"]; // Root.plist里面填写的key
excludedBundleIdsString = [excludedBundleIdsString stringByReplacingOccurrencesOfString:@" " withString:@""];
NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];
bundleId = [bundleId stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSArray *excludedBundleIds = [excludedBundleIdsString componentsSeparatedByString:@","];
if ([excludedBundleIds containsObject:bundleId]) {
NSLog(@"Not hooking excluded bundle: %@", bundleId);
shouldHook = NO;
}
return shouldHook;
}Test项目和Tweak项目目前是分离的,都会生成相应的deb文件。难道实让用户安装两个deb包,这个用户体验好像不太好吧。有没有更简单的方式,答案是有的。
- Tweak项目根目录新建layout文件夹
- 进入Test项目
.theos/_/
,找到Library
文件夹,拷贝到layout文件夹下面。 - Cydia里面卸载上面安装的Test App。进入Tweak项目,执行
make && make package && make install
进行安装,安装完成就可以在设置里面看到Test App的选项
现在只需要进入Tweak项目,把packages文件夹下的deb安装包发送给其他人就可以了,方便又快捷
实战二:构造Reveal样式的App
最终效果图
构造与交互
大部分和实战一是相同的,这里只写出不同的地方。
如上图所示,获取了应用列表,这个功能是AppList插件提供的,所以在开发前要在Cydia里面搜索安装AppList。
更改
Resources/Root.plist
内容如下: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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
<key>label</key>
<string>选择需要启用的App。</string>
<key>footerText</key>
<string>温馨提示:违权必究</string>
</dict>
<dict>
<key>bundle</key>
<string>AppList</string>
<key>isController</key>
<string>1</string>
<key>cell</key>
<string>PSLinkCell</string>
<key>label</key>
<string>App列表</string>
<key>ALNavigationTitle</key>
<string>选择需要注入的应用</string>
<key>ALSettingsPath</key>
<string>/var/mobile/Library/Preferences/com.test.www.plist</string>
<key>ALSettingsKeyPrefix</key>
<string>TestAppEnabled-</string>
<key>ALSettingsDefaultValue</key>
<string></string>
<key>ALSectionDescriptors</key>
<array>
<dict>
<key>title</key>
<string>用户App列表</string>
<key>predicate</key>
<string>isSystemApplication = FALSE</string>
<key>icon-size</key>
<string>29</string>
<key>suppress-hidden-apps</key>
<string>1</string>
<key>cell-class-name</key>
<string>ALSwitchCell</string>
</dict>
<dict>
<key>title</key>
<string>系统App列表</string>
<key>predicate</key>
<string>isSystemApplication = TRUE</string>
<key>icon-size</key>
<string>29</string>
<key>suppress-hidden-apps</key>
<string>1</string>
<key>cell-class-name</key>
<string>ALSwitchCell</string>
</dict>
</array>
</dict>
</array>
<key>title</key>
<string>Test</string>
</dict>
</plist- ALNavigationTitle 导航栏标题
- ALSettingsPath 存储文件的路径,文件用来存储自己设置的值
- ALSettingsKeyPrefix key的前缀信息
- ALSectionDescriptors 各个部分信息的描述,本例中分成两个部分:用户App列表和系统App列表
当点击App列表里面的开关时,会动态更改
com.test.www.plist
文件里面对应的键值。进行交互时,读取相应的键值,做出自己的逻辑判断即可。与Tweak进行交互
1
2
3
4
5
6
7
8%ctor {
NSString *identifier = [[NSBundle mainBundle] bundleIdentifier];
NSMutableDictionary *plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.test.www.plist"];
// 是否在用户APP列表点击开启了
if ([[plistDict objectForKey:[@"TestAppEnabled-" stringByAppendingString:identifier]] boolValue]) {
}
}
扩展
某个cell的点击操作
plist里面:cell为PSButtonCell表示可以点击,action表示执行的动作
1
2
3
4
5
6
7
8
9
10<dict>
<key>cell</key>
<string>PSButtonCell</string>
<key>label</key>
<string>我也要点击</string>
<key>icon</key>
<string>cydia.png</string>
<key>action</key>
<string>btnClicked</string>
</dict>控制器里面直接写上相应的方法
1
2
3- (void)btnClicked {
NSLog(@"我被打印了");
}
自定义cell
.h头文件里面导入PSSpecifier.h,继承PSTableCell
1
2
3
4
5
@interface CustomCell: PSTableCell
@end.m实现文件里面
1
2
3
4
5
6
7
8
9
10
11
12
13
@implementation CustomCell
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(id)identifier specifier:(PSSpecifier *)specifier { //init method
self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier specifier:specifier]; //call the super init method
if (self) {
// specifier就是模型数据
}
return self;
}
@end控制器里面通过操作PSSpecifier来更新Cell界面的值。