Tweak 开发 - CCRevealLoader
ohscar
8年前
<p>Cydia 市场里有一款叫 Reveal Loader 的插件,可以动态的将 Reveal 注入到任何 App 里。有了这个插件,再配合使用 Reveal 这个 macOS App,能够非常方便地分析别人的 iOS App 的视图布局。但是这个插件已经很久没有更新了,而 Reveal 自从改为了订阅模式收费之后,版本号像火箭一样升的飞起,现在最新的版本号已经是 7 了。Reveal Loader 插件目前已经失效。好在插件的作者开源了源码到 Github,我便在这个源码基础上做了修改,支持了最新版本的 Reveal。</p> <h2>功能实现</h2> <p>由于是借鉴 Reveal Loader 插件的使用,基本操作是一样的,就是在系统设置里新增一个选项,然后可以手动选择要注入的 App。</p> <p>这里有两个技术点:</p> <ul> <li>读取当前设备所有的 App 信息</li> <li>在系统设置中添加自己的设置</li> </ul> <p>本来以为处理这里要费一番功夫,但是在分析了 Reveal Loader 的源码后,发现目前越狱环境下有这两个问题的现成解决方案。</p> <p>新建一个 Tweak 工程,由于 tweak 要对每一个 App 都生效, MobileSubstrate Bundle filter 里要填上 com.apple.UIKit" 。工程生成好之后,将 control 文件里的 Depends 一行修改为:</p> <pre> <code class="language-objectivec">Depends: mobilesubstrate, preferenceloader, applist</code></pre> <p>可见,除了 mobilesubstrate 这个必要的依赖之外,我们在项目工程中新增了两个依赖。有了这两个依赖,再配合一个配置文件,就可以完成上面所需求的两个点。配置文件在工程根路径下的 layout 文件夹下,路径是 Library/PreferenceLoader/Preferences/CCRevealLoader.plist 相信大家去看一下这个配置文件的源码就能了解其含义。</p> <p>其中比较重要的几个字段:</p> <table> <thead> <tr> <th>字段名</th> <th>含义</th> </tr> </thead> <tbody> <tr> <td>ALSettingsPath</td> <td>配置文件的保存路径</td> </tr> <tr> <td>ALSettingsKeyPrefix</td> <td>配置文件中对应 key 的前缀</td> </tr> </tbody> </table> <p>以上这两个字段,是接下来代码中需要用到的。补充一下: theos 工程中,layout 文件夹下的文件内容,在打成 deb 包安装到设备上后,会按照同样的路径,放到设备的根路径,也就是 / 下。</p> <p>接下来,实现 tweak 的主要部分</p> <pre> <code class="language-objectivec">%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]; }</code></pre> <p>这段代码的主要逻辑是,首先读取 preferenceloader 里用户的选择,如果发现用户打开了在设置中的开关,下一步就使用 dlopen 函数加载 Reveal 的动态库。</p> <p>这里还有一个问题,就是 Reveal 动态库的获取。在原来的 Reveal loader 中,处理的逻辑是通过网络下载一个 Reveal 的动态库,而由于其下载的是一个老版本的动态库,所以,这也是这个插件失效的原因。Reveal 的动态库从 dylib 类型的动态库变成了 framework 形式的动态库,而且命名也发生过改变。我在这个工程中的做法是,从开发者的 Mac 电脑安装的 Reveal App 中,将 Reveal 的 iOS 动态库拷贝到工程的 layout 文件夹下,打包到 tweak 插件中一起发布。由于这个操作是要在打包命令之前进行,则在项目的 Makefile 中加入以下内容:</p> <pre> <code class="language-objectivec">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/</code></pre> <p>至此,这个插件的主要功能已经完成。快去用 Reveal 去学(tou)习(kui)别人的 App 吧~~~</p> <h2>参考资料</h2> <ul> <li><a href="/misc/goto?guid=4959741794840716463" rel="nofollow,noindex">iOS 越狱的Tweak开发</a></li> </ul> <p> </p> <p>来自:http://blog.nswebfrog.com/2017/03/15/reveal-loader/</p> <p> </p>