Tools needed:
So now download some BIOS update, perhaps ThinkPad T40-T42 ver. 3.23, and let it unpack to a floppy. Take file ending with FL1
, should be somewhere around a megabyte in size, in our case it's $018F000.FL1
. Use phcomp.exe /d $018F000.FL1
to decompress it. It should output $018F000.FLh
, rename it to bios.rom
for later references. Use phnxdeco bios.rom -x
to split and decompress individual modules from the BIOS image. There will be files phoenix0.B#
, where # will be a number from zero to perhaps four, five, six. These files are BiosCode modules and most probably contain the whitelist.
You can search the files for a known vendor IDs, such as HEX 8680
(Intel). The file where you find this sequence a lot of times in one place is our hero. For this particular BIOS, it's phoenix0.B1
, BiosCode1.
Now open IDA Disassembler and load this file. Say it's an unknown file and confirm disassembly. When asked whether to use 32-bit mode, say no, BIOS mostly runs in 16-bit mode. It will now try to disassemble the file. If you see only db XXh
everywhere, go to a random line in the beginning and keep holding alt+c for a while. It will try it's best.
So now you can see some instructions, such as this (highlights by IDA):
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
This is the routine which compares 4x2 bytes of some data provided by calling some function at 0F000h:517Dh against a list of eight-byte sequences stored at address pointed to by di
registry. It's a loop, at loc_B8B
we can see how the di
gets incremented (moved pointer to the next entry, eight bytes plus one for dividing zero byte) and the jumps back to try the next one. First two instructions then check if we have a zero (end of list) and if so, then stc
and go to loc_B90
to load original registry contents from stack and return. Now the stc
is important, because it's basically used to return a value, it sets CF registry bit to 1. The oposite it clc
, which sets it to zero. There's a second jump to loc_B90
("return"), and that's just after calling the clc
.
Actually there's also the di
registry, which should, after this function returns, point to the device/subsystem ID of the card, but I've tried to follow the code and don't think it's checked once again somewhere else. For now, let's try changing the stc
(HEX F9
) to clc
(HEX F8
). Highlight the stc
instruction and then switch tab to Hex View. Copy a few bytes around the F9 instruction and search for them in the main bios image, bios.rom
, open it in hex editor. Ensure you only have one match through the whole file - if not, take more bytes from IDA Disassembler. When you find it, just modify the hex value F9 to F8, at the correct place, which makes the fuction return CF=0 always.>
Now to recalculate checksum. Find the exact position of the changed byte, in my case HEX 47670 (decimal 292464). Calculate remainder of division by 4 (292464 mod 4 = 0
). Write down the result. Find text EXTD
in the bios.rom
file. There should be four seemingly random bytes following it. Think of them as being numbered zero, one, two, three. Select the byte corresponding to the remainder. Increase the byte value by one. That's because the checksum is calculated so that after adding up all four-byte numbers, the result will be zero. You've changed F9 to F8, decreasing it by one, so to restore the checksum, you have to increment the corresponding number by one.
Now you're done. Compress it using phcomp.exe bios.rom
and now you've got a modified rom (phcomp saves it as bios.ro2
) which can be returned to the BIOS-updating diskette, replacing the original $018F000.FL1 file. If all went okay, it should be the same file as in this archive (for the 3.23 ThinkPad T40-T42 BIOS I mentioned in the beginning).