跳到主要内容
版本:3.0.3

快照模块

快照模块 - ngkit

本模块用于管理 App 的 快照,以下列出了支持的管理功能:

  • 创建、备份与恢复快照
  • 读取并验证已有快照
  • 将快照归档、压缩为 留存包
  • 将留存包解压、释出为快照
  • 执行清理动作
  • 设置上述动作的最大执行时间
提示
  • 本模块的基本功能由 ngtool 命令行实用工具提供。
  • 本模块将自动管理文件系统所有者与权限,通常无需补充介入:
    • 新创建的 快照留存包 的所有者为 mobile:mobile,权限为 0755
    • 快照内容树中所有者与权限与原始文件系统保持一致。
  • 此模块支持以 OpenAPI 方式独立调用。

技术词汇

快照与留存包

本模块支持备份/恢复的内容包括:

  • App 的名称、版本和图标等元信息
  • App 的数据容器、分组容器和插件容器
  • App 用户偏好设置
  • 系统钥匙串中与 App 相关的条目
  • 剪贴板中保存的内容
  • Safari 的 Cookies 及其他浏览器数据
  • “定位”隐私授权状态及缓存的数据
  • “通知”隐私授权状态及缓存的数据
  • 其他隐私授权状态
  • 伪装参数表
  • 其他与 App 有关的 NG 持久化选项

本模块支持使用 clonefile(2) 系统调用,以 APFS CoW 写时拷贝技术 创建快照。

快照 是一个存有上述内容副本的文件夹,其目录结构大致如下:

.
├── Info.plist
├── Configuration.plist
├── BundleProxies
│ └── 0582E5CC-A544-4E44-849E-AD02901217EB
│ ├── Info.plist.enc
│ └── iTunesMetadata.plist.enc
├── DataContainers
│ └── F8C70FC2-F69D-4419-8106-11015469DAC9
│ ├── Documents
│ ├── Library
│ │ ├── Caches
│ │ └── Preferences
│ ├── SystemData
│ └── tmp
├── Library
│ ├── Caches
│ │ ├── Location
│ │ ├── Pasteboard
│ │ ├── com.apple.WebKit.Networking
│ │ └── com.apple.WebKit.WebContent
│ ├── Cookies
│ ├── Keychains
│ │ └── Combined.plist.enc
│ ├── Preferences
│ │ └── com.edgapp.hooker.json.enc
│ ├── Safari
│ ├── TCC
│ │ └── Combined.plist.enc
│ ├── Specifications
│ │ └── Combined.plist.enc
│ └── WebKit
└── SharedContainers
└── B11456FF-68EE-4780-BA84-56EE3B929D8B
└── Library
├── Caches
└── Preferences

留存包 是将快照文件夹先执行归档动作,后执行压缩动作后产出结果文件;其后缀名通常为 .tgz.tar.gz。可以将快照数据通过留存包形式通过网络传输。

归档与释出

本模块支持将快照通过 tar 归档为单个文件。

tar 为多个文件和目录创建一个档案(归档),通常与压缩工具(如 gzipbzip2)结合使用。

警告

在文件数量巨大的情形下:相比于移动、拷贝、压缩与解压而言,归档与释出过程是更为耗时的。如果快照无需用于网络传输,那么归档/压缩或解压/释出动作是没有进行的必要的,建议略去。

压缩与解压

默认情形下,本模块会将归档后的快照 tar 包以 gzip 方式压缩为 .tar.gz.tgz 文件。

可使用 use-compress-program 选项指定为 gzip/bzip2/lzma/lz4/xz 等其他压缩工具。使用 gzip 格式以取得最大兼容性,使用 xz 格式以取得最大压缩率。

备份与恢复

备份快照 (ngkit.backup)

声明

操作成败 = ngkit.backup(标识符 或 标识符列表, 留存包路径[, 备份选项])

