Award BIOS Code Injection

Foreword

This article is only for bios hackers who already done some bios hacking before, especially Award BIOS and its variant. If you haven't done any bios hacking before or is not knowledgeable enough in bios, this article maybe not useful at all. To put simply, this article basically describes an advanced and elegant way to do bios code injection.

First, let me explain that this article is not an official article, I write it merely as a documentation for myself. But, I present it to the public, since I think it might be of some use to somebody who does some bios hacking on his/her own. Any damages that may happen due to applying the technique I explain here is not my responsiblity. If you try it, then you are on your own. I suggest you to stop reading this article right now if you don't agree on my terms. It doesn't do any good to you unless you are really curious to know this advanced bios hacking technique.

Based on my previous two article i.e. Advanced Award BIOS v4.51PG Hacking Tutorial and Pinczakko's Guide to Award BIOS Reverse Engineering, it's very clear that there's a much more safe and elegant BIOS hacking technique waiting to be exploited i.e., patching the so called "POST jump table" to include a "jump" into our own custom procedure in Award Bioses. There are several reasons why I choose this approach to Award BIOS hacking :

The following is the detail of the testbed used for this radical modification :

Perhaps it sounds crazy, but the fact is: the testbed is my computer that I use for working everyday, he..he..he.. >:). OK, enough with the intro, the next section will explain the tools needed to accomplish this task. Goodluck.

Tools Of The Trade

You are only as good as your tools. Yeah, this also holds true here. We'll need some tools as follows :

Prerequisite

There are some stuff that I won't explain here and it's your homework that you should do to comprehend this article :

Now, we proceed to some more hints and conventions that we have to agreed upon throughout this article. In this article I will explain how to inject your own code into Award BIOS by patching the POST jump table. But, before that, let's clarify a few things:

Address    Hex Values       Mnemonic                 Comment 

000:6276                   RAM_POST_TESTS proc near ; CODE XREF: last_E000_POST+D 

E000:6276                                           ; last_E000_POST+18 ... 

E000:6276 8A C1                mov  al, cl          ; cl = 3 

E000:6278 E6 80                out  80h, al         ; manufacture's diagnostic checkpoint 

E000:627A 68 00 F0             push 0F000h 

E000:627D 0F A1                pop  fs              ; fs = F000h E000:627F                    

E000:627F                   ;This is the beginning of the call into E000 segment  

E000:627F                   ;POST function table 

E000:627F                      assume fs:F000 

E000:627F 2E 8B 05             mov  ax, cs:[di]     

; in the beginning : E000:627F                                           

; di = 61C2h ; ax = cs:[di] = 154Eh 

E000:627F                                           

; called from E000:2489 w/ di=61FCh (dummy) 

E000:6282 47                   inc  di              ; Increment by 1 

E000:6283 47                   inc  di              ; di = di + 2 

E000:6284 0B C0                or   ax, ax          ; Logical Inclusive OR 

E000:6286 74 0B                jz   RAM_post_return ; RAM Post Error 

E000:6288 57                   push di              ; save di 

E000:6289 51                   push cx              ; save cx 

E000:628A FF D0                call ax              ; call [61C2h] = call 154Eh 

E000:628A                                           ; (relative call addr),one of this call  

E000:628A                                           ; won't return in normal condition 

E000:628C 59                   pop  cx              ; restore all 

E000:628D 5F                   pop  di 

E000:628E 72 03                jb   RAM_post_return ; Jump if Below (CF=1) 

E000:6290 41                   inc  cx              ; Increment by 1 

E000:6291 EB E3                jmp  short RAM_POST_TESTS ; Jump 

E000:6293                   ; --------------------------------------------------------------------------- 

E000:6293                    

E000:6293                   RAM_post_return:        ; CODE XREF: RAM_POST_TESTS+10 

E000:6293                                           ; RAM_POST_TESTS+18 

E000:6293 C3                   retn                 ; Return Near from Procedure 

E000:6293                   RAM_POST_TESTS endp ......... 

E000:61C2                   E0_POST_TESTS_TABLE: 

E000:61C2 4E 15                dw 154Eh             ; Restore boot flag 

E000:61C4 6F 15                dw 156Fh             ; Chk_Mem_Refrsh_Toggle 

E000:61C6 71 15                dw 1571h             ; keyboard (and its controller) POST 

E000:61C8 D2 16                dw 16D2h             ; chksum ROM, check EEPROM 

E000:61C8                                           ; on error generate spkr tone 

E000:61CA 45 17                dw 1745h             ; Check CMOS circuitry 

E000:61CC 8A 17                dw 178Ah             ; "chipset defaults" initialization 

E000:61CC                                           ; file: E0POST.ASM and CT_TABLE.ASM 

E000:61CE 98 17                dw 1798h             ; init CPU cache (both Cyrix and Intel) 

E000:61D0 B8 17                dw 17B8h             ; init interrupt vector, also initialize  

E000:61D0                                           ; "signatures" used for Ext_Bios components  

E000:61D0                                           ; decompression 

E000:61D2 4B 19                dw 194Bh             ; Init_mainboard_equipment & CPU microcode 

E000:61D2                                           ; chk ISA CMOS chksum ? 

E000:61D4 BC 1A                dw 1ABCh             ; Check checksum. Initialize keyboard controller 

E000:61D4                                           ; and set up all of the 40: area data. 

E000:61D6 08 1B                dw 1B08h             ; Relocate extended BIOS code 

E000:61D6                                           ; init CPU MTRR, PCI REGs(Video BIOS ?) 

E000:61D8 C8 1D                dw 1DC8h             ; Video_Init (including EPA proc) 

E000:61DA 42 23                dw 2342h 

E000:61DC 4E 23                dw 234Eh 

E000:61DE 53 23                dw 2353h             ; dummy 

E000:61E0 55 23                dw 2355h             ; dummy 

E000:61E2 57 23                dw 2357h             ; dummy 

E000:61E4 59 23                dw 2359h             ; init Programmable Timer (PIT) 

E000:61E6 A5 23                dw 23A5h             ; init PIC_1 (programmable Interrupt Ctlr) 

