# Schnittstellen mit Frida exportieren

Mit dieser Funktion können Sie von Frida RPC exportierte Methoden als FIRERPA-Schnittstellenerweiterungen aufrufen. Durch das Schreiben Ihres eigenen funktionalen Hook-Codes können Sie die ultimative Kontrolle über die APP erlangen. Diese Funktion erfordert, dass Sie mit dem Schreiben von Frida-Hook-Skripten vertraut sind. Wenn Sie bereits über Skripte verfügen, müssen Sie diese geringfügig anpassen, um unsere spezifischen Methoden zu verwenden.

```{attention}
Ab Version 9.0 erfordert die von uns verwendete integrierte Frida-Version 17.x, dass Sie `frida-java-bridge` selbst in das Skript packen. Andernfalls treten Fehler wie `Java not defined` auf. Diese Änderung ist eine offizielle Änderung von Frida. Gemäß den offiziellen Änderungshinweisen müssen Sie ein neues Node.js-Projekt erstellen und die Java-Bridge importieren. Details finden Sie unter https://github.com/oleavr/frida-agent-example oder verwenden Sie unser bereitgestelltes Skript `tools/frida_script_generate.py`, um das ursprüngliche JS-Skript neu zu verpacken.
```

## Schreiben des Export-Skripts

Sie müssen Skripte in einem bestimmten Format schreiben. Die Namen der exportierten Funktionen müssen den Namenskonventionen folgen, insbesondere camelCase mit einem kleinen Anfangsbuchstaben. Für definitorische Namen wie HTTP darf der Methodenname nicht in Großbuchstaben geschrieben werden. Zum Beispiel sollte der Name `sendHTTPRequest` im Export-Skript als `sendHttpRequest` und nicht mit dem großgeschriebenen HTTP geschrieben werden. Nachfolgend finden Sie die allgemeine Struktur, der ein Skript folgen sollte.

```js
Java.perform(function() {
const String = Java.use("java.lang.String")
rpc.exports.exampleFunc1 =  function (a, b) {
        return performRpcJVMCall(function() {
                // Auf dem JVM-Thread ausführen
                return String.$new("Execute on JVM thread:" + a + b).toString()
        })
}
rpc.exports.exampleFunc2 = function (a, b) {
        return performRpcJVMCallOnMain(function() {
                // Auf dem Haupt-UI-Thread ausführen
                return String.$new("Execute on Main UI thread:" + a + b).toString()
        })
}
rpc.exports.exampleFunc3 = function (a, b) {
        return performRpcCall(function() {
                // Normalen JS-Code ausführen
                return a + b
        })
}})
```

Im obigen Beispielskript sehen Sie drei Methodendefinitionen. Der Codeblock mit dem Muster `return performRpc` ist zwingend erforderlich, da er sicherstellt, dass Ihr Wert korrekt zurückgegeben wird. Die Bedeutungen dieser drei verschiedenen `performRpc`-Aufruf-Codeblöcke sind wie folgt:

```js
return performRpcJVMCall(function() {
        // Auf dem JVM-Thread ausführen
})
```

Der Codeblock `performRpcJVMCall` bedeutet, dass Ihr Code in der JVM ausgeführt wird. Innerhalb des Blocks können Sie JVM-bezogene Funktionen verwenden, wie z.B. `Java.use` oder andere Operationen, die die Java-Ebene der Anwendung betreffen.

```js
return performRpcJVMCallOnMain(function() {
        // Auf dem UI-Thread ausführen
})
```

Der Codeblock `performRpcJVMCallOnMain` bedeutet, dass Ihr Code in der JVM und im Haupt-UI-Thread ausgeführt wird. Für Operationen, die die Benutzeroberfläche oder den Haupt-Thread betreffen, müssen Sie diese in diesem Block ausführen, um erfolgreich zu sein. Gleichzeitig müssen Sie darauf achten, dass Ihr Code die Ausführung des Haupt-Threads nicht blockiert, da dies dazu führen kann, dass die Anwendung nicht mehr reagiert oder sogar abstürzt.

```js
return performRpcCall(function() {
        // Normalen JS-Code ausführen
})
```

Im Codeblock `performRpcCall` können Sie nur einfachen JS-Code ausführen; die Logik der Java-Ebene kann hier nicht verwendet werden.

```{attention}
Möglicherweise müssen Sie den gesamten Codeblock weiterhin mit `Java.perform` umschließen, um sicherzustellen, dass die Java-Logik korrekt ausgeführt wird.
```

Nachdem Sie nun die grundlegende Logik zum Schreiben unseres Codes verstanden haben, können Sie Ihren Code an dieses Format anpassen. Wenn Sie es nur testen möchten, können Sie diesen Code auch direkt kopieren, um loszulegen. Wir werden diesen Hook-Code auch für die nachfolgende Erklärung verwenden.

## Injizieren des Export-Skripts

Die Methode zum Injizieren des Export-Skripts ist hier tatsächlich dieselbe wie im Kapitel `Persistente Frida-Skripte`.