参数及返回值

  • 标识符
  • 标识符列表
    • 文本型顺序表可选,如果传入此参数,将会批量备份 App;标识符 或此参数必选其一
  • 留存包路径 文本型可选
  • 备份选项 关联表可选
    • archive 文本型,同 留存包路径
    • snapshot 文本型,快照路径,如果指定此参数,在指定的路径创建快照后将不进行归档/压缩动作
      • 留存包路径 或此参数必选其一
    • exclude 文本型,指定备份容器数据时排除的相对路径列表
    • with-intermediate-directories 布尔型,是否为 快照路径留存包路径 创建必需的目录层级
    • exclude-assets 布尔型,备份时是否排除 App 图标资源
    • exclude-location-caches 布尔型,备份时是否排除“定位”隐私授权状态及缓存的数据
    • exclude-pasteboard 布尔型,备份时是否排除剪贴板中保存的内容
    • exclude-push-notifications 布尔型,备份时是否排除“通知”隐私授权状态及缓存的数据
    • exclude-safari 布尔型,备份时是否排除 Safari 的 Cookies 及其他浏览器数据
    • exclude-specifications 布尔型,备份时是否排除 伪装参数表
    • exclude-stubs 布尔型,备份时是否排除其他与 App 有关的 NG 持久化选项
    • exclude-tcc 布尔型,备份时是否排除其他隐私授权状态
    • use-compress-program 枚举型,详细说明见 压缩与解压
    • overwrite 布尔型,是否在 快照路径留存包路径 覆盖写入已经存在于此位置的文件夹或文件

说明

为指定的一个或多个 App 创建指定规格的 快照,并同时执行归档/压缩动作,生成 留存包

警告
  • 为确保快照完整性,创建快照前建议关闭所有 App 并保持关闭状态直至动作结束。
  • 此函数调用耗时较长,请指定合理数量的 App 执行动作,或设置合理的超时时间。

示例 1

ngkit.backup
local ok
--
-- 创建单个 App 标准留存包
ok = ngkit.backup("me.dingtone.im", "dingtone-1.tgz") -- 默认包含 Safari
--
-- 创建多个 App 标准留存包
ok = ngkit.backup(
{ "me.dingtone.im", "com.squareup.cash" }, "combined-2.tgz",
{ overwrite = true }) -- 覆盖原文件
--
-- 未进行伪装的情形下,创建不包含 Safari 浏览器数据的简易留存包
ok = ngkit.backup(
{ "me.dingtone.im", "com.squareup.cash" }, "combined-3.tgz", {
["exclude-safari"] = true, -- 排除 Safari
["exclude-specifications"] = true -- 排除伪装参数表
})

示例 2

exclude-documents.txt
./Documents
./tmp
ngkit.backup.complex
--
-- 关闭指定 App
ngkit.kill({ "com.apple.mobilesafari", "me.dingtone.im", "com.squareup.cash" })
--
-- 以日期时间为名称,创建多个 App 标准快照
local snapshot_name = "/var/root/snapshots/snapshot-" .. os.date('%Y%m%d%H%M%S')
ok = ngkit.backup(
{ "me.dingtone.im", "com.squareup.cash" }, nil,
{ -- 创建快照,不进行归档/压缩;不生成留存包
snapshot = snapshot_name,
-- 设置排除容器中部分相对路径的列表
exclude = "/var/root/rules/exclude-documents.txt",
-- 创建必需的中间目录
["with-intermediate-directories"] = true })
--
-- 如果后续需要进行网络传输,对快照进行归档/压缩
if ok then
ok = ngkit.archive(snapshot_name, snapshot_name .. ".tgz")
-- ...
end

恢复快照 (ngkit.restore)

声明

操作成败 = ngkit.restore(快照路径 或 留存包路径[, 恢复选项])

