Insights·2026-08-07

如何读取 KakaoTalk 本地数据库,以及为什么只能在 Mac 上做到

KakaoTalk 没有提供让其他程序读取聊天内容的官方 API。想用代码处理这些消息,唯一的入口是 Mac 版 KakaoTalk 存放在自己应用容器里的数据库文件。该文件由 SQLCipher 整体加密,密钥由这台 Mac 的硬件 UUID 与 Kakao 账号内部编号共同派生。读取硬件标识的方式、应用存放文件的位置、以及保护该文件夹的权限体系全都属于 macOS,因此这条流水线只能在 Mac 上运行。它也无法在 cron 下工作,会静默挂起而不是报错。越过第一步之后,其余都是常规工作:按聊天室只取新消息、逐室生成摘要、推送到 Slack,再由 Slack 中的机器人撰写工单正文并登记到 Jira。

카카오톡 DB 복호화는 맥에서만 성립한다 — 복호화 CLI와 LaunchAgent 등록 명령, cron과 LaunchAgent 대비를 담은 요약 도식
cron은 권한 대화상자를 못 띄워 조용히 멈춘다. LaunchAgent는 로그인 세션 안에서 돌아 프롬프트를 띄울 수 있다.

流水线分为四步

先看整体形状。每天早晨把 KakaoTalk 开放聊天室的对话生成摘要,发到 Slack 频道,再从那条 Slack 讨论串出发,由 AI 撰写工单正文并登记进 Jira。拆成四步:从 KakaoTalk 读取对话、逐室摘要、发送到 Slack、把 Slack 讨论串变成 Jira 议题。

第二、三、四步并不难。摘要就是把文本交给大语言模型;Slack 用一个 webhook 地址就能发帖;Jira 有正式的 REST API。三者都是有文档的正规路径。

问题出在第一步。KakaoTalk 没有让其他程序索取某个聊天室内容的官方渠道。Kakao 公开的开发者 API 方向是向外的,用于登录、发消息和分享,而不是向内读取自己的记录。所以这一步花的工夫比其余三步加起来还多。

唯一的入口

是否真的别无他法?手动的路是有的。在聊天室里可以把对话导出为 txt 或 csv 文件,我也确实保留着这样导出的文件。但它无法驱动每天无人值守运行的流水线,因为每个聊天室都得有人去点。

于是只剩一个选择:打开 KakaoTalk 已经存在我自己机器上的数据。Mac 版 KakaoTalk 把收到的消息累积在应用容器内的数据库里,容器就是 macOS 为每个应用分配的私有文件夹。原则上应用只在自己的容器内活动。

有一点需要说清楚。这不是窥探别人的数据,而是这台 Mac 的主人读取自己登录后收到的自己的对话。即便如此,这条路线是后门而不是 Kakao 打开的门,使用时必须假设它会在 Kakao 改变存储方式的任何时刻失效。

加密数据库的密钥从哪里来

图解加密 KakaoTalk 数据库的两层防护及密钥的生成材料

打开容器文件夹并不会直接看到对话,前面还有两道墙。

第一道是文件名。数据库不叫 kakaotalk.db 这类可读名称,而是一串 78 位十六进制字符,而且每台机器都不同,因为它是由账号编号与设备标识计算出来的。光看文件夹无法判断哪个文件装着消息。

第二道是加密。文件本身由 SQLCipher 整体加密,这是一个在文件层面加密 SQLite 数据库的扩展。用普通 sqlite3 命令打开会报错说这不是数据库文件,必须知道口令并使用 sqlcipher 命令。

那么口令从哪来?不是用户输入的任何东西,而是程序用两样材料算出来的:这台 Mac 的硬件 UUID,以及 Kakao 账号的内部编号。两者混合后经过标准密钥派生函数 PBKDF2 迭代十万次。PBKDF2 的作用正是通过重复计算把短输入拉长,让暴力破解变得昂贵。

有一个细节很有意思。账号编号并没有明文写在配置文件里,只留下了它的哈希,也就是经过单向函数处理后的值。所以要从零开始逐个尝试整数,直到某个值产生相同的哈希。账号编号是个不算大的整数,这种做法能在可接受的时间内完成。

