Tweak 开发 - CCRevealLoader

ohscar 7年前
   <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>