参数及返回值

  • 快照路径 文本型
  • 留存包路径 文本型
  • 恢复选项 关联表可选
    • exclude-location-caches 布尔型,恢复时是否排除“定位”隐私授权状态及缓存的数据
    • exclude-pasteboard 布尔型,恢复时是否排除剪贴板中保存的内容
    • exclude-push-notifications 布尔型,恢复时是否排除“通知”隐私授权状态及缓存的数据
    • exclude-safari 布尔型,恢复时是否排除 Safari 的 Cookies 及其他浏览器数据
    • exclude-specifications 布尔型,恢复时是否排除 伪装参数表
    • exclude-stubs 布尔型,恢复时是否排除其他与 App 有关的 NG 持久化选项
    • exclude-tcc 布尔型,恢复时是否排除其他隐私授权状态
    • ignore-missing-group-containers 布尔型,恢复时跳过当前环境下不存在的 App 分组容器
    • ignore-missing-plugin-containers 布尔型,恢复时跳过当前环境下不存在的 App 插件容器
    • use-compress-program 枚举型,详细说明见 压缩与解压
    • ignore-version-checks 布尔型,验证时是否忽略版本比对

说明

留存包 执行解压/释出动作,得到 快照;随后对快照执行恢复动作,将各类数据归位到当前环境中。各类数据归位策略如下。

不执行任何动作
  • App 的名称、版本和图标等元信息
    • 仅用于验证
执行 合并 动作
  • “通知”隐私授权状态及缓存的数据
  • 其他隐私授权状态
  • 伪装参数表
  • 其他与 App 有关的 NG 持久化选项
执行 替换 动作
  • App 的数据容器、分组容器和插件容器
    • 未安装所需 App
      • 验证失败
    • 已安装所需 App 但版本较低
      • 恢复失败,除非指定选项 ignore-version-checkstrue
    • 已安装所需 App 但版本较高
      • 打印警告信息,但继续执行 替换 动作
    • 已安装所需 App 但分组容器或插件容器不一致
      • 恢复失败,除非指定选项 ignore-missing-*-containerstrue
  • App 用户偏好设置
  • 系统钥匙串中与 App 相关的条目
  • 剪贴板中保存的内容
  • Safari 的 Cookies 及其他浏览器数据
  • “定位”隐私授权状态及缓存的数据
警告
  • 为确保数据完整性,恢复快照前建议关闭所有 App 并保持关闭状态直至动作结束。
  • 此函数调用耗时较长,请设置合理的超时时间。

示例

ngkit.restore
local ok
--
-- 恢复一个留存包
ok = ngkit.restore("dingtone-1.tgz")
--
-- 恢复一个快照
ok = ngkit.restore("/var/root/snapshots/snapshot-20230624183522")
--
-- 恢复时排除伪装参数表;忽略版本检查,允许将高版本快照恢复到低版本平台或 App
ok = ngkit.restore("combined-2.tgz", {
["exclude-specifications"] = true,
["ignore-version-checks"] = true })

读取并验证快照 (ngkit.peek)

声明

快照详情表 = ngkit.peek(快照路径 或 留存包路径[, 验证选项])

参数及返回值

  • 快照路径 文本型
  • 留存包路径 文本型
  • 验证选项 关联表可选
    • use-compress-program 枚举型,详细说明见 压缩与解压
    • validate 布尔型,验证此快照是否能在当前设备进行恢复
    • validate-all 布尔型,验证此快照是否能在当前设备进行完整恢复
    • ignore-version-checks 布尔型,验证时是否忽略版本比对
  • 快照详情表 关联表,读取或验证失败返回 nil

快照详情表

