1.30 (107)
更新至 1.30 (107)
JS API 返回值稳定化
-
所有保留到 JS 层的主要接口统一改为返回 JS 对象 / JS 数组,避免脚本直接遍历 Java
List<Map>、Map<List>等不稳定结构。 -
新增 JS 返回值规范化工具,用于将 Java 层返回的
Map、List、数组、Throwable、File、Class等转换为 JS 可稳定读取的结构。 -
推荐脚本使用点访问和数组访问:
ret.pathsresults[i].classNamemethods[i].methodName
-
不再推荐旧写法:
ret.get("sources")sources.get(i).get("path")item.get("className")
-
sources、details等字段保留为调试信息,不建议脚本逻辑依赖。
Dex Dump 与 Dex 搜索 API
-
dex.dumpDexCookies(options)返回结构稳定化。- 新增
ok - 新增
count - 新增
outputDir - 新增
paths - 新增
dumpedPaths - 保留
sources作为调试详情
- 新增
-
dex.dumpDexCookies默认输出文件名使用cookie_前缀。- 默认格式:
cookie_<index>_<addressHex>_<fileSize>.dex - 示例:
cookie_000_71d518e000_3777364.dex
- 默认格式:
-
dex.findMethods(query)改为返回 JS 数组。 -
dex.findMethod(query)未找到时返回null。 -
dex.findMethods(query)新增 / 完善 smali 特征搜索。- 支持
smaliContains - 兼容
smaliKeywords - 兼容
smaliKeyword - 兼容
smali
- 支持
-
dex.findMethods支持仅通过 smali 特征定位方法,不再要求必须提供className、methodName或proto。 -
dex.findMethods返回结果统一包含:classNamemethodNameprotodescriptorpathscorereasonsstringsinvokessmaliHead
-
dex.inspectMethodInFile(options)严格化。path必填className必填methodName必填proto可选- 不再默认历史目标方法
- 不再承担搜索职责,只用于精确检查指定方法
-
dex.locateMethodInCookieDumps(options)严格化。className必填methodName必填proto可选- 不再默认历史目标方法
-
dex.dumpClassDex(options)返回稳定字段:okcountpathsdumpedfailed
-
dex.dumpLoadedClassDex(options)返回稳定字段:okclassNamepathpathssize
-
dex.dumpMemory(options)返回稳定字段:okcountoutputDirpathsdumped
-
dex.scanMemory(options)/dex.dumpMemoryRaw(options)增加稳定candidates字段。 -
dex.runtimeSources()改为返回 JS 数组。 -
dex.runtimeLoaders()改为返回 JS 数组。 -
dex.registerLoader(loader, path)严格化。loader必须是ClassLoaderpath必须是非空字符串- 非法参数不再兜底为默认 loader
-
dex.setLimits(options)/dex.limits()返回稳定 JS 对象。
移除 Dex 旧式过渡接口
-
移除或不再作为 JS 推荐接口暴露以下过渡 API:
dex.scanDumpDirdex.fromDumpDirdex.dumpCookieDexdex.dumpFromCookiesdex.dumpRawDexdex.traceStringsdex.inspectDumpedMethoddex.findMethodInCookieDumpsdex.dumpUnpackedDexForMethoddex.dumpTargetDexdex.dumpDexForMethod
-
新脚本应直接使用:
dex.dumpDexCookies(...)ret.pathsdex.findMethods(...)dex.inspectMethodInFile(...)
JS 对象与 Java 对象边界
-
新增对象类型判断 API:
xhh.objectKind(value)xhh.isJsObject(value)xhh.isJavaObject(value)
-
xhh.objectKind(value)返回稳定 JS 对象,包含:kindisJsObjectisJavaObjectisPrimitiveisNullisUndefinedrawClassjavaClasstext
-
文档中明确推荐使用 JS 对象 / JS 数组编写脚本逻辑。
-
Java 对象仅用于调用 Android / Xposed / App 运行时方法,不建议把 Java
Map/List当作脚本数据结构使用。
Rhino / JS 语法边界
-
JS 运行时继续基于 Rhino。
-
运行时显式启用 ES6 语言版本。
-
新增
xhh.jsEngine(),用于查看当前 JS 引擎能力。 -
发现并记录 Rhino 在循环体内
const/let词法绑定上的局限:for (...) { const path = paths[i]; }在部分 Rhino 场景可能不能可靠地每轮重新绑定。
-
取消 “ES6 词法绑定自检失败即阻断脚本启动” 的行为。
-
当检测到 Rhino 不支持可靠循环词法绑定时,脚本仍可继续运行,并通过
xhh.jsEngine()查看状态。 -
新增稳定遍历 API:
xhh.each(items, callback)
-
推荐遍历
ret.paths、results等数组时使用:xhh.each(paths, function (path, i) { ... })
-
文档中新增 “JS 脚本语法边界说明”,明确
const的适用范围与推荐通用语法。
Xposed JS API
-
xposed.e(tag, msg, any)第三参处理增强。- 支持 Java
Throwable - 支持 JS Error
- 支持普通对象
- 支持字符串
- 支持
null/undefined
- 支持 Java
-
xposed.getJavaStackTrace()返回 JS 数组。 -
xposed.getAppStackTrace()返回 JS 数组。 -
xposed.stackTrace()返回 JS 数组。 -
xposed.listRemoteFiles()返回 JS 字符串数组。 -
xposed.getFrameworkProperties()返回稳定 JS 对象。 -
xposed.getModuleApplicationInfo()返回稳定 JS 对象。 -
xposed.raw.call(...)返回值经过 JS 稳定化转换。 -
Hook 回调中的
chain.getArgs()返回 JS 数组。
XHH / RPC / MCP API
xhh.info()返回稳定 JS 对象。xhh.hasGrant(name)保持布尔返回。xhh.rpc.register_method(name, callback)返回稳定 JS 对象。xhh.rpc.unregister_method(name)返回稳定 JS 对象。xhh.rpc.unregister_all_methods()返回稳定 JS 对象。- MCP
list_methods返回methods: [],每一项为稳定 JS 对象。 - MCP
invoke_method的result尽量经过 JSON-safe / JS-safe 转换。 - RPC 回调返回值统一经过稳定化处理,减少 Java 对象泄漏到 JS/MCP 边界。
Settings / Env / Console / Java API
-
settings.get(key)返回值经过 JS 稳定化转换。 -
settings.all()返回稳定 JS 对象。 -
env文档补充常用字段说明。 -
console文档补充与xposed日志的推荐使用方式。 -
Java.type(name)文档补充 Java 对象边界说明。 -
明确 Java 对象与 JS 对象的职责划分:
- Java 对象用于反射和运行时调用。
- JS 对象用于脚本配置、返回值和业务逻辑。
示例脚本
-
更新脱壳示例为新版推荐格式:
dex.dumpDexCookies(...)ret.pathsxhh.isJsObject(ret)
-
更新 Smali 特征查找示例:
- 使用
dex.findMethods({ path, smaliContains, limit }) - 使用
xhh.each(paths, callback)遍历 - 找到第一个匹配后
return false停止搜索 - 最后输出命中的 dex 文件、类名、方法名和方法签名
- 使用
-
移除旧示例中的:
scanDumpDirfromDumpDir- Java
Map.get - Java
List.get - 正则解析
dumpRet.toString()
-
新增 / 更新 smoke test 示例,覆盖主要 JS API 返回值稳定性。
文档
- 参考 Python 文档风格重写 JS API 文档。
- API 参考改为 “一个标题一个方法” 的形式,便于查阅。
scripts/js_api.rst重写为 JS API 总参考。scripts/dynamic_dex_scan/source_api.rst重写为 Dex Source API 参考。scripts/examples.rst更新为新版推荐脚本。scripts/boundaries.rst补充 JS 语法边界、对象边界和 Rhino 限制。scripts/dynamic_dex_scan/examples.rst更新脱壳与特征查找示例。- 修复 RST 标题下划线过短导致的
Title underline too short警告。
WebIDE 运行控制优化
-
优化 WebIDE 中目标应用重启与终止逻辑。
-
“重启并同步” 现在会优先使用 Root 权限关闭并重新打开目标应用。
-
“重启同步并调试” 现在会优先使用 Root 权限关闭并重新打开目标应用,再进入调试流程。
-
当设备具备 Root 权限时,WebIDE 可通过 Root 执行
force-stop与启动命令,不再要求 XiaoHeiHook 当前处于前台。 -
当设备没有 Root 权限时,仍使用普通 Android 启动方式,此时需要 XiaoHeiHook 处于前台才能可靠拉起目标应用。
-
优化 Root 启动策略。
- 优先使用
am start启动目标应用入口 Activity。 - 无法解析入口 Activity 时,回退使用
monkey启动目标应用。
- 优先使用
-
“终止调试/运行” 按钮行为调整。
- 无论当前是否处于调试模式,都会强制终止目标应用。
- 有 Root 权限时使用 Root
force-stop。 - 无 Root 权限时使用普通终止能力。
-
WebIDE API 返回信息增加运行控制状态字段,便于前端判断执行结果。
launchModerootLaunchOkrootLaunchMessageterminatedterminate
JS Runtime Java Bridge 重构
- 重构 JS Runtime 的 Java Bridge,将原本集中在
JsHookRuntime内的桥接逻辑拆分为独立模块,提升维护性。 Java.type()现在返回 JS 友好的JavaClassWrapper,不再直接返回裸java.lang.Class。- 新增 JavaClass wrapper 语法,支持直接读取静态字段,例如
Toast.LENGTH_SHORT。 - 新增 JavaClass wrapper 静态方法调用,支持
Looper.getMainLooper()等自然写法。 - 新增 JavaClass wrapper 构造函数调用,支持
new Handler(Looper.getMainLooper())。 - 新增 JavaObject wrapper,支持 Java 实例方法直接调用,例如
handler.post(...)、toast.show()。 - 新增 Java 对象字段读取与写入的 wrapper 支持。
- 新增 raw Class 入口,支持
classObject与getRawClass()获取原始java.lang.Class。 - 新增
java.lang.Class方法透传能力,支持Application.getDeclaredMethod(...)等反射写法。 - 新增 Java varargs 参数转换,支持
getDeclaredMethod("attach", ContextClass)这类调用。 - 新增 JS function 到 Java SAM 接口的自动代理转换,支持
handler.post(function () {})。 - 新增显式
Java.proxy()支持,可用于Runnable、listener、callback 等 Java 接口实现。 - 增强
Java.proxy()对单方法接口 function 写法与多方法接口 object 写法的支持。 - 增强 Rhino Context 进入逻辑,确保 Java proxy 回调可以安全调用 JS 函数。
- 增强 Java 方法、构造器和字段解析,统一处理重载评分、参数转换和返回值包装。
- 优化数字参数重载选择,使整数 JS number 优先匹配 Java
int/long,避免误选double重载。 - 修复
StringBuilder、AtomicInteger等构造返回值被错误当作 JS 基础值的问题。 - 保留低层反射 API,包括
Java.callStatic()、Java.call()、Java.newInstance()、Java.get()、Java.set()等。 - 调整
xposed.hook()等入口,支持接收 wrapper 返回的Method、Class等对象。
文件与资源桥接 API
-
新增
xhh.fs文件与资源桥接接口。 -
新增目标 App 私有目录查询能力。
xhh.fs.appDirs(context)- 通过目标 App
Context动态读取私有目录。 - 不再要求脚本硬编码
/data/user/0、/data/data或/sdcard。 - 兼容多用户、多 profile 与目标 App 真实运行用户。
-
新增基础文件操作接口。
xhh.fs.join(...)xhh.fs.exists(path)xhh.fs.isFile(path)xhh.fs.isDirectory(path)xhh.fs.mkdirs(path)xhh.fs.readText(path[, charsetOrOptions])xhh.fs.writeText(path, text[, charsetOrOptions])xhh.fs.appendText(path, text[, charsetOrOptions])xhh.fs.readBytes(path[, options])xhh.fs.writeBytes(path, bytes[, options])xhh.fs.copy(src, dst[, options])
-
文本和二进制读取默认限制为 16MB,避免目标 App 进程因误读大文件导致 OOM。
-
读取上限可通过参数调整,适合少量配置、HTML、CSS、图片等脚本资源场景。
-
新增脚本路径查询接口。
xhh.fs.scriptRoot()xhh.fs.scriptDir()xhh.fs.assetsDir()xhh.fs.assetPath(relativePath)
-
脚本根目录默认解析为当前用户 Documents 下的
XiaoHeiHook目录。 -
脚本根目录支持通过设置修改,不再在 JS API 实现中硬编码。
-
新增脚本 assets 读取接口。
xhh.fs.readAssetText(relativePath[, charsetOrOptions])xhh.fs.readAssetBytes(relativePath[, options])
-
assets资源访问严格限制在当前脚本自己的assets/目录内。 -
禁止通过 assets API 读取脚本源码、其他脚本资源或 assets 外部文件。
-
新增资源复制到目标 App 私有目录的接口。
xhh.fs.appAssetDir(context[, options])xhh.fs.copyAssetToApp(context, assetRelativePath[, targetRelativePath[, options]])xhh.fs.syncAssetsToApp(context[, options])
-
copyAssetToApp返回值包含复制后的完整目标路径,便于脚本直接用于ImageView、WebView、本地配置文件等场景。 -
资源复制目标必须位于目标 App 私有
filesDir内。 -
复制目标子路径可由脚本指定,但必须是安全相对路径。
-
禁止复制目标路径出现绝对路径、
../目录穿越或符号链接逃逸。 -
syncAssetsToApp支持递归同步当前脚本assets/目录到目标 App 私有目录。 -
syncAssetsToApp默认覆盖已有资源。 -
clean=true时只清理当前脚本自己的资源同步目录,不会删除目标 App 其他私有文件。 -
本版本不为文件 API 增加独立 grant 权限项,保持与现有脚本权限模型兼容。
多文件脚本与 assets 资源目录
-
多文件脚本支持在脚本目录中放置
assets/目录。 -
推荐目录结构:
index.jsmain.jslib/assets/data/config.jsonassets/panel/index.htmlassets/images/icon.png
-
require()仍只用于加载 JS 模块。 -
assets/只作为资源目录,不会被当作 JS 模块目录执行。 -
脚本资源在同步时会进入 XiaoHeiHook 的脚本资源体系,目标 App 进程中可通过
xhh.fs读取或复制。 -
推荐脚本先将图片、HTML、CSS、JSON 等资源复制到目标 App 私有目录,再交给目标 App 的
ImageView、WebView或普通文件 API 使用。 -
不推荐让目标 App 直接访问脚本根目录或外部存储目录。
路径安全与运行边界
- 明确文件路径边界:脚本目录、脚本 assets 目录、目标 App 私有目录是三个不同边界。
- 目标 App 私有目录必须从目标 App
Context获取。 - 不允许脚本作者手动拼接
/data/user/0/<package>。 - 多用户设备、工作资料空间、应用分身等环境下,真实路径可能不是
/data/user/0。 assets相对路径禁止使用绝对路径。assets相对路径禁止使用../穿越。assets相对路径禁止包含 NUL 字符。- 资源复制目标路径会进行 canonical 校验,确保最终位置仍在目标 App 私有目录内。
- 普通文件 API 适合操作脚本明确拿到的路径。
- 资源 API 只适合操作当前脚本自己的
assets/。 - 不建议在脚本中保存或复制敏感源码到目标 App 可读取目录。
- 不建议把大文件长期放入目标 App 私有目录,避免影响目标 App 存储占用。
示例脚本
-
更新 Toast 示例,改为推荐的简洁 wrapper 写法。
-
新增 Java Bridge smoke test 脚本,用于验证静态字段、静态方法、构造函数、实例方法、SAM proxy 和反射 fallback。
-
新增文件读写示例,演示:
xhh.fs.appDirs(context)xhh.fs.mkdirs(path)xhh.fs.writeText(path, text)xhh.fs.readText(path)
-
新增 assets 单文件复制示例,演示:
xhh.fs.readAssetText("data/config.json")xhh.fs.copyAssetToApp(context, "images/icon.png")- 使用返回的完整路径加载本地资源
-
新增 assets 批量同步示例,演示:
xhh.fs.syncAssetsToApp(context, { overwrite: true })xhh.fs.appAssetDir(context)- 使用同步后的资源目录构造本地文件路径
-
新增多文件资源展示示例。
- 示例脚本在应用启动时复制资源文件到目标 App 私有目录。
- 示例脚本会尝试在首个 Activity 中弹出 Dialog,并使用
ImageView展示复制后的图片。 - 完整代码位于
https://github.com/wojiaoyishang/XiaoHeiCat/tree/master/examples/multi_asset_showcase
文档
- 更新脚本 API 文档,补充 Java Bridge wrapper、自动 SAM 转换和
Java.proxy()新语法说明。 - 更新边界文档,说明
JavaClassWrapper、JavaObjectWrapper、JS function、JS object 与 Java 对象之间的转换规则。 - 更新桥接文档,说明
Java.type()的新行为以及 raw Class 兼容入口。 - 更新示例文档,加入
Application.attach、主线程 Handler 和 Toast 的推荐写法。 - 新增文件与资源桥接 API 文档,集中说明
xhh.fs的参数、返回值、示例和源码位置。 - 新增文件路径边界说明,重点解释脚本目录、assets 目录、目标 App 私有目录之间的关系。
- 新增文件 API 常见错误说明。
- 修复文档搜索中
ChineseStemmer is not defined导致前端搜索异常的问题。 - 调整 Sphinx 中文搜索相关配置,补充中文搜索 stemmer fallback。
- 修复 RST 标题层级与标题下划线长度问题,避免构建时出现
Title underline too short警告。