```python
app = d.application("com.android.settings")
app.attach_script(script, runtime=ScriptRuntime.RUNTIME_QJS, standup=5)
```

Nach dem Aufruf der Injektionsschnittstelle sollte Ihr Skript injiziert sein. Sie können den folgenden Aufruf ausführen, um zu überprüfen, ob das Skript ordnungsgemäß funktioniert.

```python
app.is_script_alive()
```

## Aufrufen exportierter Methoden

Die Art und Weise, wie exportierte Methoden innerhalb des Skripts aufgerufen werden, ist der nativen Verwendung von FIRERPA-Schnittstellen sehr ähnlich. Bitte sehen Sie sich den Beispielcode an.

```python
app = d.application("com.android.settings")
app.exampleFunc1("FIRE", "RPA")
```

Für Apps mit mehreren Instanzen (Multi-Instanz-Apps) müssen Sie die Instanz der jeweiligen App abrufen.

```python
app = d.application("com.android.settings", user=UID)
app.exampleFunc1("FIRE", "RPA")
```

Die obige Aufrufmethode ist äquivalent zur nachfolgenden. Sie können feststellen, dass `Func1` in der Methode zu `_func1` wird. Beide Schreibweisen sind zulässig.

```python
app.example_func1("FIRE", "RPA")
```

```python
>>> app.example_func1("FIRE", "RPA")
'Hello World:FIRERPA'
>>> app.example_func2("FRI", "DA")
'Hello World:FRIDA'
>>> app.example_func3("FIRE", "RPA")
'FIRERPA'
```

## Aufrufen exportierter Methoden (HTTP-Schnittstelle)

Neben der Unterstützung von clientseitigen Aufrufen unterstützen wir auch Aufrufe über HTTP. Sie können es im folgenden Format verwenden.

```{tip}
Ab Version 8.15 unterstützen exportierte Methoden auch den Aufruf über das JSON-RPC 2.0-Protokoll (der MultiCall-Modus wird noch nicht unterstützt).
```

Sie können `jsonrpclib` wie unten gezeigt direkt für Aufrufe verwenden. Wenn Sie mit dem JSON-RPC 2.0-Protokoll vertraut sind, können Sie auch Ihren eigenen Code schreiben, um Anfragen zu stellen. Dies ist die standardisierteste Protokollimplementierung mit der umfassendsten verfügbaren Dokumentation.

```python
import jsonrpclib
server = jsonrpclib.Server('http://192.168.0.2:65000/script/com.android.settings/0')
server.example_func1("FIRE", "RPA")
```

Natürlich können Sie auch weiterhin das folgende, frühere Aufrufprotokoll verwenden, das relativ einfach, aber weniger standardisiert ist.

```python
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"])
```

Der Abfrageparameter `user` im Link ist die UID der App-Instanz. Der Standardwert ist 0 und muss nicht angegeben werden. Handelt es sich um eine App mit mehreren Instanzen, muss hier die UID angegeben werden.

Der obige Code ruft die exportierte Schnittstelle des Skripts über HTTP auf. `com.android.settings` ist der Paketname der Anwendung und `exampleFunc1` ist der Name der exportierten Funktion. Der Anfrageparameter `args` muss mit `json.dumps` serialisiert werden. Die Parameterliste kann auch mehrere Parameter enthalten, abhängig von der Anzahl der Parameter Ihrer Methode. Die Angabe einer leeren Liste `[]` bedeutet, dass die exportierte Funktion keine Parameter hat.

Wenn für Ihre FIRERPA-Instanz Schnittstellenzertifikate aktiviert sind, müssen Sie über HTTPS zugreifen und das Zertifikatspasswort angeben.

```python
headers = {"X-Token": "Zertifikatspasswort"}
res = requests.post(url, data={"args": json.dumps(["LAM", "DA"])}, headers=headers, verify=False)
print (res.status_code, res.json()["result"])
```

## HTTP-Statuscodes

Beim Aufruf über die HTTP-Schnittstelle gibt es spezifische Statuscodes. Möglicherweise müssen Sie anhand dieser Codes Entscheidungen treffen. Die Statuscodes und ihre Bedeutungen sind wie folgt:

| Statuscode | Beschreibung des Statuscodes |
| ----------- | ----------- |
| 200 | Alles in Ordnung |
| 410 | Skript nicht injiziert oder nicht installiert |
| 500 | Skript- oder Parameterfehler |
| 400 | Fehlerhafte Parameter |


## Fehlerbehebung

Wenn beim Aufruf der Schnittstelle über die API oder HTTP Probleme wie Einfrieren oder Zeitüberschreitungen auftreten, liegt dies höchstwahrscheinlich daran, dass Ihre Anwendung im Hintergrund läuft und vom System zwangsweise in den Ruhezustand versetzt wurde. Daher müssen Sie die APP möglicherweise ständig im Vordergrund ausführen.