Pinczakko's Guide to Reversing Award BIOS DLL Support

Table of Contents

1. Foreword

Welcome to another installment of the Pinczakko Guide's series :). In this article I explain how the emerging "support" for Windows Dynamic Link Libraries (DLL) is implemented in recent Award BIOS. As you will see later, full-fledged DLL is not supported by Award BIOS. In fact, the DLL header is just discarded entirely by the Award BIOS code.

This article assumes that you have a deep understanding of Award BIOS binaries. If you haven't mastered it yet, you'd better read the prerequisite section below and make sure that you've armed yourself with enough knowledge before proceeding to read this article.

The purpose of this article is to be your reference when disassembling a DLL file in Award BIOS binary. It's important to know the execution environment of the DLL in order to understand the DLL contents. Anyway, I'm not held responsible about the correctness of any explanation in this article, you have to cross-check what I wrote here and what you have in your hand.

Note: The explanation in this article is based on 8Mbit(1MB) Award BIOS version 6.00PG (a "legacy" BIOS code base, not EFI/UEFI). The BIOS dissected here is M78GUS2H.BIN; the BIOS binary file for Gigabyte GA-MA78GM-US2H motherboard. The BIOS release date is September 17th, 2009.

2. Prerequisites

The BIOS is somehow a state of the art code that requires lots of low level x86 knowledge that only matter to such a small audience such as operating system developer, BIOS developer, driver writer, possibly exploit and virus writer (yes exploit and virus writer! coz they are curious people). Due to this fact, there are couple of things that I won't explain here and it's your homework that you should do to comprehend this guide. They are:

  • The most important thing is you have to be able to program and understand x86 assembly language. If you don't know it, then you'd better start learning it. I'm using MASM syntax throughout this article.
  • Protected mode programming. You have to learn how to switch the x86 machine from real mode to protected mode. This means you need to learn a preliminary x86 protected mode OS development. I've done it in the past, that's why I have fairly good knowledge on the matter. You can go to www.osdever.net and other x86 operating system developer site to get some tutorials to make yourself comfortable. The most important thing to master is how the protected mode data structures work. I mean how Global Descriptor Table (GDT), Interrupt Descriptor Table (IDT), x86 control registers and segment registers work.
  • What x86 "unreal-mode" is. Some people also call these mode of operation "voodoo-mode" or "flat real-mode ". It's an x86 state that's between real-mode and protected-mode. Remember that "flat real-mode" and flat protected-mode are two different beast.
  • x86 "direct hardware programming". You need to know how to program the hardware directly, especially the chips in your motherboard. You can practice this in Windows by developing an application that directly access the hardware. This is not a must, but it's better if you master it first.
  • You have to have deep understanding of Award BIOS binary file. I have written an in-depth article about the matter here. You should read that article if you haven't understand the basic structure and execution flow of an Award BIOS.
  • Some familiarity with PE file format is important. You can read Iczelion's PE tutorials about the subject if you haven't know it yet.
  • Some familiarity with DOS MZ file format is also important. The article in this link explains about the subject.
Those are all you need to know to understand this article. If you think you're prepared, move on to the next section.

3. The Rationale Behind DLL "Support" in BIOS

Recent BIOS supports 32-bit DLL or certain "derivative" form of 32-bit DLL (read: stripped to the bare essential) for several reasons:

  1. For code reuse. Several years ago the migration to UEFI-based firmware in x86/x86-64 platforms were started. This event "forces" the chipset vendors to provide one code base for both UEFI and legacy BIOS if they don't want to duplicate the chipset-specific code for both firmware types. This means, the code must be provided in 32-bit environment, specifically in the form of 32-bit Portable Executable (PE) file format because UEFI supports that file format. DLL is a file with PE file format.
  2. To ease code maintenance. The move to 32-bit base code means that the use of C language for BIOS module development is possible. Moving to higher level language from assembler means the burden on maintaining the source code for the BIOS module is relatively reduced.
There are probably other reasons for the DLL "support", but those are the ones that I'm aware of at the moment.

4. The DLL

DLL components in Award BIOS are uncompressed components. Some of them are called directly from the bootblock. That's the reason why they are not compressed at all.

You can use CBROM or a hex editor to extract the DLL components from the BIOS binary. The CBROM command to extract a DLL component is simple. For example, to extract HT.DLL from the M78GUS2H.BIN binary file, I use the following command.

CBROM32_195.exe M78GUS2H.BIN /htinit extract 
Another example, to extract ahci.DLL from the M78GUS2H.BIN binary file, I use the following command.
CBROM32_195.exe M78GUS2H.BIN /oem3 extract 
The command to use to extract the DLL varies depending on the "type" of the DLL. The following command shows how to look for the type of files within the M78GUS2H.BIN binary file.
CBROM32_195.exe M78GUS2H.BIN /D

              ******** M78GUS2H.BIN BIOS component ********

 No. Item-Name         Original-Size   Compressed-Size Original-File-Name 
