AIREITER

Frida Hook 全部命中,為什麼仍然沒有一個可用端點?

最近更新: 2026-07-31 06:15:23

三個平台的 Native 呼叫鏈,我都已經摸清楚了:裝置註冊從哪裡進入、Security SDK 在哪一層攔截請求、Native interceptor 如何改寫送出的封包,以及 JNI 透過 RegisterNatives 動態註冊了哪些方法到執行環境。Frida script 成功 attach,日誌持續滾動,每個 Hook 都能穩定命中。

但當我打開 command catalog,統計這三個平台真正能呼叫的 Native app 端點時,答案是:0。

同一個專案裡,另一個平台則已有 11 個。它們都經過實機驗證,已經遷入程式碼,並以一級 command 正常運作。

差別不在 Hook 技術。我所有 Hook 都有命中,呼叫鏈圖也畫得很完整。真正的差異,在於許多人沒意識到的一道門檻:Hook 命中與能力可用之間,其實隔著一個證據層級。這篇要談的是如何設定這道門檻、為什麼標示「尚不可用」比交付半成品端點便宜得多,以及模型在這個過程中真正能幫上什麼忙。

Hook 命中,不代表端點已可用

做 App 逆向時,很容易把「Hook 命中」誤當成終點。腳本 attach 到目標方法、log 看得到參數、回傳值與 call stack,那種「我進去了」的感覺非常真實,但也很容易誤導人。

不過 command catalog 不接受「我進去了」。它要的是:給定一個正常輸入,這個 command 能否穩定產出非空、結構正確,而且下游可以直接消費的 payload。兩者之間,距離其實很遠。

典型情況是這樣:所有 Hook 都命中,鏈路完整,log 裡甚至能看到裝置註冊請求送出、Security SDK 算出值、Native interceptor 補上 signature header。一切看似正常。結果實際跑到真機時,裝置註冊回傳零值 device ID,或 detail endpoint 拿到空 body。鏈路是通的,資料卻是空的。

這時你手上有的是什麼?是一組能觀察某個 App 版本內部行為的探針;你沒有的,則是一個可呼叫端點。若把前者當成後者交付,等於替所有後續接手的人埋下一顆地雷。

11 與 0 的差別:真正的對照組

把兩組平台並排比較,門檻就很清楚了。

對照平台(一款影片社群 App)

三款頭部內容 App

Hook 鏈路

已定位並完成實機驗證

全部已定位,所有 Hook 均命中

真實安裝狀態

取得非零裝置身分

零值 device ID/缺少真實裝置 profile

非空回應

具結構的 detail 資料

空 detail/空 body

已收錄於 command catalog

11

0

對照平台的那 11 個,並不是「Hook 比較漂亮」。每一個在遷移為一級 Python command 前,都跨過了下一節的四道證據門檻,並且保留了結構化的驗證證據。

那三個平台也不是毫無進展。Security SDK 的攔截層、Native request interceptor、JNI 動態註冊的方法批次,都已經釐清,Frida Hook 也仍保留著。但只要真實安裝狀態沒有過關,只要裝置註冊拿不到非零身分,後續輸出就會全部是空的。因此,它們的 Native app command 必須誠實維持在 0;目前可用的則是完全獨立的 Web 或 browser transport 路徑。

從整體帳來看,這批行動端能力共有 32 項,其中 23 項已經實作落地,剩下 9 項卡在「等待真實裝置 profile」;沒有任何一項被提前放進 catalog。這 9 項不是失敗,而是紀律:它精確標示了「鏈路已理解,但證據仍不足」的範圍。

端點進入 catalog 前,必須跨過四道證據門檻

拆開來看,一項 App 能力要進入 command catalog,必須同時滿足四個條件。少任何一項,都不該收錄。

第一道:真實安裝狀態。請求必須來自上游願意承認的非零安裝身分。在模擬環境或損壞 profile 上命中 Hook,不算通過。若裝置註冊回傳零值 ID,代表這一關失敗;之後不論鏈路多完整,輸出都會是空的。三個平台正是一起卡在這裡。

第二道:非空回應。請求能送出去,不等於拿得到資料。常見情況是 status code 200,body 卻是空的。這種「成功但空白的回應」比報錯更危險,因為只檢查是否丟出 exception 的流程,很容易讓它直接混過去。這道門檻要求的是結構完整、非空,而且能直接交給下游使用的資料。

第三道:完整的錯誤分類。成熟的能力失敗時,必須能說明失敗原因,而不是丟出籠統錯誤。頁面不存在、未登入、runtime 尚未就緒、回應為空,失敗機制完全不同,必須對應可區分的 error code,不能全部揉成一類。一個只能回答「失敗了」的 command,還不適合進 catalog,因為呼叫端無法據此判斷該重試、重新驗證,還是直接跳過。

第四道:封閉且可重複的測試。偶然成功一次,不算能力。同一份輸入、同一條流程,必須能重複跑通,而且要把成功結果固化成已保存的結構化驗證證據。這次成功、下次回空,表示你還沒掌控這條鏈路,只是剛好碰上某一次符合條件的 runtime 狀態。

四道門檻全部關閉後,能力才能登錄進 command catalog。只要有一道仍開著,它就還是研究資產,不是端點。這兩個詞的差異,正是整篇文章的核心。

Frida log 爆量時,按模型層級分工處理

判斷一條鏈路卡在哪道門檻,原始材料就是 Frida 產生的 trace。而 Frida trace 很容易失控:attach 幾十個方法、跑完一條完整鏈路,出現數萬到數十萬行日誌都很正常;其中大多是 polyfill、heartbeat 與不相關業務模組帶來的雜訊。

如果把整份 log 一次塞給單一模型,再問「為什麼這個 Hook 沒有資料」,得到的多半是籠統猜測;context 越大,模型越可能把兩段不相干的呼叫片段硬湊在一起。更合理的做法是先切塊,再依能力分配給不同模型層級。

這正是適合組合多個層級的案例,而不是「挑最強模型處理一切」。這四項工作,對模型的要求完全不同:

Frida log 處理步驟

所需能力

建議選擇

model id

一次閱讀完整呼叫鏈 trace

長 context,可同時讀完整條鏈路

Kimi K3

kimi-k3

標記數萬行內容(裝置註冊/網路/加密/雜訊)

成本低,可高併發執行數千次

Claude Sonnet 5

claude-sonnet-5

判斷鏈路卡在四道門檻中的哪一道

推理能力強,能明確下判斷

Claude Opus 5

claude-opus-5

比對兩份 trace 的分歧點,解釋回應為何為空

中等推理與歸因能力,能對照具體行號說明

GPT-5.6 Sol

gpt-5.6-sol

最值得特別說明的是標記這一步。它本質上是大量苦工:從數萬行裡標出雜訊,只保留裝置註冊、加密與網路相關類別,量大到不適合手動處理。這種「判斷單純、頻率極高」的任務,正是低成本層級的主場;拿推理層級來跑,純粹是在浪費錢。當 log 被壓縮為數百行已標記摘要後,再交給推理層級判斷「卡在哪道門檻」,成本與準確度才能同時對齊。

這樣拆分的效果,不用只聽我說,直接跑一輪就知道:

  1. 從一條鏈路切出一段 trace,規模可從數千行到數萬行。

  2. 先用 claude-sonnet-5 分塊標記,剔除雜訊,只留下裝置註冊、加密與網路類別。

  3. 將標記後的摘要交給 claude-opus-5,要求輸出「這條鏈路目前卡在四道門檻中的哪一道,以及證據對應哪些行」。

  4. 作為對照,把相同 raw trace 完整丟給單一模型,問同一個問題。

  5. 只要看一件事:它給你的是籠統猜測,還是明確的門檻與具體行號。這個差異,就是選擇標準。

Hook 是版本綁定的觀察探針,不是 signer

為什麼這三個平台的 Hook 會被保留,卻堅決不放進 command catalog?因為 Hook 是探針,不是 signer;兩者的本質完全不同。

Hook 綁定的是某一個特定 App build,例如 32.x 版本。它依賴的 symbol、offset 與方法布局都屬於該版本。上游一推新版本,這些位置可能全變,Hook 也會立即失效。它天生易變,而且與版本強綁定。它回答的是觀察問題:「這個版本現在內部在做什麼?」這正是 instrumentation 工具的用途。Frida 文件將 Interceptor 描述為可在 runtime 觀察與重寫呼叫的工具;它能讓你看見函式如何被呼叫、參數是什麼,但它本身不是一個能「依輸入計算 signature」的可交付成果。

能進 endpoint catalog 的 signer 則剛好相反:它必須穩定、可重複、可獨立執行,也能納入 CI。它回答的是可重用能力問題:「給我輸入,我能算出正確 signature。」即使 Hook 已讓你清楚看到 signature 演算法的骨架,這也只是演算法指紋辨識階段;距離可獨立使用的 signer,仍隔著完整的純化與差分驗證流程。

這也說明了純化階梯的優先順序:能力應先爭取純 Python 直連,接著才退而求其次,在本機 Node/V8 執行最小化的簽名片段,最後才不得已接受被動 browser bridge。Hook 甚至還沒上到這座階梯;它位於更上游的「研究」階段,而不是「實作」階段。把版本綁定的觀察探針登記為 signer,就等於替半成品研究貼上「已實作」標籤。

研究資產要怎麼歸檔,才不會兩個月後失效

不把 Hook 放進 endpoint catalog,不代表要把它丟掉。它是你投入大量時間取得的研究資產,裡面包含鏈路知識、樣本與驗證證據;直接刪除是純粹的損失。重點在於妥善歸檔,否則兩個月後連你自己都不知道當初究竟推進到哪裡。

一份 App 研究資產至少應記錄以下四項:

  • Transport 類型。鏈路走的是 Native protocol、本機 Node/V8 signature,還是被動 browser bridge。這決定日後能純化到什麼程度。

  • 證據層級。四道門檻通過了幾道。是「已定位鏈路」、還是「取得非零安裝狀態但回應為空」、或是「回應非空但無法重複」。這一項能讓下一位接手者立刻知道,距離可用究竟還差什麼。

  • App 版本/build。Hook 綁定的是哪個版本。少了這項,下次上游更新後,你無從判斷是自己的實作寫錯,還是對方換了 build。

  • 樣本摘要。以去識別化副本保留本次執行的輸入與輸出樣貌。這是重啟研究時最快的定位錨點。

把這四項記好,卡在 0 個 command 的鏈路就是能繼續推進的資產,而不是一堆過期 log。同時也能避免兩種最糟的處理方式:刪掉 Hook,假裝研究從未發生;或硬把它塞進 catalog,假裝它已經可用。後者尤其昂貴。一個「看起來能呼叫,實際只回空資料」的端點,會把成本擴散給每個信任 catalog 的下游呼叫者:他們會據此寫 integration、加上 retry,然後被空資料坑過一次,最後連整個 catalog 都不再信任。誠實標示「研究資產,0 commands」的缺口,成本只由你承擔一次,寫在 README 的一行裡。

空殼的成本高過明確缺口,在這件事上尤其明顯。

一把 key,降低多模型拆 log 的摩擦

回到第四節的 log 拆分:長 context 用來讀完整 trace、低成本層級負責批量標記、推理層級進行分類、中等推理層級做歸因。四個層級來自多家供應商,意味著四套 SDK、四種驗證方式、四種錯誤格式。要跨四個 client 分析 Frida log,多數人算一算成本與麻煩,就會放棄,最後只能拿單一模型硬啃數十萬行 trace,不是燒錢,就是根本讀不完。

AIReiter把這層摩擦拿掉:一把 key、一個相容 OpenAI 的介面,四個層級都放在後面,只要改 request body 裡的 model 欄位就能切換。

# 標記 log:使用低成本層級,支援高併發執行數千次
curl https://aireiter.com/api/v1/chat/completions \
  -H "Authorization: Bearer $AIREITER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-sonnet-5",
    "messages": [{"role": "user", "content": "<one chunk of trace + labeling instruction>"}]
  }'

# 分類門檻:切換為推理層級,其餘不變
#   "model": "claude-opus-5"
# 閱讀完整呼叫鏈:使用長 context 層級
#   "model": "kimi-k3"
# 歸因回應差異:
#   "model": "gpt-5.6-sol"

如果你已經在使用 OpenAI SDK,只要將 base_url 指向 https://aireiter.com/api/v1,其他都不用改。若使用 Anthropic SDK,則以相同 key 呼叫 POST /api/v1/messages。

這篇文章討論的工作,成本結構其實很不均衡,而折扣剛好落在最不均衡的部分。一條鏈路的 trace 有數萬到數十萬行,需要逐塊標記;單一平台就可能產生數百或數千次 claude-sonnet-5 呼叫,這是成本主體。用來閱讀完整 trace 的長 context 呼叫,每次輸入也會有數十萬 token,是另一大塊成本。分類與歸因雖然單價較高,但呼叫次數少。Claude 30% 折扣正好涵蓋標記與門檻分類這兩個最花錢的環節;GPT 半價則落在歸因層。長 context 閱讀使用 Kimi K3,同樣可透過這把 key 呼叫。

  • 取得 API key

  • 免註冊試用:先交一段 trace 給 claude-sonnet-5 標記,再讓 claude-opus-5 分類;在決定是否串接前,先看看它能否直接指出卡在哪一道門檻。

結語

在 App 逆向工程裡,Hook 命中帶來的是觀察能力:「我看得到這個版本內部正在做什麼。」endpoint catalog 要求的則是呼叫能力:「給我輸入,我能穩定產出非空且正確的資料。」兩者之間,隔著四道證據門檻:真實安裝狀態、非空回應、錯誤分類與可重複測試。

三個平台都已完整定位、所有 Hook 都命中,command 仍然是 0,這不是失敗,而是紀律。真實安裝狀態沒通過,就誠實停在 0;將 Hook 歸檔為研究資產,等取得裝置 profile 後再繼續推進。

模型在這個流程裡的角色很明確:替你切分、標記、分類與歸因數十萬行 trace,把「這條鏈路究竟卡在哪裡」從讀一下午 log 壓縮到幾分鐘。但最後「是否通過門檻」的判斷,如同差分測試中「假設是否正確」的判斷,終究不該交給模型決定;它取決於你設定的四項標準,以及能否反覆跑通的證據。至於如何讓模型在整套逆向流程中各司其職,可參考四階段總覽。