飞书多开指南:一份 .app 改出完全隔离的第二实例

目标:一台 macOS 上同时运行两个相互隔离的飞书(主实例 Lark.app + 第二实例 Lark-org2.app),登录不同账号;数据目录、TCC/通知身份互不影响;第二实例不与主实例抢全局快捷键、不会被官方升级覆盖、菜单栏图标可区分。 本文自包含:不依赖任何现成脚本,按步骤逐条执行即可完成。实测版本:143.0.7499.203(macOS,Apple Silicon)。 这是飞书折腾系列第三篇:撤回机制与加密的逆向原理见《飞书撤回消息逆向分析》,防撤回 asar 补丁见《飞书防撤回补丁复现指南》。 0. 前置条件 项 要求 系统 macOS(Apple Silicon;Intel 需把下文 clang 的 -arch 换成 x86_64 自测) 源应用 /Applications/Lark.app 已安装(是否打了别的补丁无所谓,副本会原样带上) 工具 python3 + Pillow(pip3 install pillow)、clang(Xcode CLT)、codesign/install_name_tool/otool(随 CLT) 磁盘 预留一份飞书体积(~1.5GB) 约定:下文 $VERDIR 一律指当前飞书的版本目录,先取好: VERDIR="$(ls "/Applications/Lark.app/Contents/Frameworks/Lark Framework.framework/Versions" | grep -E '^[0-9]' | sort -V | tail -1)" FW="/Applications/Lark-org2.app/Contents/Frameworks/Lark Framework.framework/Versions/$VERDIR" DATA="$HOME/Library/Application Support/LarkShell-org2" 1. 步骤一:复制 .app 并改身份 cp -R /Applications/Lark.app /Applications/Lark-org2.app /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.electron.lark.org2" /Applications/Lark-org2.app/Contents/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleName Feishu2" /Applications/Lark-org2.app/Contents/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName 飞书2" /Applications/Lark-org2.app/Contents/Info.plist 2>/dev/null || true 改 CFBundleIdentifier 后,macOS 视其为独立 app:TCC(屏幕录制/麦克风等)、通知、 Dock 固定各自一套,与主实例互不干扰。 ...

September 7, 2026 · 7 min · 1339 words

飞书防撤回补丁复现指南:手工 patch asar 的 19 处注入