================================================================================  
  0. System BIOS       20000h(128.00K)12B4Ch(74.82K)M78GUS2H.BIN
  1. XGROUP CODE       0E6B0h(57.67K)09ED3h(39.71K)awardext.rom
  2. ACPI table        0748Dh(29.14K)03479h(13.12K)ACPITBL.BIN
  3. EPA LOGO          0168Ch(5.64K)0030Dh(0.76K)AwardBmp.bmp
  4. GROUP ROM[18]     04500h(17.25K)02E43h(11.57K)ggroup.bin
  5. GROUP ROM[20]     030E0h(12.22K)02411h(9.02K)ffgroup.bin
  6. YGROUP ROM        0BC50h(47.08K)060CDh(24.20K)awardeyt.rom
  7. GROUP ROM[22]     0F630h(61.55K)008A2h(2.16K)tgroup.bin
  8. GROUP ROM[23]     0F630h(61.55K)0015Bh(0.34K)t1group.bin
  9. GROUP ROM[24]     0F630h(61.55K)0015Ch(0.34K)t2group.bin
 10. GROUP ROM[ 0]     07660h(29.59K)03041h(12.06K)_EN_CODE.BIN
 11. OEM1 CODE         0FFFFh(64.00K)012F6h(4.74K)GBTEC140.BIN
 12. OEM2 CODE         0FFFFh(64.00K)00C3Eh(3.06K)GBTEC8_2.BIN
 13. VGA ROM[1]        0EA00h(58.50K)094CFh(37.20K)VGA_DVI.BIN
 14. OEM6 CODE         0EA00h(58.50K)094D0h(37.20K)VGA_HDMI.BIN
 15. PCI ROM[A]        0FC00h(63.00K)09CBCh(39.18K)RAID750.BIN
 16. PCI ROM[B]        03600h(13.50K)02427h(9.04K)ahci.BIN
 17. OEM3 CODE         0C000h(48.00K)0704Ch(28.07K)ahci.DLL
 18. PCI ROM[C]        0B800h(46.00K)068C8h(26.20K)RTEGPXE.LOM
 19. LOGO1 ROM         00B64h(2.85K)00520h(1.28K)DBIOS.BMP
 20. OEM0 CODE         03006h(12.01K)022A6h(8.66K)SBF.BIN
 21. GV3                09003h(36.00K)03072h(12.11K)AGESACPU.ROM
 22. MINIT             0E5EFh(57.48K)0E61Eh(57.53K)MEMINIT.BIN
 23. HTINIT             05011h(20.02K)05042h(20.06K)HT.DLL
 24. 2 PE32 in MB       00550h(1.33K)0058Fh(1.39K)HT32GATE.BIN
(SP) NCPUCODE          06000h(24.00K)06000h(24.00K)NCPUCODE.BIN
(SP) HOLE0             7CD8h(31.21K)  7CD8h(31.21K)CIMRS780.B2
(SP) HOLE1             2FE8h(11.98K)  2FE8h(11.98K)CIMSB700.B2
(SP) HOLE2             C800h(50.00K)  C800h(50.00K)UI750.BIN
(SP) HOLE3             FFFFh(64.00K)  FFFFh(64.00K)GBTEC140.BIN

  Total hole area space      = 30000h(192.00K)
  Total compress code space  = A4000h(656.00K)
  Total compressed code size = 7C771h(497.86K)
  Remain compress code space = 278AFh(158.17K)

                          ** Micro Code Information **
Update ID  CPUID  |  Update ID  CPUID  |  Update ID  CPUID  |  Update ID  CPUID
------------------+--------------------+--------------------+-------------------     
1000002     0000| 
The second column (Item-Name) above is the "type". You can use the following command to find out how to work with certain "type" of item in the BIOS binary.
CBROM32_195.exe /?

That's it for the intro to the DLL in Award BIOS. The next sections will explain the details.

4.1 The DLL Execution Environment

Note: Listings in this article show "alternating" addresses in the BIOS binary because they are meant to reveal the code execution flow, not the position of the code in the BIOS binary.

The DLL file in Award BIOS executes in flat 32-bit protected mode with linear address equal to physical address and paging is disabled. Listing 0 shows the Global Descriptor Table (GDT) and the switch to 32-bit flat protected mode routine prior to execution of the DLL.

Listing 0: DLL execution environment in recent Award BIOS

Address    Hex                  Mnemonic
......... 
F000:2F0D FF FF 00 00 00 93 8F 00 CIM_GDT         dq 8F93000000FFFFh      ; 16-bit segment; limit=4GB; base addr=0000_0000;
F000:2F0D                                                                 ; present; user:data (R/W)
F000:2F15 FF FF 00 00 00 93 CF 00                 dq 0CF93000000FFFFh     ; 32-bit segment; limit=4GB; base addr=0000_0000;
F000:2F15                                                                 ; present; user:data (R/W)
F000:2F1D FF FF 00 00 00 9B CF 00                 dq 0CF9B000000FFFFh     ; 32-bit segment; limit=4GB; base addr=0000_0000;
F000:2F1D                                                                 ; present; user:code (Readable/eXecutable)
F000:2F25 FF FF 20 00 0E 9B 8F 00                 dq 8F9B0E0020FFFFh      ; 16-bit segment; limit=4GB; base addr=000E_0020;
F000:2F25                                                                 ; present; user:code (Readable/eXecutable)
F000:2F2D FF FF 00 00 00 93 0F 00                 dq 0F93000000FFFFh      ; 16-bit segment; limit=1MB; base addr=0000_0000;
F000:2F2D                                                                 ; present; user:data (R/W)