E000:61E8 B6 23                dw 23B6h             ; same as above ? 

E000:61EA F9 23                dw 23F9h             ; dummy 

E000:61EC FB 23                dw 23FBh             ; init PIC_2 

E000:61EE 78 24                dw 2478h             ; dummy 

E000:61F0 7A 24                dw 247Ah             ; dummy 

E000:61F2 7A 24                dw 247Ah 

E000:61F4 7A 24                dw 247Ah 

E000:61F6 7A 24                dw 247Ah 

E000:61F8 7C 24                dw 247Ch             ; this will call RAM_POST_tests again  

E000:61F8                                           ; for values below(a.k.a ISA POST) 

E000:61FA 00 00                dw 0 

E000:61FA                   END_E0_POST_TESTS_TABLE 

......... 

E000:2353 F8                   clc                  ; Clear Carry Flag 

E000:2354 C3                   retn                 ; Return Near from Procedure 

E000:2355                   ; --------------------------------------------------------------------------- 

E000:2355 F8                   clc                  ; Clear Carry Flag 

E000:2356 C3                   retn                 ; Return Near from Procedure 

E000:2357                   ; --------------------------------------------------------------------------- 

E000:2357 F8                   clc                  ; Clear Carry Flag 

E000:2358 C3                   retn                 ; Return Near from Procedure 

E000:2359                    

......... 

E000:247A                   sub_E000_247A proc near 

E000:247A F8                   clc                  ; Clear Carry Flag 

E000:247B C3                   retn                 ; Return Near from Procedure 

E000:247B                   sub_E000_247A endp .........

Hacking the POST Jump Table

Now we've already known all the prerequisite knowledge to do the hack. I'd like to formulate the steps that we need to do this type of hack :

By following the above guidelines, we will finally arrive at our hacked BIOS.

1. BIOS Reverse Engineering and Analysis

I have done this, the result can be seen at Pinczakko's Guide to Award BIOS Reverse Engineering. The "POST jump table" location can be seen above (in the Prerequisite section). It's very clear there that we have several candidate of dummy procedure jumps that we can replace with our own procedure jump (it's highlighted with red color).

2. Assembling Our Custom Procedure

The following is the source code of the procedure that I inject into my bios (using nasm syntax):

;---------------- BEGIN TWEAK.ASM -------------------------------------------------------------- 

BITS 16 

;just to make sure nasm prefix 66 to 32 bit instructions, we're assuming the uP         

;is in 16 bits mode up to this point (from the boot state)

    section   .text  

start:

         pushf

         push eax

         push dx

         mov eax,ioq_reg  ;patch the ioq register of the chipset

         mov dx,in_port

         out dx,eax

         mov dx,out_port

         in  eax,dx

         or  eax,ioq_mask

         out dx,eax

         mov eax,dram_reg ;patch the DRAM controller of the chipset,

         mov dx,in_port   ;i.e. the interleaving part

         out dx,eax

         mov dx,out_port

         in  eax,dx

         or  eax,dram_mask

         out dx,eax

         mov eax,bank_reg ;Allow pages of different bank to be active simultanoeusly

         mov dx,in_port

         out dx,eax

         mov dx,out_port

         in  eax,dx

         or  eax,bank_mask

         out dx,eax

         mov eax,tlb_reg  ;Activate Fast TLB lookup

         mov dx,in_port

         out dx,eax

         mov dx,out_port

         in  eax,dx

         or  eax,tlb_mask

         out dx,eax

         pop dx

         pop eax

         popf

         clc              ;indicate that this POST routine successful

         retn             ;return near to the header of the rom file


    section .data

   in_port   equ 0cf8h

   out_port  equ 0cfch

   dram_mask equ 00020202h

   dram_reg  equ 80000064h

   ioq_mask  equ 00000080h

   ioq_reg   equ 80000050h

   bank_mask equ 20000840h

   bank_reg  equ 80000068h

   tlb_mask  equ 00000008h

   tlb_reg   equ 8000006ch

;---------------- END TWEAK.ASM --------------------------------------------------------------

The code is assembled using nasm with the invocation syntax :

nasm -fbin tweak.asm -o tweak.bin 

The resulting binary file is tweak.bin. The following is the hex-dump of this binary in hexworkshop v3.02

Address   Hexadecimal Values                        ASCII               

00000000 9C66 5052 66B8 5000 0080 BAF8 0C66 EFBA .fPRf.P......f.. 

00000010 FC0C 66ED 660D 8000 0000 66EF 66B8 6400 ..f.f.....f.f.d. 

00000020 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 .....f....f.f... 

00000030 0200 66EF 66B8 6800 0080 BAF8 0C66 EFBA ..f.f.h......f.. 

00000040 FC0C 66ED 660D 4008 0020 66EF 66B8 6C00 ..f.f.@.. f.f.l. 

00000050 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0800 .....f....f.f... 

00000060 0000 66EF 5A66 589D F8C3                ..f.ZfX...

The dump above shows that we need 0x6A bytes (106 bytes) free space to inject this code in system bios.

3. Injecting The Procedure

Now,extract the system bios by using AwardBios editor. It's very simple, just open the bios file then select the System BIOS tree-item in the left pane, then click the Action|Extract File to save the system bios as a separate uncompressed binary file. As convention in this article, let's name it original.tmp.

Then, open original.tmp using hexeditor. In my original.tmp, I found a lot of padding FFh bytes in the end of segment E000h. Perhaps, this quite confusing at first, let me clarify what I mean: In my previous Award BIOS reverse engineering article, I found that the POST jump table resides in the E000h segment and the jump table contains addresses in Little-Endian 16 bit value. This means that the jump table is only for intra-segment jumps, hence, our injected procedure must reside in the same segment as the POST jump table itself, i.e. segment E000h. So, the "free space" that can be used for our procedure must reside in segment E000h. Most of the time this "free space" is padding bytes.

If you still confused, let me refresh your memory about the mapping between original.tmp in the real system address space and in the hexeditor that we use. Original.tmp size is 128KB, it uses the E000h and F000h segment during it's execution, so, if you see address 0000 0000h in your hexeditor for this file, it's basically address E000:0000h when original.tmp gets executed, and so forth. Due to this fact, we have to look for "free space", i.e. unused area or padding bytes below the address 0001 0000h in the hexeditor.

Below is the snapshot of the beginning of the padding bytes in both IDA Pro 4.50 and Hexworkshop v3.02 for exactly the same address.

In IDA Pro 4.50:

Address   Hex Values         Mnemonic    Comment 

E000:EFE0 C3                   db 0C3h ; + 

E000:EFE1 00                   db    0 ;   

E000:EFE2 00                   db    0 ;   

E000:EFE3 00                   db    0 ;   

E000:EFE4 00                   db    0 ;   

E000:EFE5 00                   db    0 ;   

E000:EFE6 00                   db    0 ;   

E000:EFE7 00                   db    0 ;   

E000:EFE8 00                   db    0 ;   

E000:EFE9 00                   db    0 ;   

E000:EFEA 00                   db    0 ;   

E000:EFEB 00                   db    0 ;   

E000:EFEC 00                   db    0 ;   

E000:EFED 00                   db    0 ;   

E000:EFEE 00                   db    0 ;   

E000:EFEF 00                   db    0 ;   

E000:EFF0 FF                   db 0FFh ;   

E000:EFF1 FF                   db 0FFh ;   

E000:EFF2 FF                   db 0FFh ;   

E000:EFF3 FF                   db 0FFh ;   

E000:EFF4 FF                   db 0FFh ;   

E000:EFF5 FF                   db 0FFh ;   

E000:EFF6 FF                   db 0FFh ;   

E000:EFF7 FF                   db 0FFh ;   

E000:EFF8 FF                   db 0FFh ;   

E000:EFF9 FF                   db 0FFh ;   

E000:EFFA FF                   db 0FFh ;   

E000:EFFB FF                   db 0FFh ;   

E000:EFFC FF                   db 0FFh ;   

E000:EFFD FF                   db 0FFh ;   

E000:EFFE FF                   db 0FFh ;   

E000:EFFF FF                   db 0FFh ;  

In Hexworkshop 3.02:

Address     Hex values                              ASCII 

0000EFE0 C300 0000 0000 0000 0000 0000 0000 0000 ................ 

0000EFF0 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................

Looking at the amount of padding bytes in original.tmp, we know that we have enough space to do the code injection. So, what we need to do is: use the hexeditor to replace 106 bytes beginning at E000:EFF0h (0000EFF0h) with the code that we already assembled (in 16-bit x86 executable binary format) in the previous step. In hexworkshop, this step is trivial, just open original.tmp and tweak.bin in the same hexworkshop, then copy and paste tweak.bin contents to original.bin, that's it :). The result in hexworkshop as follows (the hex-values highlighted in red is the injected code):

Address     Hex values                              ASCII 

0000EFD0 C300 0000 0000 0000 0000 0000 0000 0000 ................ 

0000EFE0 C300 0000 0000 0000 0000 0000 0000 0000 ................ 

0000EFF0 9C66 5052 66B8 5000 0080 BAF8 0C66 EFBA .fPRf.P......f.. 

0000F000 FC0C 66ED 660D 8000 0000 66EF 66B8 6400 ..f.f.....f.f.d. 

0000F010 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 .....f....f.f... 

0000F020 0200 66EF 66B8 6800 0080 BAF8 0C66 EFBA ..f.f.h......f.. 

0000F030 FC0C 66ED 660D 4008 0020 66EF 66B8 6C00 ..f.f.@.. f.f.l. 

0000F040 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0800 .....f....f.f... 

0000F050 0000 66EF 5A66 589D F8C3 FFFF FFFF FFFF ..f.ZfX......... 

0000F060 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................ 

0000F070 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................

If you eager to know what the code above accomplished, I provide the snapshot of my chipset datasheet below. Unfortunately, you still need know PCI protocol to make use of it. This is the snapshot for PCI device at address bus 0 - device 0 - function 0, i.e. the hostbridge of my mainboard.

Device 0 Configuration Registers - Host Bridge 

These registers are normally programmed once at system initialization time. 

Host CPU ControlDevice 0 Offset 50 

Request Phase Control (00h) ......... RW

7 CPU Hardwired IOQ (In Order Queue) Size   

Default per strap on pin MAB11#During reset. This   register can be written 0 to restrict the chip to one   level of IOQ.   

0 1-Level   

1 4-Level 

6 Read-Around-Write

0 Disable ...................................................default   

1 Enable 

5 Reserved ........................................ always reads 0

4 Defer Retry When HLOCK Active   

0 Disable ...................................................default   

1 Enable   Note: always set this bit to 1 

3-1 Reserved ........................................ always reads 0

0 CPU / PCI Master Read DRAM Timing

0 Start DRAM read after snoop complete ...... def   

1 Start DRAM read before snoop complete  

DRAM Control 

These registers are normally set at system initialization time and not accessed after that during normal system operation. Some of these registers, however, may need to be programmed using specific sequences during power-up initialization to properly detect the type and size of installed memory (refer to the VIA Technologies VT82C693A BIOS porting guide for details).  

SDRAM Settings for Registers 67-64

7 Precharge Command to Active Command Period

0 TRP = 2T

1 TRP = 3T ............................................... default

6 Active Command to Precharge Command Period

0 TRAS = 5T

1 TRAS = 6T ............................................. default 

5-4 CAS Latency      

00 1T      

01 2T      

10 3T ...................................... default      

11 reserved 

3 DIMM Type      

0 Standard      

1 Registered ............................................. default 

2 ACTIVE Command to CMD Command Period / VCM Prefetch Read Latency     

0 2T / 3T     

1 3T / 4T ................................................... default 

1-0 Bank Interleave     

00 No Interleave ......................................... default     

01 2-way     

10 4-way     

11 Reserved  

Device 0 Offset 68 - DRAM Control (00h) ...................... RW

7 SDRAM Open Page Control      

0 Always precharge SDRAM banks when        accessing EDO/FPG DRAMs.................default      

1 SDRAM banks remain active when accessing        EDO/FPG banks 

6 Bank Page Control      

0 Allow only pages of the same bank active.. def.      

1 Allow pages of different banks to be active 

5 Reserved ........................................ always reads 0

4 DRAM Data Latch Delay for EDO/FPG DRAM      

0 Latch DRAM data at CCLK rising edge .... def.      

1 Delay latch of DRAM data by ? CCLK 

3 EDO Test Mode      

0 Disable ...................................................default      

1 Enable 

2 Burst Refresh      

0 Disable ...................................................default      

1 Enable (burst 4 times) 

1 System Frequency Divider ..................................RO     

This bit is latched from MAB8# at the rising edge of RESET# (see table below). 

0 System Frequency Divider ..................................RO     

This bit is latched from MAB12# at the rising edge of RESET#.     

00 CPU Frequency = 66 MHz     

01 CPU Frequency = 100 MHz     

10 CPU Frequency = 133 MHz     

11 Reserved Note: See also Rx69[7-6] Note: MD0 is internally pulled up for EDO detection.  

Device 0 Offset 6C - SDRAM Control (00h) ................... RW

7-5 Reserved ........................................ always reads 0

4 CKE Configuration      

0 Rx6B[4]=0 RASA = CSA, RASB = CSB,

                  CKE0=CKE0, CKE1 = CKE1

      x Rx6B[4]=1 RASA = CSA, RASB = Float,

                  CASB = Float, MAB = Float,

                  CKE0 = CKE0, CKE1 = CKE0

1 Rx6B[4]=0 RASA = CSA, RASB = CSB,

                  CKE3-2 = CSA7-6

                  CKE5-4 = CSB7-6

                  CKE1 = GCKE (Global CKE)

                  CKE0 = FENA (FET Enable) 

3 Fast TLB Lookup      

0 Disable ...................................................default      

1 Enable 

2-0 SDRAM Operation Mode Select      

000 Normal SDRAM Mode ..........................default      

001 NOP Command Enable      

010 All-Banks-Precharge Command Enable (CPU-to-DRAM cycles are converted to All-Banks-Precharge commands).      

011 MSR Enable CPU-to-DRAM cycles are converted to commands and the commands are driven on MA[14:0]. The BIOS selects an appropriate          host address for each row of memory such that the right commands are generated on MA[14:0].      

100 CBR Cycle Enable (if this code is selected, CAS-before-RAS refresh is used; if it is not selected, RAS-Only refresh is used)      

101 Reserved      

11x Reserved

After this step, we proceed to next step to patch the jump table.

4. Modifying The Jump Table

Modifying the POST jump table is just a trivial task after we do the reverse engineering in the bios binary. As presented above in the prerequisite section, there are lots of jump table entries that points to "dummy" procedures.

I decided to redirect/replace the jump table entry at E000:61DEh to point to our injected procedure (at E000:EFF0h) instead to the previous "dummy" procedure. Below is the snapshot in both IDA Pro 4.50 and Hexworkshop, before the modification takes place :

In IDA Pro 4.50:

Address   Hex Values         Mnemonic    Comment 

E000:61DC 4E 23                dw 234Eh 

E000:61DE 53 23                dw 2353h             ; dummy 

E000:61E0 55 23                dw 2355h             ; dummy 

E000:61E2 57 23                dw 2357h             ; dummy 

E000:61E4 59 23                dw 2359h             ; init Programmable Timer (PIT) 

E000:61E6 A5 23                dw 23A5h             ; init PIC_1 (programmable Interrupt Ctlr) 

E000:61E8 B6 23                dw 23B6h             ; same as above ? 

E000:61EA F9 23                dw 23F9h             ; dummy 

E000:61EC FB 23                dw 23FBh             ; init PIC_2 

E000:61EE 78 24                dw 2478h             ; dummy 

E000:61F0 7A 24                dw 247Ah             ; dummy 

E000:61F2 7A 24                dw 247Ah 

E000:61F4 7A 24                dw 247Ah 

E000:61F6 7A 24                dw 247Ah ......... 

E000:2353 F8                   clc                  ; Clear Carry Flag 

E000:2354 C3                   retn                 ; Return Near from Procedure .........

In Hexworkshop 3.02:

Address     Hex values                              ASCII 

........ 

000061D0 B817 4B19 BC1A 081B C81D 4223 4E23 5323 ..K.......B#N#S# 

000061E0 5523 5723 5923 A523 B623 F923 FB23 7824 U#W#Y#.#.#.#.#x$ 

000061F0 7A24 7A24 7A24 7A24                     z$z$z$z$ 

........

Below is the snapshot in both IDA Pro 4.50 and Hexworkshop, after the modification takes place :

In IDA Pro 4.50:

Address   Hex Values                  Mnemonic    Comment 

E000:61DC 4E 23                      dw 234Eh 

E000:61DE F0 EF                      dw 0EFF0h     ;jump to our injected code 

E000:61E0 55 23                      dw 2355h 

......... 

E000:EFF0 9C                         pushf                ; Push Flags Register onto the Stack 

E000:EFF1 66 50                      push eax 

E000:EFF3 52                         push dx 

E000:EFF4 66 B8 50 00 00 80          mov  eax, 80000050h 

E000:EFFA BA F8 0C                   mov  dx, 0CF8h 

E000:EFFD 66 EF                      out  dx, eax 

E000:EFFF BA FC 0C                   mov  dx, 0CFCh 

E000:F002 66 ED                      in   eax, dx 

E000:F004 66 0D 80 00 00 00          or   eax, 80h        ; Logical Inclusive OR 

E000:F00A 66 EF                      out  dx, eax 

E000:F00C 66 B8 64 00 00 80          mov  eax, 80000064h 

E000:F012 BA F8 0C                   mov  dx, 0CF8h 

E000:F015 66 EF                      out  dx, eax 

E000:F017                          

E000:F017                         loc_EF017:              ; DATA XREF: E000:19975 

E000:F017                                                 ; E000:1997A 

E000:F017 BA FC 0C                   mov  dx, 0CFCh 

E000:F01A                          

E000:F01A                         loc_EF01A:              ; DATA XREF: E000:19962 

E000:F01A 66 ED                      in   eax, dx 

E000:F01C 66 0D 02 02 02 00          or   eax, 20202h     ; Logical Inclusive OR 

E000:F022 66 EF                      out  dx, eax 

E000:F024 66 B8 68 00 00 80          mov  eax, 80000068h 

E000:F02A BA F8 0C                   mov  dx, 0CF8h 

E000:F02D 66 EF                      out  dx, eax 

E000:F02F BA FC 0C                   mov  dx, 0CFCh 

E000:F032 66 ED                      in   eax, dx 

E000:F034 66 0D 40 08 00 20          or   eax, 20000840h  ; Logical Inclusive OR 

E000:F03A 66 EF                      out  dx, eax 

E000:F03C 66 B8 6C 00 00 80          mov  eax, 8000006Ch 

E000:F042 BA F8 0C                   mov  dx, 0CF8h 

E000:F045 66 EF                      out  dx, eax 

E000:F047 BA FC 0C                   mov  dx, 0CFCh 

E000:F04A 66 ED                      in   eax, dx 

E000:F04C 66 0D 08 00 00 00          or   eax, 8          ; Logical Inclusive OR 

E000:F052 66 EF                      out  dx, eax 

E000:F054 5A                         pop  dx 

E000:F055 66 58                      pop  eax 

E000:F057 9D                         popf                 ; Pop Stack into Flags Register 

E000:F058 F8                         clc                  ; Clear Carry Flag 

E000:F059 C3                         retn                 ; Return Near from Procedure 

.........

In Hexworkshop 3.02:

Address     Hex values                              ASCII 

........ 

000061D0 B817 4B19 BC1A 081B C81D 4223 4E23 F0EF ..K.......B#N#.. 

000061E0 5523 5723 5923 A523 B623 F923 FB23 7824 U#W#Y#.#.#.#.#x$ 

........ 

0000EFE0 C300 0000 0000 0000 0000 0000 0000 0000 ................ 

0000EFF0 9C66 5052 66B8 5000 0080 BAF8 0C66 EFBA .fPRf.P......f.. 

0000F000 FC0C 66ED 660D 8000 0000 66EF 66B8 6400 ..f.f.....f.f.d. 

0000F010 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0202 .....f....f.f... 

0000F020 0200 66EF 66B8 6800 0080 BAF8 0C66 EFBA ..f.f.h......f.. 

0000F030 FC0C 66ED 660D 4008 0020 66EF 66B8 6C00 ..f.f.@.. f.f.l. 

0000F040 0080 BAF8 0C66 EFBA FC0C 66ED 660D 0800 .....f....f.f... 

0000F050 0000 66EF 5A66 589D F8C3 FFFF FFFF FFFF ..f.ZfX......... 

........

By now, we've patched original.tmp to suit our need. The next thing to do is combining it back into one functional bios binary.

5. Recombining BIOS Component and Fixing Checksums

This step is also trivial, just open the previous bios binary from which we extract the original.tmp using awardbios editor. Then select the System BIOS tree-item in the left pane, and proceed to click the Action|Replace File menu. After that select the modified original.tmp as the file used to replace the genuine original.tmp in that bios binary. Then save this change in awardbios editor.

Actually we're done at this point, but some "nasty" Award BIOS sometimes causes awardbios editor failed to fix its checksum. To guard against this possible bug, open this modified bios binary using modbin, then do some minor changes, such as changing the bios string and then saving this change in modbin. This step, will causes modbin to recalculate all checksums and fix the possibly wrong checksums. That's all, voila' we're done :).

