Mac 上和应用包里的 .plist 大多是二进制格式:以 bplist00 开头,用文本编辑器打开是一堆乱码。在 Mac 上可以用 plutil -convert xml1 转回 XML;在 Windows、Linux 上,或者面对从 iOS 备份、崩溃日志、数据库字段里取出的 plist,通常就得自己写脚本。NSKeyedArchiver 写出的归档更麻烦:即使转成 XML,也只是一个扁平的 $objects 数组,里面全是 CF$UID 编号。
这个查看器在浏览器里把这两件事都做了。拖入文件,就能以 XML、JSON 查看;如果是键控归档,还能看到应用真正存下的对象图。
使用步骤
- 拿到 plist 文件:从应用包(
Contents/Info.plist、~/Library/Preferences/*.plist)、iOS 备份或 IPA 中复制出来,或者把数据库里以bplist00开头的字段保存成文件。 - 把它拖到上方的拖放区,或点“选择文件”。任意扩展名的二进制和 XML plist 都可以。
- 先看“概要”:二进制还是 XML、对象数、是否为 NSKeyedArchiver 归档。然后展开对象图(键控归档)、XML plist 或 JSON,按需下载。
真实输出示例
我们把这个键控归档拖进本页,复制了页面显示的内容。版式有压缩(标签和值放在一行,省略按钮),所有值原样保留。“解析耗时”每次运行都不同。
样本: test/plist/keyed.bplist:在 macOS 27 上由 Swift 脚本 test/plist/keyed.swift 调用 Foundation 写出的 NSKeyedArchiver 归档(本站自建样本,2026-11-02)
概要
格式 二进制 plist(bplist00)
大小 1.0 KB
对象表 135 个对象 · 偏移 2 字节 · 引用 1 字节
顶层对象 dict · 4 项
键控归档 NSKeyedArchiver,$version 100000 · 44 个归档对象 · $top:root
解析耗时 2 ms
NSKeyedArchiver 对象图(JSON)
{
"root": {
"id": "6F9619FF-8B86-D011-B42D-00C04FC964FF",
"blob": {
"data": "3q2+7w==",
"bytes": 4
},
"count": 3,
"url": "https://example.com/notes?id=1",
"ratio": 0.5,
"tags": [
"alpha",
"beta",
"shared value"
],
"title": "Fixture Notes",
"nested": {
"again": "shared value",
…
},
"set": [
"only"
],
"enabled": true,
"created": "2026-11-02T08:30:00.000Z",
"nothing": null
}
}
归档的类(8)
NSDictionary 2
NSArray 1
NSDate 1
NSMutableArray 1
NSNull 1
NSSet 1
NSURL 1
NSUUID 1能读什么
- 二进制 plist(
bplist00):Apple 格式的全部对象类型(字典、数组、ASCII 与 UTF-16 字符串、最长 128 位的整数、浮点数、布尔、日期、数据、UID)。引用环和越界偏移会被拒绝,不会死循环。 - XML plist,有没有 DOCTYPE 都可以。外部实体和 DTD 一律不解析。
- 任意扩展名:
.plist、.bplist、二进制.strings、PrivacyInfo.xcprivacy、.savedState文件,或者从数据库字段导出的字节。
能看到什么
- 概要:二进制还是 XML、文件大小、bplist 对象表(对象数、偏移与引用的字节宽度)、顶层类型及其条目数,以及是否为键控归档。
- XML plist:按
plutil -convert xml1的写法输出(Tab 缩进、字典键排序、base64 数据每行 68 列、日期为不带小数的 UTC),可以直接和 Mac 上的输出做 diff。 - JSON:日期写成 ISO 8601 字符串,数据写成
{ "data": base64, "bytes": n },超过 2^53 的整数写成字符串。plutil -convert json遇到日期或数据就会拒绝,这里不会。 - NSKeyedArchiver 对象图:当
$archiver为NSKeyedArchiver时,从$top开始跟随每一个CF$UID引用。NSArray、NSSet、NSOrderedSet还原为数组,NSDictionary为字典,NSString为字符串,NSData为数据,NSDate为日期,NSUUID为 UUID 字符串,NSURL为其字符串,NSNull为 null。其他类(你自己的模型对象、NSAttributedString、NSColor)保留为字典,带上$class名称和它的键,引用已解析。如果一个对象引用了仍在解码中的对象,会写成{ "$ref": n },n 是它在$objects中的下标。另有一张表列出归档中每个类的对象数。
每个视图都有“下载”和“复制”按钮。输出很大时,页面上只显示前 1 MiB,“下载”保存完整内容。
我们怎么验证的
测试样本在 test/plist/,由 test/plist/build.sh 在 macOS 27 上重新生成:一个手写的、包含全部类型的 XML plist,用 plutil -convert binary1 转成二进制;以及一个用 Swift 脚本调用 Foundation 的 NSKeyedArchiver.archivedData 写出的归档,其中一个 NSString 被两个容器共享。单元测试检查本页输出的 XML 与 plutil -convert xml1 逐字节一致,并检查还原的对象图与 Swift 脚本归档的值相同。
你的数据如何保持私密
- 查看器运行在一个单独的框架(iframe)里,来源是
tools.rea-reverse.com;解析在框架里的 Web Worker 中、在你的设备上完成。拖入的文件不会上传。 - 这个页面在正文下方展示一个广告。广告脚本运行在
rea-reverse.com上,而框架属于另一个源(origin),浏览器不允许广告脚本或本页的任何其他脚本读取你拖入的文件或解析结果。 - 框架的内容安全策略拦截它的脚本能发起的所有请求(
connect-src 'none',也不加载任何第三方资源)。框架也不能自行打开窗口或让本页跳转。它传给本页的只有自身高度和下面这些统计事件。 - 键和值都以纯文本显示。plist 里的 URL 不会变成链接,也不会被请求。
- 统计事件只包含粗粒度的值:成功或错误码、二进制或 XML、是否为键控归档、大小区间和耗时区间,不含键、值或类名。框架把事件交给本页,由本页发送。完整列表见隐私政策。
- 不想要任何广告和统计?在单独页面打开查看器。
如何自行验证
- 打开浏览器开发者工具的 Network(网络) 面板,加载本页。发往
tools.rea-reverse.com的请求就是框架:它的 HTML、样式、字体和脚本,以及 Worker 脚本/_astro/plist-worker-*.js。发往 Google 域名的请求是本页的广告。 - 清空日志,然后拖入一个 plist。在过滤框里输入
domain:tools.rea-reverse.com:最多只会再出现一次 Worker 脚本/_astro/plist-worker-*.js(为下一次运行准备的新 Worker),没有任何请求带着你的数据。广告请求可能还会继续,但它们来自本页,不带框架里的任何内容。 - 点开来自
tools.rea-reverse.com的plist-viewer/文档,查看 Headers:它的Content-Security-Policy里有connect-src 'none'和frame-ancestors https://rea-reverse.com。在 Chrome 里,Application(应用)> Frames 也能看到框架的源和策略。Worker 运行在default-src 'none'; script-src 'self'; connect-src 'none'这条策略下。 - 检查隔离:在 Console(控制台) 里,上下文选
top,运行document.getElementById('tool-frame').contentDocument,结果是null;frames[0].document会抛出SecurityError。广告脚本拿到的也是这个结果。 - 或者先打开页面再断网,然后拖入文件,照样能用。
- 在 Mac 上用
plutil -convert xml1 -o - file.plist对照。
局限
- 只读:不写出二进制 plist,也不能编辑值。
- 只支持
bplist00。部分 Apple 框架内部使用的bplist15/bplist16会被拒绝,并给出明确提示。 - 没有小数部分的浮点数(例如存成
815301000.0的NSDate)在 XML 视图里会写成<integer>,因为解析器只保留一种数字类型;plutil写的是<real>。数值本身相同。 - Foundation 基础类之外的类不做解释:你能看到它们的键,但看不到类本身如何使用这些键。
对应的命令行
在 Mac 上,plutil 能把同一个归档转成 XML,但对象图仍是用 CF$UID 编号串起来的扁平 $objects 数组(macOS 27,有删节):
$ plutil -convert xml1 -o - keyed.bplist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>$archiver</key>
<string>NSKeyedArchiver</string>
<key>$objects</key>
<array>
<string>$null</string>
…
<key>NS.time</key>
<real>815301000</real>
</dict>
<dict>
<key>$classes</key>
<array>
<string>NSDate</string>
<string>NSObject</string>
</array>
<key>$classname</key>
<string>NSDate</string>
</dict>
…
<key>$top</key>
<dict>
<key>root</key>
<dict>
<key>CF$UID</key>
<integer>1</integer>
</dict>
</dict>
<key>$version</key>
<integer>100000</integer>
</dict>
</plist>对普通二进制 plist,plutil -p 会打印可读的内容(test/plist/settings.bplist 样本,有删节):
$ plutil -p settings.bplist
{
"Big" => 9007199254740993
"Blob" => {length = 81, bytes = 0x000102030405060708090a0b0c0d0e0f ... 494a4b4c4d4e4f50}
"CFBundleIdentifier" => "com.example.fixture"
"CFBundleShortVersionString" => "2.5.0"
"Count" => 42
"Created" => 2026-11-02 08:30:00 +0000
…
"Greeting" => "Grüße, 你好 & <tags>"
…
}plutil 输出 JSON 时,遇到第一个日期或数据值就会失败:
$ plutil -convert json -o - settings.bplist
settings.bplist: Invalid object in plist for JSON format常见问题
怎样在线查看二进制 plist 又不上传文件?
拖到本页即可。文件在浏览器中一个没有网络权限的 Web Worker 里解析,不会发送到任何地方。在 Mac 上,plutil -p file.plist 打印的是同样的值。
bplist 怎么转成 XML?
拖入文件后看“XML plist”视图,点“下载”保存为 .xml.plist。输出遵循 plutil -convert xml1(键排序、Tab 缩进、base64 每行 68 列),可以直接和 Mac 上的结果做 diff。
NSKeyedArchiver 归档怎么解码?
拖到这里即可。当 $archiver 为 NSKeyedArchiver 时,页面从 $top 开始跟随每个 CF$UID,以 JSON 显示结果:数组、字典、字符串、日期、UUID 和 URL,而不是 $objects 下标。未知类保留 $class 名称和它的键。
为什么 plutil -convert json 处理我的 plist 会失败?
JSON 没有日期和二进制数据类型,所以只要 plist 里有 <date> 或 <data>,plutil 就会报 "Invalid object in plist for JSON format"。本页会把日期写成 ISO 8601 字符串,把数据写成 base64。
支持 bplist15 或 bplist16 吗?
不支持。只读取 plutil 和 NSPropertyListSerialization 写出的 bplist00。以其他 bplist 版本开头的文件会得到错误提示,而不是错误的结果。
更新于
相关工具与指南
- 用 REA 逆向 macOS 应用:Info.plist、launchd plist 等属性列表在应用包里的位置,以及该看哪些内容。
- iOS 描述文件与 IPA 查看器:读取
.mobileprovision或.ipa中的 plist,并互相比对。 - 文件类型识别:不确定文件是不是 plist 时先用它看看。