Tweak 開發 - CCRevealLoader
Cydia 市場里有一款叫 Reveal Loader 的插件,可以動態的將 Reveal 注入到任何 App 里。有了這個插件,再配合使用 Reveal 這個 macOS App,能夠非常方便地分析別人的 iOS App 的視圖布局。但是這個插件已經很久沒有更新了,而 Reveal 自從改為了訂閱模式收費之后,版本號像火箭一樣升的飛起,現在最新的版本號已經是 7 了。Reveal Loader 插件目前已經失效。好在插件的作者開源了源碼到 Github,我便在這個源碼基礎上做了修改,支持了最新版本的 Reveal。
功能實現
由于是借鑒 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 吧~~~
參考資料
來自:http://blog.nswebfrog.com/2017/03/15/reveal-loader/