最近在网上冲浪的时候注意到这两年有不少新款 CD 机，他们给这类二三十年前的潮流单品带来了诸如锂电池、PD 供电、USB DAC、SD 卡等新时代的特性，喜欢听碟片的老烧们应该挺开心的。 作为一个从 2003 年开始上网的小朋友——当时 MP3 格式与 MP3 播放器正开始占领市场——我摸到的第一台便携式音乐播放器，已经是 MP3 播放器了（理论上应该叫数字音乐播放器，或数播，但我还是更喜欢称呼他为 MP3 播放器）。我对便携 CD 机的印象仅来自《电脑报》，对 CD 的印象，除了安装软件的碟片和看动画片的 VCD 之外，就只有音像店里那些可以试听挑选的透明盒子了。想到这里，有个疑问突然在我脑海中浮现：便携 CD 机似乎也不便携。 我虽然从小使用 Google 搜索网页，但百度有个功能我很常用，那就是免费 MP3 搜索。这是 Google 没有，或者说因为版权问题不可能做的功能。在那个大家的版权意识都很淡薄、打击盗版困难重重的年代，即使被起诉多次，百度 MP3 搜索依旧在中文互联网上坚挺到了 2012 年。在 2006 年刻的备份光盘中，我还能找到一个文件夹，里面放了大量的盗版音乐，比如《酸酸甜甜就是我》《下辈子如果我还记得你》《喜欢两个人》《两只蝴蝶》，他们大抵是利用百度 MP3 搜索找到的，个个都是幼儿园版 Soha 的精选。从网上下载盗版的 MP3 格式音乐，然后在电脑上使用千千静听等软件播放，或导入播放器中聆听，是 20 年前听歌的日常。 如果再把时间往回拨几年，回到金贵的闪存容量不够大、娇气的硬盘方案不成熟、MP3 播放器性价比不高的时候，想要随时随地欣赏音乐，除了音质较差的磁带机和不自由的 MD 机，就只剩 CD 机一种选择了。而一张普通 CD 的直径也有 12cm，就算机器做得再小，裤子口袋能够塞下他，走起路来 CD 也会一直震动。哪怕有缓冲区防震，读取也还是很辛苦的一件事吧？而且一张标准音乐 CD 只能存储 70 来分钟的音乐，日常通勤倒是够了，但如果要出远门呢？ 在网上搜来搜去，除了看到 CD 机与 CD 盘片结合在一起的简单收纳包之外，还发现了这样一个包：他可以放下一个 CD 机，后面有一些插袋可以放置一些碟片。最关键的是，CD 机的位置还有个专门引出耳机线的地方。看到这儿，突然一部分记忆就苏醒了。 我上高中之前用的背包，大都有个十字开孔的橡胶片，位置靠近肩部，其他同学的包也多是这样。而在橡胶片上，通常有个头戴式耳机的标志。顾图思义，耳机线可以从那个孔里拉出来，不用再从拉链头绕一大圈。橡胶片本身也能在一定程度上防止雨水顺着耳机线流入背包。CD 机放在背包里，受到的震动也相对会小一些。小时候的我也曾试过用起这个功能：把自己的 MP3 播放器放进背包，线穿孔而出。试了一次便觉得这个功能实在是太鸡肋了：“明明那么小的东西揣口袋就好了，怎么还要多此一举呢？究竟是谁，在什么情况下才会需要这个设计呢？” 人无法想象自己没见过的东西，我当时也想象不到使用 CD 机的困扰。如果不是今天研究了一下，怕是永远都没法给当时的自己一个答案了。不过在研究过程中，我又产生了一个新问题：以前的耳机似乎都没有“线控”，如果放在包里岂不是很不方便切歌或暂停？再次经过一番搜索，在 Reddit 上看到讨论方知，名叫“线控”的配件算是当时中高端机型的标配。耳机插线控上，线控插 CD 机上，线控上的屏幕可以显示当前是第几首歌，还有按钮可以快进快退、切歌、暂停和播放。 至于“长途出行怎么办”，答案就很简单了，在收纳包里多放两张碟就是了。还可以自己扒碟、刻录，拼凑成自己的专属“歌单”。如果嫌音乐 CD 容量太小，新世纪的 CD 机也普遍支持播放数据光盘中的 MP3 文件，这样一来，CD 的 700MB 容量可比 2003 年主流 MP3 播放器的 128MB 容量大多了。把原本 44100Hz 16bit PCM 的音乐换成了有损高压的 MP3，一张 CD 能容纳的歌曲便接近了 100 首（320kbps MP3），甚至 200 首（128kbps MP3）。 互联网的发展还是太快了，MP3 播放器和能够存储、播放音乐的智能手机没能像他们的老前辈——磁带机和 CD 机——那样流行很久。iOS 和 Android 带来了更先进的手机，4G 网络则带来了更快的网速和更低的资费，串流音乐平台在这样的时代下迅速上位，替代了传统的离线音乐载体。听歌变得更便捷了，音质也变得更好了，订阅式的曲库让我们拥有了永远听不完的音乐，似乎我们做到了“听歌自由”。推荐算法也逐渐接管了我们发现音乐的方式，平台推荐的歌几乎总是我们“爱听”的。听歌也变成了一件稀松平常、甚至会令人有点审美疲劳的事情。 我们失去了去音像店用手指划过一盒盒专辑试听挑歌的快乐，失去了为偶然听到的一首宝藏歌曲买下一张专辑的激情，失去了被精心设计制作的 CD 内页击中的愉悦；而在一个个确定的文件、完美的标签分类中，我们也失去了发现音乐人费尽心思制作的彩蛋的惊喜。就像书籍遇到的情况一样，在现在这个浮躁的世界里，有很多东西都不一样了。 （后记） 关于彩蛋。原本我购买 CD 只是为了收藏，心里想着：“反正串流平台都有这些歌，解析度还都比 CD 高”。直到上个月一时兴起扒了收藏多年的《星河旅馆》CD，才发现在物理介质上，总是很容易隐藏一些彩蛋。当时，我扒完轨道后和曲目表一比较，发现竟然多了一条轨道。才知道在最后还藏了一段酒馆版的《醉鬼的敬酒曲》，以及另外三段主人公的日记；这些内容似乎没人在微博和 b 站提过，所以我还发了条影片讲这个，监制老师看到自己的小心思终于有人说出来也很开心。 这个彩蛋是通过在新轨道前加入一段很长的静音来隐藏的，只有听完整张专辑后继续坐着回味，或是注意到编号变了、时间也一直没停，才会注意到。还有另外一种常用的方法是利用 PreGap ——为了方便在曲目跳转时跳过原本在乐曲之间预留的“呼吸”空白的参数——在负数时间轴里隐藏，这个需要一直按住快退才能倒带过去，所以比较难发现。现在通过互联网发行的音乐，所有内容都是一个个确定的文件、确定的标签，想再藏点什么，已经很难了。 关于线控。群友介绍，其实在数播时代也有过线控，甚至有本体没屏幕，靠线控的屏幕显示的产品。我的第一反应就是，是不是硬盘方案的。毕竟本质还是当时人类的科技树还没满足小型化的便携需求。查了下互联网，也确实找到了 iAUDIO M3 这种和 iPod 一样使用了 1.8 吋硬盘的产品。
464XLAT 是走向 IPv6 单栈的过程中，用来让纯 IPv6 网络能够通过地址翻译访问 IPv4 网络的技术。他的工作流程是：IPv4 应用直接发送 IPv4 分组，到达 CLAT（Customer-side Translator，用户侧翻译器）进行无状态 NAT46 转换成 IPv6 分组，通过 IPv6 网络传输到达 PLAT（Provider-side Translator，运营商侧翻译器）后再进行 NAT64 转换成 IPv4 分组到达目标 IPv4 主机。 有了这个技术，IPv4 协议栈就可以在接入层上完全砍掉了，只需要有 CLAT 将 IPv4 分组送到 IPv4 网络的边界即可。CLAT 之所以叫“用户侧”，是因为他通常运行在光猫，甚至终端设备（如手机、电脑等）上。本文记录的是终端设备运行 CLAT。 2 月春节期间，我在家里的网络上设置了 NAT64 作为 PLAT，并配置了 DHCPv4 的 IPv6-Only Preferred 标记之后，DHCPv4 的地址分配骤减到 4 个，还在使用 IPv4 的只剩下智能家居、无线电台这些嵌入式设备了。 可以见到，图中 3 个平台的 IPv4 地址均为空或者 RFC 7335 指定用于 v4 转换的 192.0.0.0/29。 其实这种事情不是第一次干，早在 2019 年就尝试过一次：当时我用 tayga 在大学寝室的 OpenWrt 上配置了 NAT64，然后使用 bind9 做了 DNS64。这一套方案通过将没有 AAAA 记录的域名的 A 记录转换成 AAAA 的方式来实现 IPv4 网络的访问，也是当时的标准做法。这样操作的问题显而易见，毕竟总有应用不按照你所想的方式去查找服务器：他们使用指定的 DNS 服务器、使用 HTTPDNS，甚至使用了硬编码的 IPv4 地址。这些都是 DNS64 方案无法解决的，因此 DNS64 只能提升接入层的 IPv6 流量比例，而并不能完全抛弃接入层的 IPv4 协议栈。 所以更好的方案是，让设备去完成这个 IPv4→IPv6 的转换。那么问题来了，设备怎么知道 NAT64 可用、怎么知道使用什么前缀呢？当时 RFC 7050 提供了通过查询 ipv4only.arpa 获得 NAT64 前缀自动配置的方案，但是他依旧受到了 DNS 服务器的限制，不适合反映接入网络的真实情况，支持度也基本局限于蜂窝网络。甚至这个域名加入 IANA 的 Special-Use Domain Names 列表都是 2020 年的事情。 到了 2020 年，RFC 8781 往 Router Advertisement 中新增了一个名为 PREF64（Type 38）的选项，路由器可以在宣告默认路由、前缀等 v6 信息的同时，宣告将 IPv4 地址嵌入什么前缀就可以通过 NAT64 访问 IPv4 网络了。这才是自动发现应该有的最佳方案，毕竟 Router Advertisement 作为通常的终端设备自动发现并配置 IPv6 网络的必要手段，反应的也正是当前网络的信息。 2021 年发布的 Android、2022 年发布的 iOS 16 和 macOS 13 都支持了 PREF64 参数。同期支持的还有 RFC 8925：他往 DHCPv4 中新增了一个名为 IPv6-Only Preferred（Option 108）的选项，支持 CLAT 的设备在尝试请求 IPv4 地址时如果收到了这个选项，那么他就会主动放弃 IPv4 协议栈。（当然也可以选择直接关闭 DHCPv4，只是这样不支持 CLAT 的设备就无法访问 IPv4 了。） 2025 年秋天，Windows 发布了 CLAT 的封测，我光速报名了。这下三大厂都支持了，于是便有了文章开头的内容。当然在测试过程中，我也发现了一些问题，已经反馈回相关负责人了。 2 月 19 日，Windows Insider Preview (Canary) 的代码从 Br（溴）更新到了 Kr（氪），build number 为 29531。这个版本引入了 CLAT，只是默认没有启用。6 月 9 日，Windows 网络团队宣布：自 Build 29599.1000 起，该功能正式开始公测。如果你家也部署了 IPv6-mostly 的网络，并想体验 Windows 的 CLAT 的话，可以使用以下操作开启： 使用 netsh.exe interface clat set global permit=enabled pref64fromdns=enabled pref64fromra=enabled 开启 CLAT，并重新启动系统。 重启后可使用 netsh.exe interface clat show global 和 netsh.exe interface clat show instance interface = [interface-name] 两条指令查看情况。 除了 netsh 之外，该功能还支持使用组策略配置。想知道组策略的操作或更多 netsh 参数，请阅读 Windows 网络团队的博客。 因为 Windows 的实现还有些问题，所以暂时不太建议在自家网络之外的地方开启。等 Windows 正式版支持 CLAT 后，微软/苹果/谷歌三大商业操作系统就都有 CLAT 且 IPv6-only capable 了，接入层可以只有 IPv6 的好时代就来了！ 另外再借个位置记录一些东西，是我在配置 464XLAT 过程中遇到的一些其他的坑。 首先是 Juniper。我使用 Juniper SRX 系列产品作为家里的防火墙已经有 7 年历史了，配置文件也是祖传的。为了能够把自己的 IPv6 在传出到电信之前转换成电信 DHCPv6-PD 下发的前缀，之前正常的工作的 static NAT 是这么写的： set security nat static rule-set ctv6 from interface pp0.0 set security nat static rule-set ctv6 rule soha-home match destination-address 240e:390:REDA:CTED::/64 set security nat static rule-set ctv6 rule soha-home then static-nat prefix 2a0e:aa06:40e:beef::/64 而 IPv4 地址嵌入 IPv6 前缀也是一种 static NAT，我又加入了这样的代码： set security nat static rule-set nat64 from zone INT-soha-home set security nat static rule-set nat64 rule plat-soha match source-address ::/0 set security nat static rule-set nat64 rule plat-soha match destination-address 2a0e:aa06:40e:64::/96 set security nat static rule-set nat64 rule plat-soha then static-nat inet 结果 PLAT 一直不工作，打 trace 以后注意到系统说 The packet destination ip is not same as source ip version, drop it。原来之前因为偷懒，match 条件只写了 destination-address 而没写 source-address ::/0，导致他在处理跨地址簇报文的时候爆炸了。补回去以后工作正常，又顺手把 source NAT 那边的也都补上了。 然后是 Android 的坑。刚设置上 IPv6-Only Preferred 的时候，Android 手机直接掉线，显示 Wi-Fi 连接失败，而我可以确认他是在收到 DHCPv4 响应以后断开的，一开始我百思不得其解，直到发现了我手机上并没有 IPv6 的默认路由，紧接着注意到系统上设置了 accept_ra_min_lft=180。 我配置的 Router Advertisement 的最大发送间隔是 45s，而 Juniper 默认在 Router Advertisement 报文中填入的 lifetime 是最大发送间隔的 3 倍，即 135s，小于 Android 的默认 180s 限制，故不会自动产生默认路由。没有默认路由更别提上网了，所以 Android 就显示了“Wi-Fi 连接失败”。修改 lifetime 后 Android 也正常工作了。 这种行为比较违反 RFC，RFC 4861 可是说“receivers should handle any value”，只要 >0 就应当创建默认路由的。但 Android 这么设计是为了减少收到 RA 时拉起系统处理的电量消耗，变更发生于 2023 年 9 月。这下同样解答了我之前的另外一个疑惑，最近半年多来总是发现手机在用 IPv4 上网，而我一直以为是自己 IPv6 网太烂而回落到了 IPv4……
马年的有趣好玩的解谜夺宝红包来了！快快点击参与吧→ https://hb.lohu.info 下面是一些你应该知道的事情： 这是一个解密寻宝游戏（a.k.a. CTF），利用你的知识（现学大概也是足够的）解决所有题目，获得红包口令，口令可进入支付宝领取红包。 你应该需要电脑才能愉快玩耍，但手机可能也能解一部分。解法取巧，不涉及对服务器的任何暴力解法（爆破等）。 本次活动时间从北京时间 2026 年 2 月 16 日 22 时开始，持续 24 个小时。 你拿到的所有答案都是 8 位数字，为了方便控制红包发放时间，在支付宝领取时，前面需要加上一个“马”字。如解题得到 20260217，那么最后的红包口令是“马20260217”。 共设 3 个红包，数额分别为：总额 52 人民币元，分 68 个；总额 68 人民币元，分 28 个；总额 68 人民币元，分 36 个。红包领取规则均为“拼手气红包”，如果红包被提前领完不会补发。 如果你是和其他人一起完成的，希望你们先让一个人领取一次再瓜分，但可以在快结束的时候进行第二次领取。总之就是要把机会先留给别人。 这个游戏由 Soha 制作，在游戏结束后也将在本页面上放出题解，往年的内容也可以在博客上找到。如有疑问也请通过博客的“关于我”页面上所述的联系方式联系。 本提示可能会更新，别忘了偶尔来看看。 最后祝大家，新年快乐！ [T+03:00] “manian”更新了点提示。 [T+15:00] “ma26”已经领完。有一些人已经摸到了“manian”的最后一步但没有领到红包，他有两个要素，要凑齐以后才能领到红包。 活动结束后将在下面更新题解。已更新，请看下方的题解。 往期回顾 2025 乙巳蛇年 2024 甲辰龙年 2023 癸卯兔年 2022 壬寅虎年 2021 辛丑牛年 2020 庚子鼠年 2019 己亥猪年 2018 戊戌狗年 2016 丙申猴年 这个红包所涉及的一些代码和文件可以在 GitHub 找到。 2015 年支付宝推出了口令红包，那两年也是群友最热衷于制作解谜红包的时候，一个春节在一个群里就可以有好几道题目可以做。大家把 8 位口令按照自己的创意藏在各种地方。在做了各种群友的红包以后，我觉得这个活动形式非常有意思，于是 2016 年我第一次参与了出题，并从 2018 年持续至今。红包口令一直是 8 位数也是从那时候传下来的。 因此，今年出题的想法是返璞归真，回到十年前简单 misc 的乐趣。3 个题目也都是前几个月一个个在脑子里蹦出来，记录下来而产生的。 不得不说，2025 年是 AI agent 发展非常迅速的一年，从日志可以看出很多参与者都“雇佣”了 agent 帮他们干活。虽然我还在坚持“古法编程”，但也经常指使 AI 帮我查资料。提到这个则是因为见到了 AI agent 对红包题目的降维打击，确实没想到最后看日志会发现数名硅基生物的痕迹。向使用了 AI 解题的群友要了思考过程，注意到 AI 虽然也和人类一样，尝试的过程比较随机、会绕弯路，但靠其并发能力和广阔的脑洞，最终可以解出今年的 3 个红包。但是，作为出题人而言，从活动的本意出发，我可以接受把 AI 当作群友一同讨论，但无法接受让 AI 完全自己完成。这不光是对出题人的尊重，也是对其他徒手参与者的尊重。 接下来我们来做一下这些题目。打开页面，还是祖传的页面。有些朋友可能会注意到 .redbag 里面的 transition-duration: 1082.8359ms;，但如果看过去年的题解应该会知道这里是我忘了改（不好意思，嘻嘻），而不是一个红包。 点击“开”之后，可以看到两个域名，一个有链接，一个没链接。查看源代码还可以发现一个注释掉的域名。接下来我将一个个解答。 → 26horse.hb.lohu.info → manian.hb.lohu.info <!--p>→ ma26.hb.lohu.info</p--> 遅延好き 首先查询 DNS 记录，发现 26horse.hb.lohu.info 只有一个 AAAA 的记录，并且这个 IP 什么端口都没开。如果 ping 的话，好像也没……诶，有响应！ Linux 的 ping 工具会明确提示“(truncated)”，那这回包明显是有点东西了。至于为什么要 8、9 秒才会收到，单纯只是我想延迟了，所以把每个返回报文都缓存了一个固定的时间才返回，有些朋友如果不够耐心就会错过回包。 打开 Wireshark 观察 ICMP echo reply 就可以发现红包口令被直接写在回包的 payload 里面。 这个题目设计上是签到题，但是 52 元分 68 个的红包只被领走 41/68 个（共 32.51 元），一血在 T+00:13:20。 发个红邮包 ma26.hb.lohu.info 虽然没有给链接，但是如果直接访问的话也可以注意到其上有一个 HTTP 服务，有着和 landing 页面一样的内容，只是点击红包“开”以后的内容不一样： 请 foobar 院新年红包研究所的同志使用自己的 @hb26.foobar.ac.cn 邮箱编撰一封邮件，发送到 get@ma26.hb.lohu.info 领取。 怎么直接把获取方式写了出来，这道题未免也太简单了吧！让我们来试试： echo | curl --no-progress-meter -v \ --url smtp://ma26.hb.lohu.info \ --mail-from 'soha@hb26.foobar.ac.cn' \ --mail-rcpt 'get@ma26.hb.lohu.info' \ --upload-file - 可恶，果然没有这么简单！系统返回了“550 5.5.0 Rejected by spam filter”！而如果我们尝试修改收件人、发件人等信息，也只会获得 550 5.1.1 This mailbox is not found.、550 5.7.1 Sender domain is not in the whitelist. 等报错。 即使你写出了如下漂亮美丽的邮件正文： Date: Tue, 17 Feb 2026 14:07:29 +0800 From: "hongbao" <hongbao@hb26.foobar.ac.cn> To: <get@ma26.hb.lohu.info> Subject: New Year Red Packet Content-Type: text/plain; charset="utf-8" foobar 院新年红包研究所的同志们新年快乐！ 我是来自 foobar 院的 Soha，申请领取新年红包。 Best regards, Soha 也逃不过 Rejected by spam filter 的命运。 本来以为大家会整点活的，结果翻了半天都没见到什么有意思的邮件。 那么该怎么办呢？让我们回忆一下电子邮件反垃圾的最基本规则：首先要确定发信人是获得授权的，对应到现实中的方式就是 SPF 和 DKIM。前者在 DNS 中定义了哪些主机可以以某域名的名义发邮件，后者则是在 DNS 中公开了公钥，使用密码学手段对邮件进行签名。 查询 SPF 记录可得： $ dig +short TXT hb26.foobar.ac.cn "v=spf1 ip6:2a0e:aa06:40d:beef::/64 ip6:2a09:bac0::/29 ip6:2a0e:aa06:40e:beef::/64 ip6:2606:54c0::/30 -all" 某群友说：“我随手查了两个前缀发现都在 Soha 网内，我以为关键点不在这儿呢。”但他只要再查一个就可以发现 IP 是来自 Cloudflare 的了： $ whois 2a09:bac0:: inet6num: 2a09:bac0::/32 netname: CLOUDFLAREWARP country: US admin-c: CAC80-RIPE tech-c: CTC6-RIPE status: ALLOCATED-BY-LIR mnt-by: MNT-CLOUDFLARE created: 2019-03-14T22:39:38Z last-modified: 2019-03-19T21:12:40Z source: RIPE # Filtered Cloudflare 被称为“互联网大善人”，其中的一个原因就是提供了免费的 VPN 服务“WARP”，保护用户的原始 IP 不被自己的 ISP 追踪，转而把自己的底裤卖给 Cloudflare。所以看起来，我们利用 WARP 就可以获得 hb26.foobar.ac.cn 授权发信的 IP 了。 配置好 WARP，然后访问！诶？这回怎么连服务器都连不上了。原来是 Cloudflare 封掉了出向的 25 端口，这也是 ISP 非常常见的为了避免自己的 IP 被发垃圾邮件而“弄脏”的安全策略。 在这里，为了避免这种情况，我开了 587 端口和 2525 端口供大家使用，前者是 RFC 6409 中指定的用于 Mail Submission（也就是给用户发往自己的邮件服务提供商用的）的端口，后者则是非标准的 25 端口替代（也是 Mail Submission 用途）。虽然他们都不是正规的发邮件给对方 MTA 的端口，但是毕竟作为红包不需要这么严谨嘛。 这样，就能得到红包口令了： 550-5.2.2 Happy New Horse Year! 550 5.2.2 Hongbao: 82460732 注意：我只设置了 IPv6 的前缀，如果不强制使用 IPv6 的话，可能会使用 IPv4 连接服务器，这依旧是会失败的。 在现实当中，收件方在发件方给出 MAIL FROM 的时候通常就已经进行了 SPF 检查，不会像我一样等到整个邮件都收完的时候才拒绝（源码）。但实在想看看网友们的创造力，会不会搞出什么有趣的邮件内容，所以在 Milter 发送 EOM 事件的时候才统一检查。 当然，如果在 MAIL FROM 的时候就检查，很多朋友应该也能够更快地发现答案了。 这个红包 68 元分 28 个，被全部领完，一血发生在 T+00:15:33。这个题目应该是今年最有趣的一道，但是他的思考链条比较清晰、直接，所以如果使用 AI 在福至心灵的情况下，能够一发入魂。或许这就是这个红包在 T+10:53:37 的时候能够被全部领完的原因吧。 中古祝福 这个红包直接给了一个链接 https://manian.hb.lohu.info，点开以后可以看到一个 20 年前风格的页面。底部还说“本站建议使用 IE 5 或 IE 6 浏览，建议设置 800x600 分辨率以获得最佳体验。”真是非常 20 年前的设定呢。我们用 IE 5 打开，发现并没有什么变化，只是有两个图案因为字符集问题不能显示出来（哈哈，我欠考虑了，IE 6 是能正常显示的）。 既然和 IE 5 没关系，那么该从哪里入手呢？作为一个祝福页面，还显示了今天的日期，是不是有点奇怪了？而且在 HTML 里，显示日期的那行还有页面中唯一的一个 ID：date。其实这里就是为了暗示我们要操纵时间。开始后 3 小时我加了一句“虽然今年没有准备红包”，也是为了暗示这一点：既然今年没有，那去年或者明年会有吗。 那么怎么做才能操控时间呢？ 在 HTTP 规范 RFC 9110 中，有个名为 Date 的 header，用来表示消息产生的日期和时间。一些朋友以为这个是只有服务端才能发送的，其实不然，规范里是这样写的： A user agent MAY send a Date header field in a request, though generally will not do so unless it is believed to convey useful information to the server. For example, custom applications of HTTP might convey a Date if the server is expected to adjust its interpretation of the user's request based on differences between the user agent and server clocks. 用户代理（User Agent）可以在请求中发送 Date header，但通常不会这样做，除非认为他能传递有用的信息给服务器。例如，一些定制的 HTTP 应用中，可能会传递 Date header 以期服务器能根据用户代理与服务器时钟之间的差异来调整其对用户请求的解析。 请求方发送 Date 在现实中的例子往往出现在各类 API 的操作中，例如在对请求签名时加上 Date 的值，服务器就可以判断请求是否过期。 那么接下来，我们就能使用自己喜欢的工具，尝试为请求增加 Date 了，例如： curl -H 'Date: Fri, 05 Feb 2027 04:00:00 GMT' https://manian.hb.lohu.info/ 页面里面给出了：“现在是 2027 年 02 月 05 日，不在马年春节活动时间哦。”说明基于 Date 的时间操控有效，而且关键是活动只限于马年春节。那么再试试 2014 年的甲午马年吧，我们用 Thu, 30 Jan 2014 04:00:00 GMT 就可以获得： 还是没有红包，那么再试试壬午马年 Mon, 11 Feb 2002 04:00:00 GMT，搞定！ 注意哦，IE 5 或 IE 6 并不是白写的，一些朋友发现了 Date 的秘密，但忘了把这两个要素组合在一起。毕竟 Chrome 在 2002 年可不存在呀。 翻看日志，很多请求都是在暴力枚举目录里面可能的东西。有一位来自韶关的硅基生物，想了 114514 种 query 的 fuzz 方法，几乎把所有能用的东西都塞进 query 跑了一遍。有群友表示我的题目给的提示实在是太少了，做这道题目确实需要开一点脑洞，但我觉得新加的一句话和原有的 date 提示也已经够了。也许把时间显示加上具体的小时、分钟，会不会更容易让人想到呢？ 这个红包 68 元分 36 个，领走 23/36 个（共 42.51 元），一血发生在 T+03:54:28。 硅基生物魅力时刻 这次实在有太多网友压榨他们的硅基苦工找红包，因此在这里列一下和 AI 有关的内容。 有一位来自嘉兴的硅基生物，在尝试“中古祝福”的时候。正确设置了 Date 和 User-Agent 以后拿到了口令，但他不觉得那是口令，继续把这个填到 cookies 里、填到 UA 里、填到 query 里、填到图片地址里面尝试找到红包，最后在 T+21h 的时候放弃。 有一位来自伦敦的碳基生物，在和他的硅基生物（非 agent，只是 chat）聊天共同分析“中古祝福”的时候，硅基生物建议过设置 Date，但这位碳同志认为他是在胡说八道。而在研究“发个红邮包”的时候，硅基生物建议过使用 SPF 记录中的 IP 地址，但这位碳同志认为地址都是我的，让硅同志想想别的办法。 在“中古祝福”中，有一位来自马鞍山的硅基生物和一位来自邵阳的硅基生物凭一己之力占据了活动期间请求的 78%。他们俩都对着目录枚举了单词表。硅基生物聪不聪明不重要，但是他们是真的有毅力和耐心啊。例如邵阳的硅同志一直在 fuzz path 和 query 等参数的路上渐行渐远，而这些都是无意义的尝试。 马鞍山的硅同志在尝试“中古祝福”期间都记得要带着 IE 的 UA。他在枚举完单词表后两分钟，终于发现了可能需要写 Date header。他用 curl 测试了 2018、2027、2025 年的日期以后，终于发现了端倪，写了一个 Python 脚本，多线程地逐分钟枚举 2026 年的日期。后来可能硅同志发现不对，改成了从 1970 年开始逐日枚举，直到 2099 年。但因为没有设置 UA（不然我也不知道是 Python 了），所以最后也没拿到红包。我又跟着他的特征查询了邮件记录，发现他在“发个红邮包”的尝试过程中想到了使用 WARP，但看起来并没有强制使用 IPv6，结果一直在用 WARP 的 IPv4 地址撞墙。 在“发个红邮包”里面，有几名硅基生物通过调整正文措辞、尝试各种 header 的排列组合来尝试绕过“反垃圾邮件系统”。邮件数量最多的也是那位上面提到的来自邵阳的硅基生物，占据了系统一半的收件量。不过功夫不负有心硅，他在尝试了大概 1010 封的时候，终于意识到得用 WARP 通过 SPF 检查。很不幸的是，他在绕过后继续使用 fuzz 手段发了 95 封邮件，也不知道最后有没有注意到系统早在第一封就已经把红包口令返回了。
今年这篇总结写得比较晚，毕竟 31 号从长沙到上海，1、2 号又开车前往苏州，随便游玩并和延迟等人约饭。写总结这种不很重要的事情就咕咕为先了。之前有几年的总结标题都是对新一年的美好祈愿，但 2022 和 2025 年的标题好像都事与愿违了，顿时感觉自己是不是在立 flag。但不管，今年继续许愿，未来总要往好处想对吧。 2024 年底，由于有了明确的生活目标，我做出了离开上海、远程工作的决定。虽然在 2021 年底搬来沪国之前，我一直是远程工作，但 3 年过去，我们公司的规模已经不是那时候的规模了，作为技术负责人还是需要为公司运营考虑，需要有一个能力足够的同事在办公室和其他组的同事协调。虽然甩手离开从待遇来说也许会有更好的选择，但是我看中的更多还是自由。还好有个原本只是打算来 gap 的同事暂时不考虑读研了，我也欣赏他的能力。夏天，老板完成了他的 MBA 进修回来了，我也正式把一部分工作交给该同事，达成了远程工作的目标。 这两年天天上班，住处又有室友做饭，我的生活逐渐变成了只有计算机（不只是工作的部分）。抛去通勤，除了睡觉之外的几乎所有时间都在屏幕前。下半年在长沙开始远程之后，比较开心的是生活的秩序开始重建了。虽然与年初决定远程时预期的生活有较大出入，但大学的电钢琴重新响了起来，锅铲也出现在了我的手里。屏幕时间的减少，也带来了“拖延症”（没错，就是前两年总结经常提到的）的好转，日记的习惯也从 8 月开始恢复了。 我总是想着在过了某个时间点之后，熬过去了，我的生活就会往好的变化。虽然从到长沙以后的表现也确实如此，但这也许是一种逃避（看作不正视当前的问题的话）、也许是一种拖延（看作把问题的解决延后到某时刻）、也许是一种焦虑（看作一种对问题解决的担忧），也有可能这仨本身就是相辅相成的。 但我们不活在未来，而是活在当下。远期的东西依旧是虚无缥缈的，有了想法就要从今天开始。不放过每一天，但也不执著于每一天，没有哪一天是比其他日子更重要或更不重要的。这一年在情感上的问题也是类似，一味地盯着结束异地的目标去实现，而有意无意地忽视、或者不敢正视当下的一些东西，沟通又不足够，最后的结果就是倒闭。能认识到问题去开始修正就是好事，有了修正的效果也是好事，但是弄坏了的关系要想好起来就不容易了。 春节的时候，同事打算开新车回家，因为金华也就两三百公里，所以原计划我开到金华以后，另外俩同事再换班开回福建。结果在杭州被人追尾，解锁了年轻人的第一次事故。本来还打算换个日本的驾照，以便能带人去一些偏远或者乡下的地方玩，但是太君 7 月底把所有没能在拉闸前考完的人全部踹下了车。有点可惜，但用纽西兰之类的 IDP 也不是不行就是了。 另外的，今年初在夏威夷体验了钓鱼和浮潜，6 月在学术会议上蹭饭当蝗虫，9 月获得了浙江省第一张共用对讲机执照，这些也都十分有趣。今年还参加了 3 场朋友和同学的婚礼，观测微信朋友圈，感觉今年、尤其是 10 月，熟的不熟的似乎都在解锁这个副本。高中前桌的婚礼还是带娃的，6 月的时候还就生娃养娃的话题与他进行了学习和讨论。 至于从以前一直有的报菜名环节，今年就不循例了。一开始有这个环节，是以前处于一个比较不自信的时候想要感谢身边朋友的肯定和陪伴，同时明确地表示他们很重要。每年写总结的时候总会参考下前两年写了些什么，就变成了一款定型文一样流传下来了。但其实真正重要的人不需要说出来，只要不掩藏，对方和身边的人自会察觉；真正的朋友放在心里，就算并不常联系，关系也还是会一样地铁。那么感谢你们，祝我们在新的一年的每个瞬间都会收获幸福和快乐！
昨天是国铁可以打出纸质车票（报销凭证）的最后一天，我购买了金华南→金华的 G2317 次列车的车票并乘坐，打印出我收集的最后一张国铁车票。把藏品掏出来清点，一共 278 张，按年份整理如下： 2010 1 2015 8 2016 8 2017 12 2018 30 2019 43 2020 24 2021 22 2022 9 2023 34 2024 46 2025 41 当然这并不代表实际乘车的次数。我正式开始收藏国铁车票是从 2015 年开始的，在 2015 年前因为回老家、旅游等原因还是坐过不少火车。而且在大学的时候也有使用积分兑换过车票，积分票是不能打印出报销凭证的。 2010 年那一张杭州→金华西（后改名“金华”）的车票是今年在家里找东西的时候意外发现的。那一年我 10 岁，好朋友的爸爸的单位里正好有到杭州的自由疗养，所以他把我和朋友一起带去了。去的时候，他只有一张车票，正值国庆假期，因此是带着我和好朋友逃票到杭州的。我还记得我在路上做了若干年的 NOIP 初赛题（虽然那一年我依旧没有通过初赛）。现在逃票已经没有那么容易了，真是很有趣的经历。 从上面的数据来看，也可以看出一些有意思的记录。 我 2015 年读高中，基本只有暑假的时候和朋友出去旅游、“办会”的时候出门。但 2017 年因为“染上”了 FRC，为了外出比赛所以坐了不少车。2018 年也是，光是高三下半学期就有 14 张车票，都是因为比赛往外跑，书也不读了（所以最后考了个三本）。那时候国内的大学并不会在招生的时候考虑这个比赛的获奖，纵使我们队拿了国内的冠军、进入了世锦赛，也并不会给我的大学招生带来什么好处。但这个比赛给当时焦虑甚至可能有抑郁的我带来了不少快乐。 上大学后，时间自由不少，经常从温州往返上海去公司摸鱼，亦或是去杭州找以前的同学约饭、闲逛。所以 2019 年有 43 张车票。而 2020、2021 年均只有 2019 年的一半，更不用说和高一时期差不多数量的 2022 年，发生了什么，大家懂的都懂。 翻阅收藏的车票的时候，看着那一个个熟悉的站名、熟悉的车次，当时的记忆与情绪也会随之一点点浮现。即将见到恋人的激动、分别时的不舍，搬去其他城市的期待，办理美国签证的紧张，逃离困了我两月的上海的舒畅，和朋友旅行的悠闲。这些都停留在那一张张红色蓝色的小纸片里，在翻到他们的时候触发回忆。
蛇年的有趣好玩的解谜夺宝红包来了！快快点击参与吧→ https://hb.lohu.info 下面是一些你应该知道的事情： 这是一个解密寻宝游戏（a.k.a. CTF），利用你的知识（现学大概也是足够的）解决所有题目，获得红包口令，口令可进入支付宝领取红包。 你应该需要电脑才能愉快玩耍，但手机可能也能解一部分。不涉及任何暴力解法（爆破等）。 本次活动时间从北京时间 2025 年 1 月 28 日 20 时开始，持续 24 个小时。 你拿到的所有答案都是 8 位数字，为了方便控制红包发放时间，在支付宝领取时，前面需要加上一个“蛇”字。如解题得到 20250129，那么最后的红包口令是“蛇20250129”。 共设 2 个红包，数额分别为：总额 68 人民币元，分 48 个；总额 88 人民币元，分 28 个。红包领取规则均为“拼手气红包”，如果红包被提前领完不会补发。 如果你是和其他人一起完成的，希望你们先让一个人领取一次再瓜分，但可以在快结束的时候进行第二次领取。总之就是要把机会先留给别人。 这个游戏由 Soha 制作，在游戏结束后也将在本页面上放出题解，往年的内容也可以在博客上找到。如有疑问也请通过博客的“关于我”页面上所述的联系方式联系。 本提示可能会更新，别忘了偶尔来看看。 最后祝大家，新年快乐！ [T+03:00] 某生气的玩家说：“我只是把 Soha 打晕了啊，并没有把第一个红包下掉。”Soha（揉揉头）：“确实，解析是全球任意可解的，红包也是点击就送的。” [T+05:30] It is really easy to get the first Red Packet once you resolved the address: request it & get it. 活动结束后将在下面更新题解。已更新，请看下方的题解。 往期回顾 2024 甲辰龙年 2023 癸卯兔年 2022 壬寅虎年 2021 辛丑牛年 2020 庚子鼠年 2019 己亥猪年 2018 戊戌狗年 2016 丙申猴年 这个红包所涉及的一些代码和文件可以在 GitHub 找到。 今年的红包字面上非常直白。打开页面，还是熟悉的配方，还是熟悉的味道。 嗯，看起来是有两个红包。第一个红包直接给了一个地址，第二个红包则是以一个“生气的玩家”的口吻，介绍了一个“从 Soha 电脑上抄出的内存数据”。那么接下来逐个介绍。 “红”“包” 今年研究了一些新东西，访问 HTTPS://shebao25yisi.hb.lohu.info/ 就可以获得红包啦！ [T+03:00] 某生气的玩家说：“我只是把 Soha 打晕了啊，并没有把第一个红包下掉。”Soha（揉揉头）：“确实，解析是全球任意可解的，红包也是点击就送的。” [T+05:30] It is really easy to get the first Red Packet once you resolved the address: request it & get it. 直接访问给出的链接，打不开。要获得这个红包，共有两个关键点：第一个关键就是获得解析，而“新东西”和大写的“HTTPS”就是提示。在 2023 年 11 月，定义 SVCB（Service Binding）这个 DNS 记录的 RFC 9460 正式获得了编号。而 HTTPS 作为互联网的一大基础协议，针对其特别定义的 SVCB 记录则被命名为 HTTPS 记录。 常规的 A/AAAA 记录没有解析，加上我在开始后 3 小时强调了“第一个红包依旧存在”且“解析是全球任意可解的”。因此，解析应该是通过其他手段获得。除了常规的 A/AAAA 记录之外，就是 SRV 记录和 SVCB/HTTPS 记录了。 查询 SRV _https._tcp.shebao25yisi.hb.lohu.info 是没有相关的结果的，但是 HTTPS shebao25yisi.hb.lohu.info 就不一样了： $ dig +short HTTPS shebao25yisi.hb.lohu.info 1 . port=443 ipv6hint=2402:4e00:1801:ef0c:0:9e11:82c3:2531 不光明确了 IPv6 地址为何，还确定地告诉你就是 443 端口。 接下来就是第二个关键，怎么获得红包。原文是说“访问就可以获得”，第一个提示说“点击就送”。但是很多朋友没能理解它们的含义，不断在问“为什么端口没开”，有没有一种可能，没开就是预期的。而如果我们使用 termshark 或者 Wireshark 抓包，节目效果就非常充分了。 你就说这个 TCP RST 的 IP 分组（也叫做“IP 包”）是不是红色的吧！“Sequence Number (raw): 37085499”就是红包口令。如果你把“relative sequence number”的功能关掉的话，还能直接在上面的列表中看到 Seq=37085499，更直观。 什么？你说你用 tcpdump？那不变、又不是 1 的 seq 就更明显了吧： 这个红包很多人卡着都是因为没有意识到“服务器就是故意返回的 RST”，而都在研究“怎么找到能用的 HTTPS 服务”。如果有人扫过端口也会发现，除了 443 端口之外，其他端口连 RST 都不会给。（毕竟是直接全 DROP 了，只放行了我用 userspace 生成的 RST 报文。） 不过还有一些朋友是因为被自己家只放行 conntrack state 为 established/related 的防火墙坑了，根本没看到 RST 报文。 第二个提示基本上是把第一个提示的内容翻译了一遍，但是重点是让大家注意到我翻译“红包”一词的时候用的“Red Packet”一词，以联想到“IP packet”一词。在第二个提示出来之前，只有 4 个人领取，有了第二个提示以后，不少朋友都恍然大悟了。游戏快结束的时候还有人把这个词全大写以后发在了我频道的评论区中，虽然直接点名了答案，但红包余量还有一半，我也就睁一只眼闭一只眼了。 有些朋友在寻找解析的时候用了 ANY 类型，这是一个在 RFC 8482 中已经被判死刑的方式，很多 DNS 权威服务器和递归服务器（例如 Cloudflare）都已经不支持这种请求了。幸运的是，DNSPod（权威）和部分递归服务器目前还保留着这种支持，因此对于想不到、也没能查到 HTTPS 记录的朋友们来说，降低了一些获得成本。另外，之前研究过（可能过时），DNSPod 好像也是国内唯一支持 HTTPS 记录的服务商。 如果使用 Safari（或其内核）的浏览器，是会尝试查询 HTTPS 记录的，这些朋友会直接获得连接拒绝的提示，而其他朋友只会收到查询不到解析的提示。这是因为不论是服务模式的 ipv6hint/ipv4hint 还是别名模式，其他浏览器都不喜欢参考，只会参考 HTTPS 记录中有关 h2/h3 之类的提示。尽管草案很早就有了，标准化也已经一年多了，但 HTTPS 记录的落地还有很长的路要走。 根据数据统计，按照 /56 来计算，一共有 500+ IP 尝试访问 443 端口。该红包本来准备了 68 元，共发放 48 个，实际领走 35 个（共 47.86 元），最早于 T+03:27:48 领取。 本红包所使用的代码：https://github.com/moesoha/red-packet-2025/blob/master/part2/src/main.rs。 内存数据 我是某生气的玩家！ Soha 的红包太难做了！每年都说简单，结果我都做不出来！这次我偷偷潜入 Soha 家，发现 Soha 正在准备第二个红包，一时气不过就给送了一棍子，看出来脑袋上是起了个大红包。接着，我把电脑内存用液氮冷冻起来后拔下来，在显微镜下观察内存颗粒，手工抄写数据出来。如果上面那个红包做不出来，不如看看这个内存数据吧：Hongbao2025Memori.zip 故事很有趣啊，但听起来要分析“内存数据”的话好像很复杂的样子。其实这个题目看似需要进行内存分析，实际上只是一个普通的二进制分析，只需要找到其中和红包有关的 EFI 程序和有关的 EFI 变量即可。 首先使用 strings 应该可以注意到有非常多的 phd0、ptal 等 edk2（常见的 UEFI 环境）用于标记内存分配池的头尾魔数，以及最重要的“hongbao”内容。 $ strings -tx Hongbao2025Memori.bin | grep hongbao 18ae7ea to check your hongbao code, input here and finish with an <ENTER>: 18ae832 incorrect hongbao code: %s 18ae84e KUNG HEI FAT CHOI! Go and get your hongbao with that code! 195a038 to check your hongbao code, input here and finish with an <ENTER>: 如果使用 strings -el 则可以按照 UTF-16 来进行提取，还可以看到下面的内容： 1933018 UEFI Interactive Shell v2.2 193315c EDK II 19332a0 UEFI v2.70 (EDK II, 0x00010000) 19333e4 Mapping table 1933528 FS0: Alias(s):HD0a0b:;BLK1: 193366c PciRoot(0x0)/Pci(0x1,0x2)/USB(0x0,0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1) 19337b0 BLK0: Alias(s): 19338f4 PciRoot(0x0)/Pci(0x1,0x2)/USB(0x0,0x0) 1933a38 Press ESC in 1 seconds to skip startup.nsh or any other key to continue. 1933b7c Shell> FS0: 1933cc0 FS0:\> dir 1933e04 Directory of: FS0:\ 1933f48 01/28/2025 17:17 146,816 Hongbao2025Checker.efi 193408c 1 File(s) 146,816 bytes 19341d0 0 Dir(s) 1934314 FS0:\> Hongbao2025Checker.efi 1934458 \ HAPPY NEW YEAR OF THE SNAKE (2025) / 1934824 to check your hongbao code, input here and finish with an <ENTER>: 这些实际上就是 EFI Shell 的缓冲区内容。可以看出，内存在被抓取之前，已经依次执行了 FS0: 切换盘符、dir 列目录、Hongbao2025Checker.efi 运行程序。而尾部没有 FS0:\> 的提示符，说明程序依旧在运行、等待用户输入，就像图里的这样： 从程序运行的内容可以看出，我们是要去分析 Hongbao2025Checker.efi 了，分析红包口令的校验代码，获得真正的口令。那么怎么得到这个 Hongbao2025Checker.efi 呢？他现在是正在运行的程序，那么应当已经被加载到了内存中。而我们重新看下最上面 strings 的结果，0x18ae7ea 是提示输入、0x18ae832 是输入正确、0x18ae84e 是输入错误。再配合 binwalk，我们可以发现 0x189d000 的位置有个 PE 头、以及紧随其后有 /home/soha/red-packet-2025/part1 开头的路径，那么这里应该就是我们要找的 EFI 程序了。 接着可以用 dd 之类的工具把它抠出来，至于抠多少就不赘述了。可以根据 dir 输出的大小去猜测、可以用 hexdump 看哪里开始是 0xafafafaf 的内存池填充、分析 PE 头的结构，而且就算抠多了也不会影响。接下来就是扔进你喜欢的工具对程序进行反编译了。只需要搜索“hongbao”就可以找到有关的代码，定位到以后就可以进行反编译了。 可以看到，在使用 %8s 读入一个 8 位字符串后，将其直接作为一个 64 位的整数，在后面进行了复杂的运算后和一个值（后称 hash）进行了比较。 而 hash 是怎么拿到的呢？是通过从 0x18c09b0 里的指针偏移 0x48 的位置取出了一个函数指针并调用得到的。那个指针的赋值发生在 ModuleEntryPoint 中，为该函数的第 2 个参数所指向位置偏移 0x58 的值。经过查阅 UEFI 标准，可知那个指针实际上是 EFI_SYSTEM_TABLE+0x58 的 RuntimeServices。继续查询 EFI_RUNTIME_SERVICES 可知偏移 0x48 位置的函数指针是 GetVariable，用来获得系统中的 EFI 变量。他的前两个参数分别是变量名和 GUID，GUID 用来确定一个唯一的 vendor。 如果想了解这个函数的作用，但不想进行这么复杂的分析，也可以通过传入的参数进行猜测。直接可以看出来：第一个参数是一个字符串“BootOrder”，第二个参数是一串未知的二进制，第三个是常量 0，第四、第五个参数是接收什么返回的内容的。而第四个参数后面有进行 cmp 8 的比较、第五个参数则与一个复杂的算法进行了比较，那么第四个参数应该是 len、第五个参数是 hash，这个函数应该是一种获取数据的方式。 什么？你说你在第一个参数只看到了一个“B”？那你就是被你的工具坑了，IDA 就经常会把小端序 UTF-16 当作一个单字母的 ASCII 字符串，需要自己修正。 接着就是用这些线索去找 hash。虽然在内存中，BootOrder 有很多，但第二个参数 GUID 所对应的字节序列 61 fd 9b 5e 9c a2 4c 98 66 c2 22 3f ff fc 8f 只在内存中出现了 2 次。和 binwalk 结果进行对比，可以发现一次是在这个程序中出现，另一次就是在 0x258a018 位置处的 UEFI PI Firmware Volume, volume size: 540672, header size: 0, revision: 0, Variable Storage 中出现。 尾部的 8 字节就是我们想要的 hash，这就是一个 64 位小端序存储的整数。 BootOrder 的名字是我专门用来暗示这一步是获得“EFI 变量”的，如果有心的话，应该可以找到上述 UEFI 规范中定义的 GetVariable 函数，就算没找到，直接按照 GUID 序列搜内存也可以找到相应的数据进行解析。 有些朋友在拿到 EFI 以后自己起了一个 qemu 去启动，无一例外的都遇到了“Error: Not Found”输出，因为他们的 EFI 变量中没有我的红包口令 hash。顺便提一嘴，如果遇到了死循环式的疯狂输出，是因为没有在 EFI Shell 环境下手动执行，没有标准输入可以用所以爆了。 获得了 hash 以后，就是按照程序里的算法里面一样，遍历所有数字看什么数据可以匹配上，亦或者是按照其算法写出逆运算，然后就可以获得红包口令 78537340。 #include <stdint.h> #include <stdio.h> uint8_t code[9] = {0}; #define CODE *(uint64_t *)code #define HASH 0xeae168a83f6590dcULL #define C1 0x165667b19e3779f9ULL #define C2 0xc2b2ae3d27d4eb4fULL #define C3 0x9e3779b185ebca87ULL #define C4 0x3416ec30165668f6ULL #define C5 0x7a1435883d4d519dULL uint64_t rol(uint64_t a, uint8_t n) { return (a << n) | (a >> (64 - n)); } uint64_t ror(uint64_t a, uint8_t n) { return (a >> n) | (a << (64 - n)); } #define FOR_CODE(i) for(code[i] = 0x30; code[i] <= 0x39; code[i]++) void sol1() { // 遍历 FOR_CODE(0) FOR_CODE(1) FOR_CODE(2) FOR_CODE(3) FOR_CODE(4) FOR_CODE(5) FOR_CODE(6) FOR_CODE(7) { uint64_t R1 = C3 * rol(C3 * rol(CODE * C2, 31) ^ C4, 27) - C5; uint64_t R2 = (R1 >> 33) ^ R1; uint64_t hash = (C1 * ((C2 * R2 >> 29) ^ C2 * R2) >> 32) ^ C1 * ((C2 * R2 >> 29) ^ C2 * R2); if(hash == HASH) printf("%s\n", code); } } uint64_t inv(uint64_t x) { uint64_t r = x; for(int i = 0; i < 5; i++) { r = r * (2 - x * r); } return r; } void sol2() { // 逆运算, @saffahyjp CODE = HASH; CODE ^= CODE >> 32; CODE *= inv(C1); CODE ^= CODE >> 29 ^ CODE >> 58; CODE *= inv(C2); CODE ^= CODE >> 33; CODE = ror((ror((CODE + C5) * inv(C3), 27) ^ C4) * inv(C3), 31) * inv(C2); printf("%s\n", code); } int main() { sol1(); sol2(); return 0; } 实际上，这里的 hash 算法是带了 seed 的 XXH64，而输入的数据也是 64bit，所以眼尖的朋友可以注意到算法可逆。 经典二进制分析工具 angr 的作者 @ltfish 则给出了使用 angr 的解法： ''' 不用虚拟机或者模拟器，直接把整个文件扔到逆向工具里，找到关键字符串 Hongbao 就能定位到需要逆向的函数（0x18a7567）了。 一部分函数是 cdecl，另外一部分是 Microsoft x64 calling convention，需要注意一下，可能需要手工修复一下调用规范。 UEFI 是 edk2 所以可以直接看 GitHub 上面的源码。 0x18A7872 的地方调用的是 runtime service GetVariable，variable name 是 BootOrder。 第二个参数是 VendorGuid，16 字节。全 binary 搜一下，就能发现这个变量的位置（0x258ba84）以及值（0xEAE168A83F6590DC）。 最后看看源函数，是 lua hash，但其实只有一个 block 需要分析，在 0x18a788f。扔 angr 里面就可以了。 ''' import binascii import claripy import angr main_opts = {'backend': 'blob', 'base_addr': 0x0, 'arch': 'x86_64'} proj = angr.Project("Hongbao2025Memori.bin", main_opts=main_opts) state = proj.factory.blank_state(addr=0x18A788F) rbp = 0x7fff0000 state.regs._rbp = rbp value = claripy.BVV(0xEAE168A83F6590DC, 64) input = claripy.BVS("input", 64) state.memory.store(rbp - 0x100, value, endness='Iend_LE') state.memory.store(rbp - 0xea, input, endness='Iend_LE') simgr = proj.factory.simulation_manager(state) simgr.step() for s in simgr.active: if s.addr == 0x18A7927: print(binascii.unhexlify(hex(s.solver.eval(input))[2:])[::-1]) 该红包本来准备了 88 元，共发放 28 个，实际领走 3 个（共 7.29 元），且这 3 人都做出了另一个红包，最早于 T+02:29:30 领取。 彩蛋红包 根据往年的经验，大家总会纠结于“HTML 里面有什么”这一件事。所以依此惯例，我在页面中埋了一个彩蛋红包。 ：10828359。 该红包本来准备了 28 元，共发放 88 个，实际领走 3 个（共 1.17 元），最早于 T+01:49:28 领取。意外地少，哈哈。 要准确地估算出每道题目到底得发多少红包太难了，这次的二进制分析我本来以为会有蛮多人领的，加上之前还听说有人在 CTF 群里转发过我的预告。我一想，这么多专业玩家，对这种题目来说应该是小意思，是该多点，结果并没能如我所愿。不过大佬们业务繁忙，没准春节正在忙着旅游或者玩耍，无暇拿这两块钱的红包也是正常的。不知道如果换成 HackerGame 那样，拉长游戏时间，提交 flag 以后再根据获奖人数瓜分会不会更好一些。无论如何，感谢大家又一年的参与，我把没发完的部分重新用两个主线红包实发金额的七位数作为口令发放了个红包，感谢你读到这里。
谢邀，人在美国，刚上飞机。现在是美国太平洋时间 2024 年最后的四个半小时，而中国已经在 2025 年过了半天了。刚刚结束了湾区的旅游，正在返回位于洛杉矶的群友家。这是人生第一次不在东八区跨年，在 2024 年看着认识的朋友们都在庆祝 2025 的到来，感觉有点奇妙。 要说今年和往年的不同，就是大量出境旅游。先是七月底在日本从东京到大阪转了一圈，再是十月份从东京和朋友去丰桥圣地巡礼并在名古屋狂吃，接着十一月在香港和深圳 city walk 并与朋友约饭，最后十二月底在美国和朋友玩了洛杉矶、西雅图、旧金山。当然开头也说了，本次旅行尚未结束，接下来几天将和朋友一同前往夏威夷，最后从东京转机回家。 因为不喜欢特种兵，紧凑的行程会让我觉得累。所以除了和朋友一同进行的部分，基本都是在住处给自己充电干点活，偶尔随机游走。这样呆的时间也会比较久，因此下半年在境外的时间加起来得有俩月了。在外面的时候真的会挺想念国内的亲人的，长这么大也是从来没有像今年一样老是出去晃悠，不过算在新冠时代后再次见识了异域风情吧。 工作的内容依旧没有什么大的变化，但随着业务发展，也确实需要一个同事分担我的工作了。拖延症也依旧存在，今年并未获得什么改进，不过考虑到接下来的人生走向，这些都该好好调整了。今年没有什么开源的贡献，也没怎么配网。 今年的业余无线电活动基本还是在 6m/2m 波长的 VHF 波段用 5W 功率进行，继续钓鱼一般的心态在操作。神奇的大气和太阳总能给人惊喜：今年在 6m 最远触及了南美（根据互联网自动上报数据得知，但没有完成通联），2m 则依旧通上了俄罗斯和日本。之前考的 FCC 的执照也在旅行时派上了用场，我用他换取了日本的电台执照，并在日本和美国用当地的呼号进行了发射。 命运的安排让今年的我感觉十分不真实，十分期待接下来的 2025 年，重要的人一定会让这一年成为甜蜜美好的开始。我也要继续努力，变成更好的自己！ 最后感谢延迟，感谢清粥老师。感谢今年有女儿、阿珍、妹妹亲如家人的相伴。感谢 ltt、laosb、kkk、逐影少爷、Gogo、点心、twz 等朋友，感谢万呆呆、麻田哥哥、cyy、猴哥、zcy、曹老师、学姐、bml、吴小板、贺神、dd 等 foobar 院同事，感谢 wyy、林儿、焕焕、强哥等同学，洛谷的同事们，和其他大量群友。有你们才能让我今年更丰富多彩，我们新的一年再见！
认证门户（Captive Portal）是生活中非常常见的上网准入方式。当我们连上一个开放式（不需要密码即可连接）的 Wi-Fi 接入点以后，几乎都不能直接访问互联网，而是需要在一个网页上进行一些认证后才可以访问，这就是认证门户。 从这种 Wi-Fi 接入点的准入方式来说，我们很容易就发现一个问题：需要访问网页才能让用户开始准入认证，但怎么才能让用户去访问呢？目前通行的一种做法是直接劫持 DNS 请求或者 HTTP 请求，重定向到认证页面。这种做法在 HTTPS 时代之前很容易做到，但是 HTTPS 时代之后就遇到了困难：由于认证门户并没有用户访问的网站的证书，要劫持用户就势必要用假的证书来进行，这样一来二去也会给用户带来信任不安全证书的坏习惯。 当然，现在的系统也都有自己的做法。比如 Windows 就会尝试用 http 连接 www.msftconnecttest.com，如果请求被重定向了，那么就会弹出页面让用户进行登录。这虽然解决了一部分问题，但是怎么看怎么不清真对吧，所以在 2015 年，RFC 7710 规定了一种方式：如果 DHCP 或 RA 在指定的 option 中发送了一个 URL，那么就引导用户前往该页面进行认证。 RFC 7710 看起来确实解决了一些问题，但是如果用户此前已经登录，其实是没必要再次登录的。这样系统还是绕不开一次互联网连通性的测试，当然 DHCP 服务器可以在通过认证的情况下不发送有关 option，然而 DHCP 服务器并不一定和计费系统是耦合的，做这种耦合也是复杂且不合适的。所以 capport 工作组设计了一套认证门户的标准，也就是 RFC 8908 和 RFC 8910（代替 RFC 7710）。 我了解到 capport 工作组的这些内容，是因为前两天名为菜色狼的群友去了澳门，在澳门线下赌场看完性感荷官在线发牌之后回到了他下榻的喜来登酒店，连上 Wi-Fi 后发现了名为“到期时间”的内容。 一开始我猜测是 DHCP 租约到期时间，但是菜色狼说之前在家里和别的地方都没见过，于是猜测是某种标准，我便顺藤摸瓜找到了上面提到的两个 RFC。 RFC 8908 规定了认证门户对客户端提供一些信息的 API 格式，API 返回格式为 JSON 对象，IANA 也维护了一个注册表来登记可用的字段。目前只有随着 RFC 发布的这几个，其中 captive 字段是必须包含的： captive: boolean 表示用户是否需要登录。 user-portal-url: string 描述了用户完成准入所需访问的页面，也是之前 RFC 7710 中规定的 URL。 venue-info-url: string 描述了网络运营者想向用户展示的内容（例如地图、航班信息等）。 can-extend-session: boolean 表示用户当前的会话是否可以被延长（时间、流量等），这个字段可以让系统在时间或流量将要耗尽的时候让用户访问 user-portal-url 重新认证。 seconds-remaining: number 描述了用户当前会话的剩余时间。 bytes-remaining: number 描述了用户当前会话的剩余流量（字节数）。 其中菜色狼看到的“到期时间”就是 seconds-remaining 带来的。 RFC 8910 则是替代了之前的 RFC 7710，将 URL 的定义改成了 RFC 8908 中提到的 API 端点，同样也有 3 种通告方式： DHCPv4 Option 114 DHCPv6 Option 103 IPv6 Router Advertisement Option 37 有了这两个 RFC，认证门户上网准入的流程就是这样的了： 连上网络、从 DHCP/RA 获得地址的同时知道认证门户 API 的地址。 系统请求 API，得知设备当前的准入状态，如果需要认证则让用户访问 user-portal-url。 设备已经准入，根据 API 返回的信息选择性地进行一些信息展示。 相比劫持用户的连接、系统自己探测这些做法之外，就清晰和明确很多，也安全很多了。 而且这样一来，只需要提供一个 API 地址并返回规定的内容，并不需要一个真实的认证门户就能复现，折腾的劲儿也就上来了。接着我创建了这样一个 PHP 文件： <?php header('Content-Type: application/captive+json'); echo json_encode([ 'captive' => false, 'venue-info-url' => 'https://jin.sh', 'seconds-remaining' => 4555739950 - time(), 'bytes-remaining' => 114514000, 'can-extend-session' => false ]); 按照 RFC 8908 所描述的，这样应该会在 Android 上显示出过期时间为 2114-05-14 19:19:10（即 4555739950 时间戳），并展示一个前往“信息页” https://jin.sh 的链接。接着只要把代码挂上线，在 DHCP 服务器上根据 RFC 8910 中所描述的 DHCP Option 指向线上的 URL。对于我的 Juniper 上只需要一句： set access address-assignment pool soha-home_4 family inet dhcp-attributes option 114 string "https://soha.moe/rfc8908.php" OpenWrt 也可以在 DHCP 服务器设置中的高级设置选项卡里用类似 114,https://soha.moe/rfc8908.php 的方式来设置这个 DHCP option，但我没有可以测试的设备，没法确定这里的写法是否正确。另外 Android 不支持 DHCPv6，我这里也就没设置 DHCPv6 对应的 DHCP option。 然而手机连接后并没有任何动静，服务器没看到任何请求。根据 commit 历史，这个功能早在 2020 年已经进入 Android 主线，Android 13 应当是支持的。但是考虑到我的设备是运行 Oxygen OS 的 1+ 10T，1+ 魔改的系统可能并没有合并这个功能。因此我斥资 1500 元在某宝购买了一个全新 Pixel 6a，在原生 Android 14 上进行体验。 两天后手机到货，连上 Wi-Fi 以后如愿看到了过期时间的信息、以及一个跳转到我的个人主页的按钮“Open site”和通知信息。 RFC 对于安全的考虑还是很充分的，比如强制要求 API 和认证页面是通过 https 请求的，还考虑了 DNS 安全等方面的问题，包括但不限于这些内容在 RFC 原文中都有提到。 我在最初测试的时候并不是将 API 放在 https://soha.moe/rfc8908.php，而是直接在本地用 php -S 开了一个服务器（所以没有 https）。配上 DHCP option 以后并没有看到有任何请求，我当时以为是 Oxygen OS 完全不支持。为了测试这个功能，以及之前用来收短信的 1+ 3T 的电池已经把屏幕都彻底顶开该换了，才直接购买了一部 Pixel 6a。但是到货以后还是不行，重新阅读 RFC 才发现了有强制 https 的内容，所以 Android 不请求我的 API。重新配置才发现其实 Oxygen OS 也是会请求 API 的，只是 OnePlus 的设置 UI 并没有 merge 主线上的这一部分，也就没有展示出来，打开 venue-info-url 的通知信息也没有弹出来。 当前这个 RFC 似乎也只有 Android 实现了，虽然 Apple 员工是 RFC 8908 的一作，但是 iOS 并不支持这个功能。Windows 也似乎没有相关的跟进。 iOS 18 已经支持显示 venue-info-url，但是没有理会 bytes-remaining 和 seconds-remaining：
一年一度的有趣好玩的龙年解谜红包来了！快快点击参与吧→ https://hb.lohu.info 下面是一些你应该知道的事情： 这是一个解谜游戏，利用你的知识（现学大概也是足够的）解决所有题目，获得红包口令，口令可进入支付宝领取红包。 除去红包不说，游戏的过程也是很快乐的。 你应该需要电脑才能愉快玩耍。不涉及任何暴力解法（爆破等），为节约时间也会复用一些往年做的 UI，因此不要被往年的答案套走。 你拿到的所有答案都是 8 位数字，为了方便控制开始和结束时间，在支付宝领取时，前面需要加上一个“龙”字。如解题得到 20240210，那么最后的红包口令是“龙20240210”。 如果你是和其他人一起完成的，希望你们只领取一次，但可以在快结束的时候进行第二次领取。总之就是要把机会先留给别人。 本次活动时间从北京时间 2024 年 2 月 11 日 20 时开始，持续 24 个小时。如果红包被提前领完不会补发。 共设 2 个红包，数额分别为：总额 78 人民币元，分 16 个；总额 88.88 人民币元，分 18 个（题目简单，红包太少了，更换成）总额 58 人民币元，分 36 个。红包领取规则均为“拼手气红包”。 这个游戏由 Soha 制作，在游戏结束后也将在 Soha 的博客 https://soha.moe 上放出题解，往年的内容也可以在博客上找到。如有疑问也请通过博客的“关于我”页面上所述的联系方式联系。 最后祝大家，新年快乐！ 活动结束后将在下面更新题解。已更新，请看下方的题解。 往期回顾 2023 癸卯兔年 2022 壬寅虎年 2021 辛丑牛年 2020 庚子鼠年 2019 己亥猪年 2018 戊戌狗年 2016 丙申猴年 这个红包所涉及的一些代码和文件可以在 GitHub 找到。 连续两三年的红包领取情况都不容乐观，我十分难过和担心，虽然每年这个形式非常有趣、我出题也乐在其中，但没人领取说明大家在游戏过程中是痛苦的，比如投入了时间却没有收获。其实这八年来，我最喜欢的题目是去年的题目，环环相扣、体验完整，但这恰恰也是他的缺点，毕竟卡了一个就全卡了，有人连第一关的入口都没摸到，游戏体验极差。 今年题目在设计的时候还是以创意为主，只有两个小题目，来源于和群友的吹水以及个人今年的研究。题目其实真不难（虽然我好像每年都这么说），只需要稍微提一句，真相就会浮出水面。甚至两个红包都能一句话解，但想不出来也确实怎么想都想不出来。 因为抗击新冠的原因，本来就没回过几次老家的我已经好久没回去过了。为了回家给老人家看看，今年我在老家过年。老家在一个皖北小农村，联网、用电不太方便，经常是蹲着充笔记本并干活。出题也是断断续续的，导致活动延期到了初二才开始。不巧初二晚上走亲戚吃饭，我下午又忘了 commit 代码并在服务器上 pull，活动开始后几分钟的时候得知红包 1 出锅了，也只能先下掉红包 1，等回到住处再开通。这篇题解是在我返程的高铁上写成的，那么我们开始吧。 进入红包落地页，映入眼帘的还是这个祖传的、带生肖图案的红包。点击“开”以后，会出现两个链接： 入口1：https://loongbao24.hb.lohu.info/ 入口2：telnet://loongbao24.hb.lohu.info 再闯研究所 第一个红包的背景还是去年的“红包研究所”，因为直接用了去年的一部分 UI，我特意在游戏提示里面说“不要被往年的答案套走”。我在 2020 年的时候把一个彩蛋红包放在了落地页小图标 SVG 的注释里面，后面几年总有人分析 SVG 里面有什么特殊的，所以一定要提醒大家不要被往年的题目养成思维定势了，不可能连着两年出一样的套路对吧。 跟着入口链接可以看到一个“foobar 院新年红包研究所·所内综合办公系统”的登录页面，但是点击登录按钮之后提示“系统维护中，暂不能登录”，因为根本没有这个所谓的系统。可以看到有好些朋友尝试了访问常见的登录路径，当然都是 404。这时候其实应该考虑肯定和这个“办公系统”没关系了吧，但有朋友还是在这上面死磕，我不得已在登录按钮附近加了个“有没有一种可能，这真的只是个摆设”的注释。 这个红包的内容都在左侧的一堆“新闻”中，这道题和去年一样，模拟的是一个网络安全方面千疮百孔的倒闭研究所。纵使网安方面千疮百孔，行政人员总还是知道什么能说、什么不能说的。因此直接展示在台面上的新闻，有关的可能也只有《2023年所长办公室工作报告》里面暗示的“就只有这些新闻，凑合看下就可以了”，肯定有些神秘的东西不是直接展示在外面的。回到这些新闻，其实可以注意到 id 并不是连续的： 防范极端天气与寒潮的紧急通知 关于研究所2024年春节假期安排的通知 2023年所长办公室工作报告 2023年第一批研究所公开选聘岗位 2023年“温暖送学生”活动的通知 公开的新闻确实都写得蛮有趣的，整活多好玩啊。 在这种情况下需要学会“手贱”改改参数，看看 114513（最低减一）、114515（连续缺失）、114520（最高加一） 都是些什么情况，这是发现神秘内容所必须学会的小技巧。手贱完可发现 114513 返回的是 404，114515 返回了一则新的新闻。另外 114520 是重定向，因此增加测试 114521，也是 404。 先说 114520 这则新闻，它是重定向到了 https://loongbao24.hb.lohu.info:81/news.php?id=114520，多了个端口号，而其他新闻并没有出现这种情况。事出反常必有妖，我们可以合理猜测 81 应该是什么只对内网开放的内容。事实上，重定向的报文中也有提到这一点： HTTP/1.1 302 Found <OMITTED> Location: https://loongbao24.hb.lohu.info:81/news.php?id=114520 内部通知，点此重定向到仅内网可看的网站查阅。 81 端口是不是因为被防火墙拦了呢？有个简单的方法可以判断，虽然实用性可能没那么高，毕竟一般都是白名单制而不是黑名单制，但不妨一试。访问 82 或其他不开放的端口，发现连接会被远程服务器积极地拒绝；81 端口的请求就只有自己无限的 TCP 重传，而没有任何回音。这种差异一般可以认为 82 端口没有服务监听，而 81 端口是被防火墙过滤了。实际上，这里配置的就是 nft add rule inet filter input iifname eth0 tcp dport 81 drop。 怎么访问到 81 端口呢？第一种方法是直接在内网访问，但这种虚构研究院哪来的内网呀，那么只剩下第二种方法：绕过防火墙。通常情况，防火墙都是对入站连接进行阻拦，而不会审查出站（除非防火墙还会对出站流量检查自己状态表中 TCP 的状态），那么只要能让服务器传出消息给我们就可以了！问题来了，我们该怎么把请求送进去呢？内网吗，这不就又回到了第一个问题。 破阵的关键就在于 id=114515 的这个名为《【内部】关于研究所试用下一代 VPN 服务的通知》的新闻，这个“下一代 VPN”就是帮我们把请求送进去的工具。活动过半的时候，我说这个红包“是一道阅读理解题”，就是需要阅读这则新闻并搞懂。 这则新闻的大部分内容都是屁话。比较重要的是“网关目前也使用 Linux 部署”这句话，他暗示下面的配置方法应当和网关上的相差无几，所以就是要理解这些指令都干什么。这些指令大致的工作就是：创建 netns、sysctl、设置地址、设置 HMAC 密钥、设置 SRv6 封装和解封装的动作。从封装和解封装这一点看出来，其实 SRv6 就是一个隧道，只不过带了各种复杂的“动作”，比如 End.DX6 表示解封装（D）里面的 IPv6 报文（6）出去并发给互联（X，cross connect）的邻居。 sysctl 配置中的 net.ipv6.conf.<iface>.forwarding 大家应该都熟悉啥作用，但可能对 net.ipv6.conf.<iface>.seg6_* 感到陌生。经过查询内核文档可知 seg6_require_hmac 接收 3 个值，分别是忽略 HMAC 头（-1）、只允许没有 HMAC 头和通过 HMAC 验证的报文（0）、只允许通过 HMAC 验证的报文（1），默认值为 0。这意味着 HMAC 除了正确、错误之外，还可以是缺失的。 同样查询 ip route help，发现 hmac 参数也是可选的。就像之前说的，“手贱”改改参数总不吃亏。知道 hmac 参数可以不设置以后，我们就可以试试不设置 hmac 能不能用。唯一困难的一点就是这个操作是盲的：网关没有配置到我们发包 IP 的动作（传统隧道的“对端”），肯定不会有 SRv6 的回包，不管怎么 ping 都是没用的。 不过在新闻附带的一张截图中也有对应的暗示，仔细看过也能发现这个细节：倒数第二个指令并没有和操作步骤的第 15 点一样设置了 HMAC Key ID，但网络依旧通了。况且我本身没在步骤中交代 SRv6 segs 参数的值，说是要在“OA 系统”中获取，但是在截图中并没有打码，此处的用意是截图打码不全而泄露了数据。hmac 参数紧跟在 segs 参数之后，理应也能注意到。 一开始截图中的 hmac 参数其实是有的，活动上线一小时后我感觉难度又高了点，就把 hmac 参数去掉了。 不过有这个 HMAC 的漏洞只能把报文送过去，回来的数据怎么办呢？正如前面所说，我们没有办法去“OA 系统”新增一个对端，不能通过 SRv6 获得回包，这时候就需要另一点小漏洞了。 第二个漏洞是没有进行源地址验证，这意味着我们可以通过这个只能上送数据的 SRv6 网关以任何源地址通过网关发送 IP 分组。如果我们将源地址（第 15 点命令的 src 参数）设置为自己的公网地址，对面主机在响应我们数据时就会以我们发送的 IP 分组的源地址进行回应，我们就能从公网上收到内网主机的响应了。 这是一个常见的疏漏（国内三大运营商也是近几年才逐渐部署），没有部署源地址验证（如 uRPF、SAVA 等技术）的网络通常会成为黑产们发起 DDoS 的工具。这个攻击的原理是有些 UDP 服务（如 NTP）经常会对一个短数据片返回数倍（即放大系数）于其尺寸的数据片。对于这类服务，可能只需要 1Mbps 的带宽就可以产生几百 Mbps 的垃圾流量。利用没有源地址验证的网络，发送源地址伪装成 DDoS 受害者的短报文到这类服务，这类服务在返回报文的时候就会去到受害者处。因此源地址验证也是当今世界互联网上的一大问题。 有了以上两个漏洞还不够，还差最后一点小改动才可以完成这套组合拳。我们来解析一下第 15 个操作的关键点： ip route add fd24:fbac::/32 # 增加一条到 fd24:fbac::/32 的路由 src <VPN IP 地址> # 这条路由使用什么 IP 地址作为本机源地址 mtu 1280 # 这条路由的 MTU（最大传输单元）是多少 encap seg6 # 这条路由的分组需要使用 SRv6 mode encap # SRv6 的动作是封装 segs <网关地址> # 要求封装后的 SRv6 报文经过什么 IP hmac <HMAC Key ID> # 封装 SRv6 使用的 HMAC 密钥 ID（可空） hmac、src 已经玩过了，你觉得还有什么参数是我们可以乱玩的？没错，就是这条路由本身。既然这个研究院“内网”是可以访问 loongbao24.hb.lohu.info:81 的，假设内网 DNS 没有任何不一样的地方，那么内网解析的 loongbao24.hb.lohu.info 的 IPv6 地址也应当是一样且可达的。所以我们将路由的目的地更换为 loongbao24.hb.lohu.info 的 IPv6 地址后 ping 该地址，还是可以获得响应的： 此时我们去程的报文路径应当是：主机→SRv6 网关→Web 服务器。回程的报文路径是：Web 服务器→公网的多个 ISP→主机。那么，此时我们再访问 https://loongbao24.hb.lohu.info:81/news.php?id=114520 就不经过公网入站方向的防火墙了。 有些人吐槽 segs 参数中的地址 ping 不通。因为正常的做法是给 SRv6 节点路由一整个 /64 的空间用于分配 Segment ID（这个 /64 称为 Locator），再在其中给自己内部的动作分配。所以这个地址是路由到这个主机，不能配置在主机上，也就没法 ping 的。 前两年我在水群的时候说过，未来是 IPv6 的时代，我一定要争取每年红包都出个 IPv6 相关的：2020 年藏在 IPv6 地址里面，2021 年藏在 IPv6 路由表里面，2022 年藏在 ICMPv6 echo reply、inet6num 和地址里面，2023 年是需要使用 OSPFv3（基于 IPv6）发布 IPv6 路由，今年就是这个 SRv6。 虽然这题的背景中有 SRv6，但是其实也还是不需要有 SRv6 相关的知识。SRv6 虽然仍在发展，也不是主流方案，但也早已被各大网络设备厂商所实现。前几年我建设 AS4842 和 AS209306 所基于的 underlay“Tianhai Switching Network”也使用了 SRv6 技术，CERNET 那张搞了 4096 个 ASN 的“下一代网络”FITI 也使用了 SRv6 技术。这个红包的点子便诞生于 23 年 NickCao 曹老师将网络切换到 SRv6 的时候。思路应该还算是有趣的，但需要一点办网经验与思路，才能想到仅利用去程来绕过防火墙的方法。领到红包的几个人中，我眼熟的几个也本身都是办网专家。 最后提示一下，要利用这个半程，首先需要确认自己本地的 IPv6 防火墙不会阻拦状态异常的连接。因为去程（比如 TCP SYN）封装在 SRv6 里，防火墙不知道这个连接其实是我们发起的，但回程（比如 TCP ACK）走的是公网，防火墙能看见，就会认为这种状态是非法的，如果开了阻拦非法报文（比如我家，所以我专门改了下配置）就会被干掉。在我频道的讨论区还注意到有朋友在使用 n:1 的 NAT66，这是不对的，SRv6 报文是没法多对一 NAT 的。 还有一些朋友虽然想到了这个解法，但是因为自己的网络环境（如光猫、运营商、防火墙）拦截了 SRv6 分组，比如系统不认识这个 IPv6 Next Header，而失败。如果能够想到在这种时候要用自己别的网络环境的主机作为目标测试一下的话，或许也能够领到红包。 在我的 foobar 院邮箱和私聊窗口里，还有几封有趣的邮件和一则有趣的消息： 我也去统计了一下 NGINX 的访问日志，大家对于 URL 的想象力还蛮丰富的，可惜关键点并不在那里。还有个人扫了快两万个新闻 ID，开局的提示明明说过不含任何暴力解法了。 这个红包刚开始就被我暂时下线了两个小时，因为线上的代码和实际的版本差了很多，问题很大。那时候在外面吃饭，没法修。还有一个玩家碰出了一个非预期解：我的 PHP 使用 Host（$_SERVER['HTTP_HOST']）来判断客户端访问的端口是不是 81 端口，因为忘了专门的新端口没被 default 绑定所以不会被其抓住，导致直接重写 Host 可以欺骗 PHP 返回红包口令。还好这个发生在一开始，我在部署新代码重新上线的时候也顺手修了。 讨要红包 第二个红包入口是一个 Telnet 服务器。这个红包的点子来源于 2024 年元旦和其他几个 Telegram 频道联合举办的名为 TextPlace 的活动，这个活动和 Reddit 的 r/place 相似，只不过每个“像素”是文字，且可以设置文字的前景和背景色。最后活动结束时画布长下面这样，我们还制作了一个回顾影片：https://youtu.be/y6do9KCmjIQ。 TextPlace 是使用 Telegram 小程序实现的，因为本身就是一个复古向活动，我另外给这个活动编写了 Telnet 的服务端。在这个过程中，我顺便学习了 Telnet 协议的 RFC 规范（RFC854 及部分 Telnet Option 的 RFC），才发现 Telnet 并不是我想的一个 TCP 流这么简单（这应该也是大部分人没有注意到红包的原因），他也有客户端和服务端的协商握手过程、带内传输的控制指令。 Telnet 使用 0xff（协议中称为 IAC，Interpret as Command）作为带内控制指令的跳脱字符，并在其后跟随控制指令。用于客户端和服务端支持的能力协商的指令有以下几种： WILL （0xfb ），表示发送方想要做某事，或确认发送方正在做某事。 WON'T （0xfc ），表示发送方拒绝做某事或拒绝继续做某事。 DO （0xfd ），表示发送方想要或确认想要另一方做某事。 DON'T （0xfe ），表示发送方想要另一方停止做某事，或确认发送方已经不再想要另一方做某事。 SB <payload>（Subnegotiation Begin，0xfa <payload>），表示发送方接下来要就某个 Telnet 能力进行协商，后面跟随的是具体内容。有趣的是 0xfa 的值刚好等于 250，配合指令的名字，很难不让人面露滑稽。 SE（Subnegotiation End，0xf0），和 SB 成对出现，表示协商内容结束。 Telnet Option 的分配参见 https://www.iana.org/assignments/telnet-options/telnet-options.xhtml。 有了上面的知识再回到红包，连上 Telnet 之后，我们看到这样一句话：“I WILL give you hongbao, but you did not ask me for that!”（“我会给你红包，但是你还没问我要呢！”） 这里的 WILL 被大写了，就是指 Telnet 的 WILL 指令。打开 Wireshark，我们可以看到完整的 Telnet 数据流。在红包的艺术画结束以后，随后服务器发送过来的 Telnet 报文就是： IAC DON'T 0x22(Linemode) IAC WILL 0x01(Echo) IAC DON'T 0x01(Echo) IAC WILL 0xf0 Wireshark 会直接标出所有 option 的名字，他们都有实际的意义和作用。只有一个 0xf0 是 unknown option（未知选项），因为 0xf0 未分配，我将其用于协商本次红包。他的十进制是 240，这里的 40 取自“甲辰”的干支顺序（以 0 开始）。 服务器发送的 WILL 0xf0 表示他可以做这个，前面介绍了 Telnet 的各种指令，往上翻再看一遍，相信你也就知道该干什么了。我们往服务器发送 IAC DO 0xf0（0xff 0xfd 0xf0）后便可以在 Wireshark 中看到红包口令：IAC SB 0xf0 "51131748" IAC SE。 常见 Linux 发行版的 telnet 程序均可以直接发送：使用跳脱指令（默认为 Ctrl+]）进入 telnet 程序本身的命令行，然后使用 send do 240 指令发送即可。这道题目还可以使用这样一句话速通：echo -ne '\xff\xfd\xf0' | nc loongbao24.hb.lohu.info 23 | hexdump -C。 有些朋友是直接暴力发了 WILL/WON'T/DO/DON'T 0-255，因为这道题目的目的是让参与者注意 Telnet 的协商过程，所以勉强也算预期吧。 有个小花絮。这个 Telnet 服务器是我人力实现的半成品，也是给 TextPlace 元旦活动的产品。那个活动时就发现了有时候服务器会占用 100% CPU，并且所有连接都没有响应，新的连接直接黑屏。这显然是卡进了什么死循环，但当时触发概率很小，且手头上有别的事情干，所以并没有研究这个 bug。也是我一开始设置 15 分钟自动重启的原因。结果上线之后触发率非常高（因为大家都在暴力乱发数据），并且可能是刚重启完就又卡死了，这严重影响了游戏体验。这使得我去仔细 review 了一遍我的代码。最后发现问题是在处理 IAC 后面跟着非法 Telnet Command 的时候：我直接写了 continue 但没有修改 buffer，导致 loop 一直在处理同一个情况。修复了以后服务果然正常了。回忆了一下，当时是随手写了一个动作，想着之后再来回头理这一部分，但没标记 TODO 就给忘了。 这个红包好像挺简单的，开始两个半小时就几乎快领完了，吓得我赶紧发了个数量更多的红包。但是新的红包反而领取速度慢了，并没有保持一开始的速度。 彩蛋 今年我没有加有关彩蛋红包的任何提示，这也就回归到了彩蛋的本质：乱弄触发出来才有意思。做完红包 2 的时候，我曾和群友开玩笑说：“就算用 MS-DOS 也能至少拿到一个红包。”所以我先用 MS-DOS 演示一下领取彩蛋红包： 想不到吧，这个长得和落地页面差不多（就是丑了点）的 Terminal Art 红包上的“开”字是可以触发的！只需要使用“上下左右”键将光标移动到“开”字样上，按下回车即可触发彩蛋。 这里要提一下，PuTTY 可能在按下“上下左右”键的时候可能不会有响应，因为他不能在服务器声明“WILL Echo”、“DON'T Echo”的时候切换到输入即时返回服务器的方式，所以需要按下回车才能有反应。面对这种窗口，我觉得就和进入 Linux shell 以后顺手打一个 ls 一样，随便按按上下左右、随便按按回车，按下回车一瞬间就能看到光标位置移动了，也能大概猜出是怎么回事。因此没有给另外的提示，毕竟给了就透露答案了。 这个彩蛋红包的想法也是来自上面提到的 TextPlace 活动。当时我专门下载了 ANSI 控制序列的标准进行学习，并购买了 MS-DOS 编程手册学习当时 ANSI.SYS 支持的序列，还针对历史上在终端中使用 ANSI 控制序列进行上色写了一篇文章《DOS 下也要色色文字》。出红包时想到要做一个好看的红包图案，便顺便将其作为彩蛋红包。 还有一个有趣的事实：领取彩蛋红包的人和领取第二个红包的人重合率极低。我在参与者只领取到彩蛋红包的时候提示过“还有一个我会（WILL）给你红包还没领”（见上图），但基本没找到（或者没去找）第二个红包。对于这个情况我的猜测是：领取到彩蛋红包的这部分人都专注于 Telnet 传输了什么内容，所以他们对于第二个红包的尝试也只是在我给出的图案和文字上，并尝试输入了各种内容。只领取到第二个红包的人都专注于 Telnet 传输了什么，一上来就直接打开了 Wireshark，并被 Telnet 协商时候的“WILL <unknown option>”吸引走了。根本没尝试在 Telnet 中操作操作。 领取情况 第一个红包（78 元 发 16 个）：最早于 T+06:02:35 领取，领走 6 个，共 27.04 元。 第二个红包（第一份，88.88 元发 18 个）：最早于 T+00:40:02 领取，领走 14 个，共 67.39 元。 第二个红包（第二份，58 元发 36 个）：最早于 T+03:00:17 领取，领走 14 个，共 20.52 元。 彩蛋红包（68.88 元发 98 个）：最早于 T+00:04:26 领取，领走 52 个，共 35.01 元。 有些朋友可能不愿意使用支付宝领取红包，因此领取情况不代表真实通关情况。
在 2023 年的 RISC-V 中国峰会上，cyy 和万呆呆见到了一款看起来只是一款平平无奇的交换机，但他既然出现在了这个会场上，那就肯定和 RISC-V 有那么几毛钱关系。这个交换机采用的轩辕 1030M 以太网交换芯片来自武汉飞思灵，而里面用于管理的 CPU 就是基于 RISC-V 架构的，主频 400MHz。除此之外，这个芯片还集成了 8 路 10/100/1000BASE-T 或 100BASE-FX 的 PHY、4 路支持 1000BASE-X、SGMII 的 1G serdes、2 路支持 1000BASE-X、10GBASE-R、SGMII、QSGMII、O-USGMII 的 10G serdes，接口数量还算客观、对于各类 SFP(+) 模块的支持也不会有多大困难。另外一些参数如下： 最大帧长 16000 字节 片上缓存 1.5MB MAC 表 16K 项 VLAN 支持 4K 个 二层组播支持 4K 个 ACL 支持 256 条 典型功耗 4.5W 参数很好看，作为一个办网专家，背包里怎能不常备 10G 网卡、光纤跳线、光模块等必备工具？cyy、Easton Man、Yuuta 三人共凑出 x86/ARM MacBook 各一台、ConnectX-4/AQC107 芯片的网卡各一张，并使用 DAC 线和光模块+跳线两种方式，当场对该交换机的 10G 口双向转发能力和 RSTP 工作能力进行了测试，均正常完成。因此群友们也都挺期待该交换机面市。前两天 Milk-V 在淘宝店上架该交换机后，群友马上组团进行了购买。数辆泥头车从深圳启程，创（撞）向群友的家中。 收到以后，我连晚饭都没来得及吃就开始拆箱。该交换机一共有 8 个 1G 电口、4 个 1G SFP 接口、2 个 10G SFP+ 接口，还有一个用于连接 UART 和 JTAG 的 USB-C 精致调试接口。他没有附送 USB-C 线可以理解，毕竟只是调试接口需要，并不是一个必备的东西，但居然连电源线也不给一根，十分小气。好在我有 12V 的 PD 协商模块，配合 5.5x2.1 转 5.5x2.5 的 DC 头，可以使其正常上电。 吃完饭后便着手开始测试，首先将其连接到了电脑和家中一台闲置的路由器上，为了测试 SFP 口，还插入了闲置的 GPON/EPON 光猫 SFP 模块一根。所有端口均正常工作，并在默认配置的情况下，所有业务均能相互访问，一切工作正常。 接上串口，在开机的时候可见 OpenSBI 信息。 OpenSBI v0.7 ____ _____ ____ _____ / __ \ / ____| _ \_ _| | | | |_ __ ___ _ __ | (___ | |_) || | | | | | '_ \ / _ \ '_ \ \___ \| _ < | | | |__| | |_) | __/ | | |____) | |_) || |_ \____/| .__/ \___|_| |_|_____/|____/_____| | | |_| Platform Name : Nuclei UX600 Platform Features : timer,mfdeleg Platform HART Count : 1 Boot HART ID : 0 Boot HART ISA : rv64imafdcsu BOOT HART Features : pmp,scounteren,mcounteren,time BOOT HART PMP Count : 16 Firmware Base : 0x41000000 Firmware Size : 76 KB Runtime SBI Version : 0.2 接下来按照背面贴纸的说明，在电脑上配置了 192.168.40.0/24 内不与 .253 冲突的地址，使用 admin/admin 访问了交换机管理页面。从管理页面可以看出，该交换机有 802.1q、QinQ（及 map）、基于 MAC/IPv4 子网/以太网协议的 VLAN、风暴抑制、端口限速、流量控制、802.1p、DSCP、GVRP、GMRP、LLDP、IGMP、静态组播、基于 MAC/IPv4/IPv6 地址的 ACL、ERPS、静态/LACP 端口汇聚、RSTP、802.1x、RADIUS 服务器、SNMP、RMON、端口镜像等功能。 我试着配置 VLAN trunk，看看能否与图中下面那台 NEC 路由器（也有点灵，因此我称该测试为“灵车对创”）互联互通，测试是能用的。就是我配置某个端口 tagged 1-4094 的时候网页卡了好几秒，以至于我以为被我配掉线了，又等了几秒，发现页面的列表上直接多了 4094 个 VLAN，太暴力了。 后面我还测试了一些能测试的功能，比如 SNMP、LLDP 等，都能用。光猫 SFP 模块也能正常协商、使用。运行了几个小时后，整机温度还好，温热，似乎不烫。至于转发性能之类的，cyy 在 8 月份已经测试过，就不做另外的测试了，更高级的测试我也没办法进行。 好，就介绍到这里，下面可以不用看了。799 元买到支持这么多功能的、带 2x10G+12x1G 口的管理型交换机，要啥自行车啊！ 在进行完基本测试后，开始对他进行深入分析。首先尝试拆机，但是螺丝上太紧了，放弃拆机。反正 cyy 在八月份有拍过，这里借一下他的图片。 串口的登录凭据是 root/milkv，一进去便可以注意到 /root 下是有东西的： root@dev:/root> ls -lah total 13M drwxrwxrwx 2 1000 1000 1.1K Jan 1 1970 . drwxr-xr-x 17 1000 1000 1.2K Dec 12 12:03 .. -rw------- 1 root root 763 Jan 1 1970 .ash_history lrwxrwxrwx 1 root root 16 Jan 1 1970 config.fhme -> /mnt/config.fhme -rwxrwxr-x 1 1000 1000 35.8K Dec 12 11:48 config_pll.sh lrwxrwxrwx 1 root root 19 Jan 1 1970 config_reg.txt -> /mnt/config_reg.txt -rwxrwxrwx 1 1000 1000 219.3K Dec 12 11:49 fhcli -rwxrwxrwx 1 1000 1000 16.5K Dec 12 11:48 i2c_dev_msg_muti -rwxrwxrwx 1 1000 1000 20.1K Dec 12 11:48 i2cdetect -rwxrwxrwx 1 1000 1000 23.4K Dec 12 11:48 i2cdump -rwxrwxrwx 1 1000 1000 19.3K Dec 12 11:48 i2cget -rwxrwxrwx 1 1000 1000 22.4K Dec 12 11:48 i2cset -rwxrwxrwx 1 1000 1000 20.2K Dec 12 11:48 i2ctransfer -rwxrwxrwx 1 1000 1000 16.5K Dec 12 11:49 led_164.ko -rwxrwxrwx 1 1000 1000 12.9M Dec 12 11:49 libsdk.so -rwxrwxrwx 1 1000 1000 36.7K Dec 12 11:49 xy1000_net.ko 首先我们先不去吐槽这些程序、动态库和 kmod 为什么会在 /root，为什么会有 1000:1000 的所有者。首先映入眼帘的就是那个 .ash_history，我们打开看一下。哇，看起来 MAC 和 SN 都是直接在 shell 里面写入的！这个可能是自动程序完成的，但是万呆呆也发出了他的文件，和群友的数据对比了一下，发现 3 台机子各不相同。甚至和我有着相邻 MAC、SN 的机子一开始还误写成了我的 MAC、SN。可见这个交换机真的是手工匠心之作，MAC 地址、序列号都是纯人工写入的。 作为一个持有 OUI（MAC 地址前缀）分配的冤大头，在看到 MAC 地址的时候总会去查查 OUI 数据库，看看这个 MAC 地址属于哪个组织，该组织拥有多大的分配。对于网络设备（如网卡、交换机等），这个地址一般是由该设备的生产厂商烧入。然而我查询了这个 MAC 地址以后，发现是属于 Realtek 的，但在主板的拆机图中没能见到任何 Realtek 芯片。根据搜索引擎的结果，这个前缀的 MAC 早就在 20 年前由 Realtek 开始使用，理论上早已分配完毕，更不可能提供给 Milk-V 用于制造交换机（况且他们也没用 Realtek 芯片）。所以 MAC 地址都是偷的，不能保证其唯一性了，此处建议 Milk-V 去 IEEE 申请一下 OUI 再来生产网络设备。 接着我们继续欣赏他的 rootfs，可以注意到有着大量 user=1000 group=1000 并带着 777 权限位的文件，或者是带上了执行位权限的配置文件，比如 /etc/group、/etc/hosts。打包该系统的人的水平实在不敢恭维。但还是那句话，又不是不能用，对吧。 系统的 model 为 nuclei,ux600fd，nuclei 即芯来科技。设备树非常短，除去 CPU、中断控制器、UART、GPIO 等，就是一个 ethernet 节点了。这个节点的 compatible 是 fsl,fsl-mac，fsl 即武汉飞思灵，是交换机数据平面。在内核中对应的驱动是 xy1000_net，该驱动会在内核中将其创建为一个网络接口 eth0，这个接口经由交换模块直通面板上的调试口（不是 UART/JTAG 那个 USB-C）。该调试口默认配置是 0 号口，我没有测试，不确定是面板标注的 G1 还是 XG1，不过在 Vega 的出厂配置（/mnt/config.fhme）中也并没有启用这个调试口的 CPU 直通（cpu_port_enable=0），所以该口也只是一个普通交换接口。同时还有一个 vir0 接口，这个接口是交换机上的一个 access 接口，只会接收管理 VLAN 的、目的地为 CPU 的数据。管理 IP 和 MAC 就是配置在这个接口上的。 片上一些能力的配置是通过一串 i2c 操作脚本实现的，没有写成驱动，因此开机会刷一屏 i2c ioctl 的调试信息。根据这个网页管理页面的 URL 规则（setup.cgi?todo= 和 setup.cgi?next_file=），我搜索到了 NETGEAR 的路由器，不知道他们之间有什么关联。 结合支持的功能，再看看这么灵车的情况，这个 SoC 十有八九是买的核拼的吧。这 Milk-V Vega 大概只能算轩辕 1030M 的开发板或者评估板，在家里用没关系，但在严肃的场合是没办法去作为一个真正的交换机使用的。资料公开得还是比较全的，U-Boot、Linux Kernel、SBI 的代码等都公开在了 GitHub milkv-vega/vega-buildroot-sdk 这一个仓库中，深刻践行了 monorepo 思想，避免用户切换多个仓库，commit 历史也只有干净简洁的 1 条。另外，飞思灵这个轩辕 1030M 芯片的各种文档也都公开在了 GitHub milkv-vega/vega-files，可以学习到交换芯片的一些内幕，有兴趣的大佬也能用来移植或开发。