{
FormatVersion = "5.1", -- 快照格式版本
PlatformVersion = "13.6.1", -- 创建快照的平台版本
Generator = "edgbackup/3.1", -- 创建快照的工具版本
CreatedAt = "2020-09-01T10:46:45Z", -- 创建快照的时间
BundleProxies = { {
AppleID = "share_appleid003@163.com",
ArtistName = "dong zhengfa",
BundleIdentifier = "rn.notes.best",
BundleShortVersionString = "2.1.7",
BunldeDisplayName = "AsTools",
UUID = "0582E5CC-A544-4E44-849E-AD02901217EB"
} }, -- 捆绑包信息
DataContainers = { {
ApplicationIdentifier = "com.apple.SafariViewService",
UUID = "F8C70FC2-F69D-4419-8106-11015469DAC9"
}, {
ApplicationIdentifier = "com.apple.mobilesafari",
UUID = "150BCF2D-018C-45E1-BC0E-614E947EDD28"
}, {
ApplicationIdentifier = "rn.notes.best",
UUID = "901A8CCC-D1C7-4CB1-94E5-07306493CCB5"
} }, -- 数据容器信息
EncryptedFiles = {
"BundleProxies/0582E5CC-A544-4E44-849E-AD02901217EB/Info.plist.enc",
"BundleProxies/0582E5CC-A544-4E44-849E-AD02901217EB/iTunesMetadata.plist.enc",
"Library/Preferences/com.edgapp.hooker.json.enc",
"Library/TCC/Combined.plist.enc",
"Library/Keychains/Combined.plist.enc"
}, -- 加密信息
SharedContainers = { {
AppGroupIdentifier = "group.com.apple.safari",
UUID = "B11456FF-68EE-4780-BA84-56EE3B929D8B"
} } -- 分组容器信息
}

说明

此函数调用可以获取快照文件夹的元数据;也可以在不解压/释出留存包的情况下,获取留存包的元数据。

验证选项 关联表中传入 validate/validate-all 选项,还可以结合元数据、工具版本、当前系统版本和已安装 App 版本,对快照文件夹的完整性、可恢复性进行综合验证。

示例

ngkit.peek
local metadata, ok
--
-- 先验证快照
metadata = ngkit.peek("snapshot-0", {validate=true})
if metadata then
print("Created at: " .. metadata["CreatedAt"])
--
-- 再将快照归档为留存包,删除旧快照
ok = ngkit.archive("snapshot-0", "snapshot-0.tgz", {delete=true, overwrite=true})
if not ok then
error("failed to archive snapshot-0")
end
else
error("failed to validate snapshot-0")
end

归档与释出

归档快照 (ngkit.archive)

声明

操作成败 = ngkit.archive(快照路径, 留存包路径[, 归档选项])

参数及返回值

  • 快照路径 文本型
  • 留存包路径 文本型
  • 归档选项 关联表可选
    • delete 布尔型,操作成功后是否删除快照
    • overwrite 布尔型,是否在 留存包路径 覆盖写入已经存在于此位置的文件
    • use-compress-program 枚举型,详细说明见 压缩与解压
    • with-intermediate-directories 布尔型,是否为 留存包路径 创建必需的目录层级
  • 操作成败 布尔型

说明

快照 文件夹归档并压缩为 留存包

示例

ngkit.archive
local ok
--
-- 归档快照
ok = ngkit.archive("snapshot-1", "newdir/snapshot-1.tgz", {
overwrite = true, -- 覆盖写入
["with-intermediate-directories"] = true -- 建立中间所必需的目录层级
})
--
-- 归档并删除旧快照
ok = ngkit.archive("/tmp/snapshot-0", "/root/snapshot-0.tar.gz", {
delete = true, -- 删除旧留存包
overwrite = true -- 覆盖写入
})

释出归档的快照 (ngkit.unarchive)

声明

操作成败 = ngkit.unarchive(留存包路径, 快照路径[, 释出选项])

参数及返回值

  • 留存包路径 文本型
  • 快照路径 文本型
  • 释出选项 关联表可选
    • delete 布尔型,操作成功后是否删除快照
    • overwrite 布尔型,是否在 留存包路径 覆盖写入已经存在于此位置的文件
    • use-compress-program 枚举型,详细说明见 压缩与解压
    • with-intermediate-directories 布尔型,是否为 快照路径 创建必需的目录层级
  • 操作成败 布尔型

说明

留存包 文件解压并释出为 快照 文件夹,是 归档快照 的逆向动作。

示例

ngkit.unarchive
local ok
--
-- 释出归档的快照
ok = ngkit.unarchive("snapshot-1.tgz", "newdir/snapshot-1", {
overwrite = true, -- 覆盖写入
["with-intermediate-directories"] = true -- 建立中间所必需的目录层级
})
--
-- 释出归档的快照,并删除旧留存包
ok = ngkit.unarchive("/root/snapshot-0.tar.gz", "/tmp/snapshot-0", {
delete = true, -- 删除旧留存包
overwrite = true -- 覆盖写入
})