目标:一名工程师不依赖任何现成脚本,仅凭本文在任何一台 macOS 机器上,对飞书(Lark.app)手工完成防撤回补丁的注入、验证与排障。 撤回机制与加密等深层逆向原理见上一篇《飞书撤回消息逆向分析》;本文只讲怎么做。 实测版本:143.0.7499.203(macOS,Electron/Chromium 143,补丁 v31c)。 131 及更早版本的锚点差异较大(布局都不同),但方法论完全一致,见文末「升级适配」。 0. 前置条件 项 要求 系统 macOS(Apple Silicon / Intel 均可),已安装飞书 /Applications/Lark.app 工具 node、npx、python3 npm 包 @electron/asar(npx 自动拉取)、esbuild(语法校验用,npx 自动拉取) 权限 能读写 /Applications/Lark.app/Contents/...(默认可写) 关键事实(先记住,贯穿全文) 撤回推送会把消息 content 一并抹空(本地 DB 和前端都拿不到原文)——所以必须「渲染时存档、撤回时还原」,只改渲染布尔位只会得到空气泡。 普通消息撤回的原生样式(居中灰字「你撤回了一条消息」)来自 featureTypeParser 的 RECALLED 分支;行级闸门 return 的提示条组件只服务话题根消息(threadMessageType===1)。 原生「(已编辑)」标记不是 richText 数据元素,是富文本引擎在渲染尾部按 message.editVersion 标志挂的 suffix 槽位——所以撤回标记要改引擎门槛,而不是往数据里塞元素。 飞书主进程名是 Feishu(pgrep -x Lark 匹配不到);重启 = pkill -x Feishu + open -a Lark.app。 聊天窗口与设置窗口共用 profile_main 分区的 localStorage(file:// origin)——这是开关跨窗口生效的基础。 Info.plist 里的 AsarIntegrity(SHA256)校验实测不拦截修改后的 asar(改包后正常启动,校验逻辑只记录)。 开关语义:开(默认,frk_hide 不存在)= 显示原文 + 末位「(已撤回)」;关(frk_hide=1)= 完全恢复原生行为(parser 保留 isRecalled → 原生 RECALLED 居中灰字提示条,与未打补丁一模一样)。 1. 总流程 备份 asar(3 个) ──► 解包 ──► 定位锚点(特征串 grep) ──► 六类补丁逐文件修改 ──► 语法校验(node --check + esbuild 双检) ──► 打包 ──► 安装 ──► 重启飞书 ──► 验证(包内 grep 标记 → 功能测试 → 必要时读 leveldb 诊断) 涉及的三个 asar(相对 webcontent/ 目录;143 起 messenger-next.asar 已取消并入 messenger.asar): ...

September 7, 2026 · 11 min · 2154 words

飞书撤回消息逆向分析:原文是怎么被抹掉的

分析对象:飞书 macOS 客户端 /Applications/Lark.app(v131.0.6778.268 起分析,143.0.7499.203 复验,Electron/Chromium 内核)。 方法:静态逆向(strings / 反汇编 / asar 解包)+ 真实撤回的落盘观察 + 前端 asar 补丁注入的实测迭代。 本文讲原理与逆向过程;拿着结论去动手改客户端,见下一篇《飞书防撤回补丁复现指南》。 TL;DR:撤回是怎么实现的 撤回 = 服务端下发一条带撤回标记的消息资源更新,客户端把本地 SQLite 行打上 is_recalled=1 等标记列,前端按标记切换渲染成「撤回了一条消息」提示。而原始 content 会被一并抹掉(实测:绕过渲染门之后,被撤回的消息渲染成空气泡;DB 加载与撤回推送两路拿到的 content 均为空)。所以单纯改前端的布尔分支只能得到空气泡——防撤回必须「渲染即存档、撤回时还原」。 完整链路: 发起方:前端菜单 → JSB 命令 RECALL_GROUP_MESSAGE / RECALL_MESSAGE → Rust 核心 liblark.dylib(lark-message 模块)→ 长连接(pipe/websocket protobuf,非 REST)→ 服务端。日志串:recall_message callback for id: / recall message success: id=。 接收方:服务端推送更新后的 Message protobuf(字段含 is_recalled / recaller_identity / recall_type / recall_user_id)→ lark-message/src/logic/dependencies/processor.rs 校验(chat recall message validate failed, recaller not found 等)→ 更新本地库 → 通知前端。 本地库:SQLCipher 加密的 SQLite,messages 表同一行内:content BLOB(原始内容)+ is_recalled BOOLEAN + recaller_id BIGINT + recaller_identity INTEGER + recall_type INTEGER + recall_user_id BIGINT。 前端渲染(messenger asar,已提取实证代码): let{isRecalled:r,isDeleted:a,recallerIdentity:n}=t; return r ? <E recallerIdentity={n}/> // "X 撤回了一条消息" 提示条 : a ? <S.A/> : <N {...e}/> // 正常消息 以及 function L(e){return !e.isRecalled && !!e.content.filePath} —— 撤回消息对象上 content 依然存在,只是被布尔位挡住(但值已被掏空)。 通知/红点同步收尾:do not push notice to oneself when is recalled(自己撤回的不推通知)、thread 根消息标记 root_message_is_recalled、回复计数修正 update reply count for msg has recalled。 一、客户端架构 层 位置 作用 Electron 主进程 Contents/MacOS/Feishu 壳、窗口、升级 渲染进程 Lark Helper (Renderer) 加载 webcontent/*.asar(messenger.asar = 聊天 UI) aha-service 后台进程 Lark Helper --type=backend(加载 liblark) Rust 核心:同步、存储、JSB 命令 核心库 Libraries/liblark.dylib(84MB) 全部业务逻辑,Rust 编写,字符串含 lark-message/src/*.rs 等源码路径 DB 层 liblark 内静态链接 SQLCipher 4 本地库全加密 前后端通信:JS Bridge 命令表(全大写,如 GET_CHAT_MESSAGES、RECALL_GROUP_MESSAGE、HANDLE_PIPE_PACKET、HANDLE_WEBSOCKET_PACKET);服务端长连接推送走 pipe/websocket 包,protobuf 编码。 ...

September 7, 2026 · 3 min · 569 words
晋ICP备2026011119号 · 公安备案图标京公网安备11010602203188号