Telegram 技巧档案UIKIT · APPKIT

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。

结果

点击统计卡片可以只看对应结果。每项都链接到源码笔记中的条目。

结果
方法
20 / 20 项
  1. #1拼接类名调用 CAEmitterBehavior,实现隐形墨水(13-1)已发布 · 仍可用REA 二进制macOS 26.7 实测
    13.0 二进制证据

    "CA"、"Emitter"、"Behavior" 以 Swift 小字符串存于 __DATA.__data 0x5e75208/18/28;"behaviorWith"、"Type:" 存于 0x5e751c0/d0。完整名 CAEmitterBehavior、behaviorWithType 出现 0 次。"simpleAttractor" 编成 5 处、"valueOverLife" 编成 3 处 MOVZ/MOVK 立即数。KVC 路径 emitterBehaviors.fingerAttractor.*、randomAttractor0/1.* 以明文存于 __cstring

    macOS 26.7 实测

    类存在;+behaviorWithType: 存在;两种类型都能创建

    结论

    已发布,当前系统仍可用

  2. #2私有 KVC 键 contentsMultiplyColor 给图层着色(报告未收录)已发布 · 仍可用REA 二进制macOS 26.7 实测
    13.0 二进制证据

    __cstring 0x104b6ef30

    macOS 26.7 实测

    CALayer 响应 getter 和 setter

    结论

    已发布,仍可用

  3. #3精简 NSVisualEffectView 内部层与滤镜(12-2)已发布 · 仍可用REA 二进制macOS 26.7 实测
    13.0 二进制证据

    白名单 ["colorSaturate","gaussianBlur"] 以小字符串存于 __DATA.__data 0x5e75720/30,标记字节 0xED/0xEC

    macOS 26.7 实测

    实际层树:kCUIVariantSidebarMaterial › Backdrop(CABackdropLayer,滤镜 sdrNormalize、gaussianBlur、colorSaturate)、fill、tone、Chameleon(CAChameleonLayer)

    结论

    代码同时匹配 backdrop 和 Backdrop,结构仍吻合。它会删掉新出现的 sdrNormalize,影响未验证

  4. #4未公开对角缩放光标(14-6)已发布 · 仍可用REA 二进制macOS 26.7 实测
    13.0 二进制证据

    两个 selector 都在 __objc_methname(0x1050992e1、0x105099307)

    macOS 26.7 实测

    LLDB 在 AppKit 中解析到 +[NSCursor _windowResizeNorthWestSouthEastCursor] 等两个方法

    结论

    已发布,仍可用

  5. #5用 _shareServiceSelected: 识别并删除系统子菜单(14-5)系统结构已变化REA 二进制macOS 26.7 实测
    13.0 二进制证据

    该 selector 同时在 __cstring 和 __objc_methname

    macOS 26.7 实测

    NSTextView 默认菜单中没有它,分享项改用 _performStandardShareMenuItem:。13.0 二进制中没有新 selector

    结论

    分享项的过滤条件在 26.7 上可能失效(推断,未在 Telegram 内实测)

  6. #6深挖 AVMovableView 点击全屏按钮(14-3)已发布 · 仍可用REA 二进制macOS 26.7 实测
    13.0 二进制证据

    "AVMovableView" 编成 3 处立即数(0x102517a20、0x102518434、0x1025185f8),与源码 3 个调用点一致

    macOS 26.7 实测

    类存在,并出现在 AVPlayerView 视图树中

    结论

    已发布;内部按钮路径未验证

  7. #7重排 NSTitlebarView 子视图(14-4)已发布 · 仍可用REA 二进制macOS 26.7 实测
    13.0 二进制证据

    "NSTitlebarView" 编成 1 处立即数(0x1036c54f8);NSTitlebarContainerView 在 __cstring

    macOS 26.7 实测

    两个类都在标题栏视图树中

    结论

    已发布,结构仍在

  8. #8移动 MapKit 内部 MKZoomSegmentedControl(14-7)系统结构已变化REA 二进制macOS 26.7 实测
    13.0 二进制证据

    __cstring 0x104c3bd50

    macOS 26.7 实测

    类存在,但设置 showsZoomControls = true 后不在 MKMapView 视图树中

    结论

    代码用 if let 保护,会静默跳过(本次测试条件下)

  9. #9写资源叉并重启 Dock 永久换图标(14-2)已发布 · 仍可用REA 二进制
    13.0 二进制证据

    com.apple.ResourceFork、com.apple.FinderInfo、/usr/bin/killall、“Failed to run killall Dock”;导入 setxattr、getxattr、removexattr

    macOS 26.7 实测

    未执行(会改动系统状态)

    结论

    已发布。非沙盒的 Developer ID 分发让写自身 bundle 成为可能

  10. #10监听型 CGEvent tap 实现全局按住说话(14-1)已发布 · 仍可用REA 二进制REA 运行时观测
    13.0 二进制证据

    导入 CGEventTapCreate、CGEventTapEnable、IOHIDCheckAccess、IOHIDRequestAccess

    macOS 26.7 实测

    LLDB 把断点解析到 SkyLight 的 SLEventTapCreate;启动 40 秒内未调用

    结论

    已发布;默认未启用

  11. #11从 IOHIDSystem 读空闲时间,读 AppleSmartBattery 私有键(报告未收录)已发布 · 仍可用REA 二进制REA 运行时观测
    13.0 二进制证据

    IOHIDSystem、HIDIdleTime、AppleSmartBattery;导入 IOServiceGetMatchingService、IORegistryEntryCreateCFProperty、IOPSCopyPowerSourcesInfo

    macOS 26.7 实测

    启动时 IOServiceGetMatchingService 被调用 6 次,调用栈中没有 Telegram 帧

    结论

    已发布

  12. #12为泛化的 hw.model 调 system_profiler(报告未收录)已发布 · 仍可用REA 二进制
    13.0 二进制证据

    /usr/sbin/system_profiler

    macOS 26.7 实测

    未执行

    结论

    已发布

  13. #13Carbon 键盘布局翻译物理键(报告未收录)已发布 · 仍可用REA 二进制
    13.0 二进制证据

    导入 TISCopyCurrentKeyboardInputSource、TISCopyCurrentASCIICapableKeyboardLayoutInputSource、UCKeyTranslate

    macOS 26.7 实测

    未执行

    结论

    已发布

  14. #14CVDisplayLink 回调转主队列、共享显示链接(报告未收录)已发布 · 仍可用REA 二进制REA 运行时观测
    13.0 二进制证据

    导入 CVDisplayLinkCreateWithActiveCGDisplays、CVDisplayLinkSetOutputHandler 等 9 个

    macOS 26.7 实测

    启动 40 秒内未调用(登录页,原因未验证)

    结论

    已发布

  15. #15XOR 帧差分后 LZFSE 压缩贴纸(15-1)已发布 · 仍可用REA 二进制
    13.0 二进制证据

    导入 compression_encode_buffer、compression_decode_buffer

    macOS 26.7 实测

    未执行

    结论

    已发布

  16. #16vImage 缩放与模糊(报告未收录)已发布 · 仍可用REA 二进制
    13.0 二进制证据

    导入 20 个 vImage* 函数

    macOS 26.7 实测

    未执行

    结论

    已发布

  17. #17FileManager 方法替换规避 MTLBinaryArchive 崩溃(08-4,共享 MetalEngine)版本差异REA 二进制REA 运行时观测
    13.0 二进制证据

    _TtC11MetalEngine16FailedMoveErrors;完整 selector 字符串 replaceItemAtURL:withItemAtURL:backupItemName:options:resultingItemURL:error:;导入 method_setImplementation

    macOS 26.7 实测

    启动 40 秒内未调用 method_setImplementation

    结论

    版本差异:2025-07 的 TelegramSwift 源码中没有,13.0 已带入

  18. #18HackUtils.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. #19CAPortalLayer(源码只有声明,.m 为空)源码未使用REA 二进制macOS 26.7 实测
    13.0 二进制证据

    出现 0 次

    macOS 26.7 实测

    类存在于 QuartzCore

    结论

    与源码一致:未使用

    对应条目无。源码中只有声明,主题笔记没有把它计入技巧,见第 14 章章首说明
  20. #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 与长度按位或的结果。这带来三种编译产物:

  1. 数组字面量(如 ["CA", "Emitter", "Behavior"])在 __DATA.__data 中存成连续的 16 字节槽。
  2. 直接传参的短字面量(如 "AVMovableView")编成 2 到 4 条 MOVZ/MOVK 指令,字节不连续。
  3. 超过 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,所以没有做函数级反编译和交叉引用。

复现

在 macOS 上复现(工作目录 rea-telegram/)
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

本页目录