6. Testing The Hacked BIOS

Testing is also a trivial task, just flash the modified bios binary. I'm using uniflash to do this in my machine, since the awardflash is unable to handle my Atmel AT29C020C-90 backup-bios chip that I used in my mainboard, whereas uniflash v1.34 can handle flawlessly. Thanks to Ondrej Zary a.k.a Rainbow, who provide us with this great uniflash bios flashing utility. Thumbs up for all uniflash developer and contributor out there :).

Possible Downside and Its Workaround

During my experiment using this method to patch my bios, I encounter a weird situation that confusing at first. The bug that I encounter would hang my machine at boot, but it's very seldom and hard to reproduce, i.e. around 1 out of 30 tries. This bug is in effect if the following jump table modification is carried out.

Note

:

1. The modification I explained in the previous sections proved to be bug free after lots of testing and verifications.

2. The code is injected in the same place as explained in the previous sections.

The following is the jump table before the "buggy" patch incorporated :

Address     Hex Values         Mnemonic             Comment 

E000:61DE 53 23                dw 2353h             ; dummy 

E000:61E0 55 23                dw 2355h             ; dummy 

E000:61E2 57 23                dw 2357h             ; dummy 

E000:61E4 59 23                dw 2359h             ; init Programmable Timer (PIT) 