.........
F000:2F6D 00 62 FB FF             HT32GATE_BIN_phy_addr dd 0FFFB6200h     ; phy addr of HT32GATE.BIN start in BIOS ROM
F000:2F71                         xec_32_flat_pm_HT32GATE proc near       
F000:2F71 FA                                      cli
F000:2F72 FC                                      cld
F000:2F73 66 52                                   push    edx
F000:2F75 66 B8 04 00 F0 FF                       mov     eax, 0FFF00004h
F000:2F7B 66 33 D2                                xor     edx, edx
F000:2F7E 66 B9 0E 02 00 00                       mov     ecx, 20Eh
F000:2F84 0F 30                                   wrmsr
F000:2F86 FE C1                                   inc     cl
F000:2F88 66 B8 00 08 F0 FF                       mov     eax, 0FFF00800h
F000:2F8E 66 BA FF 00 00 00                       mov     edx, 0FFh
F000:2F94 0F 30                                   wrmsr
F000:2F96 66 5A                                   pop     edx
F000:2F98 8B C4                                   mov     ax, sp
F000:2F9A 50                                      push    ax
F000:2F9B 8C D0                                   mov     ax, ss
F000:2F9D 50                                      push    ax
F000:2F9E B9 28 00                                mov     cx, 28h ; '('
F000:2FA1 2B E1                                   sub     sp, cx
F000:2FA3 8B EC                                   mov     bp, sp
F000:2FA5 8D 3E 0D 2F                             lea     di, CIM_GDT 
F000:2FA9
F000:2FA9                         loc_F000_2FA9:                          
F000:2FA9 2E 8A 05                                mov     al, cs:[di]
F000:2FAC 88 46 00                                mov     [bp+0], al
F000:2FAF 47                                      inc     di
F000:2FB0 45                                      inc     bp
F000:2FB1 E2 F6                                   loop    loc_F000_2FA9
F000:2FB3 8B EC                                   mov     bp, sp
F000:2FB5 66 33 C0                                xor     eax, eax
F000:2FB8 8C C8                                   mov     ax, cs
F000:2FBA 66 C1 E0 04                             shl     eax, 4
F000:2FBE 89 46 1A                                mov     [bp+1Ah], ax
F000:2FC1 66 C1 E8 10                             shr     eax, 10h
F000:2FC5 88 46 1C                                mov     [bp+1Ch], al
F000:2FC8 66 33 C0                                xor     eax, eax
F000:2FCB 8C D0                                   mov     ax, ss
F000:2FCD 66 C1 E0 04                             shl     eax, 4
F000:2FD1 66 03 C4                                add     eax, esp
F000:2FD4 66 50                                   push    eax
F000:2FD6 68 27 00                                push    27h ; '''
F000:2FD9 8B EC                                   mov     bp, sp
F000:2FDB 0F 01 56 00                             lgdt    fword ptr [bp+0]
F000:2FDF 66 33 C0                                xor     eax, eax
F000:2FE2 8C D0                                   mov     ax, ss
F000:2FE4 66 C1 E0 04                             shl     eax, 4
F000:2FE8 66 03 C4                                add     eax, esp
F000:2FEB 66 8B E0                                mov     esp, eax
F000:2FEE 0F 20 C0                                mov     eax, cr0
F000:2FF1 0C 01                                   or      al, 1
F000:2FF3 0F 22 C0                                mov     cr0, eax
F000:2FF6 EB 00                                   jmp     short $+2
F000:2FF8 B8 08 00                                mov     ax, 8
F000:2FFB 8E D8                                   mov     ds, ax
F000:2FFD                                         assume ds:nothing
F000:2FFD 8E C0                                   mov     es, ax
F000:2FFF                                         assume es:nothing
F000:2FFF 8E E0                                   mov     fs, ax
F000:3001                                         assume fs:nothing
F000:3001 8E E8                                   mov     gs, ax
F000:3003                                         assume gs:nothing
F000:3003 8E D0                                   mov     ss, ax
F000:3005                                         assume ss:nothing
F000:3005 66 B8 00 F0 00 00                       mov     eax, 0F000h
F000:300B 66 C1 E0 04                             shl     eax, 4
F000:300F 66 05 21 30 00 00                       add     eax, 3021h
F000:3015 66 50                                   push    eax
F000:3017 66 68 10 00 00 00                       push    large 10h
F000:301D 66 56                                   push    esi             ; esi = phy addr of HT32GATE.BIN entry point
F000:301F                                         db      66h
F000:301F 66 CB                                   retf                    ; call HT32GATE.BIN entry point and enter 32-bit Protected Mode 

Listing 0 doesn't show how the DLL gets called. Section 4.2 will explain that in detail. The important thing from listing 0 is the listing shows the state of the segment descriptors prior to HT32GATE.BIN execution. HT32GATE.BIN calls the DLL module directly without changing anything in the segment descriptors. Therefore, the execution environment of both HT32GATE.BIN and the DLL is the same.

4.2 How The BIOS "Calls" The DLL

The process to call the DLL starts at the bootblock and continues to uncompressed parts of the BIOS binary until the DLL file gets executed.

Listing 1: BIOS code execution from reset until the call to HT32GATE.BIN

Address    Hex                  Mnemonic
F000:FFF0 EA 5B E0 00 F0                          jmp     far ptr bb_entry  
.........
F000:E05B                         bb_entry:                               
F000:E05B EA 60 E0 00 F0                          jmp     far ptr loc_F000_E060
F000:E060
F000:E060                         loc_F000_E060:
F000:E060 8E EA                                   mov     gs, dx
F000:E062 FA                                      cli
F000:E063 FC                                      cld
F000:E064 8C C8                                   mov     ax, cs
F000:E066 8E D0                                   mov     ss, ax
F000:E068                                         assume ss:nothing
F000:E068
F000:E068                         loc_F000_E068:
F000:E068 66 B9 1B 00 00 00                       mov     ecx, 1Bh
F000:E06E 0F 32                                   rdmsr
F000:E070 A9 00 01                                test    ax, 100h
F000:E073 74 20                                   jz      short init_chipset 
.........
F000:E095                         init_chipset:                           
F000:E095 BC 9B E0                                mov     sp, 0E09Bh
F000:E098 E9 D5 4F                                jmp     prep_exec_HT_init 
.........
F000:3070                         prep_exec_HT_init proc near             
F000:3070 0F 6E CC                                movd    mm1, esp
F000:3073 BC 79 30                                mov     sp, 3079h
F000:3076 E9 57 60                                jmp     c_c_exec_HT32GATE_N_HT_DLL 
.........
F000:308C C3                                      retn
F000:308C                         prep_exec_HT_init endp
.........
F000:90D0                         c_c_exec_HT32GATE_N_HT_DLL proc near   
F000:90D0 BB D6 90                                mov     bx, 90D6h
F000:90D3 E9 8E 07                                jmp     sub_F000_9864
.........
F000:913D E8 2B 00                                call    near ptr c_exec_HT32GATE_N_HT_DLL 
F000:9140 E8 C0 CE                                call    nullsub_5
F000:916A C3                                      retn
F000:916A                         c_c_exec_HT32GATE_N_HT_DLL endp
.........
F000:916B                         c_exec_HT32GATE_N_HT_DLL proc far     
.........
F000:9193 E8 BA DA                                call    sub_F000_6C50
F000:9196 72 05                                   jb      short loc_F000_919D
F000:9198 E8 25 9D                                call    exec_HT32GATE_N_HT_DLL 
.........
F000:91E4 C3                                      retn
F000:91E4                         c_exec_HT32GATE_N_HT_DLL endp
.........
F000:2EC0                         exec_HT32GATE_N_HT_DLL proc near        
F000:2EC0 66 B9 1B 00 00 00                       mov     ecx, 1Bh
F000:2EC6 0F 32                                   rdmsr
F000:2EC8 66 0F BA E0 08                          bt      eax, 8
F000:2ECD 73 31                                   jnb     short loc_F000_2F00
F000:2ECF 66 8D 1E 45 2F                          lea     ebx, HT_DLL_entry_phy_addr
F000:2ED4 67 66 2E 8B 13                          mov     edx, cs:[ebx]
F000:2ED9 66 8B F2                                mov     esi, edx 
F000:2EDC E8 86 6F                                call    sub_F000_9E65
F000:2EDF E8 CE 6E                                call    sub_F000_9DB0
F000:2EE2 72 1F                                   jb      short loc_F000_2F03
F000:2EE4 66 8D 36 6D 2F                          lea     esi, HT32GATE_BIN_phy_addr
F000:2EE9 67 66 2E 8B 06                          mov     eax, cs:[esi] 
F000:2EEE 66 8B F0                                mov     esi, eax 
F000:2EF1 E8 BC 6E                                call    sub_F000_9DB0
F000:2EF4 72 0D                                   jb      short loc_F000_2F03
F000:2EF6 66 81 C6 00 02 00 00                    add     esi, 200h       ; esi = HT32GATE entry point (discard DOS MZ header)
F000:2EFD E8 71 00                                call    xec_32_flat_pm_HT32GATE 
F000:2F00
F000:2F00                         loc_F000_2F00:                         
F000:2F00 F8                                      clc
F000:2F01 EB 09                                   jmp     short exit
F000:2F03                         ; ---------------------------------------------------------------------------
F000:2F03
F000:2F03                         loc_F000_2F03:                        
F000:2F03                                                              
F000:2F03 B0 F9                                   mov     al, 0F9h ; '·'
F000:2F05 E6 72                                   out     72h, al
F000:2F07 B0 55                                   mov     al, 55h ; 'U'
F000:2F09 E6 73                                   out     73h, al
F000:2F0B F9                                      stc
F000:2F0C
F000:2F0C                         exit:                               
F000:2F0C C3                                      retn
F000:2F0C                         exec_HT32GATE_N_HT_DLL endp
F000:2F0D FF FF 00 00 00 93 8F 00 CIM_GDT         dq 8F93000000FFFFh      ; 16-bit segment; limit=4GB; base addr=0000_0000;
F000:2F0D                                                                 ; present; user:data (R/W)
F000:2F15 FF FF 00 00 00 93 CF 00                 dq 0CF93000000FFFFh     ; 32-bit segment; limit=4GB; base addr=0000_0000;
F000:2F15                                                                 ; present; user:data (R/W)
F000:2F1D FF FF 00 00 00 9B CF 00                 dq 0CF9B000000FFFFh     ; 32-bit segment; limit=4GB; base addr=0000_0000;
F000:2F1D                                                                 ; present; user:code (Readable/eXecutable)
F000:2F25 FF FF 20 00 0E 9B 8F 00                 dq 8F9B0E0020FFFFh      ; 16-bit segment; limit=4GB; base addr=000E_0020;
F000:2F25                                                                 ; present; user:code (Readable/eXecutable)
F000:2F2D FF FF 00 00 00 93 0F 00                 dq 0F93000000FFFFh      ; 16-bit segment; limit=1MB; base addr=0000_0000;
F000:2F2D                                                                 ; present; user:data (R/W) 
F000:2F35 48                                      db  48h ; H
F000:2F36 54                                      db  54h ; T
F000:2F37 49                                      db  49h ; I
F000:2F38 4E                                      db  4Eh ; N
F000:2F39 49                                      db  49h ; I
F000:2F3A 54                                      db  54h ; T
F000:2F3B 45                                      db  45h ; E
F000:2F3C 4E                                      db  4Eh ; N
F000:2F3D 54                                      db  54h ; T
F000:2F3E 52                                      db  52h ; R
F000:2F3F 59                                      db  59h ; Y
F000:2F40 50                                      db  50h ; P
F000:2F41 4F                                      db  4Fh ; O
F000:2F42 49                                      db  49h ; I
F000:2F43 4E                                      db  4Eh ; N
F000:2F44 54                                      db  54h ; T
F000:2F45 EC 5B FB FF      HT_DLL_entry_phy_addr dd 0FFFB5BECh ; phy addr of HT.DLL entry point ("dllmain" function) address in BIOS ROM 
.........
F000:2F6D 00 62 FB FF             HT32GATE_BIN_phy_addr dd 0FFFB6200h     ; phy addr of HT32GATE.BIN start in BIOS ROM
F000:2F71                         xec_32_flat_pm_HT32GATE proc near       
F000:2F71 FA                                      cli
F000:2F72 FC                                      cld
F000:2F73 66 52                                   push    edx
F000:2F75 66 B8 04 00 F0 FF                       mov     eax, 0FFF00004h
F000:2F7B 66 33 D2                                xor     edx, edx
F000:2F7E 66 B9 0E 02 00 00                       mov     ecx, 20Eh
F000:2F84 0F 30                                   wrmsr
F000:2F86 FE C1                                   inc     cl
F000:2F88 66 B8 00 08 F0 FF                       mov     eax, 0FFF00800h
F000:2F8E 66 BA FF 00 00 00                       mov     edx, 0FFh
F000:2F94 0F 30                                   wrmsr
F000:2F96 66 5A                                   pop     edx
F000:2F98 8B C4                                   mov     ax, sp
F000:2F9A 50                                      push    ax
F000:2F9B 8C D0                                   mov     ax, ss
F000:2F9D 50                                      push    ax
F000:2F9E B9 28 00                                mov     cx, 28h ; '('
F000:2FA1 2B E1                                   sub     sp, cx
F000:2FA3 8B EC                                   mov     bp, sp
F000:2FA5 8D 3E 0D 2F                             lea     di, CIM_GDT 
F000:2FA9
F000:2FA9                         loc_F000_2FA9:                          
F000:2FA9 2E 8A 05                                mov     al, cs:[di]
F000:2FAC 88 46 00                                mov     [bp+0], al
F000:2FAF 47                                      inc     di
F000:2FB0 45                                      inc     bp
F000:2FB1 E2 F6                                   loop    loc_F000_2FA9
F000:2FB3 8B EC                                   mov     bp, sp
F000:2FB5 66 33 C0                                xor     eax, eax
F000:2FB8 8C C8                                   mov     ax, cs
F000:2FBA 66 C1 E0 04                             shl     eax, 4
F000:2FBE 89 46 1A                                mov     [bp+1Ah], ax
F000:2FC1 66 C1 E8 10                             shr     eax, 10h
F000:2FC5 88 46 1C                                mov     [bp+1Ch], al
F000:2FC8 66 33 C0                                xor     eax, eax
F000:2FCB 8C D0                                   mov     ax, ss
F000:2FCD 66 C1 E0 04                             shl     eax, 4
F000:2FD1 66 03 C4                                add     eax, esp
F000:2FD4 66 50                                   push    eax
F000:2FD6 68 27 00                                push    27h ; '''
F000:2FD9 8B EC                                   mov     bp, sp
F000:2FDB 0F 01 56 00                             lgdt    fword ptr [bp+0]
F000:2FDF 66 33 C0                                xor     eax, eax
F000:2FE2 8C D0                                   mov     ax, ss
F000:2FE4 66 C1 E0 04                             shl     eax, 4
F000:2FE8 66 03 C4                                add     eax, esp
F000:2FEB 66 8B E0                                mov     esp, eax
F000:2FEE 0F 20 C0                                mov     eax, cr0
F000:2FF1 0C 01                                   or      al, 1
F000:2FF3 0F 22 C0                                mov     cr0, eax
F000:2FF6 EB 00                                   jmp     short $+2
F000:2FF8 B8 08 00                                mov     ax, 8
F000:2FFB 8E D8                                   mov     ds, ax
F000:2FFD                                         assume ds:nothing
F000:2FFD 8E C0                                   mov     es, ax
F000:2FFF                                         assume es:nothing
F000:2FFF 8E E0                                   mov     fs, ax
F000:3001                                         assume fs:nothing
F000:3001 8E E8                                   mov     gs, ax
F000:3003                                         assume gs:nothing
F000:3003 8E D0                                   mov     ss, ax
F000:3005                                         assume ss:nothing
F000:3005 66 B8 00 F0 00 00                       mov     eax, 0F000h
F000:300B 66 C1 E0 04                             shl     eax, 4
F000:300F 66 05 21 30 00 00                       add     eax, 3021h
F000:3015 66 50                                   push    eax
F000:3017 66 68 10 00 00 00                       push    large 10h
F000:301D 66 56                                   push    esi             ; esi = phy addr of HT32GATE.BIN entry point
F000:301F                                         db      66h
F000:301F 66 CB                                   retf                    ; call HT32GATE.BIN entry point and enter PMode 
F000:3021                         ; ---------------------------------------------------------------------------
F000:3021 EA 28 30 00 00                          jmp     far ptr 0:3028h ; jump "below" to go back to real mode
F000:3021                         ; ---------------------------------------------------------------------------
F000:3026 18                                      db  18h
F000:3027 00                                      db    0
F000:3028                         ; ---------------------------------------------------------------------------
F000:3028 B8 20 00                                mov     ax, 20h ; ' ' 
F000:302B 8E D0                                   mov     ss, ax
F000:302D                                         assume ss:nothing
F000:302D 0F 20 C0                                mov     eax, cr0
F000:3030 24 FE                                   and     al, 0FEh
F000:3032 0F 22 C0                                mov     cr0, eax
F000:3035 EA 3A 30 00 F0                          jmp     far ptr back2realmode
F000:303A
F000:303A                         back2realmode:
F000:303A 66 33 C0                                xor     eax, eax
F000:303D 66 33 D2                                xor     edx, edx
F000:3040 66 B9 0E 02 00 00                       mov     ecx, 20Eh
F000:3046 0F 30                                   wrmsr
F000:3048 FE C1                                   inc     cl
F000:304A 0F 30                                   wrmsr
F000:304C 33 C0                                   xor     ax, ax
F000:304E 8E D8                                   mov     ds, ax
F000:3050                                         assume ds:nothing
F000:3050 8E C0                                   mov     es, ax
F000:3052                                         assume es:nothing
F000:3052 8E E0                                   mov     fs, ax
F000:3054                                         assume fs:nothing
F000:3054 8E E8                                   mov     gs, ax
F000:3056                                         assume gs:nothing
F000:3056 66 81 C4 2E 00 00 00                    add     esp, 2Eh
F000:305D 67 66 3E 8B 04 24                       mov     eax, ds:[esp]
F000:3063 8E D0                                   mov     ss, ax
F000:3065                                         assume ss:nothing
F000:3065 66 C1 E8 10                             shr     eax, 10h
F000:3069 66 33 E4                                xor     esp, esp
F000:306C 8B E0                                   mov     sp, ax
F000:306E C3                                      retn
F000:306E                         xec_32_flat_pm_HT32GATE endp
.........

These lines in listing 1 probably vague:

F000:2EE4 66 8D 36 6D 2F                          lea     esi, HT32GATE_BIN_phy_addr
F000:2EE9 67 66 2E 8B 06                          mov     eax, cs:[esi] 
F000:2EEE 66 8B F0                                mov     esi, eax 
.........
F000:2EF6 66 81 C6 00 02 00 00                    add     esi, 200h       ; esi = HT32GATE entry point (discard DOS MZ header) 
Let me decipher them. The value of HT32GATE_BIN_phy_addr is FFFB_6200h. Looking at the BIOS binary in that address from Hex Workshop, I found:
Offset           Hex Values                            ASCII
000B6200 4D5A 4F01 0300 0000 2000 0100 FFFF 0000 MZO..... .......
000B6210 0000 9713 0000 0000 1E00 0000 0100 0000 ................
The "MZ" header means the file is an MS-DOS executable. However, looking back to listing 1, we know that routines in HT32GATE.BIN should be executed in 32-bit flat protected mode. Therefore, let's forget about MS-DOS 16-bit real mode code execution. Now, the question is why the code added 200h into HT32GATE_BIN_phy_addr? It's because the call to HT32GATE.BIN must skip over the DOS MZ header. The format of DOS MZ header is explained in one of the link over at the Prerequisite section. I will show you the relevant excerpt below (courtesy of Max Maischein):
The old EXE files are the EXE files executed directly by MS-DOS. They were a
major improvement over the old 64K COM files, since EXE files can span multiple
segments. An EXE file consists of three different parts, the header, the
relocation table and the binary code.
The header is expanded by a lot of programs to store their copyright information
in the executable, some extensions are documented below.
The format of the header is as follows :
OFFSET              Count TYPE   Description
0000h                   2 char   ID='MZ'
                                 ID='ZM'
