抓取IOS的apsd進程流量

  • 2019 年 10 月 8 日
  • 筆記

IOS的apsd是Apple Push Service的相關進程,很多系統服務都跟他有關,比如iMessage、Homekit,因此想抓包查看他是怎麼實現的。

1.搜索發現相關資料很少,只有多年前的一個相關演講:https://blog.quarkslab.com/resources/2013-10-17_imessage-privacy/slides/iMessage_privacy.pdf

2.首先使用uncover越獄,嘗試使用ssl-kill-switch2,發現並不成功,原因不明,發了issue不只一個人有這個問題。

3. 接下來嘗試使用Frida來Hook SecTrustEvaluate(),參考部落格https://kov4l3nko.github.io/blog/2018-05-27-sll-pinning-hook-sectrustevaluate/

發現是可行的,中間人能抓到客戶端發出的第一個數據包,但是不明原因伺服器並不會回復客戶端請求。猜測是不是伺服器驗證了客戶端的證書(從蘋果官方資料中猜測是,但是我抓包沒有看到標準的TLS握手中請求客戶端證書),因此嘗試使用keychain dumper獲取客戶端apsd使用的證書,並把證書設置給中間人。

4. 使用keychain的時候有坑,首先不支援IOS12,通過Issue 36修改entitlements.xml解決,特別注意需要下載源碼按照說明重新build二進位才能使用,不然出現KILL 9錯誤。 參考pushProxy的教程,使用cydia安裝openssl後使用KeychainDumper_signed -k (記得加-k參數)可獲得私鑰。換成-i參數獲得證書。

悲劇的是,在mitmproxy里設置了客戶端證書也不行,伺服器還是不回復消息(畢竟沒開中間人也沒看到伺服器要客戶端證書啊),不知道發生了什麼。剩下的只能靠自己或者研究一下多年前的pushProxy了。

5. 由於Hook SSL證書這條路出現了不明原因的bug,所以決定Hook SSL的收發函數。

首先調查蘋果ssl的實現機制,可能是用的這個鏈接的coreTLS實現:https://opensource.apple.com/tarballs/

這裡面源碼就用了Hook發現確實在用的SecTrustEvaluate();收發函數觀察發現是SSLWrite和SSLRead,注意大小寫一定不能錯!用frida-trace發現其實SSL_write()也有,但不知為何沒有hook到調用。

最終問題解決可以看到收發的數據了,使用的程式碼如下:

 1 import frida   2 import sys   3   4   5 def on_message(message, data):   6     try:   7         if message:   8             print("[*] Received data: {0}".format(message["payload"]))   9     except Exception as e:  10         print(message)  11         print(e)  12  13  14 def run_frida_script():  15     with open("hook_sslReadWrite.js", "r") as f:  16         frida_script_code = f.read()  17         return frida_script_code  18  19 if __name__ == '__main__':  20  21     #For USB connection  22     session = frida.get_usb_device().attach("apsd")  23     script = session.create_script(run_frida_script())  24     script.on('message', on_message)  25     script.load()  26     sys.stdin.read()
function main() {        // Get SecTrustEvaluate address      var SSLWrite_prt = Module.findExportByName("Security", "SSLWrite");      var SSLRead_prt = Module.findExportByName("Security", "SSLRead");        if (SSLWrite_prt == null || SSLRead_prt == NULL) {          console.log("[Error] Security!SSLWrite/Read(...) not found!");          return;      }        //OSStatus SSLWrite(SSLContextRef context, const void *data, size_t dataLength, size_t *processed);      Interceptor.attach(SSLWrite_prt,      {          onEnter: function (args)          {              console.log("SSLWrite(" + "" + ")");              this.data = args[1];              this.processed = args[3];              //send("onEnter SSLWrite");              //var length = args[2].toInt32();              //console.log(int64(args[2]).toInt32());              //send(Memory.readByteArray(args[1], parseInt(args[2])));              //console.log(Memory.readByteArray(args[1], length));                //console.log("SSLWrite() called from:n" +     Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("n"));            },          onLeave: function (retval)          {              //send("onLeave");              var status = retval.toInt32();              if (status === 0 && !this.processed.isNull()) {                var data = Memory.readByteArray(this.data, Memory.readPointer(this.processed).toInt32());                console.log(data);              }          }      });        //OSStatus SSLRead(SSLContextRef context, void *data, size_t dataLength, size_t *processed);      Interceptor.attach(SSLRead_prt,      {          onEnter: function (args) {              console.log("SSLRead(" + "" + ")");              this.data = args[1];              this.processed = args[3];          },            onLeave: function (retval) {              var status = retval.toInt32();              if (status === 0) {                var data = Memory.readByteArray(this.data, Memory.readPointer(this.processed).toInt32());                console.log(data);              }          }      });  }    // Run the script  main();

後記:發現蘋果沒有一個公開的說明push service的文檔,看到流量獲得的資訊也有限,最後只要再查資料+逆向了……