E000:61E6 A5 23                dw 23A5h             ; init PIC_1 (programmable Interrupt Ctlr) 

E000:61E8 B6 23                dw 23B6h             ; same as above ? 

E000:61EA F9 23                dw 23F9h             ; dummy 

E000:61EC FB 23                dw 23FBh             ; init PIC_2 

E000:61EE 78 24                dw 2478h             ; dummy 

E000:61F0 7A 24                dw 247Ah             ; dummy 

E000:61F2 7A 24                dw 247Ah 

E000:61F4 7A 24                dw 247Ah 

E000:61F6 7A 24                dw 247Ah .........

The following is the jump table after the "buggy" patch incorporated :

Address     Hex Values         Mnemonic             Comment 

E000:61DE 53 23                dw 2353h             ; dummy 

E000:61E0 55 23                dw 2355h             ; dummy 

E000:61E2 57 23                dw 2357h             ; dummy 

E000:61E4 59 23                dw 2359h             ; init Programmable Timer (PIT) 

E000:61E6 A5 23                dw 23A5h             ; init PIC_1 (programmable Interrupt Ctlr) 

E000:61E8 B6 23                dw 23B6h             ; same as above ? 

E000:61EA F9 23                dw 23F9h             ; dummy 

E000:61EC FB 23                dw 23FBh             ; init PIC_2 