0002h                   1 word   Number of bytes in last 512-byte page
                                 of executable
0004h                   1 word   Total number of 512-byte pages in executable
                                 (including the last page)
0006h                   1 word   Number of relocation entries
0008h                   1 word   Header size in paragraphs
000Ah                   1 word   Minimum paragraphs of memory allocated in
                                 addition to the code size
000Ch                   1 word   Maximum number of paragraphs allocated in
                                 addition to the code size
000Eh                   1 word   Initial SS relative to start of executable
0010h                   1 word   Initial SP
0012h                   1 word   Checksum (or 0) of executable
0014h                   1 dword  CS:IP relative to start of executable
                                 (entry point)
0018h                   1 word   Offset of relocation table;
                                 40h for new-(NE,LE,LX,W3,PE etc.) executable
001Ah                   1 word   Overlay number (0h = main program)
Looking at offset 06h of the DOS MZ header, we found the "Header size in paragraphs" which translates to "multiply the value with 16". Why 16? because 1 paragraph is 16 bytes. If you recall from real mode, the physical address of a segment is obtained by shifting the value in one of the segment register to the left 4-bits, which is equivalent to multiplying with 16. Now, you see the relation, don't you ;). Looking back to the HT32GATE.BIN hex dump, we see that the value at offset 6h (that is at B6206h) is 20h. Multiplying 20h with 16, we obtain 200h. Bingo! That's the header size of the DOS MZ file inside the HT32GATE.BIN. Therefore, the entry point to the executable routine in HT32GATE.BIN is located at 200h (512) bytes after the start of the DOS MZ header.

