Global Functions 全局函数
大约 9 分钟
Global Functions 全局函数
这一页专门讲“运行时直接注入到全局作用域的函数”。我会尽量按源码实际行为来写,不只列名字,还把这些点写清楚:
- 传什么参数才算合法。
- 不传参数时默认会怎样。
- 返回值到底是什么。
- 哪些是别名,哪些只是看起来像别名。
- 传错类型时会不会报错。
先记住这 8 条
log(...args)和print(...args)都是多参数函数,但拼接方式不一样。sleep(ms)只认数字,负数会按0处理。random()返回0 ~ 1之间的小数,random(max)/random(min, max)返回整数。random(min, max)的边界是包含两端的,而且写反了也不会报错。setTimeout/setInterval返回的是任务id,不是对象。clearTimeout(id)/clearInterval(id)返回布尔值。Task(fn, delayMs?)当前本质上就是“一次性定时器别名”,不是复杂任务类。showGlobalDialog、showHostDialog、confirm用的是同一套实现。
日志与基础控制
总表
| 函数 | 典型签名 | 返回值 | 默认值 / 特殊规则 | 说明 |
|---|---|---|---|---|
log | log(...args) | null | 多参数用空格拼接 | 普通日志输出 |
print | print(...args) | null | 多参数用制表符拼接 | 更像调试打印 |
toast | toast(...args) | null | 多参数用空格拼接 | 弹宿主 Toast |
toastLog | toastLog(...args) | null | 多参数用空格拼接 | Toast + 日志双写 |
sleep | sleep(ms) | null | 非数字按 0;负数按 0 | 阻塞当前脚本线程 |
exit | exit() | 不返回 | 会中断脚本并取消定时任务 | 主动结束脚本 |
printStackTrace | printStackTrace() | null | 无 | 打印当前调用栈 |
log(...args)
参数规则
- 可以传任意数量参数。
- 每个参数都会先转成字符串。
- 最终用一个空格拼起来。
log("pkg", lpparam.packageName, "proc", lpparam.processName);
print(...args)
和 log() 很像,但当前源码里用的是制表符 \t 拼接,所以更适合“列式调试输出”。
print("pkg", lpparam.packageName, "proc", lpparam.processName);
toast(...args)
参数规则
- 可以传多个参数。
- 最终同样是用空格拼成一条文本。
- 没有可用 Android
Context时,Toast 不会真正显示。
toast("hook attached", lpparam.packageName);
toastLog(...args)
行为等于:
- 先 Toast
- 再写日志
toastLog("ready");
sleep(ms)
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
ms | number | 任意数字 | 0 | 休眠毫秒数 |
规则
- 非数字时,按
0处理。 - 负数会被修正成
0。 - 这是阻塞式休眠,会卡住当前脚本线程。
sleep(250);
sleep(-1); // 实际按 0 处理
exit()
这不是“优雅返回”,而是直接中断脚本执行。
它会做什么
- 取消所有
setTimeout任务 - 取消所有
setInterval任务 - 中断脚本运行时
所以它适合:
- 你明确知道这段脚本该停了
- 当前错误已经没必要再继续跑后续逻辑
printStackTrace()
直接打印当前堆栈,最适合临时定位“我是从哪里进来的”。
printStackTrace();
剪贴板与语言
总表
| 函数 | 典型签名 | 返回值 | 默认值 / 特殊规则 | 说明 |
|---|---|---|---|---|
setClip | setClip(text) | null | 只取第一个参数 | 写剪贴板 |
getClip | getClip() | string | 剪贴板空时返回 "" | 读剪贴板 |
getAppLanguage | getAppLanguage() | string | 无 | 返回当前系统语言标签 |
setClip(text)
参数规则
- 只读取第一个参数。
- 会先转成字符串再写入。
- 没有可用
Context时会抛异常。
setClip("https://www.wuyunai.com");
setClip(12345); // 会写成 "12345"
getClip()
返回规则
| 场景 | 返回值 |
|---|---|
| 剪贴板有文本 | 对应文本 |
| 剪贴板服务拿不到 | "" |
| 剪贴板没有内容 | "" |
没有可用 Context | 抛异常 |
const text = getClip();
log(text);
getAppLanguage()
当前直接返回:
Locale.getDefault().toLanguageTag()
所以常见结果像:
zh-CNen-USja-JP
log(getAppLanguage());
随机数
random()
支持写法
| 写法 | 返回值类型 | 范围 |
|---|---|---|
random() | number | 0 <= x < 1 的小数 |
random(max) | number | 0 到 max 之间的整数,包含两端 |
random(min, max) | number | min 到 max 之间的整数,包含两端 |
细节规则
random()
返回 Kotlin Random.nextDouble(),也就是一个:
0 <= x < 1
的小数。
random(max)
max必须是数字,否则抛错。- 返回的是整数。
- 边界包含
0和max。 - 如果你传的是负数,源码会自动把范围看成“较小值到较大值”,也就是会变成
max ~ 0。
log(random());
log(random(5)); // 可能是 0,1,2,3,4,5
log(random(-3)); // 可能是 -3,-2,-1,0
random(min, max)
min、max都必须是数字,否则抛错。- 返回的是整数。
- 边界包含两端。
min和max写反了也没关系,源码会自动交换。
log(random(3, 8)); // 3~8
log(random(8, 3)); // 仍然是 3~8
可能报错
random(max) requires a numeric max
random(min, max) requires numeric bounds
定时器与任务
总表
| 函数 | 典型签名 | 返回值 | 默认值 / 特殊规则 | 说明 |
|---|---|---|---|---|
setTimeout | setTimeout(fn, delayMs?) | number | delayMs 非数字按 0;负数按 0 | 单次执行 |
clearTimeout | clearTimeout(id) | boolean | id 非数字直接返回 false | 取消单次任务 |
setInterval | setInterval(fn, delayMs?) | number | delayMs 非数字按 0;负数按 0 | 周期执行 |
clearInterval | clearInterval(id) | boolean | id 非数字直接返回 false | 取消周期任务 |
Task | Task(fn, delayMs?) | number | 当前等价于一次性 setTimeout | 任务别名 |
setTimeout(fn, delayMs?)
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
fn | function | 回调函数 | 必填 | 不传会抛错 |
delayMs | number | 任意数字 | 0 | 延迟毫秒数 |
规则
fn不是函数时直接抛错。delayMs非数字时按0。delayMs负数时也会被修正成0。- 返回值是任务
id,后面用于clearTimeout(id)。
const id = setTimeout(() => {
log("run once");
}, 1000);
clearTimeout(id)
返回规则
| 场景 | 返回值 |
|---|---|
| 成功取消已存在任务 | true |
id 不存在 | false |
id 不是数字 | false |
const id = setTimeout(() => log("x"), 5000);
clearTimeout(id);
setInterval(fn, delayMs?)
和 setTimeout 基本同规则,但会循环执行。
特别说明
- 只要脚本没中断、任务没被取消,就会继续调度下一轮。
- 如果回调里触发脚本中断,定时器会停止。
const id = setInterval(() => {
log("tick");
}, 1000);
clearInterval(id)
规则和 clearTimeout(id) 一样,也是返回 boolean。
clearInterval(id);
Task(fn, delayMs?)
这一项最容易被写错。
当前源码里:
Task(fn, delayMs?)
本质上就是一次性的:
setTimeout(fn, delayMs)
也就是说:
- 它不是任务对象类
- 也不是带复杂
options的构造器 - 返回值同样是任务
id
Task(() => {
log("run once");
}, 800);
可能报错
setTimeout requires a function callback
setInterval requires a function callback
Task requires a function callback
Shell、项目与加载
总表
| 函数 | 典型签名 | 返回值 | 默认值 / 特殊规则 | 说明 |
|---|---|---|---|---|
shell | shell(command) | object | 空命令也会尝试执行 | 执行 shell |
loadDex | loadDex(path) | boolean | 相对路径按当前项目根目录解析 | 加载外部 Dex |
getProjectDir | getProjectDir(suffix?) | string | 无参数返回项目根目录 | 获取当前项目目录 |
require | require(path) | any | null | 自动补 .js | 载入项目内部模块 |
shell(command)
参数
| 参数 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
command | string | 任意 shell 命令文本 | "" | 执行命令 |
返回对象字段
| 字段 | 类型 | 说明 |
|---|---|---|
stdout | string | 标准输出 |
success | boolean | 是否执行成功 |
stderr | string | 标准错误 |
mode | string | 当前 shell 模式 |
mode 常见值
当前源码里可能出现:
ROOTSHIZUKUSHIZUKU_FALLBACKUSERNONE
const result = shell("id");
log(JSON.stringify(result, null, 2));
loadDex(path)
规则
path为空时返回false- 绝对路径:直接使用
- 相对路径:自动拼到当前项目目录下
- 文件不存在时返回
false - 文件存在且可读时,加载成功返回
true
loadDex("/sdcard/Download/demo.dex");
loadDex("libs/demo.dex"); // 相对项目根目录
getProjectDir(suffix?)
这个函数只在“当前脚本属于项目”时才有意义。
规则
| 调用方式 | 返回值 |
|---|---|
getProjectDir() | 项目根目录字符串,结尾通常带 / |
getProjectDir("libs/demo.js") | 项目根目录 + 你传的后缀 |
log(getProjectDir());
log(getProjectDir("libs/util.js"));
require(path)
规则
- 只适合加载当前项目里的脚本模块。
- 如果你传的路径没写
.js,源码会自动补.js。 - 会做模块缓存,同一个模块名重复
require时优先返回缓存。 - 读取不到文件或文件内容为空时,返回
null。
const util = require("./lib/util");
const api = require("api/client.js");
Hook 与反射入口
这一组函数名确实属于“全局函数”,但它们已经在专页里详细展开了。
为了避免在 API 区域里出现双份说明,这里只保留分流关系:
属于 Hook 专页的
hookhookAllhookctorhookcotrreplace
这些函数的参数签名、before / after / replace 回调、重载匹配、构造函数 Hook 写法,统一只在这里详细讲:
补一个这次实现更新后的短结论:
hook / hookAll / replace这类字符串入口,现在会先把 Rhino / Java 包装值解包,再转成真正的字符串去查找- 所以像 DexKit 结果里的
hit.name,现在通常可以直接传给这些入口
属于反射专页的
imports/importClassfindClasscallStaticMethodcallMethodinvokenewnewArrayByteArraygetField/setFieldgetStaticField/setStaticFieldprintFields
这些函数的参数匹配、类加载器、字段读写、构造对象和数组规则,统一只在这里详细讲:
这里也先记一个短结论,避免你翻页前搞混:
callStaticMethod / callMethod / invoke的methodName,现在支持先解包再转字符串getField / setField / getStaticField / setStaticField的fieldName也是同一套行为- 但真正传给 Java 方法的
...args还是按原来的类型规则匹配,不会自动把"1"变成int
宿主对话框
三个名字,其实是一套实现
源码里:
showGlobalDialogshowHostDialogconfirm
这三个最终都走同一套宿主对话框实现。
最常见写法
confirm({
title: "Confirm",
message: "Continue?",
positiveText: "OK",
negativeText: "Cancel",
onConfirm() {
toastLog("confirmed");
},
onCancel() {
toastLog("canceled");
}
});
支持字段
| 字段 | 类型 | 可填值 | 默认值 | 说明 |
|---|---|---|---|---|
title / heading | string | 任意文本 | 无 | 标题 |
message / content / body / text | string | 任意文本 | 无 | 内容文本 |
positiveText / confirmText / okText | string | 任意文本 | 无 | 确认按钮文本 |
negativeText / cancelText | string | 任意文本 | 无 | 取消按钮文本 |
cancelable | boolean | true / false | true | 是否可取消 |
onConfirm / onPositive / onOk | function | 回调函数 | 无 | 正向按钮回调 |
onCancel / onNegative | function | 回调函数 | 无 | 取消按钮回调 |
最低要求
title 和 message 里至少要有一个,否则会报错:
showGlobalDialog requires at least a title or a message
重要说明
- 这不是同步
confirm(),不会直接返回true / false。 - 当前是回调式 API,点击后通过回调通知你。
函数和对象怎么分工
你可以先这样理解:
- 一把梭的直接调用:通常是全局函数
- 需要维持状态或分模块组织:通常是全局对象
例如:
hook()是函数http.get()是对象方法files.read()是对象方法imgui.window()是对象方法mcpServer.tool()是对象方法
如果你想按“当前运行时里到底注入了哪些对象”来理解,请接着看:
