App 產品進入終點站,能做什麼事緬懷?
使用 mitmproxy + apple configurator 讓 App 永遠停留在下架前的狀態
前言
咒術迴戰
工作時間久、經手過的產品多,也開始遇到有曾經參與過的產品要進入終點站(下架);從零到有開發一個產品如同孕育一個新生命,團隊一起努力了 3–4 個月將孩子生下來;雖然後期已交由其他保母(工程師)繼續培養,最近聽到它即將進入產品生命週期的終點還是有些許遺憾。
人生也是如此,我們永遠都不知道明天是太陽先升起還是意外先來臨;唯一能做的就是珍惜當下,把事情做好。
緬懷
凡走過必留下痕跡,我們希望能在產品進入終點站之前做些什麼,讓大家還有機會回憶也讓它至少留下存在過的證明;以下方式都需要 App 還在線上,如果已經下架就真的只剩回憶了。
非技術方式 — 錄影
除了直接用 iPhone 內建的螢幕錄影功能 之外,我們也可以使用 QuickTime Player 將手機接上 Mac 使用電腦進行錄影、匯出影片。
- 在 Mac 上打開 QuickTime Player App
2. 左上角工具列選擇「檔案」->「新增影片錄製」
3. 跳出錄影介面後點擊 🔴 旁的「v」,螢幕與揚聲器選擇您接上的手機
4. 此時錄影介面就會出現手機畫面
點擊「🔴」開始錄影,回到手機上操作要錄影的內容。
錄影中會顯示當前影片大小,欲結束錄影再按一次「🔴」即可停止。
可以透過 QuickTime Player 工具列簡單的裁剪影片,最後按下「Command」+「s」匯出儲存影片到指定位置,即完成錄影留念。
影片留念的好處是未來回憶比圖片更容易串連起來,錄的越深就紀錄的越仔細,若要將更別畫面轉成圖片也可以直接截圖,很方便。
技術方式
App 技術備份可分為兩個方向;「骨」App 本身其實只是個骨架、「肉」由 API Response Data 組成才是 App 內容資料的核心。
- 骨會隨著 App 從 App Store 商城下架而消失。
- 肉會隨著 API 主機、伺服器關閉而消失。
因此我們也分成備份骨跟肉兩個技術方式。
聲明
本文僅為技術研究分享,不鼓勵利用任何技術進行非法、侵權行為。
[骨] 備份 .ipa App 安裝檔
App 在商城下架後已下載 App 的手機只要不主動刪除 App,就會一直存在在該手機上,換手機用轉移的方式也會一併轉移過去。
但如果我們不小心刪除該 App 或是換手機沒轉移,那就真的永遠跟他說再見了;此時如果有手動備份商城的 .ipa 檔案就能再次續命。
很久以前 逆向工程的文章 有提到過,但這次只需單純備份 .ipa 檔案不需敲殼,全部都是使用 Apple 官方提供的工具完成。
1.安裝 Apple Configurator 2
首先去 Mac App Store 下載安裝「 Apple Configurator 2 」
2. 將 iPhone 接上 Mac 並點擊信任電腦
接上成功後就會出現 iPhone 的主畫面。
3. 確認你的手機已安裝欲備份 .ipa 檔案的 App
我們需要在 Apple Configurator 2 卡出取代畫面,才能取得下載到暫存中的 .ipa 檔案,因此我們要先確保手機上有安裝目標 App。
4. 回到 Mac 上的 Apple Configurator 2
點兩下上面顯示的 iPhone 主畫面進入資訊頁。
切換到「App」-> 右上角「+ 加入」->「App」
完成 App Store 帳號登入後可以取得您曾經購買過的 App 列表。
搜尋找到欲備份的目標 App,選擇後點「加入」。
此時會出現等待視窗,正在 XXX 上加入 App、正在下載「XXX」。
5. 提取 .ipa 檔案
放著等他下載完成後,會跳出詢問是否要取代現有已安裝 App 視窗。
此時不要按任何動作。此時不要按任何動作。此時不要按任何動作。
我們打開一個 Finder:
左上角工具列選擇「前往」-> 「前往檔案夾」
貼上以下路徑:
1
~/Library/Group Containers/K36BKF7T3D.group.com.apple.configurator/Library/Caches/Assets/TemporaryItems/MobileApps
就可以找到下載下來正準備要安裝的目標 App .ipa 檔案:
將其複製出來即可完成 App .ipa 檔案備份。
完成檔案複製後再回去 Apple Configurator 2 點擊停止,終止操作。
[骨] 還原 .ipa App 安裝檔
一樣是將欲還原 App 的手機接上 Mac 並打開 Apple Configurator 2,進入 App 加入介面。
還原的話要選擇左下角「從我的 Mac 選擇…」
選擇備份的 App .ipa 檔案,按「加入」。
等待傳送、安裝完成,回到手機上就能重新打開 App,復活成功!
[肉] 備份最後的 API Response Data
這邊會運用到之前在 App End-to-End Testing Local Snapshot API Mock Server 文章(細節原理可參考) 中所使用到的方式跟當時弄的開源專案:
同之前用錄製 API Request & Response 來跑 E2E Testing 的技術,我們也可以用它來紀錄 App 下架、停機前最後的 API Request & Response Data。
1. 安裝 mitmproxy
1
brew install mitmproxy
mitmproxy 是一套開源的中間人攻擊,網路請求嗅探工具。
如果你不熟悉 Mitmproxy 中間人攻擊的工作原理可先參考我之前的文章:「 APP有用HTTPS傳輸,但資料還是被偷了。 」或 Mimproxy 官方文件 。
如果是用在純網路請求嗅探,用不習慣 mimproxy 介面也可以改用「 Proxyman 」可參考之前 另一篇文章 的用法。
2. 完成 mitmproxy 憑證設定
針對 HTTPS 加密連線我們需要使用根憑證抽換進行中間人攻擊,因此第一次使用須先完成手機端根憑證下載與啟用。
*如果您的 App & API Server 有實作 SSL Pinning 也需要將 Pinning 憑證加入到 mitmproxy。
- 首先確保 iPhone 手機與 Mac 電腦是連接在相同的網路環境
- 若無 WiFi 電腦連接實體網路,也可以 打開 Mac 的 WiFi 分享功能 讓手機連到 Mac 的網路
在 Terminal 啟動 mitmproxy
or mitmweb
(Web GUI 版)。
1
mitmproxy
看到這畫面代表 mitmproxy 服務已啟動,目前沒有流量進來所以是空的,掛在這個畫面不要關閉 Terminal。
- 到 Mac 網路設定查看 Mac 的 IP 位址
回到手機 WiFi 設定點擊「i」進入詳細設定,找到最下方「設定代理伺服器」:
- 伺服器輸入 Mac 電腦的 IP 位址
- 連接埠輸入 8080
- 儲存
在手機上打開 Safari 輸入: http://mitm.it/
如果出現:
1
If you can see this, traffic is not passing through mitmproxy.
代表手機的網路代理伺服器沒有設定成功或是 Mac 上沒啟動 mitmproxy
。
正常情況會出現:
此時只有 HTTP 流量能被嗅探,HTTPS 流量會報錯,我們繼續往下設定。
代表連接成功,我們找到 iOS 區塊點擊「Get mimproxy-ca-cert.pem」
- 點擊「允許」
下載完成後進入到手機的設定,會出現「已下載描述檔」點擊進入。
- 點擊進入,右上角「安裝」,輸入手機密碼完成安裝。
回到設定 -> 「一般」->「關於本機」-> 最下方「憑證信任設定」-> 啟用「mitmproxy」。
- 「繼續」完成啟用。
至此我們就完成中間人攻擊所有的前置準備工作。
要記得當前你手機所有的流量都會經過代理從你的 Mac 電腦出去, 操作完畢記得回到手機上的網路設定把代理伺服器設定關掉 ,否則手機網路 WiFi 會全部連不上對外網路。
回到 Terminal mitmproxy,一邊操做手機上的 App 就能看到所有被捕捉的 API 請求紀錄。
每個請求都能進入查看詳細 Request & Response 內容:
以上是 mitmproxy 基礎設定與實際工作。
3. 嗅探、了解 API 架構
再來就要透過 mitmproxy 的 mitmdump
服務結合我之前開發的 mitmproxy-rodo addons 錄製請求和回放請求。
我的實現原理 是將 Request 請求參數計算成 Hash 值,在回放時再次將請求拿去計算 Hash 如果在本地有找到相同 Hash 值的備份 Response 則返回,如果有多個相同 Hash 值的請求會按照順序儲存&回放。
我們可以先透過以上方法嗅探 App 的 API (或使用 Proxyman ),觀察有哪些欄位跟哪些欄位可能會影響 Hash Mapping,可以將其記錄下來,在後續設定排除, 例如有的 API 固定會帶 ?ts
這參數不影響回傳內容,但會影響 Hash 值計算導致無法找到本地的備份,我們就需要挑出來在後面的設定中排除掉 。
4.設定 mitmproxy-rodo :
使用我寫好的錄製、回放開源 Script。
細節參數設定請參考該開源專案的說明。
1
2
git clone git@github.com:ZhgChgLi/mitmproxy-rodo.git
cd mitmproxy-rodo
將上一步驟 3. 挑出來的參數填入到 config.json 設定檔案中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
"ignored": {
"*": {
"*": {
"*": {
"iterable": false,
"enables": [
"query",
"formData"
],
"rules": {
"query": {
"parameters": [
"ts",
"connect_id",
"device_id",
"device_name",
]
},
"formData": {
"parameters": [
"aidck",
"device_id",
"ver_name",
]
}
}
}
}
}
}
}
以上參數在計算 Hash 值時都會被排除,也能針對個別 Endpoint 路徑設定特定排除規則。
5. 啟用錄製,在 Terminal 下改執行:
1
mitmdump -s rodo.py --set dumper_folder=zhgchgli --set config_file=config.json --set record=true "~d zhgchg.li"
- 結尾的
"~d zhgchg.li"
意思是只截取 * .zhgchg.li 的流量。 dumper_folder
: 輸出目的目錄名稱
6. 回到手機上操作目標 App 執行欲錄製的流程路徑
- 建議重啟、重安裝 App 用最乾淨的方式開始操作
- 建議可以搭配錄影,以利記得復現步驟
邊操作的同時,就能看到輸出目錄會有很多擷取下來的 API Response Data,會照著 Domain -> API 路徑 -> HTTP 方法 -> Hash 值 -> Header-X / Content-X 存放(若相同 Hash 請求兩次,會按照順序儲存下來)。
- 重新錄製可以直接刪出輸出目錄,讓他重新擷取。
- 如果回傳的資料包含個資,請記得調整擷取內容去識別化。
[肉] 回放擷取到的 API Response Data
錄製完畢後請務必要嘗試回放一次,測試資料是否正常,若 Hash Hit 很低(回放時幾乎都沒找到對應的 Response),可以重複嗅探步驟找到那個每次執行 App 都不確定影響 Hash 值的變數並將其排除。
執行回放:
1
mitmdump -s rodo.py --set dumper_folder=zhgchgli --set config_file=config.json
dumper_folder
: 輸出目的目錄名稱- 預設本地沒有 Hash Mapping 到的 Response Data 會直接回傳 404 讓 App 空白,這樣才能知道擷取的資料有沒有效。
- 錄製擷取時有經過過的路徑頁面,回放時能重新顯示:OK!
- 錄製擷取時沒經過過的路徑頁面,回放時顯示網路錯誤:OK!
緬懷
至此,我們已經可以自行透過還原骨與最後的肉,來復現當年 App 走進終點站前的最後時光,以此緬懷那個大家一起同心協力生產出來的時光。
以此篇文章紀念第一份工作的團隊與當年從網頁後端開發邊做邊學轉職 iOS App 開發,獨立從無到有在 3–4 個月的短暫時間,與 Android、設計、PM 主管、後端同事一同順利生產出的產品,雖然它即將進入生命週期的終點站,但我永遠都會記得當年的酸甜苦辣與第一次看到他上線、有人使用的那個感動。
「謝謝」
歡迎協助貢獻
如果你也有相同的遺憾,希望本篇文章也能幫助到你,因為 mitmproxy-rodo 當初只是 POC 概念驗證下開發出的工具,歡迎協助貢獻、提出遇到的 Bug 或開 PR 修改 Bug。
有任何問題及指教歡迎 與我聯絡 。
===
View the English version of this article here.
本文首次發表於 Medium ➡️ 前往查看