REA 二进制验证
用 REA 分析官方 Telegram for macOS 13.0,核对源码笔记中的 20 项私有 API 与系统集成技巧。
结论
核对的 20 项中,16 项在 13.0 发布版二进制中有直接证据。2 项是版本差异:HackUtils 类已消失,iOS 侧 MetalEngine 的方法替换已进入 macOS 版。在 macOS 26.7 上,有 2 条私有路径已与系统结构不一致。另有 1 项是源码中只声明、从未使用的类(CAPortalLayer)。
iOS 版没有做二进制验证。原因是 App Store 的 IPA 受 FairPlay 加密,无法合法取得解密的可执行文件。macOS 二进制静态链接了 telegram-ios 子模块中的共享模块(MetalEngine、Postbox、TelegramCore、MtProtoKit),所以少数 iOS 侧非 UI 技巧得到了间接验证。
验证对象
| 项目 | 值 |
|---|---|
| 二进制来源 | https://telegram.org/dl/macos 重定向到 https://osx.telegram.org/updates/Telegram.dmg(150,663,741 字节,SHA-256 140622d3…891c17f8) |
| 版本 | 13.0(CFBundleVersion 283305),ru.keepcoder.Telegram,SDK macOS 26.2,Xcode 26.2(17C52) |
| 主可执行文件 | 通用二进制 x86_64 + arm64,234,841,072 字节;arm64 切片 109,257,712 字节 |
| 签名 | Developer ID Application: TELEGRAM MESSENGER LLP (6N38VWS5BX),hardened runtime 开启,未开 App Sandbox |
| 对照源码 | TelegramSwift 579cebbf(2025-07-29)。官方更新源最早只保留 12.6(2026-04),所以没有与源码同版本的二进制 |
| 分析机器 | Manus 云电脑,macOS 26.7(25G229),arm64 |
| 工具 | REA 6.7.0(rea-agents),Rizin 0.9.1 作为后端;未配置 Hopper、Ghidra、IDA |
方法
读取元数据。 rea inspect-plist 和 rea inspect-signature 读取 Info.plist、签名和 entitlements。
解析加载关系。 rea trace-dylib-resolution --architecture arm64 解析出 4 个可执行根、6 个镜像和 193 条加载边。rea inspect-macho 读取 113 条 load command、95 个依赖库和 4,587 个导入符号。
扫描字符串与类表。 rea execute-rizin-command 在 arm64 切片上执行 iz、ic、iS 和 pd,导出 117,189 条数据段字符串、6,393 个 ObjC 类、节表与局部反汇编。
补齐短字符串。 Rizin 的 izz 和 / 搜索在 REA 的 -q -c 模式下没有输出。我用已知字符串对照,确认这是工具限制。所以短字符串改用自写脚本定位:解码 __TEXT.__text 中的 arm64 MOVZ/MOVK 立即数,并按 Swift 小字符串的内存布局搜索整个文件。
系统实测与运行时观测。 我在 macOS 26.7 上编译检查程序,确认私有类、selector 和视图树是否仍存在。rea observe-native-calls 在 LLDB 下启动 ad-hoc 重签名的副本 40 秒,记录 8 组断点的调用。
rea inspect-native-dispatch-metadata 没有结果。REA 对该命令设置了 64 MiB 输入上限,而 arm64 切片是 104 MiB。
结果
点击统计卡片可以只看对应结果。每项都链接到源码笔记中的条目。
- #1拼接类名调用
CAEmitterBehavior,实现隐形墨水(13-1)已发布 · 仍可用REA 二进制macOS 26.7 实测13.0 二进制证据"CA"、"Emitter"、"Behavior"以 Swift 小字符串存于__DATA.__data0x5e75208/18/28;"behaviorWith"、"Type:"存于 0x5e751c0/d0。完整名CAEmitterBehavior、behaviorWithType出现 0 次。"simpleAttractor"编成 5 处、"valueOverLife"编成 3 处MOVZ/MOVK立即数。KVC 路径emitterBehaviors.fingerAttractor.*、randomAttractor0/1.*以明文存于__cstringmacOS 26.7 实测类存在;
+behaviorWithType:存在;两种类型都能创建结论已发布,当前系统仍可用
- #2私有 KVC 键
contentsMultiplyColor给图层着色(报告未收录)已发布 · 仍可用REA 二进制macOS 26.7 实测13.0 二进制证据__cstring0x104b6ef30macOS 26.7 实测CALayer响应 getter 和 setter结论已发布,仍可用
- #3精简
NSVisualEffectView内部层与滤镜(12-2)已发布 · 仍可用REA 二进制macOS 26.7 实测13.0 二进制证据白名单
["colorSaturate","gaussianBlur"]以小字符串存于__DATA.__data0x5e75720/30,标记字节 0xED/0xECmacOS 26.7 实测实际层树:
kCUIVariantSidebarMaterial›Backdrop(CABackdropLayer,滤镜sdrNormalize、gaussianBlur、colorSaturate)、fill、tone、Chameleon(CAChameleonLayer)结论代码同时匹配
backdrop和Backdrop,结构仍吻合。它会删掉新出现的sdrNormalize,影响未验证 - #4未公开对角缩放光标(14-6)已发布 · 仍可用REA 二进制macOS 26.7 实测13.0 二进制证据
两个 selector 都在
__objc_methname(0x1050992e1、0x105099307)macOS 26.7 实测LLDB 在 AppKit 中解析到
+[NSCursor _windowResizeNorthWestSouthEastCursor]等两个方法结论已发布,仍可用
- #5用
_shareServiceSelected:识别并删除系统子菜单(14-5)系统结构已变化REA 二进制macOS 26.7 实测13.0 二进制证据该 selector 同时在
__cstring和__objc_methnamemacOS 26.7 实测NSTextView默认菜单中没有它,分享项改用_performStandardShareMenuItem:。13.0 二进制中没有新 selector结论分享项的过滤条件在 26.7 上可能失效(推断,未在 Telegram 内实测)
- #6深挖
AVMovableView点击全屏按钮(14-3)已发布 · 仍可用REA 二进制macOS 26.7 实测13.0 二进制证据"AVMovableView"编成 3 处立即数(0x102517a20、0x102518434、0x1025185f8),与源码 3 个调用点一致macOS 26.7 实测类存在,并出现在
AVPlayerView视图树中结论已发布;内部按钮路径未验证
- #7重排
NSTitlebarView子视图(14-4)已发布 · 仍可用REA 二进制macOS 26.7 实测13.0 二进制证据"NSTitlebarView"编成 1 处立即数(0x1036c54f8);NSTitlebarContainerView在__cstringmacOS 26.7 实测两个类都在标题栏视图树中
结论已发布,结构仍在
- #8移动 MapKit 内部
MKZoomSegmentedControl(14-7)系统结构已变化REA 二进制macOS 26.7 实测13.0 二进制证据__cstring0x104c3bd50macOS 26.7 实测类存在,但设置
showsZoomControls = true后不在MKMapView视图树中结论代码用
if let保护,会静默跳过(本次测试条件下) - #9写资源叉并重启 Dock 永久换图标(14-2)已发布 · 仍可用REA 二进制13.0 二进制证据
com.apple.ResourceFork、com.apple.FinderInfo、/usr/bin/killall、“Failed to runkillall Dock”;导入setxattr、getxattr、removexattrmacOS 26.7 实测未执行(会改动系统状态)
结论已发布。非沙盒的 Developer ID 分发让写自身 bundle 成为可能
- #10监听型
CGEventtap 实现全局按住说话(14-1)已发布 · 仍可用REA 二进制REA 运行时观测13.0 二进制证据导入
CGEventTapCreate、CGEventTapEnable、IOHIDCheckAccess、IOHIDRequestAccessmacOS 26.7 实测LLDB 把断点解析到 SkyLight 的
SLEventTapCreate;启动 40 秒内未调用结论已发布;默认未启用
- #11从
IOHIDSystem读空闲时间,读AppleSmartBattery私有键(报告未收录)已发布 · 仍可用REA 二进制REA 运行时观测13.0 二进制证据IOHIDSystem、HIDIdleTime、AppleSmartBattery;导入IOServiceGetMatchingService、IORegistryEntryCreateCFProperty、IOPSCopyPowerSourcesInfomacOS 26.7 实测启动时
IOServiceGetMatchingService被调用 6 次,调用栈中没有 Telegram 帧结论已发布
- #12为泛化的
hw.model调system_profiler(报告未收录)已发布 · 仍可用REA 二进制13.0 二进制证据/usr/sbin/system_profilermacOS 26.7 实测未执行
结论已发布
- #13Carbon 键盘布局翻译物理键(报告未收录)已发布 · 仍可用REA 二进制13.0 二进制证据
导入
TISCopyCurrentKeyboardInputSource、TISCopyCurrentASCIICapableKeyboardLayoutInputSource、UCKeyTranslatemacOS 26.7 实测未执行
结论已发布
- #14
CVDisplayLink回调转主队列、共享显示链接(报告未收录)已发布 · 仍可用REA 二进制REA 运行时观测13.0 二进制证据导入
CVDisplayLinkCreateWithActiveCGDisplays、CVDisplayLinkSetOutputHandler等 9 个macOS 26.7 实测启动 40 秒内未调用(登录页,原因未验证)
结论已发布
- #15XOR 帧差分后 LZFSE 压缩贴纸(15-1)已发布 · 仍可用REA 二进制13.0 二进制证据
导入
compression_encode_buffer、compression_decode_buffermacOS 26.7 实测未执行
结论已发布
- #16vImage 缩放与模糊(报告未收录)已发布 · 仍可用REA 二进制13.0 二进制证据
导入 20 个
vImage*函数macOS 26.7 实测未执行
结论已发布
- #17
FileManager方法替换规避MTLBinaryArchive崩溃(08-4,共享MetalEngine)版本差异REA 二进制REA 运行时观测13.0 二进制证据_TtC11MetalEngine16FailedMoveErrors;完整 selector 字符串replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:;导入method_setImplementationmacOS 26.7 实测启动 40 秒内未调用
method_setImplementation结论版本差异:2025-07 的 TelegramSwift 源码中没有,13.0 已带入
- #18
HackUtils.findElements(byClass:)定位私有视图(14-3、14-4、14-7)版本差异REA 二进制REA 运行时观测13.0 二进制证据HackUtils出现 0 次;ObjcUtils类存在,带findElementsByClass:inView:和:array:macOS 26.7 实测ObjcUtils方法因符号已剥离,LLDB 无法下断结论版本差异:功能可能并入
ObjcUtils(未验证) - #19
CAPortalLayer(源码只有声明,.m为空)源码未使用REA 二进制macOS 26.7 实测13.0 二进制证据出现 0 次
macOS 26.7 实测类存在于 QuartzCore
结论与源码一致:未使用
- #20双重虚拟映射环形缓冲(11-1,共享模块)共享模块间接证据REA 二进制13.0 二进制证据
导入
vm_allocate、vm_remap;RingBuffer相关字符串 4 条macOS 26.7 实测未执行
结论已发布(间接)
关键发现
私有类全部走运行时字符串
inspect-macho 列出 315 个导入的 ObjC 类。与 SDK 的 6,007 个公开接口比对后,剩下 26 个类不在 SDK 头文件中。它们来自 WebKit、AuthenticationServices、JavaScriptCore、Sparkle 和 dispatch,都是公开或第三方类。所以发布版的静态链接层没有任何私有 UI 类引用。私有类只通过 NSClassFromString、KVC 和 selector 字符串在运行时取得,这与源码写法一致。
字符串拼接只藏完整名,不藏片段
Swift 把 15 字节以内的字符串直接存进 String 的两个机器字。第二个字的最高字节是 0xE0 与长度按位或的结果。这带来三种编译产物:
- 数组字面量(如
["CA", "Emitter", "Behavior"])在__DATA.__data中存成连续的 16 字节槽。 - 直接传参的短字面量(如
"AVMovableView")编成 2 到 4 条MOVZ/MOVK指令,字节不连续。 - 超过 15 字节的字面量(如
emitterBehaviors.fingerAttractor.enabled)进入__TEXT.__cstring,用strings就能看到。
所以 strings 和 Rizin 的 iz 都找不到 CAEmitterBehavior 和 behaviorWithType:,但片段和 KVC 路径仍然可读。这种拼接能躲过按完整类名做的简单扫描,挡不住稍加组合的检查。iOS 侧的 XOR 混淆(NativeTextChecking.swift)才真正去掉了明文,但 iOS 二进制无法验证。
发布版与源码存在版本漂移
TelegramSwift master 停在 2025-07-29。13.0 构建于 2026-10,用了更新的 telegram-ios 子模块。证据有两条。第一,MetalEngine 的 FileManager 方法替换(#08-05)已在 macOS 二进制中,包括 FailedMoveErrors 类和完整 selector 字符串。第二,源码中的 HackUtils 类在二进制中消失,同名能力只出现在 ObjcUtils 上。所以阅读 TelegramSwift 公开源码时,应假设它落后发布版约 15 个月。
私有路径会随系统静默失效
这些技巧依赖系统内部结构,所以会随系统版本变化。失败时多数是静默的:
NSVisualEffectView新增sdrNormalize滤镜和CAChameleonLayer。Telegram 的白名单会删掉它们。代码同时匹配大小写两种层名,所以结构上仍生效。- 系统文本菜单的分享项改用
_performStandardShareMenuItem:。Telegram 只比较_shareServiceSelected:,所以分享项可能不再被删除。 MKZoomSegmentedControl仍是已注册的类,但这次测试中没有出现在地图视图树里。Telegram 的if let让这段代码变成空操作。- 私有光标、
CAEmitterBehavior、contentsMultiplyColor、NSTitlebarView、AVMovableView在 26.7 上都还存在。
分发方式决定哪些技巧可行
官方直装版不开 App Sandbox,但开启 hardened runtime。所以它可以写自身 bundle 的资源叉并执行 killall Dock,可以读 IORegistry,也可以创建全局 CGEvent tap。创建 tap 要求用户授予输入监控权限,代码用 IOHIDCheckAccess 和 IOHIDRequestAccess 检查。这些能力在 Mac App Store 的沙盒版本中会受限。App Store 版没有验证。
运行时观测
hardened runtime 目标只有带 get-task-allow 才能被 LLDB 调试。所以我复制了 app,用 ad-hoc 签名去掉 hardened runtime,再加上 get-task-allow。原始 app 没有改动。
| 项目 | 结果 |
|---|---|
| 运行时长 | 40.4 秒,停在登录页 |
| 断点 | 8 组:NSClassFromString、method_setImplementation、CVDisplayLinkCreateWithActiveCGDisplays、CGEventTapCreate、IOServiceGetMatchingService、两个私有光标类方法、+[ObjcUtils findElementsByClass:inView:] |
| 已解析 | 7 组。ObjcUtils 方法未解析,因为发布版剥离了这些符号 |
| 事件 | 257 次:NSClassFromString 251 次,IOServiceGetMatchingService 6 次 |
NSClassFromString 来源 | 88 次来自 AppKit 解码 MainMenu.nib,2 次来自 NSApplicationMain,其余没有 Telegram 帧 |
登录页阶段没有触发 Telegram 自有的私有类查找。私有路径依赖具体界面,例如 spoiler、视频画廊、地图和 Passport。这些界面需要登录后操作,所以本次没有覆盖。
局限
- 二进制版本(13.0,2026-10)比源码(2025-07)新约 15 个月。“已发布”表示 13.0 中有证据,不表示与源码逐行一致。
- 字符串和导入表只能证明代码被编译进来,不能证明它在运行时执行。运行时观测只覆盖了启动和登录页。
inspect-macho的导入表来自dyld_info文本输出,REA 标注其为非穷尽。- 分享菜单的“可能失效”和地图控件的“静默跳过”来自对系统结构的独立测试,未在 Telegram 界面内复现。
- 没有 Hopper、Ghidra 或 IDA,所以没有做函数级反编译和交叉引用。
复现
npm install --global rea-agents # REA 6.7.0
APP=Telegram.app; BIN=$APP/Contents/MacOS/Telegram
rea inspect-plist $APP --format json > out/plist.json
rea inspect-signature $APP --format json > out/signature.json
rea trace-dylib-resolution $APP --architecture arm64 --format json > out/dylib.json
rea inspect-macho $BIN --format json > out/macho.json
lipo -thin arm64 $BIN -output Telegram-arm64
python3 rz_probe.py $PWD/Telegram-arm64 out 'iz_all=iz' 'classes=ic' 'sections=iS'
python3 smallstr_scan.py Telegram-arm64 0x4000 0x4a7ae9c 0x100004000 AVMovab NSTitleb simpleAt
codesign --force --deep --sign - --entitlements dbg-ent.plist Telegram-dbg.app
rea observe-native-calls $PWD/Telegram-dbg.app observe-startup.json --format json > out/observe-startup.json
xcrun swiftc -O private_api_check.swift -o private_api_check && ./private_api_check