Tweak 开发 - CCRevealLoader

| categories: iOS | tags: iOS

Cydia 市场里有一款叫 Reveal Loader 的插件,可以动态的将 Reveal 注入到任何 App 里。有了这个插件,再配合使用 Reveal 这个 macOS App,能够非常方便地分析别人的 iOS App 的视图布局。但是这个插件已经很久没有更新了,而 Reveal 自从改为了订阅模式收费之后,版本号像火箭一样升的飞起,现在最新的版本号已经是 7 了。Reveal Loader 插件目前已经失效。好在插件的作者开源了源码到 Github,我便在这个源码基础上做了修改,支持了最新版本的 Reveal。项目目前也已开源,地址是 https://github.com/webfrogs/CCRevealLoader

功能实现


由于是借鉴 Reveal Loader 插件的使用,基本操作是一样的,就是在系统设置里新增一个选项,然后可以手动选择要注入的 App。

这里有两个技术点:

  • 读取当前设备所有的 App 信息
  • 在系统设置中添加自己的设置

本来以为处理这里要费一番功夫,但是在分析了 Reveal Loader 的源码后,发现目前越狱环境下有这两个问题的现成解决方案。

新建一个 Tweak 工程,由于 tweak 要对每一个 App 都生效,MobileSubstrate Bundle filter 里要填上 com.apple.UIKit"。工程生成好之后,将 control 文件里的 Depends 一行修改为:

Depends: mobilesubstrate, preferenceloader, applist

可见,除了 mobilesubstrate 这个必要的依赖之外,我们在项目工程中新增了两个依赖。有了这两个依赖,再配合一个配置文件,就可以完成上面所需求的两个点。配置文件在工程根路径下的 layout 文件夹下,路径是 Library/PreferenceLoader/Preferences/CCRevealLoader.plist 相信大家去看一下这个配置文件的源码就能了解其含义。

其中比较重要的几个字段:

字段名 含义
ALSettingsPath 配置文件的保存路径
ALSettingsKeyPrefix 配置文件中对应 key 的前缀

以上这两个字段,是接下来代码中需要用到的。补充一下: theos 工程中,layout 文件夹下的文件内容,在打成 deb 包安装到设备上后,会按照同样的路径,放到设备的根路径,也就是 / 下。

接下来,实现 tweak 的主要部分

%ctor {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSDictionary *prefs = [NSDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/com.nswebfrog.revealloader.plist"] ;
    NSString *libraryPath = @"/Library/Application Support/CCRevealLoader/RevealServer.framework/RevealServer";

    NSString *keyPath = [NSString stringWithFormat:@"CCRevealEnabled-%@", [[NSBundle mainBundle] bundleIdentifier]];
    NSLog(@"CCRevealLoader before loaded %@,keyPath = %@,prefs = %@", libraryPath,keyPath,prefs);
    if ([[prefs objectForKey:keyPath] boolValue]) {
        if ([[NSFileManager defaultManager] fileExistsAtPath:libraryPath]){
            void *haldel = dlopen([libraryPath UTF8String], RTLD_NOW);
        if (haldel == NULL) {
            char *error = dlerror();
            NSLog(@"dlopen error: %s", error);
        } else {
            NSLog(@"dlopen load framework success.");
			[[NSNotificationCenter defaultCenter] postNotificationName:@"CCRevealLoaderRequestStart" object:nil];
        }

        NSLog(@"CCRevealLoader loaded %@", libraryPath);
        } else {
            NSLog(@"CCRevealLoader file not exists %@", libraryPath);
        }
    }
    else {
        NSLog(@"CCRevealLoader not enabled %@", libraryPath);
    }

    NSLog(@"CCRevealLoader after loaded %@", libraryPath);


    [pool drain];
}

这段代码的主要逻辑是,首先读取 preferenceloader 里用户的选择,如果发现用户打开了在设置中的开关,下一步就使用 dlopen 函数加载 Reveal 的动态库。

这里还有一个问题,就是 Reveal 动态库的获取。在原来的 Reveal loader 中,处理的逻辑是通过网络下载一个 Reveal 的动态库,而由于其下载的是一个老版本的动态库,所以,这也是这个插件失效的原因。Reveal 的动态库从 dylib 类型的动态库变成了 framework 形式的动态库,而且命名也发生过改变。我在这个工程中的做法是,从开发者的 Mac 电脑安装的 Reveal App 中,将 Reveal 的 iOS 动态库拷贝到工程的 layout 文件夹下,打包到 tweak 插件中一起发布。由于这个操作是要在打包命令之前进行,则在项目的 Makefile 中加入以下内容:

before-package::
	@echo "Downlading reveal server framework..."
	mkdir -p ./layout/Library/Application\ Support/CCRevealLoader/
	cp -r  /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/RevealServer.framework ./layout/Library/Application\ Support/CCRevealLoader/

至此,这个插件的主要功能已经完成。快去用 Reveal 去学(tou)习(kui)别人的 App 吧~~~

参考资料





Previous     Next

Published under (CC) BY-NC-SA