Anyway, HT32GATE.BIN is placed in the BIOS binary in uncompressed form. That's why the bootblock code can call the routine in HT32GATE.BIN directly without decompressing it first. From Award BIOS component header structure, we know that the header of an Award BIOS component contains information about the compression applied to the component. In the case of HT32GATE.BIN, the header "says" this component is not compressed:

Offset           Hex Values                            ASCII
000B61C0 A4FF 2533 2D6C 6830 2D67 0500 0050 0500 ..%3-lh0-g...P..
000B61D0 0000 007C 4020 010C 4854 3332 4741 5445 ...|@ ..HT32GATE
000B61E0 2E42 494E FECD 2017 0000 0000 0000 0000 .BIN.. .........
000B61F0 0000 0000 0000 0000 0000 0000 0000 0000 ................
000B6200 4D5A 4F01 0300 0000 2000 0100 FFFF 0000 MZO..... .......
The hex dump above shows the "-lh0-" string which means the component is not compressed.

Now, let's continue the code execution to HT32GATE.BIN. The entry point is located at FFFB_6400h.

Listing 2: HT32GATE.BIN execution until the call to HT.DLL

0000:FFFB6400                         entry_point_func proc near 
0000:FFFB6400
0000:FFFB6400                         var_38          = byte ptr -38h
0000:FFFB6400                         arg_3           = dword ptr  0Bh
0000:FFFB6400                         arg_7           = dword ptr  0Fh
0000:FFFB6400                         arg_B           = dword ptr  13h
0000:FFFB6400                         arg_F           = dword ptr  17h
0000:FFFB6400                         arg_13          = dword ptr  1Bh
0000:FFFB6400                         arg_17          = dword ptr  1Fh
0000:FFFB6400                         arg_1B          = dword ptr  23h
0000:FFFB6400                         arg_1F          = dword ptr  27h
0000:FFFB6400                         arg_23          = dword ptr  2Bh
0000:FFFB6400                         arg_27          = dword ptr  2Fh
0000:FFFB6400                         arg_2B          = dword ptr  33h
0000:FFFB6400
0000:FFFB6400 55                                      push    ebp
0000:FFFB6401 8B EC                                   mov     ebp, esp
0000:FFFB6403 83 C4 C8                                add     esp, 0FFFFFFC8h
0000:FFFB6406 8D 5D C8                                lea     ebx, [ebp+var_38]
0000:FFFB6409 36 C7 03 00 00 00 00                    mov     dword ptr ss:[ebx+0], 0
0000:FFFB6410 36 C6 43 04 00                          mov     byte ptr ss:[ebx+4], 0
0000:FFFB6415 36 C6 43 05 FF                          mov     byte ptr ss:[ebx+5], 0FFh
0000:FFFB641A 36 C6 43 06 10                          mov     byte ptr ss:[ebx+6], 10h
0000:FFFB641F 33 C0                                   xor     eax, eax
0000:FFFB6421 36 89 43 07                             mov     ss:[ebx+7], eax
0000:FFFB6425 36 89 43 0B                             mov     ss:[ebx+arg_3], eax
0000:FFFB6429 8D 05 8D 00 00 00                       lea     eax, large ds:8Dh
0000:FFFB642F 03 C6                                   add     eax, esi
0000:FFFB6431 36 89 43 0F                             mov     ss:[ebx+arg_7], eax
0000:FFFB6435 8D 05 03 01 00 00                       lea     eax, large ds:103h
0000:FFFB643B 03 C6                                   add     eax, esi
0000:FFFB643D 36 89 43 13                             mov     ss:[ebx+arg_B], eax
0000:FFFB6441 33 C0                                   xor     eax, eax
0000:FFFB6443 36 89 43 17                             mov     ss:[ebx+arg_F], eax
0000:FFFB6447 8D 05 D5 00 00 00                       lea     eax, large ds:0D5h
0000:FFFB644D 03 C6                                   add     eax, esi
0000:FFFB644F 36 89 43 1B                             mov     ss:[ebx+arg_13], eax
0000:FFFB6453 33 C0                                   xor     eax, eax
0000:FFFB6455 36 89 43 1F                             mov     ss:[ebx+arg_17], eax
0000:FFFB6459 36 89 43 23                             mov     ss:[ebx+arg_1B], eax
0000:FFFB645D 36 89 43 27                             mov     ss:[ebx+arg_1F], eax
0000:FFFB6461 8D 05 31 01 00 00                       lea     eax, large ds:131h
0000:FFFB6467 03 C6                                   add     eax, esi
0000:FFFB6469 36 89 43 2B                             mov     ss:[ebx+arg_23], eax
0000:FFFB646D 8D 05 4D 01 00 00                       lea     eax, large ds:14Dh
0000:FFFB6473 03 C6                                   add     eax, esi
0000:FFFB6475 36 89 43 2F                             mov     ss:[ebx+arg_27], eax
0000:FFFB6479 33 C0                                   xor     eax, eax
0000:FFFB647B 36 89 43 33                             mov     ss:[ebx+arg_2B], eax
0000:FFFB647F C1 CB 10                                ror     ebx, 10h 
0000:FFFB6482 66 53                                   push    bx
0000:FFFB6484 C1 C3 10                                rol     ebx, 10h
0000:FFFB6487 66 53                                   push    bx
0000:FFFB6489 FF D2                                   call    edx ; Call HT.DLL 
0000:FFFB648B C9                                      leave
0000:FFFB648C C3                                      retn
0000:FFFB648C                         entry_point_func endp
.............

