网卡MAC地址硬刷实战:NVM写入、校验和与批量回滚
简介面向需要在网卡层面修改物理地址的用户这套工具包围绕 MAC 地址硬刷展开适用于黑群晖洗白、虚拟机网卡标识统一、硬件故障后地址修复等场景涉及刷写工具调用、参数配置与芯片适配更适合具备网络与硬件基础的中高级用户动手实践。压缩包共收录 197 个文件约 4.52MB以 bin、c、mac、cfg、exe、txt、bat 为主exe 与 bat 承担刷写执行与批处理调用cfg、ini 存放参数c、h 源码与 bin、eep 固件数据对应不同芯片方案的适配逻辑另配 docx 说明与 txt 记录操作要点。已有 2552 人学习下载。读者可获取覆盖多种网卡芯片型号的硬刷方案、可复用的执行脚本与配置模板以及流程与风险提示文档便于动手前理清原理、判断网卡是否在支持范围内并对照资料完成地址读取、写入与验证的链路。1. MAC 地址硬刷改注册表不算本事写进 NVM 才算数某实验室处理一批二手网卡装机后系统里 MAC 全是出厂默认或者干脆重复用ip link set改完当场能用重启或者modprobe -r再加载立刻打回原形。很多人以为是自己命令写错了其实是改的地方不对——改的是内核里那张内存表掉电就没了。MAC 地址硬刷要动的是网卡上的非易失存储NVM/EEPROM/efuse把 6 个字节真正落到芯片里这样不管换系统、换驱动、还是把卡插到另一台机器上读出来的都是你写进去的那个值。这套工具解决的就是“软件层改了不生效、不持久”的问题适合做设备批次管理、实验室网络隔离测试、老网卡回收复用的从业者。它的边界也很清楚不是所有网卡都能写写了不备份基本等于赌运气。2. 三层改动分清楚软改、NVM 写入与厂商工具各自的边界拿到一个 MAC 地址硬刷工具第一件事不是急着插卡开刷而是先搞清楚它到底动的是哪一层。市面上挂着“MAC 修改”名头的工具实际落点差得很远有的只是帮你改注册表有的是往网卡的 MAC 寄存器里写一个值还有的才是真正读写 NVM。三者的持久性、风险、可回滚性完全不在一个量级混着用就是各种玄学问题的源头。2.1 操作系统层的软改只动内存里的过滤表Linux 下最常见的ip link set dev eth0 address 02:11:22:33:44:55走的路径是内核网络子系统调用驱动的ndo_set_mac_address回调。这个回调在多数驱动里做两件事把 6 个字节拷进net_device-dev_addr再写进网卡芯片的接收过滤寄存器。前者在内存里后者在寄存器里两者都经不起一次完整的断电。Windows 那边改注册表NetworkAddress字符串值再禁用启用网卡本质也一样驱动初始化时把字符串解析成字节数组写进寄存器。这类改动为什么“看起来成功了”因为寄存器确实变了收包过滤立即按新地址工作。但驱动 probe 的时候有一个固定动作从 NVM 里把出厂 MAC 读出来覆盖掉驱动结构体里的默认值。所以你重启、重载驱动、甚至只是让网卡进了一次 D3 电源状态再回来值就被刷回去了。这不是 bug是设计如此。软改的价值在于快和零风险适合临时联调、容器网络命名空间里造多个虚拟接口。但它永远解决不了“这台机器换系统后 MAC 变了导致授权失效”这种问题因为授权校验读的往往就是驱动从 NVM 里拿到的那个值。判断你手上的工具属于哪一层最简单的办法改完之后把机器电源线拔掉等 30 秒再上电如果值回滚了那就是软改。2.2 NVM 与 efuse 才是硬刷的真实落点NVM 是一块挂在网卡控制器上的非易失存储常见形态有三种SPI NOR flash容量几百 KB 到几 MB除了 MAC 还放 PXE Option ROM、温度校准、厂商配置、串行 EEPROM老千兆卡上常见的 93 系列小容量器件128 到 512 字节、以及 efuse一次性可编程只能把 0 烧成 1烧错无法恢复。这三种的写入方式和风险完全不同工具在动手前必须能识别出来否则就是拿卡当试验品。镜像布局也不是随便放。以常见的一款 PCIe 千兆控制器为例NVM 前 0x00 到 0x0F 通常是标识和版本MAC 地址区可能在 0x00、0x100 或更靠后的位置尾部或者固定偏移处有一个 16 位校验和字段。控制器上电后会做一次自检把整块镜像按 16 位字累加结果必须等于一个约定的魔数有的要求 0xFFFF有的要求 0xBABA具体看驱动源码里的宏定义。校验不过控制器就把自己标成“配置无效”驱动 probe 阶段直接报错退出网卡在系统里就消失了。还有一个容易翻车的细节MAC 地址在 NVM 里的字节序不一定是从低到高。有些控制器按55:44:33:22:11:02这种逆序存放你照着界面上的顺序写进去读出来就是反的。我一般会先读出原始镜像在二进制里搜一下当前 MAC 的正序和逆序两种字节串哪个能搜到就按哪个来这一步比查文档快得多。2.3 工具选型开源 ethtool 路线与厂商 DOS/EFI 路线怎么选真正能干硬刷活的工具不多大致分四条路各有各的适用面和坑。路线典型手段覆盖范围主要限制内核驱动接口ethtool -E写、ethtool -e读驱动实现了set_eeprom的型号新版本驱动大量移除了写回调直接返回不支持厂商 DOS/EFI 工具做成启动盘在纯 DOS 或 UEFI Shell 下运行厂商自家芯片覆盖最全需要单独准备启动介质工具不跨厂商通用直接操作 SPIflashrom一类工具读写 SPI flashflash 未被控制器隐藏的板型多数网卡的 flash 被控制器接管外部访问不到自写脚本 驱动调试接口通过ethtool -e/-E或 debugfs 分块读写需要自己算偏移和校验偏移和算法要自己摸错一个字节就砖选型逻辑其实很简单先确认驱动还认不认写操作认就用ethtool -E一条命令的事不认就退到厂商的离线工具做启动盘进 DOS如果连厂商工具都没有那就只剩 SPI 直读这一条路但这条路要求你能物理接触到 flash 芯片且控制器没有把它锁住。我一般会按“风险从低到高”排先在一张同型号的报废卡上把全流程跑通确认偏移、字节序、校验算法三件事都对再上真卡。批量场景下这一步尤其不能省因为不同批次的卡可能用的是不同的 NVM 版本布局有细微差别。提示efuse 类型的存储没有“再写一次”的机会动手前务必确认型号。用ethtool -i看到的驱动名和固件版本只能作为线索最终判定要看控制器手册或者拆开看丝印。3. 动手之前把网卡型号、NVM 布局和校验算法摸清楚硬刷这件事八成的失败发生在按下写入命令之前。真正花时间的不是写是确认这块卡能不能写、镜像多大、MAC 在哪个偏移、校验怎么算。这一章的四步做完后面写的时候心里才有底。3.1 用 lspci 和 ethtool 定位控制器与驱动先确定你面对的是哪颗控制器、绑的哪个驱动。这两条信息决定了后面所有的工具选择。# 列出所有以太网控制器-nn 显示厂商和设备 ID lspci -nn | grep -i ethernet # 假设网卡在 03:00.0看它绑的驱动、固件版本和 PCI 地址 ethtool -i eth0 # 读一段 PCI 配置空间确认 BAR 映射和是否被隐藏 lspci -s 03:00.0 -xxx | head -20lspci -nn输出的方括号里是厂商ID:设备ID这个组合是查手册的唯一钥匙。ethtool -i给出的driver字段告诉你内核用的是哪个模块firmware-version有时候能反推出 NVM 的版本号——如果同型号卡之间这个值不一样说明它们 NVM 布局可能不同不能套用同一份偏移表。lspci -xxx打印的是配置空间前 256 字节重点看 BAR0 的基地址和 Command 寄存器里的 Memory Space 使能位。这一步主要是排查“卡在但驱动不认”的情况如果设备 ID 读出来是ffff说明卡根本没被枚举到问题在硬件不在 NVM。3.2 读出完整 NVM 镜像并确认长度读之前先把接口 down 掉避免读的过程中有寄存器访问冲突。# 关闭接口防止读取时被上层干扰 ip link set dev eth0 down # 读 512 字节原始数据到文件raw on 表示输出二进制而不是十六进制文本 ethtool -e eth0 raw on offset 0 length 512 nvm_orig.bin # 确认文件大小正常应该是 512 的整数倍 ls -l nvm_orig.bin # 看一眼头部判断是不是全 0xFF空片或者全 0x00读失败 xxd -l 64 nvm_orig.binoffset和length两个参数要看驱动的实现。有的驱动只允许按 4 字节对齐读有的限制单次最多读 128 字节还有的只暴露前 256 字节。如果一次读不满就分段读再拼接别指望一条命令拿到全镜像。读出来的文件如果全是0xFF两种可能这块卡用的是 efuse 而不是 EEPROM驱动读不到或者驱动本身没实现读回调返回的是缓冲区初始值。用xxd看头部有没有合理的版本号、配置字基本就能区分。3.3 定位 MAC 偏移与校验和字段这一步是整个流程里最需要耐心的部分。三种定位手段从快到慢依次试。第一种已知当前 MAC 的情况下直接搜字节串。当前 MAC 用ip link show eth0看注意要区分正序和逆序。# 正序搜索比如当前 MAC 是 00:1b:21:aa:bb:cc xxd -p nvm_orig.bin | tr -d \n | grep -bo 001b21aabbcc # 逆序搜索 xxd -p nvm_orig.bin | tr -d \n | grep -bo ccbbaa211b00grep -bo输出的数字是十六进制串里的字符偏移除以 2 才是字节偏移。命中位置一般就是 MAC 字段的起始地址。如果两种顺序都搜不到说明这块卡的 MAC 不在 NVM 里可能是从控制器内部的一次性存储读的硬刷这条路走不通。第二种找校验和字段。校验和通常在镜像的固定位置比如最后两个字节或者紧跟在版本号之后。把镜像按 16 位字全加一遍看看当前总和与常见魔数的差值差值就是校验和字段该有的值再反查这个值在镜像里的位置。这个办法对不上就说明校验算法不是简单累加得去翻驱动源码。第三种翻驱动源码。drivers/net/ethernet/下面找对应驱动搜EEPROM、NVM、mac_addr这些关键词偏移宏和校验函数通常写得很直白。这是最靠谱的办法缺点是慢。注意定位到偏移后先别写用十六进制编辑器手工改一份镜像出来跟原镜像做cmp -l对比确认只有预期的那几个字节变了。4. 从 dump 到回写一次完整的硬刷流程拆解前面把地形摸清了这一章就是照着走。整个流程分备份、改字节、回写、冷启动验证四段每一段都有对应的检查点任何一段没过就不要往下走。4.1 备份把原厂镜像和校验值一起落盘备份不是简单 cp 一份要把元信息一起存下来否则出问题的时候你连“原来长什么样”都说不清。#!/usr/bin/env bash # 备份脚本把网卡信息、原始 NVM 镜像、校验值一起归档 set -euo pipefail DEV${1:?用法: nvm_backup.sh 网卡名如 eth0} STAMP$(date %Y%m%d_%H%M%S) PREFIXnvm_${DEV}_${STAMP} # 1. 保存驱动与设备信息出问题时用来比对 ethtool -i $DEV ${PREFIX}.info.txt lspci -nn | grep -i ethernet ${PREFIX}.info.txt # 2. 关接口后读原始镜像 ip link set dev $DEV down ethtool -e $DEV raw on offset 0 length 512 ${PREFIX}.bin # 3. 记录哈希回滚后要用它验证是否完全恢复 sha256sum ${PREFIX}.bin | tee ${PREFIX}.sha256 echo 备份完成: ${PREFIX}.bin脚本里的set -euo pipefail是关键任何一步失败立即中止避免在接口没关的情况下继续读。ethtool -i的输出一定要存因为驱动版本会影响-E的行为出问题时这是唯一能回溯的线索。备份文件至少存两份一份在本机一份在别的介质上。血泪经验是刷坏了之后你想从同一块卡上再读一次原镜像已经读不出来了。4.2 改字节生成新镜像并重算校验和改镜像这一步核心是“改完必须重算校验和”漏了这一步写进去的卡大概率直接不认。import struct import sys # 这两个常量必须按你的控制器实际情况改 MAC_OFFSET 0x100 # MAC 字段在镜像中的字节偏移 CSUM_OFFSET 0x1FE # 校验和字段偏移通常在镜像末尾 CSUM_TARGET 0xBABA # 校验魔数常见取值 0xFFFF 或 0xBABA def set_mac(buf: bytearray, offset: int, mac: str, reverse: bool False) - None: 把 MAC 字符串写进镜像reverseTrue 表示按逆序存放 parts [int(x, 16) for x in mac.split(:)] if len(parts) ! 6 or any(p 0 or p 255 for p in parts): raise ValueError(fMAC 格式不合法: {mac}) raw bytes(parts) buf[offset:offset 6] raw[::-1] if reverse else raw def fix_checksum(buf: bytearray, csum_off: int, target: int) - int: 先清零校验字段再按小端 16 位字累加回填差值 if len(buf) % 2 ! 0: raise ValueError(镜像长度必须是偶数) buf[csum_off:csum_off 2] b\x00\x00 total 0 for i in range(0, len(buf), 2): total struct.unpack_from(H, buf, i)[0] csum (target - (total 0xFFFF)) 0xFFFF struct.pack_into(H, buf, csum_off, csum) return csum if __name__ __main__: src, dst, mac sys.argv[1], sys.argv[2], sys.argv[3] with open(src, rb) as f: data bytearray(f.read()) set_mac(data, MAC_OFFSET, mac, reverseFalse) # 字节序按第 3 章的探测结果决定 value fix_checksum(data, CSUM_OFFSET, CSUM_TARGET) with open(dst, wb) as f: f.write(data) print(f新镜像已生成: {dst}, 校验和写入 0x{value:04X})set_mac里的reverse参数就是前面说的字节序问题第一次用某型号卡时建议两种都生成一份比对哪一份读回来是对的。fix_checksum先把校验字段清零再累加这一步不能省——如果带着旧的校验值累加算出来的新值永远差一截。CSUM_TARGET这个魔数没有通用值。判断办法拿原始镜像跑一遍累加如果结果是 0xFFFF那目标就是 0xFFFF如果你把校验字段清零后累加得到 X而原镜像该位置的值是 Y且(X Y) 0xFFFF等于某个固定值那个固定值就是目标。4.3 回写与冷启动验证写之前再确认一次接口是 down 的写完必须彻底断电不是重启。# 1. 确认接口已关闭 ip link set dev eth0 down # 2. 写入新镜像magic 参数按驱动要求填很多驱动不校验这个值 ethtool -E eth0 magic 0x3f1c offset 0x00 length 512 nvm_new.bin # 3. 直接关机而不是 reboot poweroffethtool -E的三个要素magic是驱动用来校验“你确实知道自己在干什么”的一个值填错了会被拒绝offset是起始偏移length是长度一般跟镜像大小一致。标准输入重定向给-E的写法要求数据是纯二进制不能用十六进制文本。写完直接poweroff是必须的动作。很多控制器的 NVM 只在 PCI probe 阶段读一次reboot走的是热复位部分平台的 3.3V 待机供电不断控制器内部状态没清干净读到的还是旧值。拔电源线等 30 秒或者关机后按一下电源键放电这一步别偷懒。上电后按顺序验证三件事ip link show看接口在不在、ethtool -e重新 dump 一遍看字节是不是新的、ping一下同网段设备确认收发正常。三件事都过这次硬刷才算完成。5. 硬刷翻车现场五类症状的排查路径硬刷这事儿成功的过程千篇一律失败的姿势五花八门。下面这五类是我见过频率最高的每一类都按“现象 → 原因 → 解决”拆开遇到对得上的直接照着排。5.1 写不进去返回 Operation not supported现象执行ethtool -E立刻报Cannot set EEPROM data: Operation not supported连写入的机会都没有。原因驱动根本没有实现ndo_set_eeprom回调。近几年的内核版本里出于安全和维护成本考虑大量驱动把写回调删掉了只保留读。你手上的工具再新也没用因为内核这一层就把路堵死了。解决换路线。优先找厂商提供的离线刷写工具做成启动盘在 DOS 或 UEFI Shell 下跑这类工具直接操作 BAR 映射不经过内核。如果厂商没提供退到 SPI 直读方案用flashrom一类工具在物理层读写。临时也可以试老内核或者自己编译带写回调的驱动模块但这只适合实验室环境不适合批量。5.2 写进去了但重启又变回旧 MAC现象写完当场ip link show显示新地址ping也通一断电重启就回到出厂值。原因最常见的是系统层面还有别的东西在覆盖。比如 NetworkManager 的 connection 配置里存了cloned-mac-address或者 udev 规则里有MACADDR设置开机时又写了一遍。另一个可能是写的时候没真正落到 NVM只是写进了控制器的影子寄存器。解决先查覆盖源。grep -rn mac /etc/NetworkManager/和grep -rn MACADDR /etc/udev/rules.d/两条命令扫一遍把相关配置清掉再测。如果配置干净还是回滚说明写入没落地回去确认ethtool -E的返回码是不是 0以及写完之后ethtool -e重新 dump 出来的是不是新值——不是新值就说明写操作被驱动静默忽略了。5.3 网卡从系统里彻底消失现象刷完上电lspci还能看到设备但ip link里没有对应接口dmesg里一堆 probe 失败。原因镜像写坏了校验和对不上控制器把自己标成配置无效或者 MAC 字段被写成了全 0、全 FF、组播地址第一字节最低位为 1驱动初始化时直接拒绝。解决先看dmesg | grep -i -E eth|nvm|eeprom如果报的是校验错误说明校验算法或者偏移搞错了用备份镜像回滚。回滚的办法取决于还有没有写入通道驱动读回调通常还在ethtool -e能读就能配合-E写回去前提是-E还支持。如果-E也不支持了就只能上 SPI 直读工具把备份烧回去。5.4 驱动加载报 Invalid NVM checksum 但卡还在现象接口能起来但速率协商异常或者只有百兆dmesg里有明确的 checksum 告警。原因校验和字段的位置算对了但累加范围错了。有的控制器只对配置区做校验不包含 PXE 区有的要求按大端累加还有的把校验和本身排除在累加范围外。范围差一个字节结果就全错。解决拿原始镜像反推。把原始镜像的校验字段清零后按不同的范围全镜像、前 256 字节、前 0x100 字节分别累加看哪种范围加上原校验值等于魔数。试出正确范围后写进脚本别靠猜。5.5 两台机器 MAC 撞车引发交换机告警现象批量刷完之后交换机日志里刷 MAC flapping两台机器的流量互相顶掉网络时通时断。原因批量脚本里 MAC 是写死的常量或者用了自增但没做唯一性校验。MAC 地址全球唯一靠的是 OUI 分配自己造地址必须保证本地管理位第一字节的 bit1置 1且整个内网内不重复。解决批量场景下把 MAC 生成逻辑改成“取一个固定前缀 设备序列号哈希后 3 字节”并在写入前查一遍内网 ARP 表去重。第一字节用02、06、0A、0E这类本地管理地址开头避免撞上真实厂商的 OUI。刷完先小批量接入观察交换机日志半天再铺开。6. 批量刷写与可回滚验证把后悔药提前备好单卡刷写跑通之后真正的考验在批量。几十上百张卡靠手工一条条敲命令迟早出事但脚本化也不是把单卡命令套个 for 循环就完事。我吃过一次亏批量脚本跑到第 37 张的时候那批卡的 NVM 版本跟前一批不一样偏移差了 0x10结果那 37 张里有一半写成了别的字段校验和倒是算对了卡能起来但 PXE 启动全废。从那以后我每次批量之前都强制走一遍幂等检查。第一件事是把“型号识别”做进脚本。同型号不等于同 NVM 布局ethtool -i的firmware-version字段和lspci -nn的设备 ID 组合起来做一次分桶不同桶用不同的偏移表。#!/usr/bin/env bash # 批量前分桶按 设备ID 固件版本 归类不同类用不同参数 set -euo pipefail for dev in $(ls /sys/class/net | grep -E ^eth); do ids$(lspci -nn -s $(basename $(readlink -f /sys/class/net/$dev/device)) | grep -o \[[0-9a-f]\{4\}:[0-9a-f]\{4\}\]) fw$(ethtool -i $dev | awk /firmware-version/{print $2}) echo ${dev},${ids},${fw} done | sort | uniq -c这段脚本输出的是“每种组合有多少张卡”uniq -c的计数就是分桶结果。数量少的桶要单独拿出来人工确认别图省事一把梭。第二件事是验证关卡。单卡刷完靠肉眼批量必须靠脚本卡三道关关卡检查内容不通过的处置写入前备份镜像的 sha256 是否已落盘、镜像长度是否为预期值中止该卡跳过写入后重新 dump 的内容与目标镜像逐字节cmp是否一致用备份回滚标记该卡冷启动后接口存在、MAC 正确、能 ping 通网关隔离该卡进人工排查队列cmp这一步很多人省觉得ethtool -E返回 0 就万事大吉。实际上驱动返回成功只代表“命令收下了”不代表“字节写进去了”。回读比对是唯一能证明写成功的证据。最后说一个我自己的习惯每刷完一批我会随机抽两张卡用备份镜像原样写回去再读一遍确认跟备份的 sha256 完全一致。这个动作专门验证“回滚通道还活着”。硬刷最怕的不是写错是写错之后没有退路。回滚演练看着多余真出事的时候它就是那根救命绳。希望帮到你。本文还有配套的精品资源点击获取