ThinkPad BIOS modification

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 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 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 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 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).