Отправка данных Frida¶
Функция отправки данных с помощью Frida основана на функции персистентности. Вы можете с помощью написанного вами Frida-скрипта автоматически перехватывать данные вызовов методов и отправлять их через наш специальный метод. Функция отправки данных позволяет легко загружать перехваченные скриптом данные напрямую в Redis, MQTT или во внешний HTTP-интерфейс; вы можете получать содержимое отправленных данных через Redis, MQTT или HTTP. Для максимальной эффективности сети поддерживается отправка со сжатием (zlib).
Внимание
Начиная с версии 9.0 встроенный Frida 17.x требует, чтобы вы самостоятельно упаковывали frida-java-bridge в скрипт, иначе будут возникать ошибки, связанные с Java not defined. Это изменение относится к официальным изменениям Frida. Согласно официальному описанию изменений, вам необходимо создать проект Node.js и добавить frida-java-bridge. Подробнее см. https://github.com/oleavr/frida-agent-example или используйте предоставленный нами pack_frida_script.py для упаковки js-скрипта.
Написание скрипта отправки¶
Обычно Frida-скрипты имеют функции send и log, которые позволяют отправлять данные наружу, но для 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(name, content). name обозначает тип данных; если в качестве адреса отправки настроен Redis, то это имя представляет собой имя очереди Redis. Старайтесь описывать его как можно точнее на английском языке, например product_info. content представляет содержимое данных, поддерживаются только строки и массивы байтов. В примере мы преобразовали данные в строку для отправки.
Теперь вы знаете формат написания скрипта и требования к вызову, то есть как отправлять перехваченные данные наружу. Вам также нужно продолжить чтение ниже, чтобы узнать, как настроить назначение отправки данных.
Назначение отправки данных¶
Назначение отправки данных — куда должны отправляться данные, переданные в скрипте через emit. Поддерживаются HTTP-интерфейсы, очереди Redis и MQTT; между ними есть некоторые отличия.
Обычно, если вам не нужно сопоставлять данные с исходным устройством, мы рекомендуем использовать очередь Redis. В противном случае следует использовать HTTP или MQTT (v5), поскольку особенности этих протоколов позволяют передавать больше метаданных и таким образом точнее сопоставлять устройство.
Метаданные отправки¶
Для данных, отправляемых в HTTP-интерфейс или MQTT, помимо самих данных можно получить метаданные об устройстве и скрипте на уровне протокола. Метаданные, передаваемые в протоколе, приведены в таблице ниже.
| Поле | Описание |
|---|---|
| application | Имя пакета приложения (например, com.android.settings) |
| device | Идентификатор устройства (например, 67b2a3d7-5004-ea2a-0d44-194de6ede8de) |
| encode | Кодирование данных (none |
| name | Имя данных (например, report_data) |
| script | Идентификатор скрипта (например, 7c52530d) |
| sequence | Порядковый номер отправки (например, 30) |
| timestamp | Время отправки (например, 1740023596914) |
| user | Идентификатор клонированного приложения (например, 0) |
Внимание
Из-за особенностей протокола Redis данные, отправляемые в Redis, не содержат указанных выше метаданных.
device — уникальный идентификатор устройства; его можно найти в информационной панели удалённого рабочего стола. Обычно этот идентификатор уникален и постоянен. С его помощью можно пометить устройство и установить соответствие. encode — кодирование данных; поддерживается none и zlib. Если указано zlib, для распаковки тела данных необходимо использовать zlib. name используется для обозначения типа отправляемых данных и является первым аргументом при вызове emit. sequence — порядковый номер отправленных данных, начинается с 0 и увеличивается при каждой отправке; по этому полю можно сортировать данные или проверять, не были ли потеряны отправки.
Дополнительные параметры ссылки отправки¶
Поддерживается указание некоторых динамических параметров id в ссылке отправки с помощью плейсхолдеров ${name}, которые вставляются в определённое место ссылки. Например, http://192.168.1.2/report/${device_id}. Поддерживаемые переменные приведены в таблице ниже.
| Название | Описание |
|---|---|
| device_id | Уникальный идентификатор устройства |
| device_id_short | Уникальный идентификатор устройства (короткий идентификатор устройства в кодировке BASE62) |
| android_id | Android ID |
| serialno | ro.serialno |
Отправка в HTTP¶
Вам необходимо самостоятельно написать HTTP-сервис для приёма отправляемых данных. HTTP-интерфейс отправки должен реализовывать метод POST. FIRERPA будет отправлять данные на интерфейс через POST, а поля метаданных будут закодированы как HTTP-параметры запроса, из которых вы можете извлекать и обрабатывать их. Тело данных передаётся в теле POST-запроса. FIRERPA автоматически повторяет попытку 3 раза при получении кодов состояния 502, 503, 504. Если ваш бэкенд корректно принял и обработал отправленные данные, он должен вернуть обычный текст OK или SUCCESS с кодом состояния 200, чтобы указать на успешную обработку.
Внимание
HTTP-запросы выполняются в нескольких потоках, поэтому сообщения, полученные бэкендом, могут не соответствовать порядку отправки (sequence).
Пример ссылки отправки¶
http://192.168.1.2/report/${device_id}?serialno=${serialno}
Если требуется стандартная HTTP-аутентификация
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 с аутентификацией по паролю
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 с аутентификацией по паролю
redis://:password@1.2.3.4/0
Redis с TLS и аутентификацией по паролю
rediss://:password@1.2.3.4/0?ssl_ca_data=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURUVENDQWpXZ0F3SUJBZ0lVUFIvcmcxK0x2aU5tYzNsc0...
Отправка в IPC¶
Отправка в IPC обычно не используется напрямую. Этот способ можно использовать только в динамических скриптах Pigeon, чтобы ваш скрипт задачи мог напрямую прослушивать сообщения, отправляемые методом emit, и обрабатывать их при использовании attach_script, а не отправлять напрямую во внешний HTTP или Redis. Таким образом вы сможете обрабатывать информацию, отправляемую js-скриптом, в Python-скрипте и реализовать взаимодействие js и Python.
Пример ссылки отправки¶
ipc://
Прослушивание отправляемых данных¶
Внимание
Код ниже можно использовать только в скрипте Pigeon; его нельзя выполнить вручную на вашем компьютере или устройстве.
Предположим, что внедряемое приложение — com.android.settings; просто используйте ipc://.
app = d.application("com.android.settings")
app.attach_script(script, emit="ipc://")
В скрипте задачи Pigeon используйте следующий способ для прослушивания и вывода данных из скрипта; самостоятельно интегрируйте его в класс задачи.
from lamda.executor import InnerTaskThread
def on_message(name, data):
print (name, data)
listener = HookScriptIPCListener("data", "com.android.settings", user=0,
threadcls=InnerTaskThread)
listener.set_callback(on_message)
listener.start()
Остановка прослушивателя¶
listener.stop()
Внимание
Сообщения, отправленные js-хуком до запуска прослушивателя, не будут получены прослушивателем.
Отправка в IPC+EVENT¶
ipc+event — это другая форма ссылки, которая позволяет вашему скрипту задачи получать сообщения и одновременно отправлять их в систему событий (то есть протокол состояния и управления на стороне устройства FIRERPA; см. раздел протокола состояния/управления в исходном коде Pigeon). Обычно вы не будете напрямую использовать этот способ, если только не собираетесь модифицировать Pigeon или напрямую подключаться к MQTT-протоколу управления устройством.
Этот способ позволяет автоматически отправлять информацию о приложении в MQTT-бэкенд, когда на устройстве не выполняется задача, например, интерфейс приёма сообщений js-хука, чтобы сообщения, полученные приложением, могли отправляться в бэкенд в реальном времени.
Пример ссылки отправки¶
Параметр event — фильтр, значения разделяются запятыми. В систему событий будут отправлены только события, перечисленные в нём; им соответствуют вызовы emit("nameA", ...) в js-скрипте.
ipc+event://?event=nameA,nameB,nameC
Прослушивание отправляемых данных¶
Вам необходимо самостоятельно подключиться к системе событий MQTT. События будут отправляться с типом script/emit; см. способ подключения и приёма событий в системе событий Pigeon.
Внедрение скрипта отправки¶
Конечно, первым шагом является получение экземпляра приложения. С помощью описанного выше вызова вы можете получить переменную 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. Вы можете удобно распаковать отправленные данные с помощью метода decompress стандартной библиотеки Python zlib.
zlib.decompress(data)
Удаление скрипта отправки¶
Процесс удаления скрипта отправки прост и аналогичен использованию персистентных скриптов.
app.detach_script()