清理

虽然你可以使用传统的 清理模块 来实现本节中的绝大多数类型的清理,但是以往这一方式集成在 Lua 虚拟机进程中,作为 原生函数 进行调用。也就是说,传统的清理过程会阻塞所有协程且不可打断。

因此,我们从 3.0.3 版本开始弃用原有的 清理模块。推荐使用本节中的函数进行清理动作,不仅可以设置 最大执行时间,而且能够被外部信号中断,更为未来的版本规划了协程支持。

清理系统 (ngkit.clean)

声明

操作成败 = ngkit.clean(清理类型[, 标识符 或 标识符列表, 清理选项])

参数及返回值

  • 清理类型 枚举型
    • all 以默认选项清理所有类型内容
    • data-container 清理 App 数据容器
    • group-container 清理 App 分组容器
    • plugin-container 清理 App 插件容器
    • preferences 清理 App 用户偏好设置
    • privileges 清理隐私权限(不包括推送通知和定位权限)
    • push-notifications 清理推送通知
    • keychain 清理钥匙串
    • location-caches 清理地理位置缓存
    • safari 清理 Safari 缓存
    • pasteboard 清理剪贴板
    • specifications 清理伪装规格
    • stubs 清理其他规格、插桩
    • temporary-files 清理临时文件
  • 标识符
  • 标识符列表
    • 文本型顺序表可选,如果传入此参数,将会批量清理 App 指定 清理类型 的内容
  • 清理选项 关联表可选
    • all 布尔型,是否清理所有用户(User)App 指定 清理类型 的内容;仅当清理类型为 all/privileges/push-notifications/keychain/specifications/stubs 时有效
    • exclude 文本型,指定清理容器数据时排除的相对路径列表;仅当清理类型为 all/*-container 时有效
    • force 布尔型,忽略清理过程中可能产生的警告或错误
    • no-reload 布尔型,清理过程中不重启相关服务

说明

清理指定 App 的指定 清理类型 的内容。

警告

此函数调用耗时较长,请指定合理数量的 App 执行动作,或设置合理的超时时间。

示例

ngkit.clean
--
-- 清理 Safari 浏览器的数据容器和插件容器
ngkit.clean("data-container", "com.apple.mobilesafari", { exclude = "exclude-safari.txt" })
ngkit.clean("plugin-container", "com.apple.mobilesafari")
--
-- 清理 Surge 的推送通知权限且不重启推送服务
ngkit.clean("push-notifications", "com.nssurge.kernel.surge-ios.enterprise", { no_reload = true })
--
-- 清理所有隐私权限和伪装规格
ngkit.clean("privileges", nil, { all = true })
ngkit.clean("specifications", nil, { all = true })

选项与辅助函数

设置最大执行时间 (ngkit.timeout)

声明 1

ngkit.timeout.default  -- 默认最大执行时间(秒)
ngkit.timeout.current -- 当前最大执行时间(秒)

声明 2

ngkit.timeout(超时秒)

参数及返回值

  • 超时秒 整数型

说明

此函数可设置本模块其余函数调用的最大执行时间,单位为秒。
如果某一函数调用执行超时,将会抛出可以安全捕获的 timeout 错误。

示例

ngkit.timeout
--
-- 默认最大执行时间为 60 秒
ngkit.timeout.default
--
-- 设置最大执行时间为 180 秒
ngkit.timeout(180)
--
-- 执行命令恢复动作
if pcall(function ()
local completed = ngkit.restore("archive.tgz")
if completed then ... -- 命令执行成功,且恢复成功
else ... end -- 命令执行成功,但恢复失败
end) then ...
else ... end -- 命令执行超时,或捕获其他致命错误

运行 App (ngkit.launch)

声明

操作成败 = ngkit.launch(标识符[, 是否后台运行])

参数及返回值

  • 标识符
  • 是否后台运行
    • 布尔型可选,默认为 false
  • 操作成败 布尔型

说明

如果设备处于锁屏状态,此函数调用会自动解锁设备并启动 App。

提示

此函数是 app.run 的平替,但是启动方式不同,它不依赖于额外的服务进程。

示例

ngkit.launch
--
-- 解锁并前台打开内置 Safari 浏览器
ngkit.launch("com.apple.mobilesafari")
--
-- 后台打开设置(预热)
local launched = ngkit.launch("com.apple.Preferences", true)
if launched then
-- 前台打开设置
ngkit.launch("com.apple.Preferences")
end

关闭 App (ngkit.kill)

声明

操作成败 = ngkit.kill(标识符 或 标识符列表)

参数及返回值

  • 标识符
  • 标识符列表
    • 文本型顺序表可选,如果传入此参数,将会批量关闭 App;标识符 或此参数必选其一
  • 操作成败 布尔型

说明

关闭 App,参数可以是 标识符,也可以是一组标识符组成的顺序表。

警告
  • 这个关闭 App 是不可拒绝的强杀,目标 App 在被关闭的时候不会收到任何通知,请谨慎使用。
  • 标识符 传入 *,表示杀掉所有运行中的用户(User)App。

示例

ngkit.kill
--
-- 关闭 Safari 浏览器
ngkit.kill("com.apple.mobilesafari")
--
-- 关闭 Safari 浏览器和设置
ngkit.kill({"com.apple.mobilesafari", "com.apple.Preferences"})
--
-- 关闭所有运行中的 App
ngkit.kill("*")

读取 App 详细信息 (ngkit.list/ngkit.list.topmost)

声明 1:列出或读取指定 App 详细信息

详细信息表 = ngkit.list(标识符 或 标识符列表)

声明 2:读取前台 App 详细信息

详细信息表 = ngkit.list.topmost()

参数及返回值

  • 标识符
  • 标识符列表
    • 文本型顺序表可选,如果传入此参数,将会批量读取 App 详细信息;标识符 或此参数必选其一
  • 详细信息表 关联表顺序表
    • 如果传入的是 标识符,则返回一个关联表
    • 如果传入的是 标识符列表,则返回一个顺序表,表中的每个元素都是一个关联表,关联表中包含了 App 的详细信息

说明

读取 App 的详细信息,参数可以是 标识符,也可以是一组标识符组成的顺序表。

备注
  • 如果不传入 标识符标识符列表,默认读取用户(User)App 详细信息表
  • 如果 标识符 传入*,标识读取所有类型的 App 详细信息表

详细信息表

{
group_containers = {}, -- 分组信息
data_container = "/private/var/mobile/Containers/Data/Application/D01EE89C-140B-4812-BCAE-C7290F35C583", -- 数据容器
version = "14.1.2", -- 版本
bid = "com.apple.mobilesafari", -- 标识符
identifier = "com.apple.mobilesafari", -- 标识符
plugin_containers = {
["com.apple.mobilesafari.SafariDiagnosticExtension"] = "/private/var/mobile/Containers/Data/PluginKitPlugin/FDF16AEF-2960-4E31-9F01-9E8167B0BCAF",
}, -- 插件信息
type = "System", -- 类型,用户(User)或系统(System)
name = "Safari", -- 显示名称
}

示例

ngkit.list
--
-- 读取 Safari 浏览器的详细信息
local info
info = ngkit.list("com.apple.mobilesafari")
if info then
print(info.name) -- Safari
print(info.version) -- 14.1.2
print(info.data_container) -- /private/var/mobile/Containers/Data/Application/D01EE89C-140B-4812-BCAE-C7290F35C583
end
--
-- 读取 Safari 浏览器和设置的详细信息
info = ngkit.list({"com.apple.mobilesafari", "com.apple.Preferences"})
--
-- 读取所有 App 的详细信息
info = ngkit.list("*")
--
-- 读取所有用户(User)App 的详细信息
info = ngkit.list()
--
-- 读取前台(键盘焦点)App 详细信息
info = ngkit.list.topmost()