分类: 技术相关

我和 AI 一起迁移一个十九年前的论坛:半年时间,从"生成代码"到"验证行为"

半年时间,比想象中的短,但又比想象中的长。

毕竟在大模型真正到“可用”阶段之前,谁也不会想去重构一个2008年的 PHP 中型项目。

本文包含 Github copilot 和 codex 的创作。

0.这是什么?

“口袋社区 PokeTB”是我在初中时候(~2007)与几个互联网上的网友开办的一个宝可梦主题的论坛。2008年的中国互联网,只需要一张 ICP 证书即可开办论坛,当时甚至还有类似于“我的领地(5d6d.com)”这样的“论坛站群”,注册一个账号你就可以获得自己的分站点。

image.png

以现在的数据说话思路,它至少还有14000+的主题(其中有很多 Pokemon Rom 修改的资料和工具)、以及超过28万的回帖。

image.png

这些朋友现在有的已经投身游戏行业,甚至有的曾经在神游(iQue)工作,现在已经移职到香港任天堂。

还有一些朋友,在聊天室退场,论坛贴吧刚刚兴盛的时候,自学 php,给论坛做插件。

image.png

更有一些朋友,当初为论坛设计顶头 Logo 和界面风格,现在有不少朋友已经是知名公司的插画师、独立漫画作者了。

image.png
image.png
image.png

1. 为什么要迁移旧系统

1.1 旧系统维护起来越来越贵

2007年的互联网上机器流量没有这么大。而时至今日,旧系统真的没有办法扛住现在的互联网流量。

那个时候,互联网 B/S 架构软件对于 MVC 的实践远没有现在成熟。当然,也有一些问题是 Discuz 6 特有的,后面21世纪后10年的 Discuz X 系列也以经典 MVC 架构重构过。我们没有 Follow 版本升级的原因有很多,主要还是时间精力,以及插件迁移的成本问题。

从 Discuz! 6的业务现状来看,问题很多:

1、模板、查询和业务逻辑混在一起。 Discuz 的模版引擎include/template.func.php 里的 parse_template() 读取 .htm 文件,把变量和 {template}、{subtemplate} 语法转成 PHP,生成 forumdata/templates/*_*.tpl.php。这套机制有编译缓存,代价是模板最终会变成可执行 PHP。业务入口同时还直接查数据库、准备展示数据:faq.php 在一个入口里根据 $action 查 FAQ、拼搜索条件、选模板;medal.php 一个文件管权限、分页、查询和申请写入;campaign.php 还要读配置、反序列化设置、拼外部 iframe URL。

这些文件未必不能工作。问题是数据准备、业务规则、页面输出之间没有稳定边界,改一个页面就得同时理解入口脚本、全局变量、模板语法、缓存刷新和隐含权限。

2、服务分层模糊。 pm.php 处理未读私信检查、登录判断、收件人查询、交易消息拼接,还直接更新会员表。modcp/editpost.inc.php 在一个 action 分支里完成版主权限判断、帖子查询、表单展示、主题和帖子更新。logging.php 同时管登录页面、验证码和安全问题、UCenter 登录、会员查询、会话状态和跳转。这样的代码能按历史路径跑,但没法靠类型、依赖关系和边界检查去理解。

3、数据库访问靠全局对象和字符串拼接。 Legacy 用 $db、$tablepre、$discuz_uid 这些全局变量做查询,很多 SQL 直接在入口里拼。faq.php 用 $keyword 拼 LIKE '%$keyword%',campaign.php、medal.php、invite.php、pm.php 也都在入口里拼查询和写入。旧代码通常会先过一遍 addslashes()、dhtmlspecialchars() 或 intval(),但这和参数化查询、明确的数据对象、可测试的 Repository 完全是两回事。

仓库里还留着几个 mysql_compat 兼容层,用全局 $mysql_link 把废弃的 mysql_\* API 映射到 mysqli。看得出当时的目标就是让旧应用继续跑,没打算动数据库访问模型。

4、安全和运行时机制背着历史包袱。 表单保护靠 FORMHASH、formhash()、Cookie 和入口级判断;common.inc.php 直接 error_reporting(0),顺手关掉 display_errors。这些机制不等于"完全不安全",但很多行为藏在全局初始化和约定里,排查问题时你说不清一个请求到底过了哪些保护。

5、源码里还有旧式动态执行和反序列化:UCenter 客户端用 eval() 动态实例化控制器;campaign.php 从设置里 unserialize() 配置;归档入口用 eval() 执行权限公式;模板里也有 {eval ...},viewpro_inajax.htm 就会执行显示函数和自定义作者信息。这些不能一刀切全删,有些可能是历史协议或数据格式。正确做法是逐项确认输入来源、实际用法和替代边界。

6、限流和资源保护跟业务入口耦合在一起。 发帖和私信限制由 checkflood()、$floodctrl、$maxpostsperhour、$lastpost 这些全局状态共同决定,发帖限流还会查最近一小时的帖子数。WAP 私信和回复入口各自实现了一遍表单 hash、时间间隔和写入逻辑。机制本身能表达历史业务,但你没法统一观察哪个入口受限、限流会不会误伤、一次写入失败后状态有没有恢复。这些边界是 Modern 慢慢拆出来的:入口、权限、限流、事务、任务进程,各自可验证。

7、缓存责任不清,部署时经常"代码更新了,页面没更新"。 模板编译产物、数据库缓存文件、Cookie 状态、UCenter 数据、页面输出,各归各的机制管。迁移中间我还遇到过持久卷里的旧 Twig 编译文件继续输出旧页面。后来我把构建层缓存、Twig 编译缓存、OPcache/FPM 状态、Redis 业务投影分开定义,轮换和验证也写进了发布流程。

8、GBK 编码与现代基建格格不入:2008年的 vps 配置还是 1CPU+256MB 内存+10GB 硬盘,而且当时的个人电脑配置也普遍受限,加上 IE6 横行,对于 UTF8 的支持并不是很好,所以当时大量的网站采用 gbk 编码以节约 B、S 两方资源,但随着基建(php、mysql)的退役,GBK 的支持也逐渐变成了问题。

Legacy 不是一无是处。恰恰相反,旧系统里存着大量用户用了多年、但从来没被正式写成规格的业务规则。迁移要做的不是推倒重来,是把还重要的规则从隐式代码和运行时副作用里解放出来。

1.2 这个论坛不该封存在某个人硬盘里

另一个原因更简单:论坛里装的不是我一个人的东西。

十五年,很多人发过帖、回过帖、传过图。作者、时间、权限、附件、上下文,这些数据凑在一起是一段共同记忆。

如果它只能靠一台旧电脑、一个没人敢动的 PHP 版本、一个唯一掌握全部上下文的人活着,那它其实已经半封存了。数据还在,普通用户访问不稳,维护交接不了,将来也没法安全地继续跑。

所以这次迁移不是"把旧代码改漂亮",更不是证明我能重写一个论坛。它更像数字遗产维护:把已经发生过的内容和行为尽量保住,同时把系统挪到一个别人能理解、能验证、能部署、能接着维护的地基上。这也是为什么项目坚持保留历史数据、已确认的业务语义和用户界面,不借迁移之名清理旧记录、改交互、重设计数据库。

2. “修旧如旧”:表面是升级 PHP,实际是保住一段业务历史

项目对象是一个跑了十九年的中文论坛,基础系统是 Discuz! 6.1F。目标不是重新设计一个论坛,而是在尽量保留历史数据、业务语义、权限关系和用户界面的前提下,把运行环境和代码结构带到 PHP 8.3。

重构后的系统是 PHP 8.3 的模块化单体:Composer、PSR-4/PSR-12、PDO、Router、Middleware、Twig。数据在 MySQL 8.4,Redis 管会话和部分缓存,附件走对象存储,应用和任务跑在 Docker 里。

听着像一次标准的老系统现代化。难的地方不在旧语法改新语法,在于旧系统里有大量没写进正式规格的规则:

  • 有些字段的含义已经被历史数据固定了;
  • 有些权限不只看当前角色,还看作者、版块、主题状态和已有操作;
  • 积分、余额、库存、日志、通知必须在正确的事务边界里一起成功或一起回滚;
  • 一个旧入口可能包含多个 action(modcp.php),不能因为入口名字相同就统一跳到同一个现代页面。

至于为什么不用更新、更酷的现代技术栈,比如 node 甚至 go,继续向下看。

3. 最初的教训:架构迁移的边界,和生成很多文件不等于完成迁移

今年一月的时候,我动了重构这个论坛的念头。一方面,yadex 和 yahoo 的假冒爬虫让机器频繁宕机,另一方面,discuz 的防 cc 是以 mysql 作为数据库的(会把 sid 落库),海量的异常请求把 mysql 都打垮了。加上去年(2025)一年, cursor 带给我的体验太神奇了:

那肯定是先用自己现在最熟的东西:来,让 claude code 重构成 nodejs 项目。很直接:按模块和周计划,让模型生成 Controller、Service、Repository、测试和完成报告。推进很快,新系统骨架很快就有了,然后灾难来了。

2月的模型是什么: Claude sonnet 4.6、GLM4.7(对,5都还没上),Openclaw 也还要半个月后。大家对于 Agent 工程也懵懵懂懂,自然就是一坨灾难。

Claude code 在把 plan 和 GLM Coding Plan 都烧完之后,告诉我搞定了。

结果可想而知。

这个结果给我的最重要的输入是,对模型能力的判断。也在那时,我确定了当前节点的目标:先别换语言了,让它的架构跟现代开发框架一致,“修旧如旧”。下一步再说怎么改。

很快我确立了三个黄金原则,直到现在也留在了这个阶段的项目里,帮了大忙。

1、零改表原则:一个 BS 架构的系统包含前端、后端、缓存和数据库,缓存随便扔,如果要动前后端,意味着数据结构可能要有变化,那如果数据库也一起修改了,就意味着无法做校验。虽然确保不动数据库可能会导致模型层产生垃圾兼容代码,但这是阵痛。

2、最终行为一致:虽然模版引擎从 discuz 的 template 迁移到了 Twig 这个现代引擎,但页面视觉、行为、交互应当严格保持一致。

3、TDD 原则:虽然这半年很多人已经进化到了 SDD 模式(即 Spec 驱动开发),但事实上,旧系统翻新这件事,有明确标的,采用 旧系统-写用例-改新系统-完善用例 这样 交替迭代系统侧和测试侧的方法更为有效。截止项目上线,目前维护了14000+ Unit Case、3000+行为 Case 和 300+前端 case。

虽然这些原则在大概3月就确定了,但是当时的模型确实能力不足,导致产生了一个让我很头疼的问题:文件按照规划的模块都生产了,然后 claude 报告已经完成,然而:

一是文件存在经常被当成能力存在。类、路由、单元测试都能写出来,但用户真走一遍"进页面、提交表单、发生写入、跳转、再读取"的链路,接线错误就露出来了。

二是现代设计习惯会悄悄改写旧业务。模型看到一个不好看的旧字段,就顺手推一个更"合理"的新字段;看到一个怪权限分支,就当成 bug;看到旧的 CSRF、编码或状态处理,可能直接换成一个看起来更现代的实现。

三是测试有时验证的是实现布局,不是用户行为。比如某个共享组件已经正确提供了路由,测试却要求某个特定字符串必须出现在页面源文件里。测试是失败了,但失败的对象是代码组织方式,跟业务结果没关系。

所以,我还是引入了行为(业务能力)迁移判断流程:从"按文件补齐"转成"按业务能力迁移"。能力的单位不再是某个 PHP 文件,而是一个用户或后台操作者能完成的动作,包括入口、角色、状态、读写、外部副作用和生命周期。

这个区别很关键。比如说,旧的 poll.php 不是一个能力。创建投票、投票、查看投票人、编辑、关闭、删除、个人列表,才是不同的能力。它们可能共享代码,但权限、状态和验收标准不一定共享。

4. 三种权威和目标的迭代:虽然已经可以自动化了,但前提是人依然很重要

迁移过程中,另一个浪费了很多时间的是,搞清楚"事实""目标"和"授权"。现在分三类:

实际是什么。 以可达源码、配置、数据结构和运行证据为准。历史文档可以帮你导航,但文档里的一句话不能压过代码和实际行为。

目标是什么。 以当前已确认的产品语义和领域契约为准。Legacy 是重要证据,但不是无条件的真理。旧系统的漏洞、已批准退役的入口、损坏数据的保全策略,分清楚什么是“原有设计”、“缺陷”,并能判断“是否保留”,仍需要大量的人工参与。

这次能做什么。 以当前任务的授权边界为准。普通实现和可逆修复可以继续;新的数据删除、结构变更、不可逆副作用、生产操作,要单独确认。

所以在6月、7月两个月,我基于这个方法明确了很多围栏,并且与模型专门进行了一轮历史功能确认,以明确功能边界。可以说,如果没有人工输入的判断,就没有下面完全脱手的实现。

在8月份,codex 的 /goal 稳定了,我也让 5.6-sol 在走查修复过程中使用了一个 15day+ 的 goal。不得不说,模型的进步速度实在是太快了,goal 让我真正享受一把“脱手”的感觉:

image.png

最后的这两个月提交量暴增(我从来没有在 github 上每天100+PR),而且模型已经可以稳定的自行闭环走查任务了。

5. 最难的不是分层,是守住旧行为的边界

/goal 解决了“脱手”的问题,但也让过程监控和结果是否漂移变得更难观测。

5.1 数据库"少改"不等于业务简单

项目有零改表原则,但它不是"永远不许改表"。真实含义是:新增结构之前,先核对 Legacy 的同功能表、字段和文件存储;确实要变,必须有明确的结构决定和兼容方案。

这么做的理由不是迷信旧表。历史数据本身就是系统的一部分,一个看着干净的新字段可能解释不了旧记录,一次批量清洗可能毁掉用户看不见但有意义的状态。

5.2 跨域写入必须有明确边界

论坛、积分、银行、附件、通知、ZPet 之间联动很多。ZPet 最后用的是进程内插件加唯一的同步事件总线,没有让核心业务直接调插件内部逻辑。

事件通道也不能混用:有的必须跟根事务一起提交或回滚;有的要求同步且只能有一个处理者;有的只做只读收集;有的用于跨事务投影。这个区分比"用了事件总线"重要得多。

5.3 模板迁移不是替换括号

Legacy 模板的路径是 .htm 经旧模板引擎生成可执行的 .tpl.php,运行时加载。迁到 Twig 后:

  • 旧变量、条件、循环、子模板改成 Twig 的输出、控制结构和继承;
  • 模板里混着的业务计算挪到控制器和服务;
  • 全局变量变成明确的数据输入;
  • 翻译、URL、资源路径、CSRF、BBCode、插件插槽全部重新接线;
  • Legacy 的布局、类名、语言和样式语义尽量保留;
  • 在线模板编辑和旧的可写执行产物退役,模板变更交给源码和发布流程。

6. 真实浏览器比"AI 说完成了"可靠

项目里最有效的验收方式之一:把用户动作真走一遍,同时看页面、数据库、对象存储和日志。

很多问题只有真实链路里才会出现:

  • 控制器写好了,服务工厂接错类型;
  • /bbs 挂载路径漏了,源站检查正常,公网路径不对;
  • 下载内容正确,原始文件名丢了;
  • 表单按钮值跟 Legacy 不一样,服务端直接拒绝;
  • 旧页面 CSRF token 过期后,失败回显把用户输入丢了;
  • CSS 和头像这些公共资源也走完整应用初始化,页面本身没问题,但整个请求瀑布很慢;
  • 测试库里的外围事务掩盖了真实请求中事件准备时序的错误。

不同证据回答不同问题:单元测试验证局部规则,集成测试验证组合,静态检查验证边界,浏览器验证真实装配和用户路径,发布后检查验证实际运行的版本。

7. 架构和发布:重点在边界连续

Modern 的架构可以概括成模块化单体:

  • 前台、AdminCP、ModCP 用 Twig 组织页面;
  • Front Controller、Router、Middleware 处理入口、身份、权限、CSRF 和输入边界;
  • 业务服务管论坛、用户、私信、积分、银行、附件和管理能力;
  • Repository/PDO、Redis、对象存储、邮件、任务是基础适配;
  • ZPet 通过类型化契约和唯一的同步事件总线协作;
  • Legacy 行为证据和可观察回归是系统的两根柱子。

生产部署不是把容器启动起来就完了。PHP 镜像和 Nginx 镜像分开构建,依赖层和业务源码层分开,基础镜像按摘要固定,OCI 层按 SHA 校验并复用。生产里 PHP-FPM、Nginx、常规定时任务、投影/邮件任务、附件隔离任务分开跑;数据库、Redis、备份各有独立的数据生命周期。

发布的流程:完整质量验收和静态验收先绑定最终 main 提交,本机 pre-push 登记证据;GitHub Actions 按 AMD64 构建,做依赖审计、SBOM、漏洞扫描和隔离 smoke;服务器核对 OIDC、源码 SHA、制品摘要和运行身份,然后才切换五个应用容器。失败就回退到上一份合规的 main 制品,重新轮换缓存、重载 FPM、验证入口。

这里得诚实:这是原地 Compose 更新,不是双集群、不是金丝雀、不是全业务零停机发布。两个入口的 HTTP 检查只能证明入口健康,代替不了完整业务验收。镜像回退撤不掉新版本已经写进数据库的数据,更不等于数据库回滚。

8. 性能教训:高配开发机会让错误的测量显得可靠

本机是 M5 Max、128GB 内存、2TB 硬盘。开发体验很好,盲区也明显:很多问题在开发机上不显形,部署到资源更有限、还跟 WordPress 和百科共享 CPU 的生产机后才暴露。

更根本的问题不是机器快,是早期测量的对象不完整。有过性能报告只测单条数据库读取,还把顺序循环叫成并发。这些数字能证明某个局部片段快,证明不了首页响应、FPM 队列、浏览器首屏、真实并发没问题。

上线后的分析显示,成本主要来自请求链里的重复工作:路由身份构造、插件和事件元数据检查、头像清单处理,还有 HTML、CSS、头像等资源各自启动一遍应用链路。一次优化把重复的路由身份构造从约 43,295 次降到 1,790 次;另一次把交替原型路由安装的 CPU 中位数从约 193ms 降到约 44ms;CSS 路径的 CPU 从约 333ms 降到约 93ms。

现在的经验是:从第一个纵向切片开始,就在接近生产的资源约束下测完整 HTTP 链路,分别记录启动、路由、查询、渲染、附属资源、FPM 排队、浏览器首屏。性能不能等全部功能迁完再安排。

9. 回头看:Claude Code、GLM、Copilot 和 Codex 分别改变了什么

这个项目先后用过 Claude Code 配 GLM 模型、Github Copilot 和 Codex。

工具换来换去,最大的启示是一条:工具可以换,共享事实不能依赖工具的记忆。

先是 Claude Code。早期模型也不行,工具也不完善,所以单体构建、单体测试,人工回归。但它至少给人了一个反馈:这事搞不好真的一个人能干。它更像快的代码生成器,帮忙搭骨架、补局部实现。项目复杂度上来之后,真正重要的工作变成了:从 Legacy 提炼行为证据;对照 Modern 的真实装配;找出"看起来合理"但不符合历史语义的实现;把一次线上或浏览器发现固化成回归;检查源码、测试、镜像和生产运行身份是否一致。

Github Copilot 很像 multica :你给他一个 issue(pr、issue、comment),它自己有 workspace,可以构建检测,推送上线。但是它有跟 Codex /goal 类似的问题:过程不可监控,猛猛几个小时出来一个大PR,你根本不知道它在干什么。

Codex 是能成功上线的最终推手,不只是模型能力,也基于这半年超过所有人类认知的模型、Agent 工程的发展——它真的能用在真实项目里了。

10. 下一次迁移,我会提前做什么

再来一次的话,我不会照搬这个项目的全部文档,也不会默认照搬 PHP 分层、零改表、四类事件或现有 Docker 拓扑。方法复用,业务重新调查。

第一天先立五样东西:

  1. 项目适配配置:代码位置、运行命令、数据隔离、不能碰的资源、生产边界;
  2. 能力清单:按入口、角色、状态、动作列出真实迁移表面;
  3. 最小行为证据:源码、配置、样例输入输出、已批准差异;
  4. 纵向切片:从入口到存储、响应、再次读取,走通一条真实链路;
  5. 真实性能和缓存契约:第一个切片就定好测量范围、资源预算和失效方式。

然后让程序帮着判断哪些结论需要重看:Legacy 摘要变了,重看关联事实;Modern owner 变了,选受影响的回归;产品决定被替代了,更新预期;环境不匹配、测试没跑、源码漂移,撤销"当前通过"状态。

理想的最小技能包只要四个动作:discover 发现入口、能力、来源和未知项;characterize 把行为、边界和差异固定下来;migrate 实现一个有界且已授权的纵向切片;verify 以源码身份为起点,给出证据等级和真实缺口。

模型应该拿到的是任务包和证据索引,不是几百页互相覆盖的历史记忆。

结语

遗留系统迁移最危险的时刻往往不是代码报错,是代码看起来已经完成。单元测试过了,但没有真实入口;新表有了,但解释不了旧数据;页面能打开,但丢了一个用户依赖多年的边界行为;镜像能部署,但不是刚才验收过的那个版本。

这次项目让我接受了一个不怎么激动人心但更有用的结论:AI 的价值不是让迁移变成自动驾驶,而是让人有更多时间花在真正要紧的判断上——哪些行为必须保留,哪些应该改变,哪些差异已经被批准,什么证据足以说明这次改变没有越界。

这些判断写成能力、契约、回归和发布证据之后,Claude Code、Copilot、Codex,包括下一代完全不同的工具,都只是执行和检查这套方法的不同入口。

家庭网络设计与FTTR解决方案

想写这篇很久了,大概是 2022 年的某篇提到的?

嘛,不重要了。在懒得动笔的这段时间里家里的一些设备选型也有变化,去年年底借着组内分享的机会把这篇文章写了,就也算借这个机会备份下本就属于自己的文章。

一转眼买房装修也过去快四年了。装修这件事非常感谢夫人精心设计了全屋的工作区,以及两边父母轮流抽空来帮忙盯装修和搬运东西。装修那会儿正是我在现在这组最忙的时候,现在这个组也到了“飞鸟尽良弓藏”的日子了,大概最近几个月就尘埃落定了。所以,也是时候来回顾下人生第一次买房和装修了。

(可能回顾完就该去刷题了?哈哈。)

买房

我是大概在 2020 年开始决定要买房的。结婚是其次,主要是跟家里长谈了几次之后觉得经济趋紧,家里又没有非常强的理财技能,最好还是把家里的固定的资产从老家借助我在北京的工作迁移到一线城市,虽然都在跌,至少跌幅不会太离谱。 2025 年回看这个决定,当年 1.2 万/平卖掉的老家市中心房子至今也就在 8000 /平 左右徘徊(-35%~),重要的是成交量也很感人,我现在在住的地方虽然也在跌,也不过只是亏掉了装修的钱(之前算了下扒了重装 + 所有家具家电一共 60w rmb 左右),总体来说作为保值的目的还是达成了。

扯远了,反正总之在权衡了自己能挣多少钱、家里有多少钱和未来能有多少钱这件事之后,选了现在的地方。

大体上是一个南向的两居,我俩在客厅正中间的位置打了一道镂空隔离门,门里面有窗的部分算书房,外面是客厅,勉勉强强算是做成了三室。

这个户型勉强还算方正,但是因为厨房整体是个承重墙,所以还是要考虑屋内的无线方案。至于有线,因为之前某些工作已经上了万兆多模 850nm+ 的方案,因此也算是已经选过型了。

FTTR 是什么?

FTTR 是英文「Fiber to The Room」的缩写,意为“光纤到房间”。随着中国家庭宽带事业的发展,中国的家庭宽带从早期占用电话线的拨号上网,进化到网线到楼的PPPoE,再到最高支持百兆宽带的 ADSL,再到光纤到楼,直到现在的光纤入户,宽带带宽也从16.7kbps-56kpbs到了现在的万兆宽带到家。

光纤入户可以提供高达10Gbps(北京联通现售最高带宽产品)理论值25Gbps+的下行宽带,此时家庭内的普通网线就变成了限制宽带速率的瓶颈。因此,无论是极客社区还是运营商,都纷纷开始推行光纤到房间,即在入户光猫拨号完成后,到各终端部分使用调制解调器+光纤来提供远超家用普通网线带宽的装备。

现代超六类、七类网线也可以提供超过10G的带宽,但因为电线粗大且屏蔽要求高,电模式的交换机、路由器和有线网卡费用高昂,在一般家庭内部署成本太高,所以没有铺开。

为什么要组建家庭网络?

在公司写这个是因为公司的大部分年轻人还是合租单间,除非非常 geek 的同学基本都没怎么接触过家用组网方案选型。放到互联网上,这个问题就很显而易见了:我 200T 的硬盘总不能都插一台台式机上吧?

因为需要有数据存储分离+高内网带宽诉求。

作为一个混迹互联网20年+的老登,个人数据规模(不含影视频、音频)已经超过6TB+,而且还在增长中;除此之外还有很多PT站资源、站点备份等数据,都占用了大量资源。在2021年买房之后我就在规划网络布局,考虑到个人在家使用设备习惯,有两个点需要做重点考虑:

1、个人存储数据(含PT等大型资料)约20TB左右,机械硬盘工作噪音大需独立存放:台式机长期开机电费消耗高,机械硬盘频繁启停故障率高。

2、内网数据交换比较频繁,需要大内网带宽

在局域网内实现超过2.5G的网络有光、电、无线三种方案,一起对比看下。

方案对比

项/方案光/单模光/多模电无线
速率10000M/上下行对等10000M/上下行对等10000M/上下行对等9000M/上下行对等
布线难度高单模要实现超过5G需要多路布线中多模只有成品线中线长超过30m需铺设超六类及以上电缆线,并做屏蔽处理,且面板、模块都需做定制高本质上无线万兆也强依赖布局+点位,到中心路由器还是需要以有线为主,如果全无线 Mesh 家庭环境无法解决干扰问题
设备成本高双口万兆/25G双口(一收一发)贵,而且交换机(终端多设备接入)成本高低单口万兆模块、接口成熟,近些年百元级别的万兆交换机也逐渐铺货高外设成本还好,但为了保证能至少跑到万兆需要做全链路的屏蔽,成本较高低仅需普通AC6000+的无线路由器若干即可
稳定性高高高低
升级空间高模块对换普通单模线可跑到12.5G/单路 - 25G无线模块决定了性能上限中随电协议升级最高可到15G无wifi协议升级设备要全换

因此,有线方案选择了光多模方案。

为什么不用各大运营商的“FTTR”方案?

中文社区里面已经有很多很成熟的 FTTR 自建和运营商方案对比,可以参考:https://post.smzdm.com/p/ao94n4l6/

其实本文探讨的也是 FTTR (光纤到屋内)的一种,相比起上面社区的文章,本文因为使用成品多模线,因为成品线端子巨大,在一定程度上牺牲了布线的灵活性,但是降低了很多施工的复杂度。

扯远了,来回答一下标题上的内容。

最重要的一点是,运营商提供的方案贵。主流的运营商的 FTTR 套餐需要捆绑2年合约期+超过200元的每月低消,比目前我用的套餐要高很多。另外,大部分现行方案都是基于 GPON 的单模方案,该方案到终端节点只有 2.5G 的速率,如果还要兼顾无线流量,那很难满足有线部分的速度需求。

又及,本文在实际部署中实际上是光电混合方案,电网线的大部分线路是提供给 AC + AP 使用(有线2.5G,单程1.25Gbps),因此也具备了无缝漫游的能力。

再及,在写这篇文章的今天,我刚跟北京联通签了全新的 3 年 FTTR 合约,但我不打算使用他们的 FTTR 设备。因为我现在的套餐方案算上副卡,跟升级后的套餐费率一致,相当于免费获得了上行 30M -> 上行 100M 的升级。

不过这次升级之后,北京联通给我更换了华为 F50 至尊版 光猫+路由 一体机,而且明确不能通过业务员途径更换桥接拨号。现在淘宝上有大概 240 元左右的第三方路由器可以实现桥接功能,或者也可以选择能修改 MAC 地址的猫棒(北京联通仍然只使用 MAC 地址进行绑定)来进行路由器拨号。

无线布局方案(Wifi 和 IoT 网关)

既然主干网络没有选择无线,那就要单独给无线布局考量一下。

结合户型图看一下,可以看到两个明显的问题:

1、房子户型是L型,且有贯穿承重墙,传统无线布点会有盲区

2、部分 IoT 设备位于房间边缘,需要考虑无线稳定性

因此,依靠PoE的 AC+AP 方案可以有效扩展网络覆盖布局。

无线设备我倒是没怎么纠结,因为国内、国际做AC+AP的从顶级到末端就那么几家。姑且报个菜名

CiscoArubaUbntTP-Link/水星/netcoreRuijieH3CHuaweiiKuai

因为我装修的时候刚好看公司全都升级了 Aruba 的设备,而且网络改善非常明显;且 Aruba 刚好在同年推出了相对便宜、使用简单的 InstantOn 系列家用云AC 设备,于是也没怎么纠结,直接AP11d x2 搞起。

无线 IoT部分主要解决的是一些智能设备不具备 wifi 能力,将它们扩展成为可离家管理/通知的设备。目前家庭内需要漫游的智能设备主要有蓝牙协议的家庭门锁、Zigbee协议的墙壁开关和一些无线传感器。因为都是小米系的,所以也没什么可选型的,直接小米网关就行了。

因为我和夫人都比较反感随时监听(哪怕是本地监听)的设备,所以我们全屋都没有智能音箱/智能屏。

Wifi的话(橙色圈),下面那个圈基本能 cover 我的大部分需求,上面那个圈的盲区部分基本都是有线,而且是电视区域,很少有无线设备诉求。

蓝色的IoT 网络,因为全家的智能设备基本是小米,所以选择了小米智能网关2。这个没什么好说的,就是各个 zigbee/蓝牙 设备的中心点。

介绍完了选型,下面就是我目前的家庭网络拓扑。

设备拓扑(202408版)

互联网宽带选择

在北方,特别是北京,目前为止还没有其他宽带可以选择,只能选择联通宽带。

如果想听宽带通、长城、北京移动、北京电信这几家的爱恨情仇,可以回头详细说说。

硬件(202501)
北京联通从2022年开始,已经给千兆宽带用户换用 2.5G 设备,算上线损额外预留的带宽,现在的1000M带宽基本可以跑到 1300M 左右。

F50软文介绍:https://ithome.com/0/752/323.htm

所以基本网络连接也很简单,使用2.5G电模块直接连接即可。

为什么不上10G宽带?

10G家庭宽带是今年的新产品,需要社区骨干线支持,且费用过于高昂(内测999元/月,正式上线1399/月),还需要再等等。

为什么不使用“猫棒”?

“猫棒”是一种将宽带商入户光纤直接转换为电信号,使具有 SFP+ 端口的路由器不使用光猫直接可以拨号上网的模块。

通过猫棒,可以直接使用通过SFP+接口的路由器不经过光猫直接拨号上网,省去了光猫转接这一道,可以节约多媒体箱空间并省去修改光猫桥接,直接将家庭局域网网关暴露在运营商公网中,省去联系运营商修改桥接的步骤。

北京联通提供直接修改桥接、获取独立IP地址的反馈通道,必要性不高;

2024年10月起,北京联通已经拒绝所有新装/移机客户的桥接请求,但是仍然可以通过购买第三方设备(包括不限于第三方光猫、猫棒等)来实现桥接。

现在的设备选型

明确需求

目前家庭宽带主要是两人使用,其中每人有两部手机,一个移动设备(ipad或者其他移动终端),每人一台台式机电脑,家庭有一台万兆 NAS。

新装修的大部分家电都可以连wifi,加上摄像头监控和传感器等大概有30+的常驻在网设备。

总结一下的话就是需要一个带机性能和稳定性都较好的路由来承载家庭骨干网络。

当然,现在相对稳定的设备选型自然不是一次就能搞定的。在正式介绍上面拓扑图的一些设备之前,我先给大家介绍一些之前使用过但是淘汰掉的网络节点设备。

上一代方案关键组件介绍

路由器

因为有万兆,加上社区的熏陶,我第一版自然而然就想到了软路由。因为 FTTR 需要至少4个 SFP+ 网口,因此常见的社区 ARM 软路由几乎都被放弃,初期我选择了一台 X86 台式机来承载主路由。出于稳定性考虑使用了 Windows Base + HyperV ,并在上面运行了 Openwrt X86 。

这东西闲鱼二手的企业淘汰机很便宜,几百块就可以。

它提供了足够多的 PCIE 插槽让我安装 SFP+有线网卡,而且联想的工作站做工比一般品牌和自己组装要好很多。直到我淘汰它之前,在 UPS 的帮助下它甚至都没有停过机。

以及,因为 Openwrt 足够开放,我甚至可以在没有开通 IPTV 的情况下,借助一些教程,直接通过我电视上的 kodi 来白嫖 北京联通的 iptv 看电视。

它很好,但是它仍然被淘汰了。为什么?

首先自然是发热量。它毕竟是一台台式电脑,在夏天发出的巨大热量使多媒体柜附近的空调要额外长时间工作才能将屋内温度进行平衡。这一系列下来带来的附加作用就是高昂的电费。我家一直是五档电费的常客。

其次,虽然我不需要AC(因为 Aruba InstantOn 是云AC设备),但是这种软路由不具备PoE(网线供电)能力,这使得 AP 部分需要额外附加电源。分散的电源带来了更高的故障率。

光纤和再布线

在初期布线的时候,我选了运营商 FTTR 同款的单模光纤,随着网线一起埋在了墙里。后来经过学习调研发现它在单模单线情况下只能跑到2.5G。

于是又手动全屋部署了多模线。

当前方案下的关键设备介绍

路由器

经过长时间的市场调研,我发现了一款完全符合我要求的路由器,就是现在正在用的R6812TP-AC。一方面,它提供了四个 PoE 的有线网线接口(虽然只有1Gbps,但是对于 Aruba 来说足够了),另一方面它提供了高达 8 个 SFP+ 接口,通过 SFP+ 接口我可以安装光模块来满足房间内的光纤接入要求,也可以使用 SFP+转万兆电口来给 NAS 提供足够的上下行带宽。

有线

万兆设备低价选择

2019年可以说是家庭万兆的普惠元年。彼时商业机房设施大量更新,使昂贵的光纤模块、光纤网卡一下子变成了“图拉丁”垃圾价。

但是,彼一代的万兆网卡都是基于服务器标准的 OCP 2.0 接口。不过,好在 OCP 2.0 其实也是走的 PCIE 标准,那只需要做转接卡就可以了。

当然,肯定不需要自己从头设计,万能的社区可以解决一切。

如果不愿意购买成品,使用社区开源方案去 嘉立创 洗点板子回来也是非常好的。

有线网卡升级

已经被商业淘汰的设备自然很难获得驱动更新。Mellanox 从2021年起就停止对 CX-3 系列的网卡进行驱动支持,在 Windows 10/11 的新版本中会有各种稳定性问题。不过还是能磕磕绊绊用的。

最近,我也在看新的设备升级,方向是要么过渡到下一代版本的淘汰设备(CX-4,CX-5系列),要么就使用其他公司的方案(如aqc117/103)。不过这部分我也刚开始研究,等有了新的方案再分享吧。

最近从闲鱼上淘了 CX-4x 系列芯片的华为 SP333 系列万兆卡,Win10 / Win11 免驱。先这么用吧,下次搞估计就是全屋 25G 了,努力挣钱吧。

有线的其他部分

网线

有线的网线可以委托装修公司在水电布线阶段完成,但需要注意的是,如果有条件建议自行购买网线。很多装修公司提供的所谓“六类线”很多都是没有屏蔽标准的,在实际使用中很难达到标称年限。

另外,装修公司是不负责弱电的安装的(比如网线、有线电视线、光纤),自己接面板对于动手能力强的人可以,但我选择在宽带移机安装的时候请宽带师傅顺便帮我装了。据我了解,北京的大部分宽带上门装机时候都可以提供一定程度的装机部署服务,如果你自己准备网线钳+网线头,手工费也很便宜。

入户光纤

我做了入户点位移动,所以实际上联系了两次宽带师傅上门(入户线熔接、装机)。但是其实在付了装机/移机费用后,多少次都是可以的,只要提前跟宽带师傅说好就行。

无线部署

这部分倒是没什么说的,PoE网口很好用。

NAS

这部分也没什么好说的,虽然群晖这些年有些举步不前(像〇果一样),但是 DSM 仍然是这个世界上最好用的 NAS 操作系统。

白群晖可以使用 quickconnect 和平滑购买摄像头授权,而且入正属于天然正义。

UPS

https://item.jd.com/60694084041.html

参考阅读

https://cloud.tencent.com/developer/news/1310957

https://www.haoyizebo.com/posts/6a0c2301/

https://github.com/Turnedback/OCP2-Pcie

https://github.com/KCORES/OCP2PCIe/blob/master/doc/DevGuide_zh.md

https://zhuanlan.zhihu.com/p/114822136

TPM攻击:通过 LPC 总线旁路嗅探硬件 TPM 的密钥

前言

本文对这篇文章的改动已经不能叫做翻译了,因此算作编译吧。其实在这篇文章发布出来不久,我就在 NuSX 上验证了这篇文章。当时最大的困难是给 TPM 芯片飞线,我拜托了一个做硬件的朋友帮我远程飞的线,并使用这篇文章获得了密码。

SEGA 果然还是日本厂商,在硬件平台之上的 Windows IoT 系统还是一如既往的留有设计缺陷,导致了这个密码可以用很久。

扯的有点远了,我们来开始看看它说了什么吧。

背景

TPM (全名:Trusted Platform Module)是一种计算机系统的扩展模块,用于提供安全相关的功能。其中, TPM 芯片是一种硬件安全的加密处理器,可以安全生成、储存密钥,并用来保护其他加密密钥。目前市面上流行的 TPM 主要是两个版本:1.2 和 2.0。在 2019 年的时候,有安全团队发现了部分 TPM 1.2 和 2.0 芯片,甚至是一些 Intel 平台的软件 TPM 的设计缺陷,会导致 TPM 硬件内存储的密钥信息发生泄漏。

这一攻击方法已经在2020年的29届Usenix安全大会上由一些安全研究员公开,并展示了利用这一攻击进行 IPSec 网络认证攻击,在他们的论文中表明,只需要至多45,000次攻击,即可在目标系统内获取认证密钥。

除了网络认证,TPM 的一个重要落地应用是 Windows Bitlocker ,Windows Bitlocker 是 Windows 系统内置的硬盘分区保护工具,可以以用户定义密码或透明加密的方式保护用户硬盘分区内的数据,防止任何非法访问。

在基于时序的软件攻击外,很多没有在协议上做额外保护的 TPM 模块还存在着协议旁听漏洞,这使得很多可以物理访问但软件有保护的硬件系统存在着更多的风险。比如,街机领域内的软件厂商为了防止自己软件被泄漏或非法修改,对于分发的产品内的整块硬盘采用了 TPM + Bitlocker 加密的方式。

Bitlocker 101

上图是微软官方介绍的 Windows Bitlocker 保护方式,核心是图中的 Volume Master Key(VMK),由两类来源生成,两者在解密作用上是等价的:

一是密码,密码可以是单纯的用户指定的密钥,也可以是单纯的 TPM 密钥(透明加密),或者是两者混合的方式。

另一个是恢复密钥,这一密钥在加密完成后会给出到用户或者存储到用户的 Microsoft Account 内,在用户忘记密码的场合,可以使用恢复密钥来解除保护并访问内部的文件。

通过Volume Master Key,衍生出Validation Info 和 Full Volumn Encryption Key(FVEK)。Validation Info 除密钥特征外还跟当前系统的硬件信息、启动信息做绑定,如果硬件或启动信息不匹配,会进入到恢复模式,要求用户给出恢复信息。这样的设计是防止旁路攻击,即恶意用户拿到硬盘镜像后通过其他系统进行硬盘访问。

而 FVEK 是加密硬盘的主密钥,它本身被 VMK 所保护。所以,在透明加密模式下,如果解密 VMK 的来源(TPM)被攻击产生泄漏,那么接下来的链条都会被攻击。

TPM 简单介绍

微软的官方网站上提供了大量的 TPM 基础知识,因此我们不会再展开太多篇幅来介绍 TPM 的设计和其他基础信息。但是有一点需要说明的是,可能会有朋友认为既然目标系统存在保护,在有硬件的前提下我另外启动一个系统(比如 Ubuntu或者别的Windows)就可以获取 TPM 密钥,但其实 TPM 模块本身对于密钥生成的目标系统有一个叫做“启动状态”的存储标志位,这一标志位类似于 uuid ,是目标系统初始化加密时候生成的唯一值,理论上无法被碰撞。因此很难通过旁路系统进行 TPM 攻击。

TPM 与系统之间的通讯一般是 LPC SPI 或者 I2C 总线。在本次分享内我们会对于基于 LPC 总线的模块的攻击方式,如果想在线下自行复现本文攻击,需要确定自己使用的 TPM 硬件模块的通讯协议,具体方式可以去查看对应型号 TPM 芯片型号的白皮书,然后跟本文一样通过一台逻辑分析仪来进行分析。

上手逻辑分析仪:原作者的 TPM1.2 攻击和分析记录

作者使用了一块 SLB93650 TPM 1.2 芯片来进行攻击示范,这一芯片使用的是 LPC 通讯协议。在它的生产厂商官方网站我们很容易就能找到它的针脚定义:

要嗅探 LPC 协议一共需要7根电线:LCLK(时钟)、LFRAME、LAD0、LAD1、LAD2、LDA3、GND(接地)。首先,使用飞线在主板的这一芯片上直接将这7根引脚飞线出来:

然后,我们来找点东西来阅读 LPC 协议信息。

作者使用了注释6中的逻辑分析仪来进行逻辑分析,配套的软件是 DSLogic。DSLogic 和 PulseView 都使用了 libsigrokdecode,因此是通用的。由于 LPC 是 33Mhz 频率运行的,因此使用 100Mhz 采样可以尽可能的放慢信号并进行解析。

在作者的首次捕获中遇到了两个问题:第一个是解码器将 TPM 消息报告为保留消息,并没有正确解码它们,第二个是逻辑分析仪没有足够的存储空间来捕获完整会话,只看到了启动通讯消息。因此,需要修正时钟讯号来支持自动解码,否则解码的工作量将会是天文数字。

首先,作者解决的是识别问题。这是一个 DSLogic 的逻辑问题,阅读设计文档很容易发现 TPM LPC 协议是有协议头的: START(0101) ,因此进行对应修复就可以。

另一个问题作者的解决手段有一点歪道。毕竟直接更换逻辑分析仪成本还是比较高的,于是作者采用单通道分别捕获多个独立通道的信息,然后再将它们混合。于是,就获得了所有信号。

检索 VMK(Volume Master Key)

完成上面的工作后, LPC 消息就被成功解码了。剩下的就是在转储的数据里面找到 VMK 。搜索方法也很简单(去读 Bitlocker 协议的定义),就可以知道搜索 VMK 的标志位:0x2c 0x00 0x00 0x00。

作者运气不错,第一次就捕获到了完整的信号,他给出的截图,VMK消息位于 0x00000024 的 TPM 获取命令中。

来展开看下这个消息:

用计算器转换下:

然后就是重复直到 VMK 长度的消息:

使用 FPGA + 脚本读取 TPM2.0

好用的逻辑分析仪也需要捕获+解码的过程,一旦整体流程跑通,那我们就可以使用 FPGA开发板+脚本程序来自动化这个过程。Lattice ICEStick 是一款廉价易得的开发版,配合脚本就可以很快简化这个步骤。

需要特别说明的是,TPM 2.0 协议中增加了对于发送、接收指令的校验,可以一定程度上防止消息截获,但是 Bitlocker 在使用 TPM 芯片的时候并没有启用这一功能。

举个例子,SEGA ALLS HX1 ,是一款世嘉于2019年推出的街机基板平台。它搭载了 英飞凌 SLB9665 TPM2.0 芯片,根据上面的介绍,很轻松就可以找到这一芯片的定义:

我们也来简单飞个线:

然后就可以获得信息了。

原文附录:

READING

HARDWARE

SOFTWARE

Namco SYSTEM BAN1 拆解

好久不见。我还在。

过去的两年多有很多很奇妙的事情,比如买房、装修、结婚什么的,有时间慢慢分享吧。

最大的影响还是工作依旧甚至更加繁忙,导致没有太多时间打理博客和服务器。这两年里甚至产生了让双子宕机的两次事故,在这里也跟他们仍在坚守的会员说声抱歉。

而到街机音乐游戏这边,国内的环境也在变好,虽然不是以我想像的样子。科乐美那边不太玩,但是也有新的跳舞机被世宇代理。世嘉这边,舞萌 DX 的铺货数量已经突破 1000 台 ,中二节奏New!!也已经正式铺货。不过南梦宫依旧在进行意味不明的全国场测,一台新框体在全国到处流动。

啊,果然还是想玩太鼓。

虽然街机没什么时间折腾了,但是新的基板还是在买。过去两年,我跑通了 TPM Sniffer 的流程,也解开了 SEGA ALLS 系列的 Bitlocker。Namco 的新太鼓基板 BAN1 也是基于 Windows 10 IoT + Bitlocker 的 PC 架构硬件,之前在日拍上都是天价。不过最近终于蹲到一个价格还可以的,是 機動戦士ガンダム エクストリームバーサス2 的基板。虽然最近北京因为众所周知的原因物流不太正常,但是好像赶上了末班车,总之在国庆假期期间到了。简单拆了一下,分享一下图片。

外观上,比之前的 ES3 ES1 来说,电源做在了机箱里,看起来不像是外挂了。其他的接口之类的倒是跟其他家的 PC Based 基板没什么区别。贴纸贴住的是一组 RTL8111 千兆网卡接口和两个 USB ,以及主板上板载的 HDMI 接口。

加密狗还是万年不变的优盘序列号,可以通过量产工具使用第三方优盘仿制。不过看介绍有使用圣天狗 HL 系列的加密狗进行保护。

先从上面的硬盘口打开看看吧。是双SSD。简单插电脑看了下,是一块系统盘(64GB)和一块游戏硬盘(128GB)组成,全盘都是 Bitlocker 加密的。

两块硬盘和内存和加密狗应该都是整机解决方案内提供的,全部都是 innodisk 和 silicon power 的牌子。看了下通电时间,都是395次 4000小时 左右,看来高达这个游戏一年多就被换掉了。

那么接下来拆开看看吧。

说实话,这个里面真的挺磕碜。都不用跟 Taito TypeX4 比,就连跟 SEGA ALLS 比,都比较寒酸。不过好在还是提供了显卡的固定支架。

电源没有看到铭牌,也没继续往下拆。但是一个 80Plus 的认证标签倒是贴在了最外面,有点怪。

三洋的风扇。

有一个很有趣的细节, BAN1 的 FPanel 上插了一个拓展电路板,用于提供上电自动开机和硬盘指示灯的功能。我试了下,把这个小电路板拔下来就不能自动开机了。好怪。

显卡是 ALLS HX2 Lite 同款的 GTX 1050Ti 。

内存也是 Innodisk 的,两根 4G 2400。

主板提供了 1 条 PCIE x16 、 1 条 PCI 和两条 PCIE x1 接口,还提供了四个独立的 RS232 COM 控制芯片(两个在主板 IO 上,两个作为扩展口,扩展在 PCIE 接口上)。话说主板上的 PCI Localbus 的图标好怀旧啊。

其实拆机的一个目的是找一下有没有独立的 TPM 芯片,但是找了一圈好像并没有发现,只看到了 TPM 的插针。那既然没找到,只能从镜像入手看看了。

总之就拆到这里,后面再分享更多 BAN1 的情报。

在 Linux 上挂载 System357 硬盘

按:4月份的时候这篇文档的西班牙语版就已经出现在国外论坛上,但其实方法早就流传很多年了。 System357 硬盘比起家用 PS3 来说,省略了安装自制系统-导出 eid_root_key 的步骤,使用了同一套密钥组。这次的文章翻译会省略一部分提取 eid_root_key 的步骤。PS3 的系统研究,包括本文,都大量参考了 PSDev Wiki 的相关内容(https://www.psdevwiki.com/ps3/),在这里也一并感谢。

前言

本文主要描述的内容是将 System357/369 的硬盘挂载到 Linux 电脑中。在本翻译中,所有的操作环境是基于 Linux Mint 20.1 x64 的 Cinnamon desktop 标准版进行的。对于中国的用户来说,这个发行版镜像可以从境内镜像站快速获得:

https://mirrors.bfsu.edu.cn/linuxmint-cd/stable/20.1/linuxmint-20.1-cinnamon-64bit.iso

而在本文中,我们使用太鼓达人绿板(太鼓の達人 グリーンVer),S357C-11E,ST4100-1-NA-HDD0-A 作为解密源硬盘。

已知的研究已经对于 PS3内置硬盘(英文) 的加密情况有了大致的了解。每个家用版型号(即除所有街机基板型号)都有独有的一套密钥。在解密后,PS3 的系统有特有的分区表,以及在某些型号上高达10个的分区。其实并不太需要知道所有分区的功能,只需要关注几个特定分区即可。比如说, dev_hdd0 是用户数据、系统分区、配置和 swap 区域,dev_flash2 是在一些有 NOR flash 的型号上保存终端和其登录用户数据的区域。

上图是 PS3 硬盘逻辑分区布局图,可以在下面的链接查看原文
https://www.psdevwiki.com/ps3/Talk:Harddrive

开工前的准备工作

1)首先,安装(我推荐安装,因为大陆众所周知的原因,部署编译环境非常蛋疼)或以 LiveCD 的模式启动 Linux Mint ,在用户(本文默认为 woodu)目录下创建如下目录:

/home/woodu/ps3
/home/woodu/ps3/dev_hdd0
/home/woodu/ps3/dev_hdd1
/home/woodu/ps3/dev_hdd2
/home/woodu/ps3/dev_flash1
/home/woodu/ps3/dev_flash2
/home/woodu/ps3/dev_flash3

2)下载 PS3 HDD Keygen.sh 脚本并解压到 /home/woodu/ps3 执行或从这里手动从hex值生成 ata_key.bin 和 flash_key.bin,这些key 就是解开 ps3 硬盘的加密的密钥。

灵魂的5

3)安装必须的编译工具(apt install build-essential gcc linux内核header 等),并下载 bswap16.ko 的压缩包(此压缩包包含了针对 4.15.0-54-generic kernel in Linux Mint 19.2 的编译成品,在本文中将重新编译适合当前内核的版本)。

下载后直接进到 source 目录中,并 make 即可。之后将成品 bswap16.ko 也移动到 ps3 目录下。

获得素材:bswap16.ko

4)完成以上两步后,你的 ps3 目录应该是这样的,除 ata_key.bin 和 flash_key.bin 之外,其他的 key 可以帮你解开其他的分区,根据需要使用即可。

是这样的

开工

接下来需要在 Terminal(终端)里执行一系列指令,以正确的将硬盘挂载为可读写的分区。

为了防止将原版硬盘破坏,你可以使用 dd 命令或在 windows 下为硬盘制作整盘镜像,持有整盘镜像的你可以将它挂载为虚拟设备,比如使用下面的指令把你放在 /path/to/your.img 的文件挂载到 /dev/loop1 :

losetup loop1 /path/to/your.img

相反,如果你胆子大(像我一样买了好几台357c),可以尝试直接用移动硬盘盒或直接连接 SATA 到设备上,此时,使用的设备盘符就是 /dev/sdx (取决于你的实际情况,你需要使用一些系统指令确定 x 在你电脑上的真实值)。

1)打开 Terminal 并 sudo su 进入 root 下,因为设备操作一直需要 root 权限。

2)安装在准备工作时编译的 bswap16.ko ,它承担了将 Big Endian 到 Little Endian 的转换工作。

insmod /home/woodu/ps3/bswap16.ko

3)将镜像/物理设备进行挂载。

使用镜像:

cryptsetup create -c bswap16-ecb -d /dev/zero ps3hdd-bs /dev/loop1

使用物理磁盘:

cryptsetup create -c bswap16-ecb -d /dev/zero ps3hdd-bs /dev/sdx

4)使用准备工作获得的 ata_key.bin 和 357 使用的对应算法进行解密挂载:

cryptsetup create -c aes-xts-plain64 -d /home/woodu/ps3/ata_key.bin -s 256 ps3hdd /dev/mapper/ps3hdd-bs

并识别它的分区:

kpartx -a /dev/mapper/ps3hdd

5)稍事休息,确认磁盘已经正确解密:

ls -la /dev/mapper/

如果正确的话,应该可以看到 ps3hdd ps3hdd1 ps3hdd2 ps3hdd3 等一系列分区,分别指向 /dev/dm-* 。

其中 ps3hdd1 是 VFLASH ,ps3hdd2 是 dev_hdd0 ,ps3hdd3 是 dev_hdd1 。

6)继续挂载 VFLASH 吧。 VFLASH 是在第一层加密后又有第二层加密的双层结构,所以要把刚刚解密的指令再来一次。先挂载 VFLASH 容器:

cryptsetup create -c aes-xts-plain64 -d /home/woodu/ps3/vflash_key.bin -s 256 -p 8 ps3vflash /dev/mapper/ps3hdd1

并分区:

kpartx -a /dev/mapper/ps3vflash

7)上面的这一切都做完以后,应该就可以映射分区了。

首先,挂载主分区(如果需要写入的话,你需要安装可写 ufs2 的内核模块,并把指令里面的 ro 替换为 rw):

mount -t ufs -o ufstype=ufs2,ro /dev/mapper/ps3hdd2 /home/woodu/ps3/dev_hdd0

接下来是一些 FAT12 FAT16 FAT32 的分区,这些分区系统会自动识别,但自动挂载的分区可能会有问题,因此还是建议通过命令行手动挂载:

mount -t vfat /dev/mapper/ps3hdd3 /home/woodu/ps3/dev_hdd1
mount -t vfat /dev/mapper/ps3vflash2 /home/woodu/ps3/dev_flash1
mount -t vfat /dev/mapper/ps3vflash3 /home/woodu/ps3/dev_flash2
mount -t vfat /dev/mapper/ps3vflash4 /home/woodu/ps3/dev_flash3

有一些尺寸上的注意:dev_flash1 往往大概 200Mb 左右大,dev_flash2 往往在16Mb 左右, dev_flash3 往往只有约 512Kb 。

8)全部做完之后,就可以在系统里确认整个硬盘的挂载情况了。

lsblk -b /dev/sdx

9)挂载完毕之后,就获得了对 System357 硬盘的完全控制权。但请注意,千万不要修改对于此硬盘的任何权限和所有者的信息。如果要修改数据,请使用 root 用户,但你可以在普通用户的文件管理器内经过授权后浏览文件。

10)请记得,浏览/使用结束后卸载掉分区,尤其是你修改了内容的情况下。

umount -l /home/woodu/ps3/dev_hdd0
umount -l /home/woodu/ps3/dev_hdd1
umount -l /home/woodu/ps3/dev_flash1
umount -l /home/woodu/ps3/dev_flash2
umount -l /home/woodu/ps3/dev_flash3
kpartx -d /dev/mapper/ps3vflash && cryptsetup remove ps3vflash
kpartx -d /dev/mapper/ps3hdd && cryptsetup remove ps3hdd
cryptsetup remove ps3hdd-bs

常见问题(略)

我看了眼,感觉没什么特别的。

主要是如果需要确认是否解密成功(每一个调用 cryptsetup 的地方),需要看下已挂载节点的内容是否大部分填0,填0就问题不大。

致谢

感谢 graf_chokolo 在 PS3 逆向工程中卓有成效的贡献,并提供了 Linux 上的支持,没有他,kpartx 就不能支持 PS3 的分区表。

感谢 3141card 对于 PS3 硬盘的加密算法和读取方式的研究。

感谢 guerrini97 修复了作者原来旧脚本中的问题,并重构了 bswap16 模块,并为 nbd-client 提供能力。

感谢 Decaf Code 重构 bswap16 模块并兼容现有内核。

感谢 einsteinx2 指引解锁了 PS3 硬盘的 8% 空间,并为本文提供灵感。

感谢 Yugonibblit dump了 CECHG01 型号的分区表,并确认了该型号及衍生型号的加解密钥和对应算法。

感谢 mlody95pl 指出了本文的错误并编译了 UFS 模块。

后记

从我获得太鼓旧框开始的 2020 年下半年,真的是太精彩了,尤其是太鼓达人在中国国内的圈子里。现在,虹版在中国的代理也已经有官方暗示了,而官方框体也通过各种形式进到了国内。希望中国的太鼓达人有个美好的明天。

服务器再搬家

昨天刚好是7月13号,距离我把主服务器从 sakura vps 迁出刚好整一年。这一年里我使用的是论坛成员推荐的海星云服务器。虽然存在比较大的丢包情况,但在七牛 cdn 的加成下总体也还算比较平稳。但在上个月,因为备案失效而被迫从中国大陆地区迁出的口袋维基迁移到这台服务器的时候,每个月 1TB 的流量配额就显得特别捉襟见肘了。

VPS 6月流量总图

为此,我还特意把剩余的几个月加钱升级了高配,就为了多买一点流量。临近一年整,靠升配续命终归不是正道,于是服务器再次搬家就成为了一个议题。

这不,好容易有个闲暇时间,赶紧把家搬了。

这次选用的是 Wenjing network 旗下的 HostKVM 。看 IP 分配,感觉跟之前海星在同一个机房,但丢包情况得到了不少改善。按照老规矩,观察几天,看看情况。

太鼓达人(旧框体)电源详图

前文详情:https://woodu.me/waiwangkandaodetaigu-system256-neibudianlubantu/

时隔接近两年,我也算是终于把很久之前看的“ System256 只需要一个 ATX 电源即可启动”这句话给研究了一下。

这篇文章主要是展开介绍了一下太鼓达人(旧框体,以 11 亚洲版为例)的电源供应模块,包含详情图和说明书。

首先是详情图,从上一篇文章可以看到,太鼓旧框体的 System 256 主机下面是 JVS IO 和电源供应板,电源供应板有两枚。这两枚电路板一枚负责 +5V 供电,另一路负责 +12V 供电。

有一点不吐不快的是,我之前因为思维惯性,在电路板的 PCB 上找了很久的型号,但是今晚翻到说明书发现它的铭牌是在电容上。真是令人感到很尴尬。

但这个电源转换版真的是日本非常常见的型号,由 TDK-Lambda 提供。具体来说的话,是之前的 DENSEI-LAMBDA 制作的。

这个系列的电源板,输入电压是100v,所以中国版的太鼓达人也是需要内置 220v~ 转 110v~ 的变压器的。而型号命名规则,是 VS{功率}B-{输出电压},具体的定义和系列是下面的图,点击这里可以下载源 PDF文档(日本网络可能需要科学上网)

话不多说,上图吧。首先第一块是 +12V 输出板,型号是 VS75B-12 。

然后第二块是 +5V 输出板,型号是 VS50B-5 。

有点糊了……太晚了就这样吧)

以及最后,我尝试废物利用改造了 蜗牛星际 拆下来的 120w 电源(毕竟50w+75w=125w),来当作比较简单的256启动电源。

放一个 14 的启动视频好啦。大家晚安。

近况和Natsu.APP

自打 WordPress 升级编辑器为 Gutenberg 之后,我就一直懒着没有写新的文章。也加上最近确实比较忙。

先从最近的开始说吧。海底光缆好像又出事儿了。博客和论坛所在的这台东京服务器闪断的问题一直没有特别好的缓解,感觉明年或多或少肯定还要再折腾一下。

稍远一点,沙特在美帝又一次准备收拾我国的时候干了一件刷新大家下限的事情。无论当事人多不是人,这种玩法是违反所有人的共识的。当然,前提是大家的共识是同一件事。如果有人对这个有异议,那就可以不用聊了。

再稍远一点,大狗老师因为我的《番禺游记》通过邮件联系上了我。聊了很多,感谢大狗老师为我们这些普通的不能再普通的玩家留下了记录。更多的不能再说了,等待更多的新情报吧。

更远一点,是中秋节。潍坊的舞萌maimai 机台坏掉了。具体来说的话是机器里面的两台 LG 液晶显示器坏掉了——背光挂了。潍坊的这台机器是最早的一批精文世嘉机器,屏幕的型号是 LC420EUN 。如果在阅读文章的您也需要维修的话,欢迎您给我发邮件。我会尽我的可能来帮助您。

我只买了灯条,剩下的还是皇家的机修操作的。

再远一点,在公司内部遇到了不少感兴趣的同好,大家约定了在上周末进行了一次聚餐。喜欢大家。

(这个是不可能有图的www)

闲白的最后,是已经咕咕咕掉的8月本来要写的东西。潍坊的太鼓达人14街机坏掉了。

潍坊的 太鼓达人14
潍坊的太鼓达人14机器

这台机器是我用一些资源交换的手段换来的,因为是复制版所以质量就不是很好。委托在潍坊的朋友发了个快递过来。顺便看了一下 SYSTEM256 的复制。

想起来三年前跟老外聊天,老外表示2010年的时候就已经完全解密了 System256 。

买了适宜的编程器和储存芯片,却被 Block 在了自己不会拆装储存芯片。只有这种时候才有点后悔自己没有学相关的专业。

硬盘的复制倒是比较轻松,直接在命令行下用 dd 即可。不过失败的尝试是使用 TF-IDE 和 SATA-IDE 都失败了,看上去很难给 System256 使用新一点的储存设备了。

闲白完了,现在可以聊聊 natsu.app 了。我是个小城孩子,潍坊城不大,但是好在市面比较平和。所以虽然不像从小在农村长大的孩子那样有非常丰富的上山下水的经历,但是赶上了小城开发的我也在各种保留了7、80年代风貌的改造场地获得了自己的乐趣。

山东毕竟还算是北方(虽然近几年我才知道山东是算华东的),所以冬天蛮冷的一般也不怎么出去玩,最多就是在大院里玩玩雪。所以更多有意思的回忆还是夏天。最多最多的印象是一台金凤牌的电扇。从小用到大,好像现在还是可以继续使用的。

找到了类似型号的同牌子电扇说明书,但是我家那个稍微高级一点,可以上下摇摆,框子周围还有一圈彩色的灯。

但是,更多的还是怀念那种无所事事的夏天。不过这张想象中的图跟小时候的真实场景倒是没什么关系就是了。

更多的原因还是因为前段时间 Google 开放 .app 后缀注册,我顺手买到了 natsu.app 。虽然作为一个喜欢水君的家伙应该爱屋及乌地喜欢冬天,但是冬天更多的是带着人情味儿的回忆。夏天则是真的是没心没肺的记忆。于是就拜托 @FeiyaZ 鸭子老师帮我画了一下这张图。

构图是我想的,不过动笔完全是鸭子老师的。稍微拆了一下图层用 svg.js 做了一下动画,也算是某种程度上的纪念了吧。

如果以后开家店,就以这个为基础起名吧。毕竟最早想到的 hanabi (烟花)的名字,总有一种转瞬即逝的短命感,哈哈。

替换 Discuz! 6 的默认验证码

PTB 今年已经是第 11 年了,代码也是 10 年前的代码了。
这次又遇到什么问题了呢?就是我们的验证码机制被攻破了。
而且其实挺遗憾的,因为 PTB 最初的安全设计是严进宽出,即你注册需要有相当繁琐的步骤和漫长的验证期,之后几乎没有什么限制。

然后很尴尬,我们被撞库了。

就是前一分钟我们还在微信群里面谈笑风生一分钟之后刷出来发现对面那个人的 ID 在论坛灌了 1000 多个台湾找小姐这样。

然后我就按照之前双子的思路打开了验证码。

但是很尴尬, Discuz 6 有一个很严重的逻辑漏洞。这其实也不是逻辑漏洞,是当年大家对 ajax 的理解都不到位而已。

按照惯例,我们来读代码。

DISCUZ_ROOT./ajax.php:64


...
} elseif($action == 'checkseccode') {

	if($seclevel) {
		$tmp = $seccode;
	} else {
		$key = $seccodedata['type'] != 3 ? '' : $_DCACHE['settings']['authkey'].date('Ymd');
		list($tmp, $expiration, $seccodeuid) = explode("\t", authcode($_DCOOKIE['secc'], 'DECODE', $key));
		if($seccodeuid != $discuz_uid || $timestamp - $expiration > 600) {
			showmessage('submit_seccode_invalid');
		}
	}
	seccodeconvert($tmp);
	strtoupper($seccodeverify) != $tmp && showmessage('submit_seccode_invalid');
...

以及

DISCUZ_ROOT./include/global.func.php:888


function submitcheck($var, $allowget = 0, $seccodecheck = 0, $secqaacheck = 0) {
	if(empty($GLOBALS[$var])) {
		return FALSE;
	} else {
		global $_SERVER, $seclevel, $seccode, $seccodedata, $seccodeverify, $secanswer, $_DCACHE, $_DCOOKIE, $timestamp, $discuz_uid;
		if($allowget || ($_SERVER['REQUEST_METHOD'] == 'POST' && $GLOBALS['formhash'] == formhash() && (empty($_SERVER['HTTP_REFERER']) ||
			preg_replace("/https?:\/\/([^\:\/]+).*/i", "\\1", $_SERVER['HTTP_REFERER']) == preg_replace("/([^\:]+).*/", "\\1", $_SERVER['HTTP_HOST'])))) {
        		if($seccodecheck) {
        			if(!$seclevel) {
        				$key = $seccodedata['type'] != 3 ? '' : $_DCACHE['settings']['authkey'].date('Ymd');
        				list($seccode, $expiration, $seccodeuid) = explode("\t", authcode($_DCOOKIE['secc'], 'DECODE', $key));
        				if($seccodeuid != $discuz_uid || $timestamp - $expiration > 600) {
        					showmessage('submit_seccode_invalid');
        				}
        				dsetcookie('secc', '', -86400 * 365);
        			} else {
        				$tmp = substr($seccode, 0, 1);
        			}
        			seccodeconvert($seccode);
        			if(strtoupper($seccodeverify) != $seccode) {
        				showmessage('submit_seccode_invalid');
        			}
				$seclevel && $seccode = random(6, 1) + $tmp * 1000000;
        		}
			if($secqaacheck) {
        			if(!$seclevel) {
        				list($seccode, $expiration, $seccodeuid) = explode("\t", authcode($_DCOOKIE['secq'], 'DECODE'));
        				if($seccodeuid != $discuz_uid || $timestamp - $expiration > 600) {
        					showmessage('submit_secqaa_invalid');
        				}
        				dsetcookie('secq', '', -86400 * 365);
        			}
        			require_once DISCUZ_ROOT.'./forumdata/cache/cache_secqaa.php';
        			if(md5($secanswer) != $_DCACHE['secqaa'][substr($seccode, 0, 1)]['answer']) {
        			        showmessage('submit_secqaa_invalid');
        			}
				$seclevel && $seccode = random(1, 1) * 1000000 + substr($seccode, -6);
        		}
			return TRUE;
		} else {
			showmessage('submit_invalid');
		}
	}
}

不难看出,其实对于验证码的过期,后台的判断只是基于时间(600秒)和是否是属于这个用户。
这其实是有很大的问题的,因为没有重试次数限制,对于不复杂的非中文验证码,一秒钟发出几千个请求cover掉所有数字和大部分字母组合是很轻松的事情。
因此干脆就把它从代码里面摘掉。

主要修改的是下面几个文件。因为现在就是这么做的,因此具体的细节就不展开说了。

include/javascript/post_editor.js
摘掉里面对原有验证码的前端校验

template/seccheck.htm
摘掉原有验证码的展示代码,同位置增加极验的前端代码

ajax.php
屏蔽掉前端校验接口
global.func.php
common.inc.php
摘掉原有验证码后端校验的内容部分,保留 Cookies 校验部分
增加极验后端校验代码

总结一下,其实 discuz 6 在代码设计上真的是完美无缺的。这么老的代码改起来也没有什么不适感(最起码比在公司里吃的那些 s**t 要好得多太多了)。基本就是搜“submit_seccode_invalid”就可以解决这次的问题。
总之双站都是靠极验老版本去 cover 机器人了,毕竟防御价值没那么高,就不去针对 geetest v3 做适配升级了。
屏蔽掉机器人就已经解决绝大部分问题了。