E000:61EE 78 24                dw 2478h             ; dummy 

E000:61F0 F0 EF                dw EFF0h             ; dummy 

E000:61F2 7A 24                dw 247Ah 

E000:61F4 7A 24                dw 247Ah 

E000:61F6 7A 24                dw 247Ah 

.........

After further analysis, I conclude that this kind of bug very possibly related to timing issue and race condition during the code execution in POST. If we take a look closely at the jump table redirection, we see that this bug occur if we modify/redirect the jump table entry after the initialization of the Programmable Interrupt Controller (PIC) in the mainboard. Perhaps, the best way to avoid this is to place our jump table modification before the PIC initialization. Based on my testing result, doing so proved to be flawless and successfully eradicate the bug. I summarised some guidelines to avoid this bug in your jump table modification below :

That's all about the possible downsides of this method and their workaround. I'm not an experienced hardware hacker, thus it's possible that my explanation in this section is wrong. I really sorry about that, since I'm still in the process of learning about this subject too.

Critical Update

A Very Subtle Bug and Its Patch

After a more thorough testing, the bug that's caused by a race condition as explained in the previous section is not eradicated completely yet. It's true that previous explanation was being written after only up-to 30-40 boot-reboot cycle. With a thorough (a few hundred times) testing I found out that the bug still occured in around once in 50-60 boot-reboot cycle. After analyzing the previous patch that I made, I'm not aware that it has a bug. Only after a careful code-reading and code-execution-timing-scenario analysis I found out that the patch above was the major cause of the bug. The solution is to loosen the timing during the PCI cycles used to initialize the chipset registers. The working and tested solution for exactly the same purpose as the patch described in the above section is provided below in Fasm syntax. It takes more space, but it works perfectly.

;------------------------------ file: mem_optimize.asm ----------------------------------- 

use16  

start:

         pushf

         cli

         mov  cx, 0x50           ;patch the ioq register of the chipset

         call Read_PCI_Bus0_Byte

         or   al, 0x80

         mov  cx, 0x50

         call Write_PCI_Bus0_Byte

         mov  cx, 0x64           ;DRAM Bank 0/1 Interleave = 4-way

         call Read_PCI_Bus0_Byte

         or   al, 2

         mov  cx, 0x64

         call Write_PCI_Bus0_Byte

         mov  cx, 0x65           ;DRAM Bank 2/3 Interleave = 4-way

         call Read_PCI_Bus0_Byte

         or   al, 2

         mov  cx, 0x65

         call Write_PCI_Bus0_Byte

         mov  cx, 0x66           ;DRAM Bank 4/5 Interleave = 4-way

         call Read_PCI_Bus0_Byte

         or   al, 2

         mov  cx, 0x66

         call Write_PCI_Bus0_Byte

         mov  cx, 0x67           ;DRAM Bank 6/7 Interleave = 4-way

         call Read_PCI_Bus0_Byte

         or   al, 2

         mov  cx, 0x67

         call Write_PCI_Bus0_Byte

         mov  cx, 0x68           ;Allow pages of different bank to be active simultanoeusly

         call Read_PCI_Bus0_Byte

         or   al, 0x44

         mov  cx, 0x68

         call Write_PCI_Bus0_Byte

         mov  cx, 0x69           ;Fast DRAM Precharge for Different Bank

         call Read_PCI_Bus0_Byte

         or   al, 0x8

         mov  cx, 0x69

         call Write_PCI_Bus0_Byte

         mov  cx, 0x6C           ;Activate Fast TLB lookup

         call Read_PCI_Bus0_Byte

         or   al, 0x8

         mov  cx, 0x6C

         call Write_PCI_Bus0_Byte

         popf

         clc              ;indicate that this POST routine successful

         retn             ;return near to the header of the rom file

;-- Read_PCI_Byte__ -- 

;in: cx = dev_func_offset_addr 

;out: al = reg_value  

Read_PCI_Bus0_Byte:

         mov   ax, 8000h

         shl   eax, 10h

         mov   ax, cx

         and   al, 0FCh

         mov   dx, 0CF8h

         out   dx, eax

         mov   dl, 0FCh ; '?'

         mov   al, cl

         and   al, 3

         add   dl, al

         in    al, dx

         retn   

;-- Write_Bus0_Byte -- 

;in: cx = dev_func_offset addr 

;al = reg_value to write  

Write_PCI_Bus0_Byte:

         xchg  ax, cx

         shl   ecx, 10h

         xchg  ax, cx

         mov   ax, 8000h

         shl   eax, 10h

         mov   ax, cx

         and   al, 0FCh

         mov   dx, 0CF8h

         out   dx, eax

         add   dl, 4

         or    dl, cl

         mov   eax, ecx

         shr   eax, 10h

         out   dx, al

         retn 

;------------------------------ file: mem_optimize.asm -----------------------------------

Assembling the patch source code in fasmw (fasm for windows) is done by pressing CTRL+F9. As simple as that (^__^). This new patch only initializes one register at a time and gives enough "CPU clock-cycle" to the PCI bus intensive routine. Personally, I think that to appropriately initialize a PCI chipset it's not enough just by relaxing the read-write timing, but more importantly we have to initialize only one register at a time in order to minimize the "sudden-load" in the chipset. This is especially true for performance-related registers within the chipset. In my tests for this new patch, I placed the call to the patch in a few places within the POST-jump-table an everyone of them work flawlessly as expected. The testing has been carried out more than 100 boot-reboot cycle for each variant.

Below is the comparison from the latest variant that undergoes code-injection.

E000:61C2                         Begin_E000_POST_Jmp_Table 

E000:61C2 4E 15                     dw 154Eh                  ; restore warm-boot flag 

E000:61C4 6F 15                     dw 156Fh                  ; dummy 

E000:61C6 71 15                     dw 1571h                  ; initialize KBC (Keyboard Controller), halt on error 

