
用户不满健身房App强制社交信息流,逆向工程其应用
来源标题:My gym forced a social media down my throat. So I reverse-engineered its app
一名新加入某健身房的使用者因不满其App首页强制展示社交动态和打卡后弹出发布故事提示,对该App进行了逆向工程。该App仅有的实用功能是出示二维码进出场,但首页充斥会员自拍。该用户已超过12年不使用社交媒体,认为无法关闭或筛选的信息流是对其心理战。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
我最近加入了一家新健身房。健身房相当不错,器材好、管理好、品牌形象也好。
它可不是那种随便糊弄、靠 vibe coding 拼出来的烂 App。作为产品来说,它其实相当不错,UI/UX 很顺滑,设计语言统一且经过深思熟虑。我只在里面发现了一个 bug。
不过对我来说,它唯一的用途就是在闸机口出示二维码来签到和签退。
但问题来了。App 的首页是一个社交信息流。你一签退,它就提示你发一条本次锻炼的动态。首页信息流里全是大家的健身房自拍。每次打开 App,我都被迫看到一些本质上跟 Tinder 差不多的东西。
我朋友觉得这是个天才的商业模式。按她的说法,健身的人真的很想炫耀自己的身体,但发在普通社交媒体上会被人侧目。所以这家健身房公司确实找到了很好的产品市场契合点。
然而,我对不受自己控制的社交媒体和信息流态度极其强硬。任何 App 只要给我看一个我关不掉、排不了序、也过滤不了的信息流——我都视之为针对我的心理战。
我已经超过 12 年没用过社交媒体了。我非常坚持屏蔽广告、屏蔽 YouTube 评论、Shorts 和推荐视频,还会清洗 YouTube 的标题和缩略图。对于不得不用东西,比如 LinkedIn¹,我会用浏览器扩展把信息流彻底屏蔽掉。
我想看看能不能把二维码的逻辑单独提取出来,绕开 App 里那一堆臃肿的东西。
于是我开始挖。很快我发现代码每 15 秒变一次。那个界面还禁止截屏,跟 Android 的 FLAG_SECURE 一致。我怀疑这是某种 TOTP。生成是在服务端做的吗?
我做了最简单的一件事:关掉 Wi-Fi。App 立刻告诉我,在不重新联网的情况下,这些码还能继续有效 N 天²。
我用的是 GLM 5.3 和 DeepSeek v4.1 Flash。在我的使用过程中,GLM 烧掉的 token 成本大概是 DeepSeek 的 10 到 20 倍,进展却没多多少。DeepSeek 看起来快得多。
说明一下,为了给这家健身房和我自己匿名,我省略了一些细节。我不想丢掉我的健身房会员。我其实挺喜欢那家健身房的。
这个 App 是用 Flutter 写的,大部分逻辑编译进了 Dart 原生代码。我们用 blutter 来检查这部分,用 jadx 看 Android 侧的代码,用 Android 的 SDK 工具检查安装包。后来我们用 OpenSSL 和 WebCrypto 验证加密,用 zbar 独立解码二维码。
我时不时给 DeepSeek 一些提示,主要靠直觉,因为我以前从没干过这种事。有时候它似乎会在某个方向上钻牛角尖,而我觉得实现方式肯定比那更简单。只要告诉它我的猜测,或者说我觉得肯定有更简单的办法,就能把它引到正确的方向上。
我们很快弄明白了,二维码是一个 URL,里面包含四样东西:一个编码后的账号标识、一个加密的过期时间戳、一个随机 nonce,以及一个校验和。
过期时间用 AES-128-CTR 加密。校验和用另一个独立的密钥计算。所以我们需要这两个密钥,针对我的账号。
我最初的想法是让 DeepSeek 驱动一个 Android 模拟器,登录进去,把密钥提取出来。但我很快意识到,我那台从朋友那借来的、用了十年的笔记本,根本不可能以任何合理的速度跑 Android 软件模拟。
DeepSeek 建议用一台 root 过的手机。这时我想起来,我手边正好有一台还能用的、已经 root 的 Android 手机。
我把它开机,通过无线 ADB 连上,让 DeepSeek 去调查。结果发现,这个 App 会为我们关心的那些调用记录请求和响应的 payload。密钥材料就明明白白地躺在一个手机上的纯文本日志文件里。
密钥提取出来之后,我们做了一个简单的 SvelteKit Web App 来生成二维码。
过程中有几个错误的假设。过期时间戳必须以毫秒为单位,不是秒。编码是 base64url,不是标准 base64,而且保留了 padding。
我们还有一个错误的 AES 实现,却通过了它自己的往返测试。它加密某个东西,再解密,得到了原始输入。但两个方向犯的是同一个错误,所以这并不能证明它就是 AES。
跟 WebCrypto 对比之后,差异就暴露出来了。OpenSSL 和已知的测试向量确认了这一点。浏览器端 App 用的是 WebCrypto。
然后我们拿原始 App 的实际输出做比对。给定相同的输入,我们的实现生成了字节完全一致的 URL。我们还独立解码了二维码图像,确认它包含正确的 URL。
到这一步,我们有了一个能用的原型。这个 Web App 可以用提取出来的密钥在本地生成二维码。这并没有解决独立登录的问题,也没能免除密钥轮换后需要重新获取新密钥的麻烦,但已经足够去健身房试一下了。
第二天,我去了健身房,非常自豪地给朋友看我做出来的东西,但心里还是有点没底,不知道它能不能在闸机上通过。
再也不用仅仅为了进一家场馆举几块铁,就被迫让自己的注意力被那些暗黑模式的、操纵性的社交媒体劫持了。
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。