Отправка данных 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_idAndroid ID
serialnoro.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()