E000:61C8 D2 16                     dw 16D2h                  ; 1. check Fseg in RAM, beep on-error; 

E000:61C8                                                     ; 2. identify FlashROM chip 

E000:61CA 45 17                     dw 1745h                  ; chk CMOS circuit 

E000:61CC 8A 17                     dw 178Ah                  ; Chipset reg Default values (code in awardext.rom, data in Fseg) 

E000:61CE 98 17                     dw 1798h                  ; 1. init CPU Flags 

E000:61CE                                                     ; 2. disable A20 

E000:61D0 B8 17                     dw 17B8h                  ; 1. init interrupt vector 

E000:61D0                                                     ; 2. initialize "signatures" used for Ext_BIOS components 

E000:61D0                                                     ;    decompression. 

E000:61D0                                                     ; 3. init PwrMgmtCtlr 

E000:61D2 4B 19                     dw 194Bh                  ; 1. init FPU 

E000:61D2                                                     ; 2. init microcode (init CPU) 

E000:61D2                                                     ; 3. init FSB (clock gen) 

E000:61D2                                                     ; 4. init W87381D VID regs 

E000:61D4 BC 1A                     dw 1ABCh                  ; update flags n BIOS data area 

E000:61D6 08 1B                     dw 1B08h                  ; 1. NNOPROM n ROSUPD decompression 

E000:61D6                                                     ; 2. Video BIOS initialization 

E000:61D8 C8 1D                     dw 1DC8h                  ; init video controller, video BIOS, EPA Procedure 

E000:61DA 42 23                     dw 2342h                  ; init KB?? 

E000:61DC 4E 23                     dw 234Eh                  ; dummy 

E000:61DE 53 23                     dw 2353h                  ; dummy 

E000:61E0 55 23                     dw 2355h                  ; dummy 

E000:61E2 57 23                     dw 2357h                  ; dummy 

E000:61E4 59 23                     dw 2359h                  ; init mobo timer 

E000:61E6 A5 23                     dw 23A5h                  ; init Interrupt Controller 

E000:61E8 B6 23                     dw 23B6h                  ; init Interrupt Controller cont'd 

E000:61EA F9 23                     dw 23F9h                  ; dummy 

E000:61EC FB 23                     dw 23FBh                  ; init Interrupt Controller cont'd 

E000:61EE 78 24                     dw 2478h                  ; dummy 

E000:61F0 7A 24                     dw 247Ah                  ; dummy 

E000:61F2 7A 24                     dw 247Ah 

E000:61F4 7A 24                     dw 247Ah 

E000:61F6 7A 24                     dw 247Ah 

E000:61F8 7C 24                     dw 247Ch                  ; call ISA POST tests (below) 

E000:61F8                         End_E000_POST_Jmp_Table

       

E000:61C2                         Begin_E000_POST_Jmp_Table 

E000:61C2 4E 15                     dw 154Eh 

E000:61C4 6F 15                     dw 156Fh                      ; dummy procedure 

E000:61C6 71 15                     dw 1571h                      ; initialize KBC (Keyboard Controller), halt on error 

E000:61C8 D2 16                     dw 16D2h                      ; 1. check Fseg in RAM, beep on-error; 

E000:61C8                                                         ; 2. identify FlashROM chip 

E000:61CA 45 17                     dw 1745h                      ; chk CMOS circuit 

E000:61CC 8A 17                     dw 178Ah                      ; Chipset reg Default values (code in awardext.rom, data in Fseg) E000:61CE 98 17                     dw 1798h                      ; 1. init CPU Flags 

E000:61CE                                                         ; 2. disable A20 

E000:61D0 B8 17                     dw 17B8h                      ; 1. init interrupt vector 

E000:61D0                                                         ; 2. initialize "signatures" used for Ext_BIOS components 

E000:61D0                                                         ;    decompression. 

E000:61D0                                                         ; 3. init PwrMgmtCtlr 

E000:61D2 4B 19                     dw 194Bh                      ; 1. init FPU 

E000:61D2                                                         ; 2. init microcode (init CPU) 

E000:61D2                                                         ; 3. init FSB (clock gen) 

E000:61D2                                                         ; 4. init W87381D VID regs 

E000:61D4 F0 EF                     dw 0EFF0h                     ; PatchChipset <--- our patch 

E000:61D6 BC 1A                     dw 1ABCh                      ; update flags n BIOS data area 

E000:61D8 08 1B                     dw 1B08h                      ; 1. NNOPROM n ROSUPD decompression 

E000:61D8                                                         ; 2. Video BIOS initialization 

E000:61DA C8 1D                     dw 1DC8h                      ; init video controller, video BIOS, EPA Procedure 

E000:61DC 42 23                     dw 2342h                      ; init KB?? 

E000:61DE 4E 23                     dw 234Eh 

E000:61E0 53 23                     dw 2353h                      ; dummy procedure 

E000:61E2 55 23                     dw 2355h                      ; dummy procedure 

E000:61E4 57 23                     dw 2357h                      ; dummy procedure 

E000:61E6 59 23                     dw 2359h                      ; init mobo timer 

E000:61E8 A5 23                     dw 23A5h                      ; init Interrupt Controller 

E000:61EA B6 23                     dw 23B6h                      ; init Interrupt Controller cont'd 

E000:61EC F9 23                     dw 23F9h                      ; dummy procedure 

E000:61EE FB 23                     dw 23FBh                      ; init Interrupt Controller cont'd 

E000:61F0 78 24                     dw 2478h                      ; dummy procedure 

E000:61F2 7A 24                     dw 247Ah                      ; dummy procedure 

E000:61F4 7A 24                     dw 247Ah                      ; dummy procedure 

E000:61F6 7A 24                     dw 247Ah                      ; dummy procedure 

E000:61F8 7C 24                     dw 247Ch                      ; call ISA POST tests  

E000:61F8                         End_E000_POST_Jmp_Table ......... 

E000:EFF0                         Patch_Chipset proc near 

E000:EFF0 9C                        pushf 

E000:EFF1 FA                        cli 

E000:EFF2 B9 50 00                  mov   cx, 50h ; 'P' 