这些都不是我的发现。一位名为 blluv 的开发者公开了 gist,kakaocli 项目也做过整理,我的代码在头部注释中标注了这两个来源。因此本文只解释结构与约束,不提供可直接复制的密钥派生代码,需要的话去看原始出处即可。

为什么只能在 Mac 上做到

图解使该流水线只能在 Mac 上运行的四个 macOS 依赖项

经常有人问能不能移植到 Windows 或 Linux。现在这套代码在那里跑不起来,实现被 macOS 绑住的地方有四处。

第一,构成密钥的硬件标识是 macOS 专有的。它是通过 macOS 的 ioreg 命令读取的 IOPlatformUUID,Windows 没有对应的值。

第二,文件位置属于 macOS 沙箱结构。在 ~/Library/Containers 下有以应用 bundle id 命名的文件夹,数据库就在其中。Windows 版 KakaoTalk 的存放位置与文件结构都不同。

第三,访问控制是 macOS 的 TCC,即当一个程序要触碰另一个应用的数据时向用户征求同意的隐私机制。其他操作系统没有对应物。

第四,移动端从一开始就不在范围内。iOS 与 Android 在系统层面禁止一个应用访问另一个应用的数据。也就是说这套实现成立的条件是 Mac 版 KakaoTalk。

有一个误读需要更正:这并不意味着在 Windows 上读不到聊天记录。Windows 版 KakaoTalk 把消息以 .edb 文件存放在 %LocalAppData% 下并用 AES 加密,其密钥派生方式已由韩国的取证研究公开(如 blog.system32.kr)。因此移植到 Windows 不是被堵死的问题,而是把密钥派生按那边的规则重写的工作。本文只谈 Mac,是因为我的流水线跑在 Mac 上,而不是因为 Windows 关着门。

实务上剩下的限制只有一条:必须有一台 Mac 长期开机来跑流水线。

不能用 cron —— 为什么必须是 LaunchAgent

既然是每天运行的任务,放进 cron 看起来理所当然。我一开始就是这么做的,结果失败了。失败的方式很难缠,值得单独记下。

没有报错,也没有拒绝提示,就是停在那里。原因是 TCC。读取 KakaoTalk 容器需要完全磁盘访问权限,而 macOS 在索取该权限时会在屏幕上弹出对话框。cron 作为后台守护进程无法把窗口推到用户面前,于是它既问不出口,也没有被拒绝,只是等待一个永远不会到来的回答。

解法是更换执行主体:用 LaunchAgent 代替 cron。LaunchAgent 是运行在已登录用户会话内的 macOS 调度器,因此能够弹出授权窗口,一旦获准之后便可安静运行。

由此又派生出一个陷阱。权限是按可执行文件绑定的。如果让 sqlcipher 直接读取 KakaoTalk 容器,就得单独给这个二进制授权,而且每次 Homebrew 更新它,提示都会重新出现。因此改由 Python 先把文件从容器复制到 /tmp,sqlcipher 只接触副本,把需要授权的主体收敛为一个。

解密后的明文数据库同样要留意。放在 /tmp 里按默认权限,同一台机器上的其他进程都能读取。它装着完整的对话记录,所以一写出来就立刻收紧为仅所有者可读。

解密所需的 CLI 与 LaunchAgent 注册
# SQLCipher CLI — brew autoremove로 지워지면 파이프라인이 통째로 멈춘다
brew install sqlcipher

# cron이 아니라 LaunchAgent로 등록한다
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.edb.kakao-briefing.plist
launchctl list | grep kakao-briefing

读到之后 —— 只取新消息,逐室摘要

到这一步,手里就是一个明文 SQLite 文件,之后都是常规的数据处理。

每次都对全部内容做摘要在成本和时间上都不可行,所以每个聊天室记录最后处理过的消息编号,只取更新的部分,这就是常说的高水位标记。为了避免周末跳过运行造成消息丢失,回溯窗口留得比较宽,重复则由消息编号挡住。

