当写代码的边际成本归零,我让 AI 复刻了那台 AWTRIX 闭源服务器
我桌上那块 32×8 的 LED 矩阵屏,叫 AWTRIX2,德国玩家 Blueforcer 的 DIY 项目,定位是 LaMetric Time 的平价替代。后者官方卖 $199,国内代购往往要一千多块。这项目硬件开源、社区也活跃,2020 年前后国内 B 站一搜一堆"网红像素时钟"教程,两三百块就能 DIY 一台,最流行的玩法是拿来显 B 站粉丝数。但它有一处一直被忍着:驱动它的"服务器"是闭源的,一个 B4J 打包的 Java 程序。作者后来重写了 AWTRIX3,纯固件全开源,直接去掉了那个闭源 server。可那是给新硬件(Ulanzi TC001)准备的,我这块老主板用不上,官方也早停更了。
以前遇到这种闭源黑箱,逆向重写这种事想都懒得想,光是啃字节码、对协议就是几天枯燥脏活,整体速度永远卡在最慢的环节上,之前聊阿姆达尔定律也讲过这个。但现在这个节点不一样了:AI 让写代码的边际成本接近于零。这意味着那些"理论可行、就是懒得动"的事,现在都值得做。试错几乎不花成本,大方向不对推倒重来也亏不了多少。
下面以这块屏为例,走一遍。重点不是"我做出了什么",是这种事现在怎么搞:一套能迁移到任何闭源系统的打法。
一、别碰黑箱,先让 AI 啃 GitHub 上仅有的源码
闭源的只是 server。但一个项目的周边:控制器、客户端、社区集成、文档,这些都是开源的。AWTRIX2 的 GitHub 上就有 Controller 仓库,HomeAssistant、Node-RED 的集成代码(社区自己写的,量还不少),B4X 论坛里散落的片段。
第一件事不是反编译,是把能找到的源码全丢给 AI,让它拼出系统全貌。AI 读代码比人快几个数量级:半小时不到,它就替我把碎片缝成了一张完整的逻辑架构:ESP8266 主板驱动 32×8 WS2812B 矩阵,client-server 架构,server 是 Java(B4J),通信走 MQTT,端口 7001。
这步几乎零成本,收益却极大。还没碰那个闭源 jar,就已经知道:这是一套标准 MQTT 系统,不是什么私有黑魔法。后面所有判断都踩在这张地图上。
二、study 架构,找到那个"撬动点"
有了架构图,下一个判断是:不用全懂,要找到撬动整个系统的那个点。
AI 把数据流画出来后,撬动点一眼可见:MQTT 是通信骨干。server 对设备做的所有事,说白了都是往 broker 发二进制报文。所以,不用破设备固件,不用重写 server 的全部业务,只要搞懂"MQTT 上跑的报文长什么样",就能伪装成 server,直接指挥设备。
这是这套打法的关键:边际成本低,所以要找最短路径、快速验证,而不是追求全面理解。切入点找对,后面顺水推舟;找错,推倒重来也亏不了几个 token。
三、为什么先找切入点,再反编译
有人会问:既然最后还是要反编译,为什么不一开始就对着 jar 猛啃?

因为固件是 C 编译的二进制,破起来比 Java 难几个数量级;而 server 是 Java,能还原成源码。更关键的是,直接对着整个 jar 瞎翻,大概率迷失在无关代码里。切入点决定了你去反编译什么、看哪个文件。
MQTT 这个点确定后,反编译才有的放矢。开源工具 cfr 一行命令:
java -jar cfr.jar awtrix_business.jar --outputdir decompiled
几十个 .java 落盘,直接读会迷路。让 AI 锁定通信核心 matrix.java,逐行抠它往 broker 发的报文格式,结论干净得出奇:
[0] | x坐标(2字节) | y坐标(2字节) | R G B | 文本
第一个字节 0 是命令码(画字),坐标大端序整数,后接颜色和 UTF-8 文本。就这么多。一个看似神秘的黑箱,被还原成十几条人话能懂的命令:0 画字、1 贴图、8 刷新、9 清屏……放在五年前,这是资深逆向工程师几天的工作。
四、反编译不只抠协议,还要看 app 怎么实现
很多人逆向到协议就停了。但 server 里还有一座金矿:官方 app 和轮播引擎的实现。
让 AI 继续读 main.java,重点看两段:tickTimer_Tick(每帧怎么画)和 ChangeApp(怎么切 app)。结论可以直接当复刻的模板:
• 每帧流程固定:清屏 → 遍历当前 app 的绘制命令 → 刷新上屏。
• 轮播靠两个计时器:一个按 AppDuration 切 app,一个按 ScrollSpeed 刷新画面。
照搬这套模型,用 Python(FastAPI)重写,核心就是一个轮播引擎加一个 app 框架:
@register
class Hello(App):
name = "Hello"
def tick(self, n, ctx): # 返回本帧要画的命令
return [{"type":"clear"},
{"type":"text","x":0,"y":1,"text":"Hi"},
{"type":"show"}]
写个类实现 tick(),加 @register 就进轮播。逆向到这里,等于把官方"怎么组织这套系统"也学过来了,不只是协议。点亮屏幕只用了 20 行,三步模仿每帧:清屏、画字、刷新。
五、让像素活起来,用数据调手感
文字 app 写腻了,开始在这块 32×8 的屏上跑游戏。设备字体只有 ASCII,画任意图形得另走一条路:Pillow 整帧渲染成 RGB565 位图,用贴图命令整块下发。
迷你超级马里奥,6×6 像素的小马力欧,两帧跑步动画,前方滚动关卡:
_FRAMES = [
[".RRRR.", "RRRRRR", "SSSHH.", "SSBBBS", ".BBRB.", "HH..HH"],
[".RRRR.", "RRRRRR", "SSSHH.", "SSBBBS", ".BBBB.", "HH.H.."],
]
更有意思的是让两个 AI 在屏上打乒乓球。"像人"是个模糊需求,AI 帮着翻译成具体参数:板有个反应概率(左 80%、右 72%),追不上就漏球;碰板边缘加速;左右板独立反应自然不同步;输的人开球,中间停 2 秒模拟真实规则。
调参数时,每改一版就离线跑 3000 帧统计:反弹数、失误数、加速帧占比、两板同步率。用数据调手感,而不是凭感觉。这套"AI 把模糊翻译成参数 + 数据验证"的循环,是现在做硬件、游戏这类项目最舒服的形态。
六、那个卡死的画面
部署十几秒后画面卡住,马力欧定在半空。第一反应不是怀疑硬件,是翻日志,一行字在疯狂刷:
[engine] tick error: image index out of range
根因在引擎设计:画一帧的流程是"画图 → 发送 → 帧号+1",全在同一个异常捕获里。画图抛异常,后两步被跳过,帧号不自增,下一帧还是同一个帧号、同一个异常,死循环。
异常源头是 PIL 的 putpixel 越界报错。修复是给画点加范围守卫:
def safeput(img, x, y, color):
if 0 <= x <= img.width-1 and 0 <= y <= img.height-1:
img.putpixel((x, y), color)
在实时渲染里,"卡住不动"九成是某个异常被吞掉了。先翻日志找 error,别怀疑硬件。这条经验,也是 AI 从日志一行字定位出来的。
七、这套 vibe 可以迁移
回头看,整个过程抛开 AWTRIX 的具体细节,是一套通用打法,拢共五条:
别碰黑箱,先让 AI 啃周边仅有的源码,建立全局认知;
画架构图,找撬动点(这套里是 MQTT),追求最短路径而非全面理解;
切入点确定后再反编译,抠协议细节,有的放矢;
反编译多看一眼 app 实现,把官方的组织方式也学过来;
AI 把模糊需求翻译成参数,数据验证手感。
每一步都是 AI 在接管脏活:读代码、翻字节码、查 API、定位 bug,曾经卡人的环节现在几乎零成本。剩下的,就是你想做什么。这正是 之前聊 vibe coding 的判断:AI 接管 How 之后,人的护城河只剩 What 和 Why。
以前逆向一个商业硬件,是少数极客几周的特权;现在,一个愿意提问、愿意动手的人,一个下午就能复刻一套。门槛从来不在技术,在"值不值得动手",而 AI 把这个"值得"的阈值,压到了接近零。
#AWTRIX #AI编程 #逆向工程
作者声明本文无利益相关,欢迎值友理性交流,和谐讨论~
