# Использование Frida для отправки данных

Функция отправки данных с помощью Frida основана на механизме персистентности. Вы можете использовать написанный вами скрипт Frida для автоматического перехвата данных вызовов методов и их отправки с помощью нашего специального метода. Эта функция позволяет легко загружать перехваченные скриптом данные напрямую в Redis или на внешний HTTP-интерфейс. Вы можете получать отправленные данные через Redis или HTTP-интерфейс. Кроме того, для максимальной эффективности сети поддерживается отправка данных после сжатия (zlib).

```{attention}
Начиная с версии 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 имеют функции `send` и `log`, которые позволяют отправлять данные вовне. В FIRERPA для отправки данных необходимо использовать специальный метод. Ниже приведен пример шаблонного кода для перехвата трафика okhttp. Это всего лишь демонстрационный скрипт, и, возможно, вы не сможете использовать его в реальных условиях. Этот скрипт мало чем отличается от обычного, единственное различие — использование метода `emit`. Это встроенный метод FIRERPA, с помощью которого вы можете удобно и систематизированно отправлять данные во внешние системы.

```js
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 перед отправкой.

Теперь, когда вы ознакомились с форматом и требованиями к вызовам для написания скрипта, то есть как отправлять перехваченные данные вовне, вам необходимо прочитать далее, чтобы узнать, как настроить **место назначения для отправки данных**.

## Место назначения для отправки данных

Место назначения для отправки данных определяет, куда должны отправляться данные, переданные через `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)                                      |

```{attention}
Из-за особенностей протокола Redis, данные, отправляемые в Redis, не содержат вышеуказанных метаданных.
```

`device` — это уникальный ID устройства, который вы можете найти в информационной панели удаленного рабочего стола. Обычно этот ID уникален и постоянен. Вы можете использовать его для маркировки устройств и установления соответствий. `encode` — это кодирование данных, поддерживаются `none` и `zlib`. Если кодирование — zlib, вам потребуется использовать zlib для распаковки тела данных. `name` используется для маркировки типа отправляемых данных, это также первый параметр метода `emit`. `sequence` представляет собой индекс отправляемых данных, начиная с 0, который увеличивается при каждой отправке. Вы можете использовать это поле для сортировки данных или проверки наличия потерь при отправке.


### Дополнительные параметры в ссылке для отправки

При формировании ссылки для отправки вы можете указать в ней некоторые динамические параметры ID. Вы можете вставить их в определенные части ссылки, используя формат заполнителя `${name}`. Например, в виде `http://192.168.1.2/report/${device_id}`. Поддерживаются следующие переменные:


| Имя      | Описание |
| ----------- | ----------- |
| device_id      | Уникальный ID устройства       |
| device_id_short   | Уникальный ID устройства (короткий ID в кодировке BASE62)       |
| android_id   | Android ID        |
| serialno   | ro.serialno        |


### Отправка по HTTP

Вам необходимо самостоятельно написать HTTP-сервис для приема отправляемых данных. Интерфейс для отправки данных по HTTP должен реализовывать метод POST. FIRERPA будет отправлять данные через POST, при этом поля метаданных будут закодированы как параметры запроса HTTP, из которых вы сможете их извлечь и обработать. Тело данных передается в теле POST-запроса. FIRERPA автоматически выполнит 3 повторные попытки при получении кодов состояния 502, 503, 504. Если ваш бэкенд правильно принял и обработал данные, он должен вернуть чистый текст `OK` или `SUCCESS` и установить код состояния `200` для подтверждения успешной обработки.

```{attention}
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`.

```{attention}
Поддерживаются только Redis-серверы в автономном режиме (standalone), кластеры Redis не поддерживаются.
```

```{attention}
Кроме поля пароля, не используйте заполнители переменных в ссылке на 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`, которая представляет экземпляр приложения, в которое вы хотите внедрить скрипт. Затем вам нужно будет использовать ее для выполнения последующих операций внедрения или отсоединения.

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

С помощью следующего интерфейса внедрите вышеупомянутый скрипт в приложение. При перехвате данных они будут отправлены в очередь `report_data` в Redis. В этом примере ваше устройство должно иметь прямой доступ к соответствующей службе на `192.168.1.10`, иначе отправка данных не удастся.

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

Вы также можете настроить это следующим образом, и тогда ваши данные будут отправляться на HTTP-интерфейс, а не в Redis (поддерживается https).

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

Когда вы отправляете большие объемы данных, включение сжатия может значительно повысить пропускную способность сети. Вы можете использовать `encode` для включения функции сжатия отправляемых данных. Конечно, вам также потребуется выполнить операцию распаковки полученных данных.

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

## Распаковка отправленных данных

По умолчанию отправляемые данные не сжимаются. Если вы включили сжатие, вам необходимо на принимающей стороне использовать кодировку zlib для распаковки данных. Вы можете легко распаковать данные с помощью метода `decompress` из стандартной библиотеки Python `zlib`.

```python
zlib.decompress(data)
```

## Удаление скрипта для отправки данных

Процесс удаления скрипта очень прост и аналогичен использованию в скриптах персистентности.

```python
app.detach_script()
```