最近分析一个开源越狱插件--Volbrate ,这个插件是给iPhone音量键增加震动的,功能很简单。亮点在于,这个插件提供了在手机设置中做一些功能修改和设定
如图所示:
通过搜索知道是利用了PreferenceBundle,theos第9个模板--preference_bundle_modern。只写过tweak,对PreferenceBundle不了解,所以就边分析边学怎么写PreferenceBundle。
有关tweak的编写,资料好多,就不再说了。推荐看狗神的《iOS应用逆向工程》第二版
可知,其作用于springboard
简单来说,就是hook音量键,然后添加震动功能。 关键hook代码:
由代码可知,hook VolumeControl类里面的三个函数。-(float)volume
获取音量值-(void)increaseVolume
增加一格音量-(void)decreaseVolume
减少一格音量
tweak中震动的实现是 调用一个private API :AudioServicesPlaySystemSoundWithVibration
Apple官方并没有这个函数的文档。搜索这个函数,出来最多就是are there apis for custom vibrations in ios 。翻译部分内容: 其函数原型:void AudioServicesPlaySystemSoundWithVibration(SystemSoundID inSystemSoundID, id arg, NSDictionary* vibratePattern)
有意思的是怎么才能写PreferenceBundle,在设置中随时修改某些参数。达到修改tweak功能的作用。比如可以做一个 是否启用hook的开关... Preference Bundles可以作为iPhone设置中的扩展程序,开发者能编写自己想要的bundles,安装后位于手机/Library/PreferenceBundles/
目录下。
Theos的PreferenceBundles模板结合了MobileSubstrate的PreferenceLoader PreferenceLoaders是MobileSubstrate其中的一个工具,可以把tweak扩展PreferenceBundles注入到iOS的设置中
Tweak.xm不能直接调用PreferenceBundle来获取一些修改后的变量值,而是通过另一种方式,比如从某个plist文件读取,变量的plist文件位于/User/Library/Preferences
目录。
PreferenceBundles作为tweak的扩展,直接就在tweak的工程目录 新建PreferenceBundles工程 利用theos nic.pl来创建新建PreferenceBunldes工程
如图依次填入,工程名,BundleID,工程前缀 最后的目录结构如图:
这时候tweak的makefile会自动增加一些东西
entry.plist 确定在设置应用中的入口图标,文字等。
Info.plist 这个文件保存工程信息,不需做什么修改
Root.plist Root.plist可以看做是PreferenceBundle的UI布局文件。其中要跟tweak交互的变量就声明在这里,比如:
entry.plist 和 Root.plist文件的所用的键值详细内容,可参考Preferences specifier plist Info.plist和Root.plist都在Resources文件夹里。且工程所用到图片,图标文件也保存在Resources目录下。
代码文件 模板会新建两个文件,XXXRootListController.h
和 XXXRootListController.m
,XXX
就是之前设置的工程前缀。XXXRootListController
必须继承PSListController
或者PSViewController
,且必须实现- (id)specifiers
方法,因为PSListController
依赖_specifiers
来获得metadata和group。iphonedevwiki 的示例代码:
kNameOfPreferencePlist指的就是Root.plist。 这些theos都已经替我们做好了。其他逻辑代码就写在XXXRootListController.m里,可以有多个.m文件。 如何在PreferenceBundle中设置和读取要交互的变量,具体方法可参考iphonedevwiki
在tweak的constructor(%ctor)中完成PreferenceBundle的加载,Preferences 的示例代码:
notificationCallback()
获取需要的变量值。CFNotificationCenterAddObserver()
是CoreFoundation/CFNotificationCenter.h
中的函数,注册监听事件,设置监听相关变量改变后所做的事。
第四个参数:Darwin消息字符串,在Root.plist文件中设置相关变量的消息字符串,然后在Tweak.xm文件中要写上对于的消息字符串。key为PostNotification。当对应的变量改变时,就会发送这个消息字符串。然后监听事件就会接受到消息。 比如在Root.plist文件中:
makefile的编写跟tweak差不多
编译是跟tweak一起编译,不用做其他操作。
如何写PreferenceBundle的中文资料没找到,所以参考的都是英文资料,所以有些地方翻译的不好,大致意思应该明白。写的较为简单,算是基本了解怎么写PreferenceBundle吧。
https://stackoverflow.com/questions/12966467/are-there-apis-for-custom-vibrations-in-ios https://github.com/derv82/Exchangent/wiki/Part-6:-Preferences,-Preferences,-a-little-Tweak,-and-Heaps-of-More-Preferences http://sharedinstance.net/2015/02/settings-the-right-way-redux/ http://iphonedevwiki.net/index.php/PreferenceBundles http://iphonedevwiki.net/index.php/PreferenceLoader
%hook VolumeControl
-(void)increaseVolume {
if(volVibrationOptions == 2) {
[FeedbackCall vibrateDevice];
} if(volVibrationOptions == 1 && volMax == YES){
[FeedbackCall vibrateDevice];
} else {
%orig;
}
}
-(void)decreaseVolume {
if(volVibrationOptions == 2) {
[FeedbackCall vibrateDevice];
} if(volVibrationOptions == 1 && volMin == YES){
[FeedbackCall vibrateDevice];
} else {
%orig;
}
}
-(float)volume {
x = %orig;
if(x == 0){
volMin = YES;
} if(x == 1){
volMax = YES;
} if(x > 0 && x < 1) {
volMin = NO;
volMax = NO;
}
return %orig;
}
%end
+ (void)vibrateDeviceForTimeLengthIntensity:(CGFloat)timeLength vibrationIntensity:(CGFloat)vibeIntensity {
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
NSMutableArray* arr = [NSMutableArray array];
[arr addObject:[NSNumber numberWithBool: YES]]; //vibrate for time length
[arr addObject:[NSNumber numberWithInt: timeLength*1000]];
[arr addObject:[NSNumber numberWithBool: NO]];
[arr addObject:[NSNumber numberWithInt: 50]];
[dict setObject: arr forKey:@"VibePattern"];
[dict setObject:[NSNumber numberWithFloat: vibeIntensity] forKey:@"Intensity"];
AudioServicesPlaySystemSoundWithVibration(kSystemSoundID_Vibrate, nil, dict);
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!