Pinczakko的AwardBIOS逆向工程指导
作 者: beiyu
时 间: 2007-04-05,10:15
链 接: http://bbs.pediy.com/showthread.php?threadid=42166
Pinczakko的AwardBIOS逆向工程指导
作者:Pinczakko
翻译:beiyu http://beiyu.bokee.com
Email: beiyuly@gmail.com
时间:2006.6.6
ida的使用和最后展望没有翻译,希望有兴趣的朋友能够补上。
目录
Pinczakko的AwardBios逆向工程指导 1
1.序言 2
2.准备工作 2
2.1.PCI BUS 3
2.2.ISA BUS 4
3.一些硬件特性 4
3.1. BIOS 芯片地址 5
3.2. 晦涩的硬件接口(Port) 6
3.3. "可重定位" 硬件Port 8
3.4. Expansion ROM Handling 9
4.一些软件特性 10
4.1.call指令特性 10
4.2. retn Instruction Peculiarity 10
5. 用到的工具 13
5.1. 我们的需求 13
5.2. IDA Pro技术介绍 13
5.2.1. IDA Pro介绍 13
5.2.2. IDA Pro Scripting And Key Bindings 19
6. Award BIOS 文件结构 26
6.1.压缩部分 26
6.2. 纯二进制部分 27
6.3. 真实系统(Mainboard)中的内存印象 27
7. 反汇编BIOS 28
7.1. Bootblock 29
7.1.1. "Virtual Shutdown" routine 29
7.1.2. Chipset_Reg_Early_Init routine 29
7.1.3. Init_Interrupt_n_PwrMgmt routine 35
7.1.4. Call To "Early Silicon Support" Routine 36
7.1.5. Bootblock Is Copied And Executed In RAM 37
7.1.6. Call to bios decompression routine and the jump into decompressed system bios 39
7.1.6.1. Enable FFF80000h-FFFDFFFFh decoding 40
7.1.6.2. Copy lower 128KB of BIOS code from ROM chip into RAM 40
7.1.6.3. Disable FFF8_0000h-FFFD_FFFFh decoding 40
7.1.6.4. Verify checksum of the whole compressed BIOS image 40
7.1.6.5. Look for the decompression engine 41
7.1.6.6. Decompress the compressed BIOS components 41
7.1.6.7. Shadow the BIOS code 60
7.1.6.8. Enable the microprocessor cache then jump into the decompressed system BIOS 60
7.2. System BIOS a.k.a Original.tmp 61
7.2.1. Entry point from "Bootblock in RAM" 61
7.2.2. The awardext.rom and Extension BIOS Components (lower 128KB bios-code) Relocation Routine 62
7.2.3. Call to the POST routine a.k.a "POST jump table execution" 64
7.2.4. The "segment vector" Routines 68
7.2.5. "chksum_ROM" Procedure 72
7.2.6. Original.tmp Decompression Routine for The "Extension_BIOS Components" 72
7.2.7. Microcode Update Routine 90
8. 激昂展望 92
9. 结束语 92
1.序言
我非常欢迎你能够来实践复杂的Award Bios的代码研究工作。本文不是一篇官方的Award Bios逆向工程的文章,也不是由Award公司内部人员编辑的。我只是一个好奇的普通人,我真的很喜欢搞清楚我的电脑的Bios是怎样工作的。我写这篇文章的是为了公开我的发现和研究,从而回报那些我所犯的错误,都是我在逆向工程进程当中所犯的。你有几个可能性来读这篇文章,也许你是一个老资格的黑客,也许你是一个像我一样的系统程序设计爱好者,也许你只是一个好奇的外行。只有一点是肯定的,你肯定可以从这篇文章有所收获,可以提高你的技巧。无论如何,我已经写了一个准备章节,来保证你吸收这篇文章所具备的知识。
除非你自己反汇编了Bios的文件,你是不会理解搞清楚BIOS的工作的。
这篇文章的目的是消除疑惑,定位好你自己,在开始对BIOS的逆向工程工作中,为你提供一个参考。
2.准备工作
1.我必须承认,这个工作需要x86的知识。
2.保护模式下的编成开发知识。你必须学会怎样让x86机器从实模式转移到保护模式。也就是说,你必须学会初步的x86保护模式OS开发。www.osdever.net是一个很好的学习这方面知识的网站。最重要的事情是保护模式的数据结构是怎样工作的。我的意思是GDT、IDT、x86控制寄存器和段寄存器是怎样工作的,特别是award bios用他们来实现他的奇妙的地方——稍后文章解释。
3.什么是x86的不真实模式。他是一个x86机器在真是模式和保护模式之间的的状态——稍后文章解释。
4.X86直接硬件编程开发。你需要知道怎样编程直接制硬件,特别是在你主板上面的。你可以联系这个,通过windows上的直接访问硬件程序开发练习。这个不是必需的,但是如果你懂的话,会给你带来很多方便。你也需要知道一些x86总线协议,比如PCI和ISA——稍后文章解释。
5.你必须理解大部分你的主板芯片的手册。比如北桥和南桥控制寄存器。
2.1.PCI BUS
官方的PCI总线标准系统是由PCISIG(PCI Special Interest Group)维持的。他可能是某种公司,他介于Intel和其他大公司,比如Microsoft。他将要被Arapahoe (PCI-Express a.k.a PCI-e) and Hypertransport代替。但是PCI曾经是在保持一种标准。Hypertransport向后兼容PCI。Arapahoe也是一样。只是这个PCI的标准是没有公开的。
首先,PCI BUS是一个32位宽度的总线。通讯需要32bit的地址模式。读写操作需要32位地址。64位PCI Bus不是天生就是,他使用了双重地址回路实现。所以你可以说PCI就是一个32位总线的系统。
其次,这个总线系统定义位置是,控制端口PORT CF8h – CFBh,数据端口CFCh – CFFh。这些端口用来配置相应的PCI芯片,比如读写PCI芯片的配置寄存器值。
第三,这个总线系统强制我们和PCI通讯需要遵守下面的法则(从用户CPU观点):
1. 写目标总线号,设备号,功能号和偏移/寄存器号到配置地质端口,然后使能bit置1。通俗讲就是,写寄存器的地址到你想要写入的PCI地址端口。
2. 从一个配置数据端口执行一个one-byte, two-byte, or four-byte I/O读操作或者写操作。通俗讲就是,读写数据从你想要读写的PCI端口。
作为一个提示,据我所知,每一个今天用到的BUS/通讯协议,使用简单的法则来使芯片互相通讯,而这些芯片有一个复杂的总线协议。
有了上面的定义,这里提供一个x86的汇编码片断,来说明怎样使用这些配置端口。
No. Mnemonic (masm syntax) Comment
1 Pushad 保存所有通用寄存器的值
2 mov eax,80000064h 把将要访问的PCI芯片寄存器的地址放入eax
(offset 64h device 00:00:00 or hostbridge)
3 mov dx,0CF8h 地址端口放入dx。因为是PCI,我们用CF8h作为端口,来打开访问这个设备。
4 out dx,eax 发送PCI地址端口到processor的I/O空间
5 mov dx,0CFCh 数据端口放入dx。因为是PCI,我们用CFCh作为端口,来和这个设备数据通信。
6 in eax,dx 将从这个设备读出的数据放入eax
7 or eax, 00020202 改变数据(this is only example, don‘t try this in your machine, it may hang or even destroy your machine)
8 out dx,eax 将数据发送回设备
9 ............ -
10 Popad 出栈所有寄存器值
11 Ret 返回
我想上面的代码已经非常清晰了。这里有一个PCI寄存器地址格式例子:
mov eax,80000064h
the 80000064h is the address. The meaning of these bits are:
bit position 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
binary value 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0
hexadecimal value 8 0 0 0 0 0 6 4
? Bit 31是一个使能标志。如果这个位置设置了,我们就给与PCI bus读写通信的权利了,否则就是禁止。那就是为什么我们在最左边有一个8的原因。
? Bits 30 - 24 保留 bits。
? Bits 23 - 16 是 PCI Bus 号。
? Bits 15 - 11是PCI 设备号。
? Bits 10 - 8 是PCI 功能号。
? Bits 7 - 0 是偏移地址。
80000064h的意思就是我们通讯的设备是bus 0, device 0, function 0, 偏移地址是64h。实际上这个是我们主板上面的北桥芯片中的存储控制配置寄存器。大多数环境下,bus 0, device 0, function 0是Hostbridge,你需要参考自己的芯片数据表来改变这个。大概来讲,他们要作如下工作:读取偏移地址,改写数据,写回设备。
2.2.ISA BUS
AFAIK(恕我直言),ISA bus 不是标准的总线。因此,实际上任何ISA设备可以存在于系统的16-bit I/O地址空间。我对ISA bus的经验很有限(CMOS chip ,mainboard‘s hardware monitoring chip- Winbond W83781D)。这两个芯片用了上面提到的PCI bus通用算法:
1. 先送出你想要读写的设备的地址。只有那样,你才可以通过这个设备的数据端口发送接收数据。
2. 通过数据端口,发送接收将要通过设备读写的数据。
我的硬件监视芯片用端口295h作为地址端口,296h作为数据端口。CMOS用70h作为地址端口,71h作为数据端口。
3.一些硬件特性
X86平台存在很多hack,特别是他的bios。这个要归功于向下兼容。这章要讨论一对在我BIOS反汇编中遇到的问题。
3.1. BIOS 芯片地址
最重要的负责bios代码处理的芯片是南桥和北桥芯片。由于这方面,北桥负责系统地址空间管理,比如bios shadowing,处理访问RAM和处理事务,用bios ROM作为南桥的目标,南桥最后积存bios rom。南桥主要负责使能rom解码控制,这将要寄存要访问的bios rom的存储地址。下面展示的地址可以存在于系统DRAM和bios rom芯片中的任何一个,这取决于在bios代码执行时,南桥和北桥寄存器的设置。
Physical Address Also Known As Used by Address Aliasing Note
000F_0000h - 000F_FFFFh F_seg / F_segment 1 Mbit, 2 MBit, and 4 MBit BIOS alias to FFFF_0000h - FFFF_FFFFh in all chipset just after power-up
000E_0000h - 000E_FFFFh E_seg / E_segment 1 Mbit, 2 MBit, and 4 MBit BIOS alias to FFFE_0000h - FFFE_FFFFh in some chipset just after power-up
上面的地址范围包含了bios代码和很多的系统特性。所以你不得不参考你的芯片数据表来理解它。而且,在bios代码运行后的时间里,注意上面的地址要被bios代码占据的是F_seg i.e. F_0000h - F_FFFFh。无论怎样,相当的操作系统可能会认为这段地址没有用,而且会把它用于自己的目的。上面提到的地址只是当bios代码访问或者其他代码直接访问bios rom的时候,反映了bios rom芯片到系统地址空间映射。就像我们要看到的一样,这个映射可以通过程序设计一些芯片寄存器来改变。
超过1m的Bios芯片,比如2m和4m的芯片有一个非常与众不同的低bios区域地址,i.e. C_seg, D_seg和其他低"segment(s)"。大多数情况,这个区域被映射到了靠近4GB地址范围。这个地址范围处理是从类似北桥到PCI地址范围来解决。这个配置下芯片行为如下:
? 北桥作为一个地址传送装置:在不同方式和普通内存地址比较的状态下,它对这个特别的内存地址有反应,内存地址直接指向RAM。相反,这个特别的内存地址由北桥转到南桥,从而解码。
? 南桥作为地址解码器:它解码这个特别的内存地址,这个地址指向正确的芯片,比如bios芯片。这方面,如果地址范围不被允许在南桥控制寄存器解码,南桥要返回“void”(bus地址周期结束)。
下面是一个例子:
Physical Address Also Known As Used by Address Aliasing Note
000F_0000h - 000F_FFFFh F_seg / F_segment 1 Mbit, 2 MBit, and 4 Mbit BIOS alias to FFFF_0000h - FFFF_FFFFh in all chipset just after power-up
000E_0000h - 000E_FFFFh E_seg / E_segment 1 Mbit, 2 Mbit, and 4 Mbit BIOS alias to FFFE_0000h - FFFE_FFFFh in some chipset just after power-up
FFFD_0000h - FFFD_FFFFh D_seg / D_segment 2 Mbit, and 4 Mbit BIOS -
FFFC_0000h - FFFC_FFFFh C_seg / C_segment 2 Mbit, and 4 Mbit BIOS -
FFF8_0000h - FFFB_FFFFh - 4 Mbit BIOS -
结论是:现代芯片组表现为效法F_seg and E_seg 处理。这是一个证据,证明现代x86系统保持着向下兼容。无论如何,卖主已经远离x86,这些“杂牌电脑(cludge)”往往被认为是过去的东西。
下面是在刚刚系统加电启动后,VIA693A芯片组(北桥)系统内存映射,根据芯片数据表。
Table 4. System Memory Map
Space Start Size Address Range Comment
DOS 0 640K 00000000-0009FFFF Cacheable
VGA 640K 128K 000A0000-000BFFFF Used for SMM
BIOS 768K 16K 000C0000-000C3FFF Shadow Ctrl 1
BIOS 784K 16K 000C4000-000C7FFF Shadow Ctrl 1
BIOS 800K 16K 000C8000-000CBFFF Shadow Ctrl 1
BIOS 816K 16K 000CC000-000CFFFF Shadow Ctrl 1
BIOS 832K 16K 000D0000-000D3FFF Shadow Ctrl 2
BIOS 848K 16K 000D4000-000D7FFF Shadow Ctrl 2
BIOS 864K 16K 000D8000-000DBFFF Shadow Ctrl 2
BIOS 880K 16K 000DC000-000DFFFF Shadow Ctrl 2
BIOS 896K 64K 000E0000-000EFFFF Shadow Ctrl 3
BIOS 960K 64K 000F0000-000FFFFF Shadow Ctrl 3
Sys 1MB ? 00100000-DRAM Top Can have hole
Bus D Top DRAM Top-FFFEFFFF
Init 4G-64K 64K FFFEFFFF-FFFFFFFF 000Fxxxx alias
最重要的要考虑到的东西是地址别名,比如你看到的FFFE_FFFFh- FFFF_FFFFh范围就是000Fxxxxh别名,这个就是bios rom芯片地址映射的地方(我得主板)。但是,我们不得不认为,这个是在启动阶段最初的时候(reset后)。在芯片重新被bios改编程序后,这个地址范围就会映射到了RAM中。我们认为这个是作为加电启动默认值。作为一个标记,主要的x86芯片用这个地址作为别名,至少是F-segment地址范围。
另外一个事实就是我们不得不考虑:大部分芯片组在加电后,寄存器中,只提供默认F-segment地址配置,其他bios rom段保持不可访问。这些段的地址配置将要少后由bootblock代码在改变了相关芯片组寄存器后配置(大部分是南桥寄存器)。这里研究的芯片属于这个组。
现代系统连接bios rom芯片和南桥芯片是通过LPC(Low Pin Count)接口。无论怎样,本文中的南桥没有这样的接口。它是一个老的芯片,使用ISA bus作为和bios rom的接口。
3.2. 晦涩的硬件接口(Port)
下面提到的一些晦涩的硬件接口没有在芯片数据文档提到。注意,这些信息是从Intel ICH5,VIA 586B和VIA596B的数据表中得到。
I/O Port address Purpose
92h Fast A20 and Init Register
4D0h Master PIC Edge/Level Triggered (R/W)
4D1h Slave PIC Edge/Level Triggered (R/W)
Table 146. RTC I/O Registers (LPC I/F桪31:F0)
I/O Port Locations If U128E bit = 0 Function
70h and 74h Also alias to 72h and 76h Real-Time Clock (Standard RAM) Index Register
71h and 75h Also alias to 73h and 77h Real-Time Clock (Standard RAM) Target Register
72h and 76h Extended RAM Index Register (if enabled)
73h and 77h Extended RAM Target Register (if enabled)
注意:
1. I/O位置的70h和71h是标准的服务于真实时间时钟的ISA接口。表格147所示。72h和73h作为访问扩展RAM。扩展RAM单元的访问依然通过索引配置。I/O地址72h作为地址指针,73h作为数据寄存器。索引地址127h以上不可用。如果不需要扩展RAM,它就变得不可用了。
2. 软件比如保留地址70h的bit7。当顺序写入这个地址的时候,软件必须先读出这个位置的值,然后写入现同的值到bit7。注意70h不是可以直接读取的。唯一的方法是通过alt访问,读取相应寄存器的值。如果NMI#(不可屏蔽中断)使能没有在普通操作下改变,那么软件能够二者选一的读取这个bit一次,然后保留这个值,一边随后的所有写入端口70h操作。
RTC(通路控制)包含了两个索引寄存器配置,用于被两个分离索引和目标寄存器(70/71h or 72/73h)访问,如147表格所示。
Table 147. RTC (Standard) RAM Bank (LPC I/F桪31:F0)
Index Name
00h Seconds
01h Seconds Alarm
02h Minutes
03h Minutes Alarm
04h Hours
05h Hours Alarm
06h Day of Week
07h Day of Month
08h Month
09h Year
0Ah Register A
0Bh Register B
0Ch Register C
0Dh Register D
0Eh?Fh 114 Bytes of User RAM
3.3. "可重定位" 硬件Port
系统I/O空间中,有一些硬件端口种类可以重定位。在这个bios,那些端口包括smbus-related端口和电源管理相关端口。这些端口当然是基本地址。这些所谓的基本地址是通过可以编程的基址寄存器控制的。Smbus由smbus基址寄存器,电源管理由电源管理I/O基址寄存器。所以这些端口是可编程的,bootblock历程在bios历程执行开始的时候初始化这些地址寄存器的值。由于这些端口的可编程特性,就必需要开始bios bootblock的逆向工程来查出哪个端口地址用来这些可编程硬件端口。否则,就会搞不清楚稍后逆向工程中怪异端口的事件。例如:
Address Hex Mnemonic
F000:F604 BE C4 F6 mov si, 0F6C4h ; addr of chipset reg mask
F000:F607 next_PCI_reg: ; CODE XREF: Chipset_Reg_Early_Init+29
F000:F607 2E 8B 0C mov cx, cs:[si]
F000:F60A BC 10 F6 mov sp, 0F610h
F000:F60D E9 F8 00 jmp Read_PCI_Byte
F000:F60D ; ---------------------------------------------------------------------------
F000:F610 12 F6 dw 0F612h
F000:F612 ; ---------------------------------------------------------------------------
F000:F612 2E 22 44 02 and al, cs:[si+2]
F000:F616 2E 0A 44 03 or al, cs:[si+3]
F000:F61A BC 20 F6 mov sp, 0F620h
F000:F61D E9 02 01 jmp Write_PCI_Byte
F000:F61D ; ---------------------------------------------------------------------------
F000:F620 22 F6 dw 0F622h
F000:F622 ; ---------------------------------------------------------------------------
F000:F622 83 C6 04 add si, 4
F000:F625 81 FE 04 F7 cmp si, 0F704h ; are we done yet?
.........
F000:F6F4 48 3B dw 3B48h ; B#0 D#7 F#3: PwrMngmt&SMBus - PwrMngmt IO Base Addr lo_byte
F000:F6F6 00 db 0 ; and mask
F000:F6F7 00 db 0 ; or mask
F000:F6F7 ;
F000:F6F8 49 3B dw 3B49h ; B#0 D#7 F#3: PwrMngmt&SMBus - PwrMngmt IO Base Addr hi_byte
F000:F6FA 40 db 40h ; and mask
F000:F6FB 40 db 40h ; PwrMngmt IO Base Addr = IO Port 4000h
.........
F000:F643 B9 90 3B mov cx, 3B90h ; B#0 D#7 F#3: PwrMngmt&SMBus - SMBus IO Base Addr lo_byte
F000:F646 B0 00 mov al, 0 ; set SMBus IO Base lo_byte to 00h
F000:F648 BC 4E F6 mov sp, 0F64Eh
F000:F64B E9 D4 00 jmp Write_PCI_Byte
F000:F64B ; ---------------------------------------------------------------------------
F000:F64E 50 F6 dw 0F650h
F000:F650 ; ---------------------------------------------------------------------------
F000:F650 B9 91 3B mov cx, 3B91h ; B#0 D#7 F#3: PwrMngmt&SMBus - SMBus IO Base Addr hi_byte
F000:F653 B0 50 mov al, 50h ; ‘P‘ ; set SMBus IO Base hi_byte to 50h,
F000:F653 ; so, now SMBus IO Base is at port 5000h !!!
F000:F655 BC 5B F6 mov sp, 0F65Bh
F000:F658 E9 C7 00 jmp Write_PCI_Byte
F000:F658 ; ---------------------------------------------------------------------------
F000:F65B 5D F6 dw 0F65Dh
.........
F000:F66A BA 05 40 mov dx, 4005h ; access ACPI Reg 05h
F000:F66D B0 80 mov al, 80h ; ‘? ; setting reserved bit?
.........
当然,还有更多的可重定向硬件端口,但是至少你已经看到了这些提示。所以,一旦逆发现bios中的代码有点象访问怪异的端口,你将会知道它去哪里。
3.4. Expansion ROM Handling
有一对问题需要考虑到,比如video bios和其他扩展rom处理。这里是基本bios中PCI扩展rom处理run-down:
1. 系统bios检测所有的系统中的pci芯片,初始化他们的BARs(基址寄存器)。一旦初始化结束,系统就拥有了一个可用的广阔的系统地址配置。
2. 通过广阔的系统地址配置,系统bios一个接一个的拷贝需要的PCI扩展rom到RAM,这些扩展在(C000:0000h - D000:FFFFh),并且执行每一个模块或者初始化每一个模块。
至于ISA扩展rom,以后版本文章会讨论。
4.一些软件特性
在bios代码中有一些棘手的区域和rom中一些可执行部分有关。下面介绍:
4.1.call指令特性
Call指令在rom bios芯片内部的bios代码执行时不可用。这由于call指令使用桟,而我们不能在bios rom中写入来使用桟。这里使用桟是因为要压入call指令执行时写入保存的返回地址。我们很清楚的知道,这个时候地址指针ss:sp指向的时rom:我们不能写入。DRAM这个时候不能使用。它还没有被bios代码检测。我们根本就不知道有RAM存在!
4.2. retn Instruction Peculiarity
Retn指令特性,这里有ROM_call宏定义:
ROM_CALL MACRO RTN_NAME
LOCAL RTN_ADD
mov sp,offset DGROUP:RTN_ADD
jmp RTN_NAME
RTN_ADD: dw DGROUP:$+2
ENDM
例子:
Address Hex Mnemonic
F000:6000 F000_6000_read_pci_byte proc near
F000:6000 66 B8 00 00 00 80 mov eax, 80000000h
F000:6006 8B C1 mov ax, cx ; copy offset addr to ax
F000:6008 24 FC and al, 0FCh ; mask it
F000:600A BA F8 0C mov dx, 0CF8h
F000:600D 66 EF out dx, eax
F000:600F B2 FC mov dl, 0FCh
F000:6011 0A D1 or dl, cl ; get the byte addr
F000:6013 EC in al, dx ; read the byte
F000:6014 C3 retn ; Return Near from Procedure
F000:6014 F000_6000_read_pci_byte endp
......
F000:6043 18 00 GDTR_F000_6043 dw 18h ; limit of GDTR (3 valid desc entry)
F000:6045 49 60 0F 00 dd 0F6049h ; GDT physical addr (below)
F000:6049 00 00 00 00 00 00 00 00 dq 0 ; null descriptor
F000:6051 FF FF 00 00 0F 9F 00 00 dq 9F0F0000FFFFh ; code descriptor:
F000:6051 ; base addr = F 0000h; limit=FFFFh; DPL=0;
F000:6051 ; exec/ReadOnly, conforming, accessed;
F000:6051 ; granularity=byte; Present; 16-bit segment
F000:6059 FF FF 00 00 00 93 8F 00 dq 8F93000000FFFFh ; data descriptor:
F000:6059 ; base addr = 00h; seg_limit=F FFFFh; DPL=0;
F000:6059 ; Present; read-write, accessed;
F000:6059 ; granularity = 4 KByte; 16-bit segment
......
F000:619B 0F 01 16 43 60 lgdt qword ptr GDTR_F000_6043 ; Load Global Descriptor Table Register
F000:61A0 0F 20 C0 mov eax, cr0
F000:61A3 0C 01 or al, 1 ; set PMode flag
F000:61A5 0F 22 C0 mov cr0, eax
F000:61A8 EA AD 61 08 00 jmp far ptr 8:61ADh ; jmp below in 16-bit PMode (abs addr F 61ADh)
F000:61A8 ; (code segment with base addr = F 0000h)
F000:61AD ; ---------------------------------------------------------------------
F000:61AD B8 10 00 mov ax, 10h ; load ds with valid data descriptor
F000:61B0 8E D8 mov ds, ax ; ds = data descriptor (GDT 3rd entry)
......
F000:61BC B9 6B 00 mov cx, 6Bh ; DRAM arbitration control
F000:61BF BC C5 61 mov sp, 61C5h
F000:61C2 E9 3B FE jmp F000_6000_read_pci_byte ; Jump
F000:61C2 ; ------------------------------------------------------------------
F000:61C5 C7 61 dw 61C7h
F000:61C7 ; ------------------------------------------------------------------
F000:61C7 0C 02 or al, 2 ; enable VC-DRAM
你看到的,必需要考虑retn指令被当前ss:sp寄存器值影响,ss寄存器还没有加载到正确的16-bit保护模式使用!这些代码怎么会执行?答案有点复杂。让我们看看ss寄存器的值,它在上述调用之前就巧妙的处理了。
Address Hex Mnemonic
F000:E060 8C C8 mov ax, cs
F000:E062 8E D0 mov ss, ax ; ss = cs (ss = F000h a.k.a F_segment)
F000:E064 assume ss:F000
Note: this routine is executed in real-mode
就如你看到的,ss寄存器装入了f000h(当前bios代码16-bit段在实模式)。这段代码说明隐藏的描述缓存寄存器(存在为每一个选择/段寄存器)被加载入ss*16 or F_0000h 的物理地址值。并且这个值会返回,尽管机器转变成了上述的16-bit保护模式,因为ss寄存器没有重载。Intel Software Developer Manual Vol.3片断:
8.1.4. First Instruction Executed
The first instruction that is fetched and executed following a hardware reset is located at physical address FFFFFFF0H. This address is 16 bytes below the processor抯 uppermost physical address. The EPROM containing the software-initialization code must be located at this address. The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor while in real-address mode. The processor is initialized to this starting address as follows. The CS register has two parts: the visible segment selector part and the hidden base address part. In real address mode, the base address is normally formed by shifting the 16-bit segment selector value 4 bits to the left to produce a 20-bit base address. However, during a hardware reset, the segment selector in the CS register is loaded with F000H and the base address is loaded with FFFF0000H. The starting address is thus formed by adding the base address to the value in the EIP register (that is, FFFF0000 + FFF0H = FFFFFFF0H).
The first time the CS register is loaded with a new value after a hardware reset, the processor will follow the normal rule for address translation in real-address mode (that is, [CS base address = CS segment selector * 16]). To insure that the base address in the CS register remains unchanged until the EPROM based software-initialization code is completed, the code must not contain a far jump or far call or allow an interrupt to occur (which would cause the CS selector value to be changed).
Ddj (Doctor Dobbs Journal)的一个小片断:
At power-up, the descriptor cache registers are loaded with fixed, default values, the CPU is in real mode, and all segments are marked as read/write data segments, including the code segment (CS). According to Intel, each time the CPU loads a segment register in real mode, the base address is 16 times the segment value, while the access rights and size limit attributes are given fixed, "real-mode compatible" values. This is not true. In fact, only the CS descriptor cache access rights get loaded with fixed values each time the segment register is loaded - and even then only when a far jump is encountered. Loading any other segment register in real mode does not change the access rights or the segment size limit attributes stored in the descriptor cache registers. For these segments, the access rights and segment size limit attributes are honored from any previous setting (see Figure 3). Thus it is possible to have a four giga-byte, read-only data segment in real mode on the 80386, but Intel will not acknowledge, or support this mode of operation.
现在,你知道重点在于描述缓存寄存器,特别是它的基地址部分。Ss可见部分只是一个“place holder”和“register-in-charge”,对于真实地址计算/变换是一个隐藏的描述缓存。无论你对这个描述缓存做什么, 当任何代码、栈或者数据值地址被转换计算的时候,它都要受到影响。在我们看来,我们不得不在16-bit保护模式使用基址是F_0000h的物理地址的“堆栈段”。这不是问题,因为ss描述缓存寄存器的基址已经在上面的代码中赋予了F_0000h值。这就解释了为什么上面的代码能够正确执行,下面是一个例子:
Address Hex Mnemonic
F000:61BF BC C5 61 mov sp, 61C5h
F000:61C2 E9 3B FE jmp F000_6000_read_pci_byte ; Jump
F000:61C2 ; ------------------------------------------------------------------
F000:61C5 C7 61 dw 61C7h
这段代码里面我们已经给ss:sp指向F_61C5h,为retn指令服务。实际上,我们已经做了,因为ss包含了F_0000h(它的描述缓存基址部分)和你看到(sp contains 61C5h)的物理地址,ss:sp是F_0000h+61C5h ,物理地址是F_61C5h。
5. 用到的工具
本节介绍逆向工程分析所需的工具。将有一节单独解释IDA Pro反汇编工具。
5.1. 我们的需求
开始进行之前,我们需要以下工具:
1、 IDA Pro反汇编工具。我使用IDA Pro V4.3。你可以使用你喜欢的交互式反汇编工具。我觉得IDA Pro最适合我。我们之所以需要交互式反汇编工具,因为我们要反汇编的BIOS代码并不是普通的代码。当驻留在ROM中执行的一些时候并没有栈可用,而是使用了一些栈的技巧来进行过程/例程调用。
2、 一个好的二进制编辑器。我使用HexWorkshop ver3.0b。该二进制编辑器最大的一个好处是它可以计算打开文件的所选范围内的校验和。
3、 LHA2.55,用来修改BIOS二进制。如果你仅想解压缩并分析压缩的BIOS组件,也可使用winzip或其他可以处理LZH/LHA文件的压缩/解压缩工具。
4、 BIOS修改工具,例如CBROM,我使用v2.08,v2.07和1.24。以及MODBIN,有两种:modbin6 for award bios ver. 6和modbin 4.50.xx for award bios ver. 4.5xPGNM。使用这些工具更容易查看BIOS组件。可从www.biosmods.com下载。
5、 一些芯片集数据表,这取决于你要解剖的主板BIOS代码。www.com.by上有一部分pdf格式的数据表。我解剖的主板是VIA693A-596B,我当然有这个数据表。
6、 Intel Software Developer Manual Volume 1, 2 and 3。BIOS有时使用一些外来指令集。另外有些很难记住的数据结构需要查询,如GDT、IDT等。
5.2. IDA Pro技术介绍
本小节介绍使用IDA Pro。如果抓住了这些概念,你可以方便地使用IDA pro。
5.2.1. IDA Pro介绍
逆向代码工程通过分析软件的可执行文件来实现对软件所使用算法的理解。在大多数情况下,软件仅发布它的可执行文件而没有源代码。BIOS也同样如此,我们可获得的仅仅是执行代码。逆向代码工程在以下工具的帮助下实现:调试器,反汇编工具,二进制文件编辑器即二进制编辑器,ICE()等。我们在本小节中仅讨论反汇编工具,例如IDA Pro反汇编工具。
IDA Pro是一款强大的反汇编工具。它支持插件和脚本组件支持50种以上的处理器结构。但功能强大的工具一般都有缺陷,就是难以掌握使用,IDA Pro也不例外。
IDA Pro有多个版本:免费版、标准版和高级版。最新的免费版为IDA Pro version 4.3 (AFAIK),可在http://www.dirfile.com/ida_pro_freeware_version.htm下载。
There are several editions of IDA Pro: freeware edition, standard edition and advanced edition. The latest freeware edition is IDA Pro version 4.3 (AFAIK) and it抯 available for download at http://www.dirfile.com/ida_pro_freeware_version.htm. It抯 the most limited of all IDA Pro version. It only supports x86 processor and doesn‘t come with plugin feature, but it comes at no cost, that‘s why it‘s presented here. Fortunately, it still comes with scripting feature. The standard and advanced editions of IDA Pro 4.3 of course differ from this freeware edition, they come with support for plugin and support for much more processor architecture. We are going to learn how to use the scripting feature in the next section.
Now, let抯 start to use IDA Pro freeware version to open a BIOS binary file. First, IDA Pro freeware version has to be installed. After the installation finished, one special step must be carried-out to prevent unwanted bug when this version of IDA Pro opens up a BIOS file with *.rom extension. To do so, one must edit the IDA Pro configuration file that抯 located in the root directory of the IDA Pro installation directory. The name of the file is ida.cfg. Open this file by using any text editor (such as notepad) and look for the following lines:
DEFAULT_PROCESSOR = {
/* Extension Processor */
"com" : "8086" // IDA will try the specified
"exe" : "" // extensions if no extension is
"dll" : "" // given.
"drv" : ""
"sys" : ""
"bin" : "" // Empty processor means the default processor
"ovl" : ""
"ovr" : ""
"ov?" : ""
"nlm" : ""
"lan" : ""
"dsk" : ""
"obj" : ""
"prc" : "68000" // PalmPilot programs
"axf" : "arm710a"
"h68" : "68000" // MC68000 for *.H68 files
"i51" : "8051" // i8051 for *.I51 files
"sav" : "pdp11" // PDP-11 for *.SAV files
"rom" : "z80" // Z80 for *.ROM files
"cla*": "java"
"s19": "6811"
"o": ""
"*": "" // Default processor
}
Notice the line: "rom" : "z80" // Z80 for *.ROM files
This line must be removed or just replace the "z80" with "" in this line to disable the automatic request to load z80 processor module in IDA Pro upon opening a *.rom file. The bug occurred if the *.rom file is opened while this line is not changed ince freeware IDA Pro doesn‘t come with z80 processor module. Thus, opening *.rom file by default will terminate IDA Pro. Some motherboard BIOS files comes with *.rom extension by default, even though it‘s very clear that it won‘t be executed in z80 processor. Fixing this bug will ensure that we will be able to open motherboard BIOS file with *.rom extension flawlessly. Note that the steps needed to remove other file-extension to processor-type "mapping" in this version of IDA Pro is similar to the z80 processor that is just described.
Now let‘s proceed to open a sample BIOS file. This BIOS file is da8r9025.rom, BIOS file for Supermicro H8DAR-8 (OEM Only) motherboard. This motherboard used AMD-8131? HyperTransport? PCI-X Tunnel chip and AMD-8111? HyperTransport? I/O Hub chip. The dialog box below will be displayed when you start IDA Pro freeware version 4.3.
Just click OK to proceed. Then the next dialog box shown below will be displayed.
In this dialog box, you can try one of the three options, but we will just click on the Go button. This will start IDA Pro with empty workspace as shown below
Then locate and drag the file to be disassembled to the IDA Pro window (as shown above). In this case, IDA Pro will show the following dialog box.
In this dialog box, we will select Intel 80x86 processors: athlon as the Processor type in the drop down list box. Then click on the Set button to activate the new processor selection. Let the other option as it is. Code relocation will be carried out by using IDA Pro scripts in later section, then click OK. IDA Pro then shows the following dialog box.
This dialog box asks us to choose the default operating-mode of the x86 compatible processor during the disassembling process. AMD64 Architecture Programmer抯 Manual Volume 2: System Programming, February 2005 in section 14.1.5 page 417 states that:
"After a RESET# or INIT, the processor is operating in 16-bit real mode."
In addition, IA-32 Intel? Architecture Software Developer抯 Manual Volume 3: System Programming Guide 2004 section 9.1.1 states that:
"Table 9-1 shows the state of the flags and other registers following power-up for the Pentium 4, Intel Xeon, P6 family, and Pentium processors. The state of control register CR0 is 60000010H (see Figure 9-1), which places the processor is in real-address mode with paging disabled."
Thus, we can conclude that any x86 compatible processors start their execution in 16-bit real mode just after power-up and we have to choose 16-bit mode in this dialog box. It抯 accomplished by clicking No in the dialog box. Then the following dialog box pops up.
This dialog box told us that IDA Pro can抰 decide where the entry-point located. We have to locate it ourselves later. Just click OK to continue to the main window for the disassembly process.
Up to this point we are able to open the binary file within IDA Pro. This is not a trivial task for people new to IDA Pro. That‘s why it‘s presented in a step-by-step fashion. However, the output in the workspace is not yet usable. The next step is learning the scripting facility that IDA Pro provides to make sense about the disassembly database that IDA Pro generates.
5.2.2. IDA Pro Scripting And Key Bindings
Now we will proceed to try to decipher IDA Pro disassembly database shown in the previous sub-section with the help of the scripting facility. Before we proceed to analyze the binary, we have to learn some basic concepts about the IDA Pro scripting facility. IDA Pro scripts syntax are similar to C programming language. The syntax as follows:
1. IDA Pro scripts only recognize one type of variable, i.e. auto. There are no other variable types such as int, char, etc. The declaration of variable in an IDA Pro script as follows:
auto variable_name;
2. Every statement in an IDA Pro script ends with a semicolon (;), just like in the C programming language.
3. Function can return a value or not, but there抯 no return type declaration. The syntax as follows:
static function_name(parameter1, parameter2, parameter_n, ...)
4. Comment in an IDA Pro script starts with double-slash (//). The IDA Pro scripting engine ignores anything after the comment in the corresponding line.
5. // comment
6. statement; // comment
7. IDA Pro "exports" its internal functionality to the script that we build by using header files. These header files must be "included" in our script so that we are able to access that functionality. At least one header file must be included in any IDA Pro script, i.e. idc.idc. The header files are located inside a folder named idc in the IDA Pro installation directory. One must read the *.idc files inside this directory to learn about the functions that are exported by IDA Pro. The most important header file to learn is idc.idc. The syntax used to include a header file in an IDA Pro script is:
8. #include < header_file_name >
9. The entry point of an IDA Pro script is the main function, just as in the C programming language.
Now is the time to put the theory into a simple working example, an IDA Pro sample script.
#include <idc.idc>
// relocate one segment
static relocate_seg(src, dest)
{
auto ea_src, ea_dest, hi_limit;
hi_limit = src + 0x10000;
ea_dest = dest;
for(ea_src = src; ea_src < hi_limit ; ea_src = ea_src + 4 )
{
PatchDword( ea_dest, Dword(ea_src));
ea_dest = ea_dest + 4;
}
Message("segment relocation finished (inside relocate_seg function)../n");
}
static main()
{
Message("creating target segment (inside entry point function main).../n");
SegCreate([0xF000, 0], [0x10000, 0], 0xF000, 0, 0, 0);
SegRename([0xF000, 0], "_F000");
relocate_seg([0x7000,0], [0xF000, 0]);
}
The square bracket, i.e. [ ] in the script above is an operator used to form the linear address from its parameters by shifting the first parameter to left four bits and then adding the second parameter into the result, for example: [0x7000, 0] means (0x7000 << 4) + 0 , i.e. 0x7_0000 linear address. This operator is just the same as MK_FP( , ) operator in previous versions of IDA Pro. One must read idc.idc file to see the "exported" function definition to understand this script completely, such as the Message, SegCreate and SegRename function. Another "exported" function that maybe of interest can be found in numerous *.idc file in the idc directory of IDA Pro installation folder. To be able to use the function, its definition have to be looked up in the exported function definition in the corresponding *.idc header file. For example, SegCreate function is defined in idc.idc as follows:
// Create a new segment
// startea - linear address of the start of the segment
// endea - linear address of the end of the segment
// this address will not belong to the segment
// ‘endea‘ should be higher than ‘startea‘
// base - base paragraph or selector of the segment.
// a paragraph is 16byte memory chunk.
// If a selector value is specified, the selector should be
// already defined.
// use32 - 0: 16bit segment, 1: 32bit segment
// align - segment alignment. see below for alignment values
// comb - segment combination. see below for combination values.
// returns: 0-failed, 1-ok
success SegCreate( long startea,long endea,long base, long use32,
long align,long comb);
A 512KB BIOS binary file must be opened in IDA Pro with the loading address set to 0000h to be able to execute the sample script above. This loading scheme is the same as explained in the previous sub-section. In this case, we will just open the binary file of Supermicro H8DAR-8 motherboard as in the previous sub-section and then execute the script. First, we must type the script above in a plain text file. We can use notepad or another ASCII file editor for this purpose. We will name the file as function.idc. The script then executed by clicking on the File | IDC file... menu or by pressing F2, then the dialog box below will be shown.
Just select the file and click open to execute the script. If there抯 any mistake in the script, IDA Pro will warn you with a warning dialog box. Executing the script will display the corresponding message in the message pane of IDA Pro as shown below.
The script above relocates the last segment (64KB) of the Supermicro H8DAR-8 BIOS code to the right place. One must be aware that IDA Pro is only an advanced tool to help the reverse code engineering task, it抯 not a magical tool that抯 going to reveal the overall structure of the BIOS binary without us being significantly involve in the process. The script relocates/copies BIOS code from physical/linear address 0x7_0000-0x7_FFFF to 0xF_0000-0xF_FFFF. The logical reason behind this algorithm is explained below.
AMD-8111 HyperTransport IO Hub Datasheet chapter 4 page 153 says that:
Note: The following ranges are always specified as BIOS address ranges. See DevB:0x80 for more information about how access to BIOS spaces may be controlled.
Size Host Address Range[31:0] Address translation for LPC bus
64K bytes FFFF_0000h ?FFFF_FFFFh FFFF_0000h ?FFFF_FFFFh
64K bytes 000F_0000h ?000F_FFFFh FFFF_0000h ?FFFF_FFFFh
In addition, AMD64 Architecture Programmer抯 Manual Volume 2: System Programming, February 2005 in section 14.1.5 page 417 says that:
"Normally within real mode, the code-segment base address is formed by shifting the CS-selector value left four bits. The base address is then added to the value in EIP to form the physical address into memory. As a result, the processor can only address the first 1 Mbyte of memory when in real mode. However, immediately following RESET# or INIT, the CS selector register is loaded with F000h, but the CS base-address is not formed by left-shifting the selector. Instead, the CS base address is initialized to FFFF_0000h. EIP is initialized to FFF0h. Therefore, the first instruction fetched from memory is located at physical-address FFFF_FFF0h (FFFF_0000h+0000_FFF0h). The CS base-address remains at this initial value until the CS selector register is loaded by software. This can occur as a result of executing a far jump instruction or call instruction, for example. When CS is loaded by software, the new base-address value is established as defined for real mode (by left shifting the selector value four bits)."
From the references above, we conclude that address 000F_0000h ?000F_FFFFh is an alias to address FFFF_0000h ?FFFF_FFFFh, i.e. they both points to the same physical address range. Whenever the host (CPU) accesses some value in 000F_0000h ?000F_FFFFh address range, it‘s actually accessing the value at FFFF_0000h ?FFFF_FFFFh range and the reverse is also true. From this fact, we know that we have to relocate 64KB of the uppermost BIOS code to address 000F_0000h ?000F_FFFFh for further investigation. This decision is made based on my previous experience with various BIOS binary files, they generally references address with F000h used as the segment value within the BIOS code. Also, note that the last 64KB of the BIOS binary file is mapped to last 64KB of the 4GB address space, i.e. 4GB-64KB to 4GB, that‘s why we have to relocate the last 64KB.
Simple script that is only several lines can be typed and executed directly within IDA Pro without opening a text editor. IDA Pro provides a specific dialog box for this purpose and it can be accessed by pressing Shift+F2. This is more practical for simple task, but as the number of the routine grows, one might consider coding the script as described in the previous explanation due to limitation of the number of instruction that can be entered in the dialog box. In this dialog box, enter the script to be executed and click OK to execute the script. Below is an example script.
Note that there is no need for #include statement in the beginning of the script, since by default all of the functions that are exported by IDA Pro in its scripts header files (*.idc) is accessible within the scripting dialog box shown above. The main function is also doesn抰 need to be defined. In fact, anything you write within the dialog box entry will behave as if it‘s written inside the main function in an IDA Pro script file.
Anyway, you might want to go to the IDA Palace for more IDA Pro script samples. It will take a while to grasp them, but IDA Palace is definitely the place to go if you‘re curious about IDA Pro scripting.
At present, we are able to relocate the binary within IDA Pro; the next step is to disassemble the binary within IDA Pro. Before that, we need to know how the default key binding works in IDA Pro. Key binding is the "mapping" between the keyboard button and the command carried-out when the corresponding key is pressed. The cursor must be placed in the workspace before any command is carried-out in IDA Pro. The key binding is defined in idagui.cfg file that‘s located in IDA Pro installation directory. An excerpt of the key binding (hot key) is provided below.
"MakeCode" = ‘C‘
"MakeData" = ‘D‘
"MakeAscii" = ‘A‘
"MakeUnicode" = 0 // create unicode string
"MakeArray" = "Numpad*"
"MakeUnknown" = ‘U‘
"MakeName" = ‘N‘
//"MakeAnyName" = "Ctrl-N"
"ManualOperand" = "Alt-F1"
"MakeFunction" = ‘P‘
"EditFunction" = "Alt-P"
"DelFunction" = 0
"FunctionEnd" = ‘E‘
One can alter idagui.cfg to change the default key binding, but we will only consider the default key binding. Now we have grasped the key binding concept, let‘s see how to use it in our binary. In the previous example, we are creating a new segment, i.e. 0xF000. Now, we will go to the first instruction that‘s executed in the BIOS within that segment, i.e. address 0xF000:0xFFF0. Press G, the dialog box below will be shown.
In this dialog box, enter the destination address. You must enter the address in complete form (segment:offset) as shown above, i.e. F000:FFF0. Then, click OK to go to the intended address. Note that you don‘t have to type the leading 0x character, since by default, the value within the input box is in hexadecimal. The result will be as shown below (inside IDA Pro workspace).
The next step to do is to convert the value in this address into a meaningful machine instruction. To do so, press C. The result is as shown below.
Then, we can follow the jump by pressing Enter. The result is as shown below.
We can return from the jump that we‘ve just made by pressing Esc.
Up to this point, you‘ve gained significant intuition to use IDA Pro. You just need to consult the key bindings in idagui.cfg in case want to do something and don‘t know what key to press.
________________________________________
Now we‘re armed. What we need to do next is to understand the basic stuff by using the hex editor before proceeding through the disassembling session.
6. Award BIOS 文件结构
6.1.压缩部分
内存映射16进制表:
1. 0000h - 3AACh : XGROUP ROM (awardext.rom), Award扩展rom. 包含由system bios调用的例程,比如original.tmp。
2. 3AADh - 97AFh : CPUCODE.BIN, bios的微代码。
3. 97B0h - A5CFh : ACPITBL.BIN, acpi 表。
4. A5D0h - A952h : Iwill.bmp, BMP logo。
5. A953h - B3B1h : nnoprom.bin, I haven‘t know yet what this component‘s role。
6. B3B2h - C86Ch : Antivir.bin, bootsector 防病毒。
7. C86Dh - 1BEDCh : ROSUPD.BIN, 我得bios中的自定义部分,用于显示自定义的启动logo和提示符。
8. 2_0000h - 3_5531h : original.tmp, 我得特殊的bios中的system BIOS部分。大多数的2m的bios在2_0000h - 3_xxxxh (if you look from within hex editor)都有original.tmp。 有一些4M的bios有original.tmp在bios二进制文件的最开始的部分,比如0000h。
注意:
a. 在压缩的 ROSUPD.BIN 和 original.tmp 之间有填充 FFh bytes. 这些填充bytes 在压缩 original.tmp 和纯二进制BIOS 部分之后都有发现。一个例子:
Address Hex ASCII
00037D00 2A42 4253 532A 0060 0070 0060 0060 00A0 *BBSS*.`.p.`.`..
00037D10 3377 4670 8977 ACCF C4CF 0100 00FF FFFF 3wFp.w..........
00037D20 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................
00037D30 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................
b. 这些压缩了的部分可以很容易的,通过拷贝和粘贴到新的二进制文件,通过Hexworkshop 。然后,解压这个新的文件,用LHA2.55或者winzip。如果我们使用winzip,那么改编扩展名为.lzh,这样winzip就可以自动识别了。识别我们剪切的部分非常简单,只要看到“-lh5-”字符串就可以了。2 bytes的“-lh5-”在文件的最前面,文件最后面总是00h,正好在下一个压缩了的文件前面,正好在填充bytes或者某些checksum前面。我要给你两个例子下面。高亮的bytes是要所的文件的最初和最后的标志。
我得bios中的压缩的CPUCODE.BIN:
Address Hex ASCII
00003AA0 4E61 19E6 9775 2B46 BA55 85F0 0024 382D Na...u+F.U...$8-
00003AB0 6C68 352D DC5C 0000 00A0 0000 0000 0140 lh5-./.........@
00003AC0 2001 0B43 5055 434F 4445 2E42 494E BCAA ..CPUCODE.BIN..
00003AD0 2000 0038 3894 9700 52C4 A2CF F040 0000 ..88...R....@..
00003AE0 4000 0000 0000 0000 0000 0000 0000 0000 @...............
........
000097A0 0E3C 8FA7 FFF4 FFFE 9FFF D3FF FFFB FF00 .<..............
000097B0 24D9 2D6C 6835 2DFA 0D00 00A6 2100 0000 $.-lh5-.....!...
我得bios中的压缩的 ORIGINAL.TMP:
Address Hex ASCII
0001FFF0 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................
00020000 251A 2D6C 6835 2D09 5501 0000 0002 0000 %.-lh5-.U.......
00020010 0000 5020 010C 6F72 6967 696E 616C 2E74 ..P ..original.t
00020020 6D70 0CD9 2000 002D 7888 F0FD D624 A5BA mp.. ..-x....$..
........
00035510 019E 6E67 BF11 8582 88D9 4E7C BEC8 C34C ..ng......N|...L
00035520 401D 189F BDD0 A176 17F0 4383 1D73 BF99 @......v..C..s..
00035530 00C9 FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................
00035540 FFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF ................
6.2. 纯二进制部分
内存印象:
1. 3_6000h - 3_6C4Ah : 这个例程初始化DRAM控制器(在host btidge中的),和我的bios中的DRAM时钟。
2. 3_7000h - 3_7D1Ch : 解压缩例程部分。这个例程包含了LZH解压引擎,可以解压缩上面的bios压缩部分。
3. 3_C000h - 3_CFE4h : 这个区域包含了不同的例程,低128KB是bios地址解码启动,默认的VGA初始化(如果系统bios错误执行),剩下的是Hostbridge初始化例程。
4. 3_E000h - 3_FFFFh : 包含Boot Block代码。
注意:一些部分之间是填充. 一些是FFh bytes 一些是 00h bytes.
6.3. 真实系统(Mainboard)中的内存印象
我们已经注意到了内存映象,前面提到了bios二进制代码的。在主板bios芯片中,有一点不太一样,并且更加复杂。我得主板的映象如下(和你的可能不一样,参考芯片组文档):
1.Bios二进制文件中的0_0000h - 3_FFFFh映射到了系统内存中的FFFC_0000h - FFFF_FFFFh。由于我得系统中的北桥,地址FFFF_0000h-FFFF_FFFFh 只是F_0000h - F_FFFFh 的别名或者也可以说是在“实模式行话” F000:0000h - F000:FFFFh。注意这个映射适合在加电后,因为它是芯片组加电默认的值。在芯片组被bios重新编程后,不保证是否有效。有一些“kludge(杂牌电脑)”,它们是由系统决定的。你不得不参考Intel Software Developer Manual Volume 3(system programming)和你的芯片数据表。
2.归功于第一点的解释,bios中的纯二进制部分映射如下(加电后):
1)BootBlock : F000:E000h - F000:FFFFh
2)Decompression Block : F000:7000h - F000:7D1Ch
3)早期 DRAM 控制器 和 DRAM 初始化 : F000:6000h - F000:6C4Ah
3.压缩的bios部分在他们通过不同方式被释放后映射到系统内存空间。他们依靠这个解压模块例程,但是不同的bios文件中,他们的映射很少有看起来相同的。这些映射(我得如下,你的可能不一样,但是段地址很可能相同):
1)original.tmp a.k.a System BIOS : E000:0000h - F000:FFFFh
2)awardext.rom a.k.a Award 扩展 ROM : 4100:0000h - 4100:xxxxh。稍后被original.tmp重新部署到6000:0000h - 6000:xxxxh,比如在它执行之前。
在我们进行我们的旅途中,我们必需要注意这样的映射。
注意:
由于完全复杂的映射到真实系统bios二进制地址,我们很容易迷路。但是,有一个技巧可以方便我们的工作,在我们反汇编进程中,通过使用IDA Pro:
从纯二进制部分开始反汇编进程。在地址F000:FFF0h (3_FFF0h 从hex editor看二进制)开始反汇编。为了做到这个,用IDA Pro打开二进制文件(VD30728.BIN , i.e. Iwill VD133 BIOS binary),然后反汇编这个文件,通过设置它的地址映射到C000:0000h ,记住让段名无效,那样我们就可以在系统中,执行的时候看到实模式地址。通过IDA Pro的Scripts调整另外的段地址,记住调整地址配置来符合芯片数据表。
7. 反汇编BIOS
由于Intel system programming Guide,我们就要开始在f000:fff0h地址反汇编了(看看上面的内存映射,调整IDA Pro来适应它)。你可能会问:这怎么可能?Intel Software Developer Manual Vol. 3 (PROCESSOR MANAGEMENT AND INITIALIZATION - First Instruction Executed) 介绍:
在硬件reset后得到并执行的第一条指令所在的物理地址是FFFFFFF0H。
答案是:北桥芯片组使用000F_xxxxh 作为FFFE_FFFFh-FFFF_FFFFh 的别名。而且,注意在这个地址的转移之后南桥没有意义。它只是直接将地址传到Bios Rom芯片。因此,在加电reset后,地址FFFF_FFF0h 和 F_FFF0h (or F000:FFF0 in "real-mode lingo")没有什么困难。它是那么简单。这个是BootBlock区域。它总是有一个far jump跳入bootblock区域,主要的在F000:E05Bh 。从这点看,我们能够继续主要的纯二进制部分的反汇编。实际上,很多纯二进制部分代码没有执行,因为你的系统bios很少错误,并且Bootblock POST进程发生了,除非你把它搞糟了。
7.1. Bootblock
从这点看,我们可以返汇编bootblock例程了。现在,我要给你一些不明显的和重要的在已经反汇编了的bios代码区域。这个是关于我的bios,你的可能不同,但是恕我直言很相近。
7.1.1. "Virtual Shutdown" routine
Address Hex Mnemonic
F000:E07F BC 0B F8 mov sp, 0F80Bh ; ret from this jmp redirected to 0E103h (F000:E103h)
F000:E082 E9 7B 15 jmp Chipset_Reg_Early_Init
7.1.2. Chipset_Reg_Early_Init routine
Address Hex Mnemonic
F000:F600 Chipset_Reg_Early_Init proc near ; CODE XREF: F000:E082j
F000:F600 66 C1 E4 10 shl esp, 10h
F000:F604 BE C4 F6 mov si, 0F6C4h ; addr of chipset reg mask
F000:F607 next_PCI_reg: ; CODE XREF: Chipset_Reg_Early_Init+29j
F000:F607 2E 8B 0C mov cx, cs:[si]
F000:F60A BC 10 F6 mov sp, 0F610h
F000:F60D E9 F8 00 jmp Read_PCI_Byte
F000:F60D ; ---------------------------------------------------------------------------
F000:F610 12 F6 dw 0F612h
F000:F612 ; ---------------------------------------------------------------------------
F000:F612 2E 22 44 02 and al, cs:[si+2]
F000:F616 2E 0A 44 03 or al, cs:[si+3]
F000:F61A BC 20 F6 mov sp, 0F620h
F000:F61D E9 02 01 jmp Write_PCI_Byte
F000:F61D ; ---------------------------------------------------------------------------
F000:F620 22 F6 dw 0F622h
F000:F622 ; ---------------------------------------------------------------------------
F000:F622 83 C6 04 add si, 4
F000:F625 81 FE 04 F7 cmp si, 0F704h ; are we done yet?
F000:F629 75 DC jnz next_PCI_reg
F000:F62B BA D0 04 &nbs
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow