使用 Frida 上報資料

使用 Frida 上報資料的功能基於持久化功能,您可以透過您編寫的 Frida 腳本自動攔截方法呼叫的資料,並透過我們的特定方法進行資料上報。資料上報功能可以讓您輕鬆地將腳本攔截到的資料直接上傳到 Redis 或外部 HTTP 介面,您可以透過 Redis 或 HTTP 介面接收到上報的資料內容。同時為了最大化網路效能,支援資料壓縮後上報 (zlib)。

注意

自 9.0 版開始,我們內建使用的 Frida 17.x 將需要自行打包 frida-java-bridge 至腳本中,否則會出現 Java not defined 相關錯誤。這項變更是 Frida 官方的變更,根據官方的變更說明,您需要建立新的 Node.js 專案並引入 Java bridge,詳細請參考 https://github.com/oleavr/frida-agent-example 或使用我們提供的 tools/frida_script_generate.py 對原來的 JS 腳本進行二次封裝。

編寫上報腳本

第一步,您需要改寫您的 Frida 腳本。通常 Frida 腳本具備 sendlog 等功能,可以讓您將資料傳送到外部。對於 FIRERPA,您需要使用特定的方法來傳送資料。以下使用我們針對 okhttp 流量攔截的一個範本程式碼做示範,它只是一個示範腳本,您可能無法正常使用此腳本。以下腳本與常規腳本並無太多不同之處,唯一的區別是使用了一個 emit 方法,這是 FIRERPA 的內建方法,您可以透過它方便且系統化地將資料提交到外部。

Java.perform(function() {
        Java.use("com.android.okhttp.internal.http.HttpEngine").getResponse.implementation = function() {
                var response = this.getResponse()
                var data = {}
                data["url"] = response._request.value._url.value._url.value
                data["body"] = response.body().string()
                emit("report_data", JSON.stringify(data))
                return response
        }
})

emit 方法共有兩個參數:emit(name, content)。其中 name 代表資料的類型,如果您設定的上報位址為 Redis,那麼此名稱代表的是 Redis 佇列名稱。您需要盡量使用準確的英文來描述,例如 product_info 等。content 則代表該資料的內容,類型僅支援字串 (string) 和位元組陣列 (bytes)。在上述範例中,我們將其轉換為 JSON 字串提交。

現在,您已經了解了編寫腳本所需的格式和呼叫要求,也就是如何將 Hook 到的資料提交到外部。您還需要繼續閱讀下文,以了解如何設定資料上報的目的地

資料上報目的地

資料上報目的地,代表您在腳本中 emit 的資料應該傳送到何處。目的地支援的類型有 HTTP 介面、Redis 佇列、RabbitMQ 佇列,它們之間稍有不同。

通常情況下,如果您無需關心資料來源等資訊,即無需將資料來源機器進行匹配,我們建議使用 Redis 佇列。反之,您則應使用 HTTP 或 MQTT (v5),因為這兩種協定的特性,我們可以在協定中攜帶更多的元資料,讓您可以進行更精確的裝置匹配。

上報的元資料

對於上報到 HTTP 介面和 RabbitMQ 目的地的資料,除了包含原始的上報資料外,您還可以透過協定層取得關於裝置及腳本的元資料。協定中附帶的元資料如下表所示。

欄位描述
application應用程式的套件名稱 (如 com.android.settings)
device裝置 ID (如 67b2a3d7-5004-ea2a-0d44-194de6ede8de)
encode資料編碼 (如 none)
name資料名稱 (如 report_data)
script腳本 ID (如 7c52530d)
sequence上報順序 (如 30)
timestamp上報時間 (如 1740023596914)
user多開應用程式 ID (如 0)

注意

由於 Redis 協定的特性,上報到 Redis 目的地的資料不包含任何上述元資料。

device 為裝置的唯一 ID,您可以在遠端桌面的資訊欄中找到它,通常這個 ID 是唯一且固定的。您可以透過它來標記裝置,從而建立對應關係。encode 為資料編碼,編碼支援 nonezlib。若編碼為 zlib,則您需要使用 zlib 來解壓縮資料主體。name 用來標記此上報資料的類型,它也是您使用 emit 方法時的第一個參數。sequence 代表上報資料的索引,從 0 開始,每次上報都會遞增。您可以透過此欄位對資料進行排序,或檢查是否存在上報遺失的情況。

上報連結的額外參數

在您組合上報連結時,支援您在連結中指定一些動態的 ID 參數。您可以透過變數預留位置 ${name} 的格式將其安插在連結的特定位置。透過諸如 http://192.168.1.2/report/${device_id} 的形式使用,支援的變數如下。

名稱名稱描述
device_id裝置唯一 ID
device_id_short裝置唯一 ID (BASE62 編碼的短裝置 ID)
android_idAndroid ID
serialnoro.serialno

上報到 HTTP

您需要自行編寫一個接收上報資料的 HTTP 服務,HTTP 上報介面需實作 POST 方法。FIRERPA 將透過 POST 向介面上報資料,同時會將元資料中的各個欄位編碼為 HTTP 查詢參數,您可以從中提取並處理。對於資料主體,則是攜帶在 POST 請求的 body 中。FIRERPA 在收到 502、503、504 狀態碼時會自動重試 3 次。如果您的後端正確接收並處理了上報的資料,應回傳純文字的 OKSUCCESS 並將狀態碼設為 200,以表示處理成功。

注意

HTTP 請求是多執行緒的,後端接收到的訊息可能不遵循上報順序 (sequence)。
範例上報連結 http://192.168.1.2/report/${device_id}?serialno=${serialno}
如需密碼驗證 http://user:password@192.168.1.2/report/${device_id}?serialno=${serialno}

上報到 MQTT

上報到 MQTT,對於上報的元資料,您應從 UserProperty 中提取。支援 TLS、支援使用者名稱密碼、支援單向憑證驗證 (伺服器憑證校驗),不支援雙向校驗。

範例上報連結 mqtt://test.mosquitto.org:1883/script/${device_id}/report
如需密碼驗證 mqtt://rw:readwrite@test.mosquitto.org:1884/script/${device_id}/report

如需單向驗證 mqtts://test.mosquitto.org:8883/script/${device_id}/report?verify=true&ca=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVBekNDQXV1Z0F3SUJBZ0lVQlkxaGxDR3ZkajROaEJYa1ovdUxVWk5JTEF3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2daQXhDekFKQmdOVkJBWVRBa2RDTVJjd0ZRWURWUVFJREE1VmJtbDBaV1FnUzJsdVoyUnZiVEVPTUF3RwpBMVVFQnd3RlJHVnlZbmt4RWpBUUJnTlZCQW9NQ1UxdmMzRjFhWFIwYnpFTE1Ba0dBMVVFQ3d3Q1EwRXhGakFVCkJnTlZCQU1NRFcxdmMzRjFhWFIwYnk1dmNtY3hIekFkQmdrcWhraUc5dzBCQ1FFV0VISnZaMlZ5UUdGMFkyaHYKYnk1dmNtY3dIaGNOTWpBd05qQTVNVEV3TmpNNVdoY05NekF3TmpBM01URXdOak01V2pDQmtERUxNQWtHQTFVRQpCaE1DUjBJeEZ6QVZCZ05WQkFnTURsVnVhWFJsWkNCTGFXNW5aRzl0TVE0d0RBWURWUVFIREFWRVpYSmllVEVTCk1CQUdBMVVFQ2d3SlRXOXpjWFZwZEhSdk1Rc3dDUVlEVlFRTERBSkRRVEVXTUJRR0ExVUVBd3dOYlc5emNYVnAKZEhSdkxtOXlaekVmTUIwR0NTcUdTSWIzRFFFSkFSWVFjbTluWlhKQVlYUmphRzl2TG05eVp6Q0NBU0l3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNRTBIS21JemZUT3drS0xUM1RISGUrT2JkaXphbVBnClVabUQ2NFRmM3pKZE5lWUdZbjRDRVhieVA2ZnkzdFdjOFMyYm9XNmR6ckg4U2RGZjl1bzMyMEdKQTlCN1UxRlcKVGUzeGRhL0xtM0pGZmFIamtXdzdqQndjYXVRWmpwR0lOSGFwSFJscGlDWnNxdUF0aE9neFc5U2dEZ1lsR3pFQQpzMDZwa0VGaU13K3FEZkxvL3N4RktCNnZRbEZla01lQ3ltakxDYk53UEp5cXloRm1QV3dpby9QRE1ydUJUelBICjNjaW9CbnJKV0tYYzNPalhkTEdGSk9majdwUDBqL2RyMkxINzJlU3Z2M1BRUUZsOTBDWlBGaHJDVWNSSFNTeG8KRTZ5akdPZG56N2Y2UHZlTElCNTc0a1FPUnd0OGVQbjB5aWRyVEMxaWN0aWtFRDNuSFloTVVPVUNBd0VBQWFOVApNRkV3SFFZRFZSME9CQllFRlBWVjZ4QlVGUGlHS0R5bzVWMytIYmg0TjlZU01COEdBMVVkSXdRWU1CYUFGUFZWCjZ4QlVGUGlHS0R5bzVWMytIYmg0TjlZU01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBRGdnRUJBR2E5a1MyMU43MFRoTTYvSGo5RDdtYlZ4S0xCalZXZTJUUHNHZmJsM3JFRGZaK09LUloyajZBQwo2cjdqYjRUWk8zZHpGMnA2ZGdicmxVNzFZLzRLMFRkeklqUmozY1EzS1NtNDFKdlVRMGhaL2MwNGlHRGcveFdmCitwcDU4bmZQQVl3dWVycnVQTldtbFN0V0FYZjBVVHFSdGc0aFFEV0J1VUZESlR1V3V1QnZFWHVkejc0ZWgvd0sKc013ZnUxSEZ2ank1WjBpTURVOFBVRGVwalZvbE9DdWU5YXNobFM0RUI1SUVDZFNSMlRJdG5BSWlJd2lteDgzOQpMZFVkUnVkYWZNdTVUNVhtYTE4Mk9DMC91L3hSbEVtK3R2S0dHbWZGY04wcGlxVmw4T3JTUEJnSWxiKzFJS0pFCm0vWHJpV3IvQ3E0aC9KZkI3TlRzZXpWc2xna0Jhb1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K

在上述連結中,相關資料將會被傳送到 script/${device_id}/report,您可以透過如 mosquitto_sub -L mqtt://test.mosquitto.org:1883/script/+/report 來訂閱這些訊息。

上報到 Redis

Redis 上報相對簡單,因其並未攜帶任何元資料,所以無法直接區分資料來源。您可能需要透過動態改寫注入腳本的方式來實現此行為。對於 Redis 上報,FIRERPA 會直接將資料主體透過 LPUSH 的方式推入佇列。例如,在上文的範例腳本中,上報資料將會被推入 report_data 佇列中。

注意

僅支援單例模式 (standalone) 的 Redis 服務,不支援 Redis 叢集。

注意

除了密碼欄位,請勿在 Redis 連結中加入變數預留位置。此連結為標準 Redis 函式庫支援的連結,修改其他部分可能導致無法正確解析。
範例上報連結 redis://1.2.3.4/0
如需密碼驗證 redis://:password@1.2.3.4/0

TLS + 密碼

rediss://:password@1.2.3.4/0?ssl_ca_data=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURUVENDQWpXZ0F3SUJBZ0lVUFIvcmcxK0x2aU5tYzNsc0...

注入上報腳本

當然,取得 app 實例是第一步。您可以透過上述呼叫來取得一個 app 變數,它代表了您需要注入的應用程式實例,接著您需要透過它來繼續下面的注入或脫離操作。

app = d.application("com.android.settings")

透過以下介面將上述上報腳本注入到應用程式中。當攔截到資料時,資料將會被提交到 Redis 的 report_data 佇列。在範例中,您的裝置需要能直接存取 192.168.1.10 上的相關服務,否則將無法上報資料。

app.attach_script(script, emit="redis://192.168.1.10/0")

您也可以這樣設定,如此一來您的資料將被提交到 HTTP 介面而不是 Redis (支援 https)。

app.attach_script(script, emit="http://192.168.1.10/dataReport")

當您上報的資料量很大時,啟用壓縮可以大幅提升網路傳輸的吞吐效能。您可以使用 encode 來啟用上報資料壓縮功能。當然,您之後也需要對上報的資料進行解壓縮操作。

app.attach_script(..., encode=DataEncode.DATA_ENCODE_ZLIB)

上報資料解壓縮

預設情況下,上報資料沒有任何壓縮。如果您設定了上報壓縮,您還需要在接收端使用 zlib 編碼來對上報的資料進行解壓縮。您可以使用 Python 官方函式庫 zlib 的 decompress 方法來方便地對上報資料進行解壓縮。

zlib.decompress(data)

移除上報腳本

移除上報腳本的過程很簡單,和持久化腳本中的用法一樣。

app.detach_script()