E000:EFF5 E8 6D 00                  call  Read_PCI_Bus0_Byte 

E000:EFF8 0C 80                     or    al, 80h 

E000:EFFA B9 50 00                  mov   cx, 50h ; 'P' 

E000:EFFD E8 7F 00                  call  Write_PCI_Bus0_Byte 

E000:F000 B9 64 00                  mov   cx, 64h ; 'd' 

E000:F003 E8 5F 00                  call  Read_PCI_Bus0_Byte 

E000:F006 0C 02                     or    al, 2 

E000:F008 B9 64 00                  mov   cx, 64h ; 'd' 

E000:F00B E8 71 00                  call  Write_PCI_Bus0_Byte 

E000:F00E B9 65 00                  mov   cx, 65h ; 'e' 

E000:F011 E8 51 00                  call  Read_PCI_Bus0_Byte 

E000:F014 0C 02                     or    al, 2 

E000:F016 B9 65 00                  mov   cx, 65h ; 'e' 

E000:F019 E8 63 00                  call  Write_PCI_Bus0_Byte 

E000:F01C B9 66 00                  mov   cx, 66h ; 'f' 

E000:F01F E8 43 00                  call  Read_PCI_Bus0_Byte 

E000:F022 0C 02                     or    al, 2 

E000:F024 B9 66 00                  mov   cx, 66h ; 'f' 

E000:F027 E8 55 00                  call  Write_PCI_Bus0_Byte 

E000:F02A B9 67 00                  mov   cx, 67h ; 'g' 

E000:F02D E8 35 00                  call  Read_PCI_Bus0_Byte 

E000:F030 0C 02                     or    al, 2 

E000:F032 B9 67 00                  mov   cx, 67h ; 'g' 

E000:F035 E8 47 00                  call  Write_PCI_Bus0_Byte 

E000:F038 B9 68 00                  mov   cx, 68h ; 'h' 

E000:F03B E8 27 00                  call  Read_PCI_Bus0_Byte 

E000:F03E 0C 44                     or    al, 44h 

E000:F040 B9 68 00                  mov   cx, 68h ; 'h' 

E000:F043 E8 39 00                  call  Write_PCI_Bus0_Byte 

E000:F046 B9 69 00                  mov   cx, 69h ; 'i' 

E000:F049 E8 19 00                  call  Read_PCI_Bus0_Byte 

E000:F04C 0C 08                     or    al, 8 

E000:F04E B9 69 00                  mov   cx, 69h ; 'i' 

E000:F051 E8 2B 00                  call  Write_PCI_Bus0_Byte 

E000:F054 B9 6C 00                  mov   cx, 6Ch ; 'l' 

E000:F057 E8 0B 00                  call  Read_PCI_Bus0_Byte 

E000:F05A 0C 08                     or    al, 8 

E000:F05C B9 6C 00                  mov   cx, 6Ch ; 'l' 

E000:F05F E8 1D 00                  call  Write_PCI_Bus0_Byte

E000:F062 9D                        popf 

E000:F063 F8                        clc 

E000:F064 C3                        retn 

E000:F064                         Patch_Chipset endp 

E000:F065 

E000:F065                         Read_PCI_Bus0_Byte proc near    ; CODE XREF: Patch_Chipset+5 

E000:F065                                                         ; Patch_Chipset+13 

E000:F065 B8 00 80                  mov   ax, 8000h 

E000:F068 66 C1 E0 10               shl   eax, 10h 

E000:F06C 89 C8                     mov   ax, cx 

E000:F06E 24 FC                     and   al, 0FCh 

E000:F070 BA F8 0C                  mov   dx, 0CF8h 

E000:F073 66 EF                     out   dx, eax 

E000:F075 B2 FC                     mov   dl, 0FCh ; '?' 

E000:F077 88 C8                     mov   al, cl 

E000:F079 24 03                     and   al, 3 

E000:F07B 00 C2                     add   dl, al 

E000:F07D EC                        in    al, dx 

E000:F07E C3                        retn 

E000:F07E                         Read_PCI_Bus0_Byte endp 

E000:F07E 

E000:F07F                         Write_PCI_Bus0_Byte proc near   ; CODE XREF: Patch_Chipset+D 

E000:F07F                                                         ; Patch_Chipset+1B 

E000:F07F 91                        xchg  ax, cx 

E000:F080 66 C1 E1 10               shl   ecx, 10h 

E000:F084 91                        xchg  ax, cx 

E000:F085 B8 00 80                  mov   ax, 8000h 

E000:F088 66 C1 E0 10               shl   eax, 10h 

E000:F08C 89 C8                     mov   ax, cx 

E000:F08E 24 FC                     and   al, 0FCh 

E000:F090 BA F8 0C                  mov   dx, 0CF8h 

E000:F093 66 EF                     out   dx, eax 

E000:F095 80 C2 04                  add   dl, 4 

E000:F098 08 CA                     or    dl, cl 

E000:F09A 66 89 C8                  mov   eax, ecx 

E000:F09D 66 C1 E8 10               shr   eax, 10h 

E000:F0A1 EE                        out   dx, al 

E000:F0A2 C3                        retn 

E000:F0A2                         Write_PCI_Bus0_Byte endp

       

If you compare both of the jump-table, the latter has a patched jump-table with the jump into the chipset-patching-procedure located right after the FSB initialization. I've been experimenting with other possibilities, such as inserting the call into the chipset-patching-procedure inside the "Chipset reg Default values (code in awardext.rom, data in Fseg)",i.e. call to E000:178Ah in the jump-table and it worked flawlessly.

Closing Note

Finally we're done. Yeah, this bios hacking method is very possibly my ultimate bios hacking trick to date. I haven't found any new elegant way to accomplish it. But, remember to pay attention to the timing issue for your injected code. I believe that I might have made obscure mistakes in this article. Thus, it's always open for corrections and improvements. Thanks for reading this humble article. I hope it's of some use for you.

I'm waiting for any comments, corrections and suggestions from the reader. Don't hesitate to mail me.

Copyright © Darmawan M S a.k.a Pinczakko