iOS SpringBoard tweak外掛,雙擊圖示快速啟動debugserver

TalkingData發表於2020-04-16

debugserver+lldb很好用,但啟動起來太麻煩?我們開發了一款iOS SpringBoard tweak小外掛,簡化debugserver啟動過程。老鐵,請雙擊!

0x00 懶是第一生產力

我們經常要通過debugserver對App進行除錯,有書籍論壇對相關的技術和實踐進行了說明,但實際應用起來還是有些麻煩。先要重籤拷貝,再要啟動終端ssh到iPhone啟動debugserver,各種ls加grep找到想除錯的應用,敲命令啟動debugserver,然後Mac本地終端啟動lldb。這樣折騰下來,至少要開兩個終端,有的時候甚至更多。GitHub上有個issh工具對上述操作有封裝和優化,但是還是需要敲命令找App,再執行debugserver。

所以做個tweak提升一下生產力。只需雙擊應用圖示,即可一鍵啟動debugserver

程式碼見:Github

https://github.com/TalkingDat...

執行介面

我們所用的開發環境是iOS 13.3,但是並沒有用到特殊版本的API,低版本手機應該也OK。

下面簡單分享開發過程:

0x01 通過圖示找到應用執行路徑

從介面找邏輯,逆向發現SpringBoard的圖示是SBIconView。並且有一個叫屬性 applicationBundleIdentifierForShortcuts 返回的是圖示對應的App的Bundle ID。通過Bundle ID構造LSApplicationProxy物件,並且獲得canonicalExecutablePath屬性,也就是應用的可執行檔案路徑。

Class LSApplicationProxy_class = objc_getClass("LSApplicationProxy");NSObject* proxyObj = [LSApplicationProxy_class performSelector:@selector(applicationProxyForIdentifier:) withObject:bundle];NSString * canonicalExecutablePath = [proxyObj performSelector:@selector(canonicalExecutablePath)];複製程式碼

0x02 尋找注入點新增擴充套件

接續看SBIconView,圖示上有兩個手勢物件:

  • 單擊,用來啟動App。
  • 長按,進入編輯狀態,執行刪除和排列圖示等操作。

所以,我們來給圖示互動加個雙擊擴充套件。

%hook SBIconView

- (void)didMoveToWindow{    %orig;    UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleDoubleClick:)];    [doubleTap setNumberOfTapsRequired:2];    [self addGestureRecognizer:doubleTap];    NSArray * ges = self.gestureRecognizers;    for(UITapGestureRecognizer * each in ges){        if([each isKindOfClass:[UITapGestureRecognizer class]]){            [each requireGestureRecognizerToFail: doubleTap];        }    }}複製程式碼

這裡額外說一句,[each requireGestureRecognizerToFail: doubleTap]新增了雙擊手勢指揮,由於iOS內部維護了手勢的狀態機,我們進行單擊操作的時候,其實產生了兩種Possible State。第一種是識別為單擊,然後結束。第二種是識別為雙擊的第一下並等待第二下的發生,然後根據兩次點選之時間間隔閾值來判斷是不是合法的雙擊。

所以我們手動增加了約束,相當於指定了識別的優先順序,只有雙擊失敗了,才繼續執行單擊回撥。這種操作會帶來一點幾乎無感的瑕疵:單擊後等待雙擊識別失敗的延遲,延遲的值就是雙擊識別執行的閾值(大約零點幾秒)。

0x03 debugserver啟動和關閉

debugserver是一個二進位制檔案,狗神的教程裡有如何重籤,issh把這些過程給簡化了。先看一下debugserver的許可權:
-rwxr-xr-x 1 root admin 9876848 Jan 19 11:28 /iOSRE/tools/debugserver*

再來看一下SpringBoard的許可權:
-rwxr-xr-x 1 root wheel 71264 Dec 5 13:15 SpringBoard*

屬主使用者都是root,沒毛病。找個函式呼叫一下:

  1. system函式
  2. posix_spawn函式
  3. NSTask ,物件導向方便管理,非同步執行,不會block UI,就用它了。

程式碼如下:

task = [[NSTask alloc]init];[task setLaunchPath:bin_serverpath];[task setArguments:args];[task launch];複製程式碼

每次server在launch之前,要把之前的task結束掉。

- (void)interrupt; // Not always possible. Sends SIGINT.複製程式碼
- (void)terminate; // Not always possible. Sends SIGTERM.複製程式碼

NSTask標頭檔案裡竟然告訴我 Not always possible。事實上,在呼叫的時候,還真的不怎麼possible,實際測試第一次server正常啟動,後續由於沒成功關閉,所以第二次就沒法啟動了。

所以還是換種方式關閉吧。簡單粗暴的 kill 函式:

NSTask * task = [TaskManager sharedManager].runningTask;if(task){    kill(task.processIdentifier,SIGKILL);    task = nil;}複製程式碼

0x04 新增UI互動

直接用Alert,又有按鈕又有輸入框,不過UIAlertView已經被廢棄掉了,需要用UIAlertController。由於彈出Controller需要父Controller,通過View找到當前的Controller,做正向的應該都寫過這段程式碼吧:

@implementation UIView(find)-(UIViewController*)findViewController{    UIResponder* target= self;    while (target) {        target = target.nextResponder;        if ([target isKindOfClass:[UIViewController class]]) {            break;        }    }    return (UIViewController*)target;}@end複製程式碼

0x05 優化一下使用者體驗

輸入框裡的IP和debugserver的path,每個人都不一樣,所以在第一次輸入完成之後,把這些值用NSUserDefault持久化儲存起來,下次直接讀取填充。

0x06後記

之前在相關技術論壇讀到討論用Root身份執行App的帖子,學習完帖子裡的技巧,增強對作業系統的理解以及實踐之後,發現如果真的想RootApp執行,其實SpringBoard本身就是一個RootApp,我們把SpringBoard當做RootViewController,很容易把一些系統工具做出介面,從而提升生產力。比如砸殼、重籤、拷貝App等。

**
作者:TalkingData小張同學

本文版權歸TalkingData所有,如需轉載請註明來源**

相關文章