Using Frida Exported Interfaces¶
Through this feature, you can implement methods exported by Frida RPC as FIRERPA interface extensions. You can achieve ultimate control over apps by writing functional Hook code yourself. This feature requires you to be familiar with writing Frida hook scripts. If you already have existing scripts, you’ll need to slightly modify them to use our specific methods for implementation.
Writing Export Scripts¶
You need to write scripts in a specific format. Exported function names must follow the naming convention, which must be camelCase with the first letter lowercase. For definitional names like HTTP, method naming cannot use all uppercase formats. For example, a name like sendHTTPRequest
should be written as sendHttpRequest
in the export script, not using all uppercase HTTP. Below is the general structure a script should follow.
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
})
}})
In the example script above, you can see three method definitions. The return performRpc
pattern code block is mandatory, as it ensures your values are correctly returned. The three different performRpc calls represent the following meanings:
return performRpcJVMCall(function() {
// Execute on JVM thread
})
The performRpcJVMCall code block represents executing your code in the JVM. You can use JVM-related functions inside the block, such as Java.use or other operations involving the application’s Java layer.
return performRpcJVMCallOnMain(function() {
// Execute on UI thread
})
The performRpcJVMCallOnMain code block represents executing your code in the JVM and on the UI main thread. For operations involving UI or the main thread, you need to execute them in this code block to succeed. At the same time, you should ensure your code does not block the main thread execution, otherwise it may cause the application to become unresponsive or even crash.
return performRpcCall(function() {
// Execute Normal JS code
})
In the performRpcCall code block, you can only execute some basic js code. You cannot use Java layer logic here.
Attention
You may still need to use Java.perform to wrap the entire code block to ensure Java logic executes properly.
Now that you understand the basic writing logic of our code, you can adapt your code to this format. If you just want to test, you can also directly copy this code to start experiencing it. We will also use this Hook code for explanation below.
Injecting Export Scripts¶
The method of injecting export scripts here is actually exactly the same as in the “Persistent Frida Scripts” chapter.
app = d.application("com.android.settings")
app.attach_script(script, runtime=ScriptRuntime.RUNTIME_QJS, standup=5)
After calling the injection interface, your script should be injected. You can execute the following call to check if the script is working properly.
app.is_script_alive()
Calling Exported Methods¶
The way to call exported methods in the script is very similar to using FIRERPA interfaces natively. See the example code.
app = d.application("com.android.settings")
app.exampleFunc1("FIRE", "RPA")
For multi-instance applications, you need to get the instance of the multi-instance application
app = d.application("com.android.settings", user=UID)
app.exampleFunc1("FIRE", "RPA")
The above calling method is equivalent to the one below. You can see that Func1
in the method becomes _func1
. Both are acceptable.
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'
Calling Exported Methods (HTTP Interface)¶
In addition to supporting client calls, we also support calling via HTTP. You can use it in the following form.
Tip
Starting from version 8.15, exported methods also support calls using the jsonrpc 2.0 protocol (MultiCall mode is not yet supported).
You can directly use jsonrpclib to make calls as shown below. Or, if you are familiar with the JsonRPC 2.0 protocol, you can write your own code to make requests. This is the most standard implementation with the most comprehensive reference documentation.
import jsonrpclib
server = jsonrpclib.Server('http://192.168.0.2:65000/script/com.android.settings/0')
server.example_func1("FIRE", "RPA")
Of course, you can still continue to use the previous calling protocol shown below. It is relatively simple but less standardized.
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"])
The query parameter user
in the link is the UID of the application instance, which is 0 by default and does not need to be specified. If it’s a multi-instance application, the UID needs to be specified here.
The above code calls the script export interface via HTTP. com.android.settings
is the application package name, and exampleFunc1
is the exported function name. The request parameter args
must be serialized using json.dumps
. The parameter list can also use multiple parameters simultaneously, depending on the number of parameters in your method. Providing an empty list []
means the exported function has no parameters.
If your FIRERPA has enabled interface certificates, then you need to access it via https and provide the certificate password.
headers = {"X-Token": "certificate_password"}
res = requests.post(url, data={"args": json.dumps(["LAM", "DA"])}, headers=headers, verify=False)
print (res.status_code, res.json()["result"])
HTTP Status Codes¶
Calling through the HTTP interface has specific status codes. You may need to make judgments based on them. The statuses and their meanings are as follows:
Status Code | Description |
---|---|
200 | Everything is normal |
410 | Script not injected or not installed |
500 | Script or parameter abnormality |
400 | Parameter error |
Troubleshooting¶
If you experience hang or timeout issues when calling interfaces through API or HTTP interface methods, it’s likely because your application is in the background and has been forcibly put to sleep by the system. So you may need to keep the APP running in the foreground at all times.