之前折腾机械革命新版开机动画时,发现一个比较有意思的问题:
部分机械革命官方 BIOS 里面其实已经包含新版开机动画,但正常运行官方 BIOS 更新程序后,ROM Hole 并没有被刷新,所以机器还是显示旧版动画。
后来通过分析 AMI AFU 的 UCP 封装,发现可以在完全不修改官方 BIOS @ROM 内容的情况下,只修改更新程序携带的刷写参数,让 AFU 单独执行:
/L
也就是:
Program all ROM Holes
这样就可以生成对应的:
ROMHOLES_DIRECT.EXE
本文记录一下具体怎么手动制作。
本文以
XxRPxxxxN135MRO58_CAP.EXE为例。不同 BIOS 的 Offset、模块大小、原始命令可能不同,不能机械照抄本文中的十六进制地址。
真正通用的是 UCP 的结构和修改思路。
先说原理
机械革命这类 AMI Aptio BIOS 更新 EXE,本质上可以看成:
Windows PE 程序
+
AMI UCP 数据
UCP 部分通常类似:
@UAF
├── @UII
├── @W64
├── @CMD
└── @ROM
其中最重要的是两个模块:
@CMD
存放 AFU 启动时自动执行的参数。
以及:
@ROM
存放真正要刷入的官方 BIOS 固件。
比如这个官方更新包里的 @CMD 是:
/p /b /n /r /x /capsule
而我们想做的 ROM Hole 更新程序,只需要:
/L
所以整个修改过程的核心其实就是:
官方 CAP.EXE
↓
保留 @ROM 完全不变
↓
把 @CMD 改成 /L
↓
重新计算 UCP checksum
↓
生成 ROMHOLES_DIRECT.EXE
这样不会修改 BIOS 固件本身,只改变 AFU 对官方固件的刷写方式。
为什么不能直接修改 BIOS?
如果 BIOS 使用了 AMI Secure Flash,那么修改 @ROM 中的 BIOS 内容可能会导致数字签名失效。
之前测试过修改 BIOS 内部动画资源的固件,结构检查全部能够通过:
FFS Checksums ................. Pass
Check ROMLayout ............... Pass
ROM File Size checking ........ Pass
ROM ID Checking ............... Pass
ROM File Verification Status .. Pass
但真正刷写时仍然可能出现:
18 - Error: Secure Flash Rom Verify fail.
所以,如果官方 BIOS 本身已经包含新版动画,最理想的办法就是:
完全不要修改 @ROM。
只修改 AFU 的 /L 参数。
一、需要准备的软件
推荐准备:
HxD
或者:
010 Editor
用于查看和修改十六进制文件。
还建议准备:
Python 3
后面计算 checksum 会方便很多。
本文示例文件:
XxRPxxxxN135MRO58_CAP.EXE
二、先确认官方 BIOS 中确实有新版动画
这一点非常重要。
不是所有 BIOS 都已经放入新版动画。
我们之前确认过,机械革命新版动画对应的 FFS GUID 是:
1FB553B5-73E8-4290-9B95-B75D01D4CC9A
新版资源为:
800 × 600
GIF
60 帧
20 ms / 帧
总时长约 1.2 秒
如果官方 BIOS 里面这个位置只有:
800 × 600
1 帧 GIF
那即使执行 /L,最终也只会刷入静态 Logo。
所以:
官方 BIOS 本身有新版动画
→ 可以考虑做 ROMHOLES_DIRECT.EXE
官方 BIOS 本身没有新版动画
→ 单纯改 /L 没有意义
本文这个:
XxRPxxxxN135MRO58_CAP.EXE
里面已经确认存在新版 60 帧动画。
三、找到真正的 @UAF
用 HxD 打开:
XxRPxxxxN135MRO58_CAP.EXE
搜索 ASCII:
@UAF
需要注意:
文件内部可能出现不止一次类似字符串。
真正的 UCP overlay 一般位于 EXE 后部,并且后面能够连续解析出:
@UII
@W64
@CMD
@ROM
在本文这个文件中:
@UAF Offset:
0x119600
模块分布:
@UAF 0x119600
@UII 0x119610
@W64 0x119670
@CMD 0x11EF90
@ROM 0x11EFC0
可以理解成:
0x119600 @UAF
│
├── @UII
├── @W64
├── @CMD
└── @ROM
四、找到 @CMD
搜索:
@CMD
本文示例真正需要修改的位置是:
0x11EF90
原始十六进制:
40 43 4D 44 30 00 00 00 A7 11 00 00 00 00 00 00
17 00 00 00 17 00 00 00 2F 70 20 2F 62 20 2F 6E
20 2F 72 20 2F 78 20 2F 63 61 70 73 75 6C 65 00
其中:
40 43 4D 44
转换成 ASCII 就是:
@CMD
后面的:
2F 70 20 2F 62 20 2F 6E 20 2F 72 20 2F 78 20 2F
63 61 70 73 75 6C 65
就是:
/p /b /n /r /x /capsule
五、理解 @CMD 模块结构
这个模块大致可以拆成:
Offset +00 4 bytes "@CMD"
Offset +04 4 bytes Module Size
Offset +08 2 bytes Checksum
Offset +0A 2 bytes Reserved
Offset +0C 4 bytes Reserved
Offset +10 4 bytes Command Length
Offset +14 4 bytes Command Length
Offset +18 ... Command String
原来的命令:
/p /b /n /r /x /capsule
长度为:
23 bytes
也就是:
0x17
所以这里可以看到:
17 00 00 00
17 00 00 00
原 @CMD 模块总大小:
0x30
即:
48 bytes
六、把 @CMD 改成 /L
我们最终需要 AFU 执行:
/L
对应 ASCII:
2F 4C
长度:
2 bytes
所以两个 Command Length 都要变成:
02 00 00 00
字符串部分:
2F 4C 00
其中最后:
00
是字符串结束符。
再按照 16-byte alignment 补零。
新的完整 @CMD:
40 43 4D 44 20 00 00 00 20 2C 00 00 00 00 00 00
02 00 00 00 02 00 00 00 2F 4C 00 00 00 00 00 00
解释一下:
40 43 4D 44
即:
@CMD
模块大小:
20 00 00 00
也就是:
0x20
新的 checksum:
20 2C
也就是:
0x2C20
两个命令长度:
02 00 00 00
02 00 00 00
最后:
2F 4C
就是:
/L
七、不能只把原字符串覆盖成 /L
这是整个过程中最容易出错的地方。
原来的 @CMD:
0x30 bytes
新的:
0x20 bytes
少了:
0x10
也就是:
16 bytes
所以不能简单地:
/p /b /n /r /x /capsule
直接改成:
/L
然后让后面的数据留在那里。
正确做法是:
重新构造整个 @CMD module。
因此原始位置:
@CMD 0x11EF90
@ROM 0x11EFC0
修改之后:
@CMD 0x11EF90
@ROM 0x11EFB0
也就是说:
@ROM
整体向前移动:
0x10
字节。
注意:
Offset 变化不代表 @ROM 内容发生变化。
只是因为前面的 @CMD 变短了。
八、修改 @UAF 总大小
因为整个 UCP 少了:
0x10
字节,所以最外层:
@UAF
记录的 Size 也必须修改。
原始 @UAF Header:
40 55 41 46 D0 20 92 00 0F 43 10 00 FF FF FF FF
其中:
40 55 41 46
就是:
@UAF
原始 Size:
D0 20 92 00
按照 Little Endian:
0x9220D0
因为我们少了:
0x10
所以新的 Size 是:
0x9220C0
写入文件:
C0 20 92 00
九、重新计算 @CMD Checksum
AMI UCP 这里使用的是:
16-bit Little Endian Additive Checksum
要求整个模块所有 16-bit word 相加后满足:
sum & 0xFFFF == 0
计算时先把 checksum 字段清零:
@CMD + 0x08
设成:
00 00
然后按 Little Endian WORD 对整个 module 求和。
最终:
checksum = (-sum) & 0xFFFF
本文新的 /L 模块得到:
0x2C20
所以写入文件:
20 2C
十、重新计算 @UAF Checksum
最外层:
@UAF
也有自己的 16-bit checksum。
位置:
@UAF + 0x08
本文原始:
0F 43 10 00
修改后:
1F 43 10 00
也就是 checksum:
0x430F
变成:
0x431F
注意:
10 00
属于后面的字段,不要一起修改。
因此新的 @UAF Header:
40 55 41 46 C0 20 92 00 1F 43 10 00 FF FF FF FF
十一、最重要的一步:确认 @ROM 一个字节都没变
制作完成以后,必须检查:
@ROM
的 SHA-256。
原始文件:
@ROM Offset:
0x11EFC0
@ROM Size:
0x91C710
修改以后:
@ROM Offset:
0x11EFB0
@ROM Size:
0x91C710
虽然位置向前移动了:
0x10
但是 @ROM 内容必须完全一样。
本文这个官方 @ROM 的 SHA-256:
514cce8bceb61fb57b8d3fdb15ef55fb41dd55555e3b72e9a3e2dba51dc2e16d
所以生成前:
514cce8bceb61fb57b8d3fdb15ef55fb41dd55555e3b72e9a3e2dba51dc2e16d
生成后也必须是:
514cce8bceb61fb57b8d3fdb15ef55fb41dd55555e3b72e9a3e2dba51dc2e16d
如果两者不同:
不要运行生成的 EXE。
十二、检查最终文件大小
原始官方文件:
10,729,168 bytes
修改后的文件:
10,729,152 bytes
刚好减少:
16 bytes
也就是:
0x10
这与:
@CMD
0x30
↓
0x20
完全对应。
十三、最终得到的结构
修改前:
@UAF
├── @UII
├── @W64
├── @CMD
│ └── /p /b /n /r /x /capsule
└── @ROM
└── 官方 BIOS
修改后:
@UAF
├── @UII
├── @W64
├── @CMD
│ └── /L
└── @ROM
└── 官方 BIOS,完全不变
所以这个:
ROMHOLES_DIRECT.EXE
实际上并不是“修改版 BIOS”。
更准确地说,它是:
使用官方原始 BIOS,但改变 AFU 刷写区域选择的更新程序。
十四、为什么 /L 可以更新开机动画?
AMI AFU 对 /L 的定义是:
Program all ROM Holes
也就是刷新 BIOS 中的 ROM Hole。
机械革命新版开机动画就在对应 ROM Hole 内。
我们之前确认的动画 GUID:
1FB553B5-73E8-4290-9B95-B75D01D4CC9A
如果官方 BIOS 已经包含新版:
800 × 600
60 帧 GIF
但正常刷 BIOS 以后机器还是旧动画,那么很可能就是:
正常 BIOS Region 已更新
↓
ROM Hole 没刷新
↓
Flash 中仍然保留旧动画
执行:
/L
之后:
官方 BIOS 的 ROM Hole
↓
写入 SPI Flash
↓
新版开机动画
十五、哪些情况下不能这样做?
这个方法不是万能的。
至少要满足:
1. 官方 BIOS 使用 AMI UCP 封装
2. EXE 中存在 @UAF / @CMD / @ROM
3. 官方 @ROM 本身已经包含新版动画
4. AFU 支持 /L
5. ROM Hole 与当前机器平台匹配
如果官方 BIOS 本身只有:
1 帧静态 Logo
那么:
/L
只会把那个静态 Logo 刷进去。
不会自动变成新版动画。
另外,不同平台不能混用:
X6AR45xU
XxMT4xxx
XxRPxxxx
X6AR5xxx
虽然 BIOS 的某些结构可能很像,但并不代表 ROM 可以跨平台刷。
十六、可以用 Python 自动制作
如果不想每次都用 HxD 手工修改,可以直接用下面这个 Python 脚本自动完成。
脚本会做这些事情:
读取原厂 EXE
↓
寻找真正的 @UAF
↓
解析 @UII / @W64 / @CMD / @ROM
↓
读取原始 @CMD
↓
重建成 /L
↓
重新计算 @CMD checksum
↓
调整 @UAF size
↓
重新计算 @UAF checksum
↓
确认 @ROM 完全没有变化
↓
生成 ROMHOLES_DIRECT.EXE
其中最重要的一项保护是:
如果脚本发现生成后的 @ROM 和原始 @ROM 有任何一个字节不同,就会直接报错,并拒绝生成输出文件。
脚本如下:
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import hashlib
import struct
from pathlib import Path
ALIGN = 0x10
def u32(b: bytes | bytearray, off: int) -> int:
return struct.unpack_from('<I', b, off)[0]
def p32(b: bytearray, off: int, v: int) -> None:
struct.pack_into('<I', b, off, v)
def p16(b: bytearray, off: int, v: int) -> None:
struct.pack_into('<H', b, off, v)
def sum16le(data: bytes | bytearray) -> int:
if len(data) & 1:
data = bytes(data) + b'\x00'
return sum(
struct.unpack(
'<' + 'H' * (len(data) // 2),
data
)
) & 0xFFFF
def fix_low16_checksum(blob: bytearray, checksum_off: int) -> None:
# UCP 的 +8 位置低 16 bit 使用 16-bit additive checksum。
# 保留 DWORD 的高 16 bit。
p16(blob, checksum_off, 0)
p16(
blob,
checksum_off,
(-sum16le(blob)) & 0xFFFF
)
if sum16le(blob) != 0:
raise RuntimeError('checksum calculation failed')
def find_uaf(data: bytes) -> int:
# 寻找 size 字段刚好延伸到文件结尾的 @UAF。
# 这样可以避免误匹配 PE 主体中的 "@UAF" 字符串。
pos = 0
candidates = []
while True:
pos = data.find(b'@UAF', pos)
if pos < 0:
break
if pos + 16 <= len(data):
size = u32(data, pos + 4)
if size >= 16 and pos + size == len(data):
candidates.append(pos)
pos += 1
if len(candidates) != 1:
raise RuntimeError(
f'expected exactly one terminal @UAF, '
f'found {len(candidates)}: {candidates}'
)
return candidates[0]
def parse_modules(data: bytes | bytearray, uaf_off: int):
uaf_size = u32(data, uaf_off + 4)
p = uaf_off + 16
end = uaf_off + uaf_size
mods = []
while p < end:
if p + 16 > end:
raise RuntimeError('truncated module header')
tag = bytes(data[p:p + 4])
size = u32(data, p + 4)
if (
size < 16
or size % ALIGN
or p + size > end
):
raise RuntimeError(
f'bad module at 0x{p:X}: '
f'tag={tag!r}, size=0x{size:X}'
)
mods.append((tag, p, size))
p += size
if p != end:
raise RuntimeError(
'module parsing did not end at UAF boundary'
)
return mods
def sha256(x: bytes | bytearray) -> str:
return hashlib.sha256(x).hexdigest()
def build_cmd_module(old: bytes, command: str) -> bytes:
cmd = command.encode('ascii')
if b'\x00' in cmd:
raise ValueError(
'command may not contain NUL'
)
# 结构:
#
# 16-byte module header
# +
# 两个 DWORD command length
# +
# NUL 结尾的命令字符串
# +
# 16-byte alignment padding
raw_len = 24 + len(cmd) + 1
new_size = (
raw_len + ALIGN - 1
) & ~(ALIGN - 1)
m = bytearray(new_size)
# 保留原始 module header
m[:16] = old[:16]
m[0:4] = b'@CMD'
# 更新 module size
p32(m, 4, new_size)
# +8 的低 WORD 是 checksum。
# +0xA 的高 WORD / reserved 保持原值。
p16(m, 8, 0)
# 两个 command length
p32(m, 16, len(cmd))
p32(m, 20, len(cmd))
# command
m[24:24 + len(cmd)] = cmd
# NUL terminator
m[24 + len(cmd)] = 0
# 修复 @CMD checksum
fix_low16_checksum(m, 8)
return bytes(m)
def main() -> None:
ap = argparse.ArgumentParser(
description=(
'Convert an AMI UCP CAP EXE to a /L '
'ROM-Holes-only variant without modifying @ROM.'
)
)
ap.add_argument(
'input',
type=Path
)
ap.add_argument(
'output',
type=Path
)
ap.add_argument(
'--command',
default='/L',
help='replacement @CMD string (default: /L)'
)
args = ap.parse_args()
# 读取原始 EXE
original = args.input.read_bytes()
# 找到真正的 @UAF
uaf = find_uaf(original)
# 解析 UCP modules
mods = parse_modules(
original,
uaf
)
by_tag = {
tag: (off, size)
for tag, off, size in mods
}
if (
b'@CMD' not in by_tag
or b'@ROM' not in by_tag
):
raise RuntimeError(
'required @CMD/@ROM modules not found'
)
cmd_off, cmd_size = by_tag[b'@CMD']
rom_off, rom_size = by_tag[b'@ROM']
old_cmd = original[
cmd_off:
cmd_off + cmd_size
]
old_rom = original[
rom_off:
rom_off + rom_size
]
# 检查原始 module checksum
if sum16le(old_cmd) != 0:
raise RuntimeError(
'original @CMD checksum is not valid'
)
if sum16le(old_rom) != 0:
raise RuntimeError(
'original @ROM checksum is not valid'
)
# 读取原始命令字符串
old_len1 = u32(old_cmd, 16)
old_len2 = u32(old_cmd, 20)
old_text = old_cmd[
24:
24 + min(old_len1, old_len2)
].decode(
'ascii',
'replace'
)
# 构建新的 /L @CMD
new_cmd = build_cmd_module(
old_cmd,
args.command
)
# 替换整个 @CMD module。
#
# 允许 @CMD 变短或变长,
# 所以后面的 @ROM Offset 会自动移动。
out = bytearray(
original[:cmd_off]
+ new_cmd
+ original[cmd_off + cmd_size:]
)
delta = len(new_cmd) - cmd_size
# @UAF 位于 @CMD 前面,
# 所以 @UAF 自己的 Offset 不变。
old_uaf_size = u32(
original,
uaf + 4
)
p32(
out,
uaf + 4,
old_uaf_size + delta
)
# 重新计算 @UAF 的低 16-bit checksum,
# 保留高 16-bit metadata。
uaf_blob = bytearray(
out[uaf:]
)
fix_low16_checksum(
uaf_blob,
8
)
out[uaf:] = uaf_blob
# ==============================
# Validation
# ==============================
# @UAF Size 必须刚好到 EOF
if (
uaf + u32(out, uaf + 4)
!= len(out)
):
raise RuntimeError(
'new @UAF size does not reach EOF'
)
# 重新解析修改后的 modules
new_mods = parse_modules(
out,
uaf
)
# 检查每个 module checksum
for tag, off, size in new_mods:
if sum16le(
out[off:off + size]
) != 0:
raise RuntimeError(
f'bad checksum after rebuild: {tag!r}'
)
# 检查整个 @UAF checksum
if sum16le(out[uaf:]) != 0:
raise RuntimeError(
'bad @UAF checksum after rebuild'
)
# 重新寻找 @ROM
new_rom_entry = next(
(
x
for x in new_mods
if x[0] == b'@ROM'
),
None
)
if not new_rom_entry:
raise RuntimeError(
'@ROM disappeared after rebuild'
)
_, new_rom_off, new_rom_size = (
new_rom_entry
)
new_rom = out[
new_rom_off:
new_rom_off + new_rom_size
]
# 最重要的保险:
# @ROM 必须逐字节完全不变。
if new_rom != old_rom:
raise RuntimeError(
'@ROM changed; refusing to write output'
)
# 所有验证通过后才写入输出文件
args.output.write_bytes(out)
print(
f'Input: {args.input}'
)
print(
f'Output: {args.output}'
)
print(
f'@UAF offset: 0x{uaf:X}'
)
print(
f'Old @CMD: {old_text!r}'
)
print(
f'New @CMD: {args.command!r}'
)
print(
f'@CMD size: '
f'0x{cmd_size:X} -> '
f'0x{len(new_cmd):X}'
)
print(
f'File size: '
f'{len(original)} -> '
f'{len(out)} '
f'({delta:+d})'
)
print(
f'@ROM SHA-256: '
f'{sha256(old_rom)} '
f'(unchanged)'
)
print(
f'Output SHA-256: '
f'{sha256(out)}'
)
print(
'Validation: PASS'
)
if __name__ == '__main__':
main()
使用方法
把上面的代码保存成:
make_romholes_direct.py
然后把 Python 脚本和原厂 BIOS EXE 放在同一个目录。
例如:
BIOS\
├── make_romholes_direct.py
└── XxRPxxxxN135MRO58_CAP.EXE
在这个目录打开 CMD 或 PowerShell。
执行:
python make_romholes_direct.py XxRPxxxxN135MRO58_CAP.EXE XxRPxxxxN135MRO58_ROMHOLES_DIRECT.EXE
脚本默认把 @CMD 修改成:
/L
也可以显式指定:
python make_romholes_direct.py XxRPxxxxN135MRO58_CAP.EXE XxRPxxxxN135MRO58_ROMHOLES_DIRECT.EXE --command "/L"
如果一切正常,最后应该看到类似:
Input: XxRPxxxxN135MRO58_CAP.EXE
Output: XxRPxxxxN135MRO58_ROMHOLES_DIRECT.EXE
@UAF offset: 0x119600
Old @CMD:
'/p /b /n /r /x /capsule'
New @CMD:
'/L'
@CMD size:
0x30 -> 0x20
File size:
10729168 -> 10729152 (-16)
@ROM SHA-256:
514cce8bceb61fb57b8d3fdb15ef55fb41dd55555e3b72e9a3e2dba51dc2e16d
(unchanged)
Output SHA-256:
5c24e34d98b37461037926ad1f0e8c3ac881bd11d2a0cc032b3d89cda0da7221
Validation:
PASS
其中最重要的是下面两项:
@ROM SHA-256 ... (unchanged)
Validation: PASS
只有看到:
unchanged
以及:
PASS
才说明:
@ROM 没有被修改
UCP module checksum 正常
@UAF checksum 正常
模块结构正常
如果脚本出现任何:
RuntimeError
或者没有显示:
Validation: PASS
不要运行生成出来的 EXE。
脚本做了什么?
整个自动处理过程相当于:
原厂 CAP.EXE
↓
自动寻找 @UAF
↓
解析所有 UCP Module
↓
找到 @CMD
↓
读取原始命令
↓
重新构建 @CMD = /L
↓
重新计算 @CMD checksum
↓
自动调整 @UAF size
↓
重新计算 @UAF checksum
↓
重新寻找 @ROM
↓
逐字节比较原始 @ROM 和新 @ROM
↓
完全一致
↓
输出 ROMHOLES_DIRECT.EXE
因此它比手工用 HxD 修改更适合批量处理不同机型的官方 BIOS。
不过这个脚本只能解决:
怎么把官方 AFU 参数改成 /L
它不会自动判断官方 BIOS 里有没有新版开机动画。
所以在制作之前仍然需要先确认:
官方 @ROM
↓
对应的 ROM Hole
↓
1FB553B5-73E8-4290-9B95-B75D01D4CC9A
↓
确实包含新版 800×600 / 60帧 GIF
如果官方 BIOS 本身没有新版动画,那么即使成功生成:
ROMHOLES_DIRECT.EXE
刷进去的也仍然是官方 BIOS 自带的旧动画或静态 Logo。
十七、最终检查清单
在真正运行自己制作的 ROMHOLES_DIRECT.EXE 之前,至少检查:
[ ] 平台型号正确
[ ] BIOS版本对应
[ ] @ROM 中确实存在新版动画
[ ] 原始 @ROM SHA-256 与修改后完全一致
[ ] @CMD 已变成 /L
[ ] @CMD checksum 正确
[ ] @UAF size 正确
[ ] @UAF checksum 正确
[ ] 文件结构可以正常解析
其中最重要的是:
@ROM 必须完全没变
如果修改 @CMD 的过程中连 @ROM 都变化了,说明文件重建过程有问题,不要拿去刷。
总结
手动制作 ROMHOLES_DIRECT.EXE 的核心其实只有一句话:
保留官方 BIOS
只把 AFU 参数改成 /L
完整流程:
官方 CAP.EXE
↓
找到 @UAF
↓
找到 @CMD
↓
读取原始 AFU 参数
↓
重建 @CMD 为 /L
↓
重新计算 checksum
↓
调整 @UAF
↓
确认 @ROM 完全没变
↓
输出 ROMHOLES_DIRECT.EXE
这样做的好处是:
不用修改 BIOS 内部固件,也不用重新签名,只是让官方 AFU 把原本没有刷新的 ROM Hole 写进去。
如果官方 BIOS 中本来就包含新版机械革命开机动画,这种方式通常比直接修改 BIOS 本身干净得多。
风险提示
BIOS 属于底层固件。
即使只是修改 AFU 的刷写参数,也仍然属于固件刷写操作。
本文中的 Offset:
0x119600
0x11EF90
0x11EFC0
以及:
@CMD Size
@UAF Size
Checksum
都是针对本文示例:
XxRPxxxxN135MRO58_CAP.EXE
得到的结果。
其他 BIOS 不要直接照抄这些地址。
正确做法应该是重新解析自己手里的官方 EXE,再根据实际结构修改。
操作前请确认机器型号、平台和 BIOS 版本,并自行承担刷写固件产生的风险。