Экспорт интерфейсов с помощью Frida¶
Эта функция позволяет вам экспортировать методы, определённые через Frida RPC, и вызывать их как расширения интерфейса FIRERPA. Вы можете написать собственный код для хуков, чтобы достичь максимального контроля над приложением. Эта функция требует знакомства с написанием скриптов-хуков для Frida. Если у вас уже есть готовые скрипты, вам потребуется немного их изменить, используя наши специальные методы.
Внимание
Написание скрипта для экспорта¶
Вам необходимо написать скрипт в определённом формате. Имена экспортируемых функций должны соответствовать соглашению об именовании camelCase, то есть начинаться с маленькой буквы. Для определительных имён, таких как HTTP, в названии метода нельзя использовать полностью заглавные буквы. Например, имя sendHTTPRequest в скрипте экспорта должно быть записано как sendHttpRequest, а не с использованием заглавных букв HTTP. Ниже приведена общая структура, которой должен следовать скрипт.
Java.perform(function() {
const String = Java.use("java.lang.String")
rpc.exports.exampleFunc1 = function (a, b) {
return performRpcJVMCall(function() {
return String.$new("Execute on JVM thread:" + a + b).toString()
})
}
rpc.exports.exampleFunc2 = function (a, b) {
return performRpcJVMCallOnMain(function() {
return String.$new("Execute on Main UI thread:" + a + b).toString()
})
}
rpc.exports.exampleFunc3 = function (a, b) {
return performRpcCall(function() {
return a + b
})
}})
В приведённом выше примере скрипта вы можете видеть три способа определения методов. Использование блока кода в стиле return performRpc является обязательным, так как это гарантирует корректный возврат вашего значения. Эти три различных блока вызова performRpc имеют следующее значение:
return performRpcJVMCall(function() {
// Выполняется в потоке JVM
})
Блок кода performRpcJVMCall означает, что ваш код будет выполняться в JVM. Внутри этого блока вы можете использовать функции, связанные с JVM, например, Java.use или другие операции, затрагивающие Java-уровень приложения.
return performRpcJVMCallOnMain(function() {
// Выполняется в UI-потоке
})
Блок кода performRpcJVMCallOnMain означает, что ваш код будет выполняться в JVM и в основном UI-потоке. Операции, связанные с UI или основным потоком, должны выполняться внутри этого блока для успешного завершения. При этом необходимо следить, чтобы ваш код не блокировал выполнение основного потока, иначе это может привести к зависанию или сбою приложения.
return performRpcCall(function() {
// Выполняется обычный JS-код
})
В блоке кода performRpcCall вы можете выполнять только базовый JS-код и не можете использовать логику Java-уровня.
Внимание
Теперь, когда вы ознакомились с основной логикой написания нашего кода, вы можете адаптировать свой код под этот формат. Если вы просто хотите протестировать, вы можете скопировать этот код, чтобы начать. В дальнейшем мы будем использовать этот же код хука для объяснений.
Внедрение скрипта для экспорта¶
Метод внедрения скрипта для экспорта здесь точно такой же, как и в разделе Постоянные скрипты Frida.
app = d.application("com.android.settings")
app.attach_script(script, runtime=ScriptRuntime.RUNTIME_QJS, standup=5)
После вызова интерфейса для внедрения ваш скрипт должен быть внедрён. Вы можете выполнить следующий вызов, чтобы проверить, работает ли скрипт нормально.
app.is_script_alive()
Вызов экспортированных методов¶
Способ вызова экспортированных методов из скрипта очень похож на использование нативных интерфейсов FIRERPA. Пожалуйста, посмотрите на пример кода.
app = d.application("com.android.settings")
app.exampleFunc1("FIRE", "RPA")
Для клонированных приложений вам необходимо получить экземпляр клонированного приложения.
app = d.application("com.android.settings", user=UID)
app.exampleFunc1("FIRE", "RPA")
Приведённый выше вызов эквивалентен следующему. Вы можете заметить, что Func1 в названии метода изменилось на _func1. Оба варианта допустимы.
app.example_func1("FIRE", "RPA")
>>> app.example_func1("FIRE", "RPA")
'Hello World:FIRERPA'
>>> app.example_func2("FRI", "DA")
'Hello World:FRIDA'
>>> app.example_func3("FIRE", "RPA")
'FIRERPA'
Вызов экспортированных методов (через HTTP-интерфейс)¶
Помимо поддержки вызовов через клиент, мы также поддерживаем вызовы через HTTP. Вы можете использовать следующий формат.
Совет
Вы можете напрямую использовать jsonrpclib для вызовов, как показано ниже. Или, если вы знакомы с протоколом JsonRPC 2.0, вы можете написать свой собственный код для запросов. Это наиболее стандартизированная реализация протокола с самой полной документацией.
import jsonrpclib
server = jsonrpclib.Server('http://192.168.0.2:65000/script/com.android.settings/0')
server.example_func1("FIRE", "RPA")
Конечно, вы все еще можете продолжать использовать предыдущий протокол вызова, который относительно прост, но менее стандартизирован.
import json
import requests
url = "http://192.168.0.2:65000/script/com.android.settings/exampleFunc1?user=0"
res = requests.post(url, data={"args": json.dumps(["FIRE", "RPA"])})
print (res.status_code, res.json()["result"])
Параметр запроса user в ссылке — это UID клонированного приложения. По умолчанию он равен 0 и его можно не указывать. Если это клонированное приложение, здесь необходимо указать UID.
Приведённый выше код вызывает экспортированный интерфейс скрипта через HTTP. com.android.settings — это имя пакета приложения, а exampleFunc1 — имя экспортированной функции. Параметр запроса args должен быть сериализован с помощью json.dumps. Список параметров может содержать несколько аргументов в зависимости от количества параметров вашего метода. Пустой список [] означает, что экспортированная функция не имеет параметров.
Если в вашем FIRERPA включена аутентификация по сертификату, вам необходимо использовать https для доступа и предоставить пароль сертификата.
headers = {"X-Token": "пароль_сертификата"}
res = requests.post(url, data={"args": json.dumps(["LAM", "DA"])}, headers=headers, verify=False)
print (res.status_code, res.json()["result"])
Коды состояния HTTP¶
При вызове через HTTP-интерфейс используются определённые коды состояния, которые могут понадобиться вам для проверок. Коды и их значения следующие:
Устранение неполадок¶
Если при вызове интерфейса через API или HTTP вы сталкиваетесь с зависанием или тайм-аутом, скорее всего, это связано с тем, что ваше приложение находится в фоновом режиме и было принудительно приостановлено системой. Поэтому вам может потребоваться обеспечить, чтобы приложение всегда работало на переднем плане.