Listing 2 shows a call to an address in EDX register (in the end of the entry point function execution). What is the value of EDX register at that point? We should revert to listing 1 to find the value because listing 2 shows that EDX register is unchanged in the entry point function. The last EDX value in listing 1 prior to calling HT32GATE.BIN is HT_DLL_entry_phy_addr as shown below:

F000:2ECF 66 8D 1E 45 2F                          lea     ebx, HT_DLL_entry_phy_addr
F000:2ED4 67 66 2E 8B 13                          mov     edx, cs:[ebx]
F000:2ED9 66 8B F2                                mov     esi, edx 
.........
F000:2F45 EC 5B FB FF             HT_DLL_entry_phy_addr dd 0FFFB5BECh     ; phy addr of HT.DLL entry point ("dllmain" function) address in BIOS ROM 
Now, we know the flow of the code in HT32GATE.BIN. The last routine it called is a routine in HT.DLL.

Now, let's move to our final destination, the DLL file, namely HT.DLL. HT.DLL is also an uncompressed part of the Award BIOS binary that I dissect. The hex dump below shows the header of the HT.DLL component.

Offset           Hex Values                            ASCII
000B1180 1F60 2D6C 6830 2D20 5000 0011 5000 0000 .`-lh0- P...P...
000B1190 007A 4020 0106 4854 2E44 4C4C 0675 200F .z@ ..HT.DLL.u .
000B11A0 0000 0000 0000 0000 0000 0000 0000 0000 ................
As you can see in the preceding hex dump, HT.DLL is "-lh0-"-compressed, which means it's not compressed at all.

Now, let's move to the DLL file itself. As you can see in listing 1, HT32GATE.BIN calls an address which is far away from the start of the HT.DLL file in the BIOS (HT.DLL starts at FFFB_1180h in the BIOS ROM). That particular address (FFFB_5BECh) is the entry point of HT.DLL, the "dllmain" function as shown in listing 3.

Listing 3: HT.DLL execution

.............
0000:FFFB5BEC                                     ; BOOL __stdcall agesa_entry(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)
0000:FFFB5BEC                                                     public agesa_entry
0000:FFFB5BEC                                     agesa_entry     proc near
0000:FFFB5BEC
0000:FFFB5BEC                                     global_func_table= cpu_fam_0Fh_func_table_t ptr -0B30h
0000:FFFB5BEC                                     l_hinstDLL      = dword ptr -0AB0h
0000:FFFB5BEC                                     _x_way_capable  = byte ptr -0AAAh
0000:FFFB5BEC                                     var_9           = byte ptr -9
0000:FFFB5BEC                                     var_8           = byte ptr -8
0000:FFFB5BEC                                     p_global_func_table= dword ptr -7
0000:FFFB5BEC                                     hinstDLL        = dword ptr  8
0000:FFFB5BEC                                     fdwReason       = dword ptr  0Ch
0000:FFFB5BEC                                     lpReserved      = dword ptr  10h
0000:FFFB5BEC
0000:FFFB5BEC 55                                                  push    ebp             ; p_l_hinstDLL + AA9h = p_global_func_table 
0000:FFFB5BED 8B EC                                               mov     ebp, esp
0000:FFFB5BEF 81 EC 30 0B 00 00                                   sub     esp, 0B30h
0000:FFFB5BF5 E8 FA DA FF FF                                      call    read_APIC_base?
0000:FFFB5BFA 0F B6 C0                                            movzx   eax, al
0000:FFFB5BFD 85 C0                                               test    eax, eax
0000:FFFB5BFF 0F 84 84 00 00 00                                   jz      exit
0000:FFFB5C05 8D 85 D0 F4 FF FF                                   lea     eax, [ebp+global_func_table]
0000:FFFB5C0B 50                                                  push    eax
0000:FFFB5C0C 6A 00                                               push    0               ; cpu node number ?
0000:FFFB5C0E E8 C7 D7 FF FF                                      call    init_ht_func_table
0000:FFFB5C13 59                                                  pop     ecx
0000:FFFB5C14 59                                                  pop     ecx
0000:FFFB5C15 8B 45 08                                            mov     eax, [ebp+hinstDLL]
0000:FFFB5C18 89 85 50 F5 FF FF                                   mov     [ebp+l_hinstDLL], eax
0000:FFFB5C1E 8D 85 D0 F4 FF FF                                   lea     eax, [ebp+global_func_table]
0000:FFFB5C24 89 45 F9                                            mov     [ebp+p_global_func_table], eax
.............
0000:FFFB5C89                                     exit:                                   
0000:FFFB5C89 C9                                                  leave
0000:FFFB5C8A C3                                                  retn
0000:FFFB5C8A                                     agesa_entry     endp
.............
As you can see in listing 3, an ordinary DLL expects three input parameters. However, the current execution environment is not Windows or Wine. Therefore, the "ordinary" DLL calling convention doesn't hold in this case. Looking back to listing 2, we see that HT.DLL was called with single 32-bit parameter which is pushed to the stack right before the DLL is executed. The disassembly of HT.DLL above is obtained by opening the DLL directly in IDA Pro without altering the DLL file at all. That's why IDA Pro "guess" that the DLL is an ordinary DLL which runs in Windows which results in the disassembly above.

5. Conclusion

The reverse engineering work on DLL support in recent Award BIOS shows that Award BIOS doesn't really support DLL file format as in Windows because it doesn't parse the DLL header at all. It just jumps directly to the DLL entry point function (in 32-bit flat protected mode) and execute the DLL. My analysis of the DLL with IDA Pro also shows that there is only one exported function in the DLL, i.e. the DLL entry point and there is no imported function. It doesn't matter though because Award BIOS doesn't even care about the DLL header ;).

6. Closing

What I've explained above possibly far too premature to be ended here. However, if you follow this article from beginning to end, you'll be able to understand the "BIG Picture" of how DLL file is handled in recent Award BIOS. If you find any mistake(s) within this article or have any suggestion, please contact me.


copyright © Darmawan M S a.k.a Pinczakko

Comments