原文地址:http://www.endeer.cz/bios.tools/thinkpad-biosmod.html
需要的工具:
– phcomp.exe(Phoenix Technologies 公司)—— 用于对联想提供的 BIOS 镜像进行解压以及后续的压缩。
– phnxdeco.exe(作者 Anthony Borisow,需要 cygwin1.dll)—— 从 BIOS 映像中分离出各个模块。
– IDA 反汇编器 —— 至少可以用略微可读的方式看机器代码。
– 十六进制编辑器(如果没有的话,可以选择 EditPad Pro)。
– 如果能有一个虚拟软盘驱动器,来修改 BIOS 映像,比如 Cenatek RAM Disk NT 1.5(可惜 Cenatek 已经不提供了,被新版本取代了,似乎不支持软盘驱动器)。
那么现在就可以下载某个 BIOS 更新了,比方说 ThinkPad T40-T42 ver. 3.23,然后让它解压到软盘上。取以 FL1
结尾的文件,大小应该在一兆左右,在我们的例子中是 $018F000.FL1
。使用 phcomp.exe /d $018F000.FL1
解压它,应该输出 $018F000.FLh
,将其重命名为 bios.rom
,后文会引用此名字。使用 phnxdeco bios.rom -x
从 BIOS 映像中分离和解压各个模块,会有一些名如 phoenix0.B#
的文件,其中 #
是一个数字,从 0 到或 4 或 5 或 6。这些文件是 BiosCode
模块,很可能包含白名单。
可以在文件中搜索已知的厂商 ID,比如十六进制的 8680
(Intel)。在哪个同一文件中找到这个序列的次数多,哪个文件就是我们要找的。在这儿这个特定的 BIOS 里,它是 phoenix0.B1
,BiosCode1。
现在打开 IDA 反编译器,加载这个文件。会说是未知文件,确认反编译。当问到是否使用 32 位模式时,选 no,因为 BIOS 大多以 16 位模式运行。现在它会尝试反编译这个文件。如果你看到到处都只有 db XXh
,就到开头的随便一行,一直按住 alt+c 一会儿。它将尽力而为。
现在你就可以看到一些指令了,比如这个(IDA 的高亮部分)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
seg000:0B3A sub_B3A proc near ; CODE XREF: seg000:0C45p seg000:0B3A push ax seg000:0B3B push bx seg000:0B3C push dx seg000:0B3D mov di, 0AC9h seg000:0B40 call sub_2E3 seg000:0B43 seg000:0B43 loc_B43: ; CODE XREF: sub_B3A+54j seg000:0B43 cmp word ptr cs:[di], 0 seg000:0B47 jnz short loc_B4C seg000:0B49 stc seg000:0B4A jmp short loc_B90 seg000:0B4C ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ seg000:0B4C seg000:0B4C loc_B4C: ; CODE XREF: sub_B3A+Dj seg000:0B4C mov dx, 100h seg000:0B4F call far ptr 0F000h:517Dh seg000:0B54 cmp ax, cs:[di] seg000:0B57 jnz short loc_B8B seg000:0B59 mov dx, 102h seg000:0B5C call far ptr 0F000h:517Dh seg000:0B61 cmp ax, cs:[di+2] seg000:0B65 jnz short loc_B8B seg000:0B67 mov dx, 12Ch seg000:0B6A call far ptr 0F000h:517Dh seg000:0B6F cmp ax, cs:[di+4] seg000:0B73 jnz short loc_B8B seg000:0B75 mov dx, 12Eh seg000:0B78 call far ptr 0F000h:517Dh seg000:0B7D cmp ax, cs:[di+6] seg000:0B81 jnz short loc_B8B seg000:0B83 call sub_54F7 seg000:0B86 jb short loc_B8B seg000:0B88 clc seg000:0B89 jmp short loc_B90 seg000:0B8B ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ seg000:0B8B seg000:0B8B loc_B8B: ; CODE XREF: sub_B3A+1Dj seg000:0B8B ; sub_B3A+2Bj ... seg000:0B8B add di, 9 seg000:0B8E jmp short loc_B43 seg000:0B90 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ seg000:0B90 seg000:0B90 loc_B90: ; CODE XREF: sub_B3A+10j seg000:0B90 ; sub_B3A+4Fj seg000:0B90 pop dx seg000:0B91 pop bx seg000:0B92 pop ax seg000:0B93 retn seg000:0B93 sub_B3A endp |
这个例程要调用 0F000h:517Dh
处的某个函数提供的 4×2 个字节的数据,与存储在 di
寄存器指向的地址的一组 8 字节序列进行比较。这是一个循环,在 loc_B8B
处我们可以看到 di
是如何被递增的(移动指针到下一个条目,8 个字节加 1 个用于分割的字节 0),然后跳回尝试下一个。前两条指令然后检查是否遇到了表示列表结束的 0,如果是的话,然后 stc
并转到 loc_B90
,从栈中加载原始寄存器内容并返回。stc
很重要,因为它基本上是用来返回一个值的,它将 CF
寄存器位设为 1
,反之则是 clc
,会将其设为 0
。还有另一个跳转会跳到 loc_B90
(return
),那是在调用 clc
之后。
其实,在这个函数返回后,di
寄存器仍然指向卡的 device/subsystem ID,但我尝试过跟踪代码,好像没有在其他地方再有检查。现在,我们来试着把 stc
(十六进制 F9
)改成 clc
(十六进制 F8
)。高亮显示 stc
指令,然后切换标签到十六进制视图。复制 F9
指令周围的几个字节,并在主 BIOS 映像即 bios.rom 中搜索它们。在十六进制编辑器中打开它,确保在整个文件中只有一个匹配 —— 如果不是,那就从 IDA 反汇编器中取更多的字节。当找到它的时候,只需要在正确的地方将十六进制值 F9
修改为 F8
,这样就可以使函数总是返回 CF=0
。
现在要重新计算校验和。找到修改后的字节的准确位置,在我的例子中是十六进制 47670
(十进制 292464
)。计算除以 4 的余数(292464 mod 4 = 0
),写下结果。在 bios.rom 文件中找到文本 EXTD。它后面应该有四个看似随机的字节,把它们看作是编号为 0、1、2、3 的字节。选择余数对应的字节,将字节值加 1。这是因为校验和的计算方法是将所有四个字节的数字相加后,结果为零。你把 F9
改成了 F8
,减少了 1,所以要恢复校验和,就必须把相应的数字加 1。
这就完成了。用 phcomp.exe bios.rom
压缩它,得到了一个修改后的 ROM(phcomp 保存为 bios.ro2),回到 BIOS 更新软盘中,用它替换掉原来的 $018F000.FL1
文件。如果一切顺利的话,应该和这个存档里的文件是一样的(针对开头提到的 3.23 ThinkPad T40-T42 BIOS)。