使用 Frida 导出接口¶
您可以通过此功能实现将 frida RPC 导出的方法作为 LAMDA 接口扩展调用。您可以通过自行编写功能性 Hook 代码实现对 APP 控制的极致。此功能需要您熟悉 frida hook 脚本的编写,如果您已有现存脚本,您需要对您的脚本做稍微的改写,使用我们的特定方法来实现。
Frida Hook 接口导出及数据上报
编写导出脚本¶
您需要编写特定格式的脚本,这仍然不是官方 frida 的方法,导出函数名必须遵守下面的命名规范,务必是首字母小写的 camelCase
。对于定义性的名称如 HTTP
,您的方法命名不可使用全大写格式,比如常规名称 sendHTTPRequest
,您应在此写为 sendHttpRequest
,而不是使用全大写的 HTTP
。
Java.perform(function() {
rpc.exports = {
exampleFunc1: function (a, b) {
return performRpcJVMCall(function() {
var String = Java.use("java.lang.String")
var msg = String.$new("Hello World").toString()
return msg + ":" + a + b
})
},
exampleFunc2: function (a, b) {
return performRpcJVMCallOnMain(function() {
var String = Java.use("java.lang.String")
var msg = String.$new("Hello World").toString()
return msg + ":" + a + b
})
},
exampleFunc3: function (a, b) {
return performRpcCall(function() {
return a + b
})
},
}
})
您可以看到三种定义方式,其中 return performRpc..
代码块是必须有的,他用来确保您的值可以正确的返回。您的代码务必包含此代码块。对于 performRpcJVMCall
,他代表在 JVM 中执行您的代码,您可以在块里使用 Frida Java 相关功能,Java.use 等。performRpcJVMCallOnMain
,代表在 JVM 中执行您的代码,您可以在块里使用 Frida Java 相关功能,Java.use 等,同时,在应用的主进程执行,对于涉及到 UI 的操作,您需要在这个代码块里执行。最后一个 performRpcCall
,您只能在此执行一些基础的 js 代码,不可在此编写 Java 层的逻辑。注意:虽然 performRpc 代码块可以让您执行 Java,您仍然可能需要使用 Java.perform
来封装整体代码。
现在您已经了解了我们代码的基本编写逻辑,您可以将您的代码封装成这种格式的代码,如果您只是想测试,也可以直接复制这段代码来开始体验,我们下面也是利用此 Hook 代码进行讲解。
注入导出脚本¶
这里的注入导出脚本的方法和 持久化 Frida 脚本
章节中的其实是完全一样的使用方法。
app = d.application("com.android.settings")
app.attach_script(script, runtime=ScriptRuntime.RUNTIME_QJS, standup=5)
调用了注入接口后,您的脚本也应该就注入了,您可以执行如下调用来检查脚本是否正常。
app.is_script_alive()
调用导出方法¶
调用脚本内的导出方法的方式和您原生使用 LAMDA 接口是很相似的,请看示例代码。
app = d.application("com.android.settings")
app.exampleFunc1("LAM", "DA")
以上的调用方法等价于下面的,您可以发现,方法中的 大写
变成了 _小写
,这两种都是可以的。
app.example_func1("LAM", "DA")
>>> app.example_func1("LAM", "DA")
'Hello World:LAMDA'
>>> app.example_func2("FRI", "DA")
'Hello World:FRIDA'
>>> app.example_func3("LAM", "DA")
'LAMDA'
调用导出方法(HTTP接口)¶
除了支持您使用 LAMDA 客户端调用外,我们还支持您通过 HTTP 的方式调用,HTTP 方式调用与客户端调用有一些区别。
import json
import requests
url = "http://192.168.0.2:65000/script/com.android.settings/exampleFunc1"
res = requests.post(url, data={"args": json.dumps(["LAM", "DA"])})
print (res.status_code, res.json()["result"])
使用以上代码来通过 HTTP 的方式调用导出接口。其中 com.android.settings
代表应用的包名,exampleFunc1
代表导出函数的名称。请求的参数 args
必须使用 json.dumps
进行序列化。参数列表也可以同时使用多个参数,依据您的方法参数个数决定,提供空列表 []
代表该导出函数无参数。
如果您的 LAMDA 启用了接口证书,那么您需要使用 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 接口调用有特定的状态码,您可能需要根据他做判断,状态及其意义如下
状态码 200 一切正常
状态码 410 脚本未注入或未安装
状态码 500 脚本或参数异常
状态码 400 参数错误
问题排查¶
如果您通过 API 或者 HTTP 接口方式调用接口出现了卡死或者超时的问题,那大概率是因为您的应用处于后台被系统强制休眠,所以您可能需要使 APP 始终处于前台运行的状态。