消息并非全是同一类。数据库把文本、图片、广告和系统通知混在同一张表里,每类带有类型编号。只摄入文本以及回复和链接分享,其余丢弃。少了这道过滤,摘要提示词会被广告文案塞满。

摘要按聊天室逐个调用。最初是把所有聊天室合并成一次调用,结果某个消息暴涨的聊天室触发超时,同一次调用里其他所有聊天室的摘要也一起消失了。改成逐室循环并各自捕获异常后,一个聊天室出问题不会拖垮其余。每个聊天室还设了最近消息数量上限。

成本也做了切分。普通开放聊天室的摘要由本地运行的小模型处理,只有关乎业务判断的聊天室和日程抽取交给更强的模型;失败时会自动向上回退。

从 Slack 到 Jira —— AI 写的是工单正文,不是 PRD

摘要生成后通过 Slack webhook 发到频道。webhook 是最简单的集成方式:Slack 发放一个地址,往该地址提交文本就会在频道里出现一条消息。

这里有一个实现陷阱。最初的版本用正则表达式在摘要 markdown 中查找标题行,据此切出每个聊天室的正文。后来把 Telegram 输出改成纯文本,标题符号消失,这段解析就静默失效了。现在摘要步骤会另外落一份以聊天室名为键的 JSON,Slack 发送方读取该 JSON,展示格式的变化不再影响数据通路。

进入 Slack 之后由第二个机器人接手,它通过 Socket Mode 连接,即主动向 Slack 建立连接而不是对外暴露公开地址,因此在内网或个人服务器上运行也无需打通防火墙。

提及机器人后,消息会交给大语言模型并返回结构化 JSON:是否要创建议题、属于哪个项目、议题类型与优先级,以及标题和正文。正文要求覆盖现象、复现方式与预期结果三块。

这里有个用词需要说准。人们常把这一步称作 AI 写 PRD,但实际产物是工单正文,而不是一份独立的策划文档。它不是在产出产品需求文档,而是在填好一张开发者可以直接接手的工单。混淆这个区分会让预期错位。

判断以置信度划线。高于 0.8 直接创建议题并在讨论串里回复链接;介于 0.5 与 0.8 之间则询问人是否创建;低于 0.5 只记录日志并忽略。以消息时间戳防止同一条消息被处理两次。

人留下的位置正在于此。重复的汇总、整理与撰写交给机器,人负责裁定模棱两可的情况并决定要不要做。

当 Kanana 走向 B2B,这一层就会消失

这条流水线的第一步,是因为没有官方通道才修的绕行路。绕行路的寿命只到正门打开那天为止。

Kakao 已经展示了这个方向。2026 年 4 月 23 日,在首尔 COEX 举办的世界 IT 展上,Kakao 以其 AI 品牌 Kanana 发布了『今日简报』,该功能会分析用户过去的对话,梳理出重要日程、纪念日与待办事项。同时展示的还有情境感知能力,例如在商定见面地点的对话中推荐餐厅,或识别生日话题并联动礼物功能。它和我通过解密数据库做出来的东西目的相同。

不过发布的范围面向个人用户,没有提及 B2B 或企业用途。而眼下真正需要的并不是个人的每日简报,而是让团队频道的对话变成工作工单的那一层。

这一层有打开的余地。2026 年 7 月,Kakao 将四款 Kanana-2 轻量模型开源,并强调了工具调用能力,也就是模型自行调用外部系统功能。读取对话、做出判断并登记到另一个系统,正是这项能力的用武之地。

那一天到来时,我的解密代码就作废了。这是正确的结果。绕行路只在没有正门时才有价值,正门出现后就该毫不留恋地弃用。做自动化资产时最好从一开始就把这一点算进去,标清哪些层是临时绕行、哪些层是长期资产,这样平台变动时需要重建的范围才会清晰。

在这条流水线里,长期资产不是解密,而是它后面的一切。逐室拆分摘要、以置信度划分自动与人工确认的关卡、以及防重复的键设计,无论输入是 KakaoTalk 还是官方 API 都照用不误。