Exportar Interfaces con Frida¶
Puede utilizar esta función para llamar a los métodos exportados por Frida RPC como extensiones de la interfaz de FIRERPA. Puede escribir su propio código de hook funcional para lograr el máximo control sobre la APP. Esta función requiere que esté familiarizado con la escritura de scripts de hook de Frida. Si ya tiene un script existente, necesitará modificarlo ligeramente para utilizar nuestros métodos específicos.
Atención
Escribir el Script de Exportación¶
Necesita escribir un script en un formato específico. Los nombres de las funciones exportadas deben seguir la convención de nomenclatura camelCase, comenzando con una letra minúscula. Para nombres definitorios como HTTP, el nombre del método no debe usar mayúsculas completas. Por ejemplo, el nombre sendHTTPRequest debe escribirse como sendHttpRequest en el script de exportación, en lugar de usar HTTP todo en mayúsculas. A continuación se muestra la estructura general que debe seguir un script.
Java.perform(function() {
const String = Java.use("java.lang.String")
rpc.exports.exampleFunc1 = function (a, b) {
return performRpcJVMCall(function() {
// Ejecutar en el hilo de la JVM
return String.$new("Execute on JVM thread:" + a + b).toString()
})
}
rpc.exports.exampleFunc2 = function (a, b) {
return performRpcJVMCallOnMain(function() {
// Ejecutar en el hilo principal de la UI
return String.$new("Execute on Main UI thread:" + a + b).toString()
})
}
rpc.exports.exampleFunc3 = function (a, b) {
return performRpcCall(function() {
// Ejecutar código JS normal
return a + b
})
}})
En el script de ejemplo anterior, puede ver tres tipos de definiciones de métodos. El bloque de código con el patrón return performRpc es obligatorio, ya que garantiza que su valor se devuelva correctamente. Los significados de estos tres diferentes bloques de llamada performRpc son los siguientes:
return performRpcJVMCall(function() {
// Ejecutar en el hilo de la JVM
})
El bloque de código performRpcJVMCall significa que su código se ejecuta en la JVM. Dentro de este bloque, puede usar funcionalidades relacionadas con la JVM, como Java.use u otras operaciones que involucren la capa Java de la aplicación.
return performRpcJVMCallOnMain(function() {
// Ejecutar en el hilo de la UI
})
El bloque de código performRpcJVMCallOnMain significa que su código se ejecuta en la JVM y en el hilo principal de la UI. Para operaciones que involucran la UI o el hilo principal, debe ejecutarlas dentro de este bloque para que tengan éxito. Al mismo tiempo, debe tener cuidado de que su código no bloquee la ejecución del hilo principal, ya que podría causar que la aplicación no responda o incluso se cierre.
return performRpcCall(function() {
// Ejecutar código JS normal
})
El bloque de código performRpcCall solo le permite ejecutar código JS básico. No puede usar lógica de la capa Java aquí.
Atención
Ahora que comprende la lógica básica de escritura de nuestro código, puede adaptar su propio código a este formato. Si solo quiere probarlo, puede copiar directamente este fragmento de código para comenzar. En las siguientes secciones, también usaremos este código de hook para las explicaciones.
Inyectar el Script de Exportación¶
El método para inyectar el script de exportación aquí es exactamente el mismo que el descrito en el capítulo Script de Frida Persistente.
app = d.application("com.android.settings")
app.attach_script(script, runtime=ScriptRuntime.RUNTIME_QJS, standup=5)
Después de llamar a la interfaz de inyección, su script debería estar inyectado. Puede ejecutar la siguiente llamada para verificar si el script funciona correctamente.
app.is_script_alive()
Llamar a los Métodos Exportados¶
La forma de llamar a los métodos exportados dentro del script es muy similar a usar las interfaces nativas de FIRERPA. Vea el código de ejemplo.
app = d.application("com.android.settings")
app.exampleFunc1("FIRE", "RPA")
Para aplicaciones clonadas, necesita obtener la instancia de la aplicación clonada.
app = d.application("com.android.settings", user=UID)
app.exampleFunc1("FIRE", "RPA")
El método de llamada anterior es equivalente al siguiente. Puede notar que Func1 en el método se convierte en _func1. Ambas formas son válidas.
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'
Llamar a los Métodos Exportados (Interfaz HTTP)¶
Además de admitir llamadas desde el cliente, también admitimos llamadas a través de HTTP. Puede usarlo de la siguiente manera.
Consejo
Puede usar jsonrpclib directamente para realizar la llamada como se muestra a continuación. O, si está familiarizado con el protocolo JSON-RPC 2.0, también puede escribir su propio código para realizar la solicitud. Esta es la implementación de protocolo más estandarizada y con la documentación más completa.
import jsonrpclib
server = jsonrpclib.Server('http://192.168.0.2:65000/script/com.android.settings/0')
server.example_func1("FIRE", "RPA")
Por supuesto, todavía puede continuar usando el siguiente protocolo de llamada anterior, que es relativamente más simple pero menos estandarizado.
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"])
El parámetro de consulta user en el enlace es el UID de la aplicación. El valor predeterminado es 0 y no necesita ser especificado. Si es una aplicación clonada, debe especificar el UID aquí.
El código anterior llama a la interfaz de exportación del script a través de HTTP. com.android.settings es el nombre del paquete de la aplicación, y exampleFunc1 es el nombre de la función exportada. El parámetro de la solicitud args debe ser serializado usando json.dumps. La lista de parámetros también puede contener múltiples argumentos, dependiendo del número de parámetros de su método. Proporcionar una lista vacía [] indica que la función exportada no tiene parámetros.
Si su FIRERPA tiene habilitado un certificado de interfaz, necesita acceder usando HTTPS y proporcionar la contraseña del certificado.
headers = {"X-Token": "contraseña_del_certificado"}
res = requests.post(url, data={"args": json.dumps(["LAM", "DA"])}, headers=headers, verify=False)
print (res.status_code, res.json()["result"])
Códigos de Estado HTTP¶
Las llamadas a través de la interfaz HTTP tienen códigos de estado específicos que puede necesitar para tomar decisiones. Los estados y sus significados son los siguientes:
| Código de estado | Descripción del estado |
|---|---|
| 200 | Todo normal |
| 410 | Script no inyectado o no instalado |
| 500 | Excepción en el script o en los parámetros |
| 400 | Error en los parámetros |
Solución de Problemas¶
Si encuentra problemas de bloqueo o tiempo de espera al llamar a la interfaz a través de la API o la interfaz HTTP, es muy probable que su aplicación esté en segundo plano y haya sido suspendida por el sistema. Por lo tanto, es posible que necesite mantener la APP ejecutándose en primer plano en todo momento.