先介绍 DMI 。 DMI 是英文单词 Desktop Management Interface 的缩写,也就是桌面管理界面,它含有关于系统硬件的配置信息。计算机每次启动时都对 DMI 数据进行校验,如果该数据出错或硬件有所变动,就会对机器进行检测,并把测试的数据写入 BIOS 芯片保存。所以如果我们在 BIOS 设置中禁止了 BIOS 芯片的刷新功能或者在主板使用跳线禁止了 BIOS 芯片的刷新功能,那这台机器的 DMI 数据将不能被更新。如果你更换了硬件配置,那么在进行 WINDOWS 系统时,机器仍旧按老系统的配置进行工作。这样就不能充分发挥新添加硬件的性能,有时还会出现这样或那样的故障。
SMBIOS(System Management BIOS , SMBIOS) 是主板或系统制造者以标准格式显示产品管理信息所需遵循的统一规范。 DMI(Desktop Management Interface, DMI) 就是帮助收集电脑系统信息的管理系统, DMI 信息的收集必须在严格遵照 SMBIOS 规范的前提下进行。 SMBIOS 和 DMI 是由行业指导机构 Desktop Management Task Force (DMTF) 起草的开放性的技术标准,其中, DMI 设计适用于任何的平台和操作系统。 DMI 充当了管理工具和系统层之间接口的角色。它建立了标准的可管理系统更加方便了电脑厂商和用户对系统的了解。
DMI 的主要组成部分是 Management Information Format (MIF) 数据库。这个数据库包括了所有有关电脑系统和配件的信息。通过 DMI ,用户可以获取序列号、电脑厂商、串口信息以及其它系统配件信息。
对于符合 SMBIOS 规范的计算机,可以通过访问 SMBIOS 的结构获得系统信息,共有两种办法可以访问:
1. 通过即插即用功能接口访问 SMBIOS 结构,这个在 SMBIOS2.0 标准里定义了,从 SMBIOS 2.1 开始这个访问方法不再被推荐使用。
2 .基于表结构的方法,表内容是 table entry point 的数据,这个访问方法从 SMBIOS 2.1 以后开始被使用,从 2.1 开始,以后的版本都推荐使用这种访问方式。在 2.1 版本中允许支持这两种方法中的任意一种和两种都支持,但在 2.2 以后的版本,必须支持方法 2 。在最新的 2.7.0 版中第一种方法已经废弃。
鉴于市场上计算机已经均支持 SMBIOS2.3 标准,所以只考虑方法 2 ,基于表结构的访问方式。基于表结构访问 SMBIOS 的过程是先找到 Entry Point Structure ( EPS )表,然后通过 Entry Point Structure ( EPS )表的数据找到 SMBIOS 结构表。
对于非 EFI 的系统,访问 SMBIOS EPS 表的操作过程如下:
1 .从物理内存 0x000F0000-0x000FFFFF 之间寻找关键字 “ _SM_” 。
2 .找到后再向后 16 个字节,看后面 5 个 BYTE 是否是关键字 “_DMI_” ,如果是, EPS 表即找到。
对于 UEFI (是 BIOS 的下一代版本)系统,可能通过搜索 EFI 配置表中的 SMBIOS GUID(SMBIOS_TABLE_GUID) ,然后使用指向 SMBIOS 的指针来定位 EPS 表。具体可参考 UEFI 规范。
SMBIOS EPS 表结构如下:
位置 |
名称 |
长度 |
描述 |
00H |
关键字 |
4BYTE |
固定是”_SM_” |
04H |
校验和 |
1BYTE |
用于校验数据 |
05H |
表结构长度 |
1BYTE |
Entry Point Structure 表的长度 |
06H |
Major 版本号 |
1BYTE |
用于判断SMBIOS 版本 |
07H |
Minor 版本号 |
1BYTE |
用于判断SMBIOS 版本 |
08H |
表结构大小 |
2BYTE |
用于即插即用接口方法获得数据表结构长度 |
0AH |
EPS 修正 |
1BYTE |
|
0B-0FH |
格式区域 |
5BYTE |
存放解释EPS 修正的信息 |
10H |
关键字 |
5BYTE |
固定为“_DMI_” |
15H |
校验和 |
1BYTE |
Intermediate Entry Point Structure (IEPS) 的校验和 |
16H |
结构表长度 |
2BYTE |
SMBIOS 结构表的长度 |
18H |
结构表地址 |
4BYTE |
SMBIOS 结构表的真实内存位置 |
1CH |
结构表个数 |
2BYTE |
SMBIOS 结构表数目 |
1EH |
Smbios BCD 修正 |
1BYTE |
|
通过 EPS 表结构中 16H 以及 18H 处,得出数据表长度和数据表地址,即可通过地址访问 SMBIOS 数据结构表。从 EPS 表中的 1CH 处可得知数据表结构的总数,其中 TYPE 0 结构就是 BIOS information , TYPE 1 结构就是 SYSTEM Information 。
每个结构的头部是相同的,格式如下:
位置 |
名称 |
长度 |
描述 |
00H |
TYPE 号 |
1BYTE |
结构的TYPE 号 |
01H |
长度 |
1BYTE |
本结构的长度,就此TYPE 号的结构而言 |
02H |
句柄 |
2BYTE |
用于获得本SMBIOS 结构,其值不定 |
每个结构都分为格式区域和字符串区域,格式区域就是一些本结构的信息,字符串区域是紧随在格式区域后的一个区域。结构 01H 处标识的结构长度仅是格式区域的长度,字符串区域的长度是不固定的。有的结构有字符串区域,有的则没有。
下面以 TYPE 0 ( BIOS information )为例说明格式区域和字符串区域的关系。 TYPE 0 ( BIOS information )格式区域如下:
位置 |
名称 |
长度 |
描述 |
00H |
TYPE 号 |
1BYTE |
结构的TYPE 号,此处是0 |
01H |
长度 |
1BYTE |
TYPE 0 格式区域的长度,一般为14H ,也有13H |
02H |
句柄 |
2BYTE |
本结构的句柄,一般为0000H |
04H |
Bios 厂商信息 |
1BYTE |
此处是bios 卖方的信息,可能是OEM 厂商名,一般为01H ,代表紧随格式区域后的字符串区域的第一个字符串 |
05H |
BIOS 版本 |
1BYTE |
BIOS 版本号,一般为02H ,代表字符串区域的第二个字符串 |
06H |
Bios 开始地址段 |
2BYTE |
用于计算常驻BIOS 镜像大小的计算,方法为 (10000H-BIOS 开始地址段)×16 |
08H |
BIOS 发布日期 |
1BYTE |
一般为03H ,表示字符区第三个字符串 |
09H |
BIOS rom size |
1BYTE |
计算方法为(n +1 )×64K ,n 为此处读出数值 |
0AH |
BIOS 特征 |
8BYTE |
Bios 的功能支持特征,如PCI,PCMCIA,FLASH 等 |
12H |
Bios 特征扩展 |
不定 |
|
紧随 TYPE 0 ( BIOS information )结构区域之后,即在 Bios 特征扩展域后面的就是 TYPE 0 ( BIOS information )字符串区域,一个例子如下所示:
db ‘System BIOS Vendor Name’,0 ; 字符串以零结尾,第一个字符串:BIOS厂商 db ‘4.04’,0 ; 第二个:BIOS版本 db ‘00/00/0000’,0 ; 第三个:BIOS发布日期 db 0 ; 以0为整个字符中区域的结尾每个字符串都以 00H 作为结束标志,上面的例子中有三个字符串。如果我们要找下一个 TYPE ,因为最后一个字符串以 00H 结尾,而整个字符区域又以 00H 结尾,故只要在字符串区域找到连续的 0000H 即可。例如,一个带字符串域的完整 BIOS Information 例子如下:
BIOS_Info LABEL BYTE db 0 ; Indicates BIOS Structure Type db 13h ; Length of information in bytes dw ? ; Reserved for handle db 01h ; String 1 is the Vendor Name db 02h ; String 2 is the BIOS version dw 0E800h ; BIOS Starting Address db 03h ; String 3 is the BIOS Build Date db 1 ; Size of BIOS ROM is 128K (64K * (1 + 1)) dq BIOS_Char ; BIOS Characteristics db 0 ; BIOS Characteristics Extension Byte 1 db ‘System BIOS Vendor Name’,0 ; db ‘4.04’,0 ; db ‘00/00/0000’,0 ; db 0 ; End of strings一个不带字符串域的完整 BIOS Information 例子:
BIOS_Info LABEL BYTE db 0 ; Indicates BIOS Structure Type db 13h ; Length of information in bytes dw ? ; Reserved for handle db 00h ; No Vendor Name provided db 00h ; No BIOS version provided dw 0E800h ; BIOS Starting Address db 00h ; No BIOS Build Date provided db 1 ; Size of BIOS ROM is 128K (64K * (1 + 1)) dq BIOS_Char ; BIOS Characteristics db 0 ; BIOS Characteristics Extension Byte 1 dw 0000h ; Structure terminator注意,当从 EPS 表中得到结构表的开始地址后,可以直接按结构来寻找相应的 TYPE 号,找到后直接读取就是该 TYPE 对应的结构的格式区域信息,然后向后移动结构区域长度(结构区域长度由该结构的 01H 处读出)个 BYTE ,即是该 TYPE 结构的字符串区域。
由上面介绍可知,获得 BIOS 信息的办法就是:
1 .通过 EPS 表的 12H 和 14H 数据找到 TYPE 结构表,然后找到 TYPE 0 的内存地址 ( 不一定是首个 ) 。
2 .由 TYPE 0 结构区域中得出相应 BIOS 信息是否存在(存在则是上面所述的 01H,02H,03H 依次排布,不存在则是相应的位置上为 00H )。
3 .如存在信息,则从字符串区域中读取对应 BIOS 信息。
获得其他类型的 SMBIOS 结构信息的方法类似,只是 TYPE 结构区域有所不同。
相信大家都用过一些系统检测软件 , 或者至少用过 Windows 优化大师里面的系统检测。实际上,应用程序程序就可以通过访问 SMBIOS 来获得这些信息。这里举一个例子,演示如何找到 BIOS 的版本号 (BIOS Version) 和电脑的厂商 (Manufacturer) 。不过我们不采用编程的方式,而是通过系统自带的 debug 命令来进行相关的操作。
内存的物理地址区间 0x000F0000-0x000FFFFF 使用了 32 位中的 20 位,先要在这个区间内寻找关键字 “ _SM_” ,以定位到 EPS 表。我们要采用 20 位地址模式,即高位字左移 12 位,因此值 0x000F0000 表示为 F000:0000 ,地址区间为 F000:0000 - F000:FFFF 。在 cmd 中输入 "debug", 回车 , 这样就进入了 debug 环境,它的命令提示符为 - , 以后出现在 - 后面的内容都为将要输入的命令。
在地址区间内搜索关键字 "_SM_" 的起始地址:
-s f000:0 ffff '_SM_'
以我的电脑为例 , 响应可能是下面这样的:
-s f000:0 ffff '_SM_'
F000:6C00
这个地址就是 EPS 表的起始地址。也有可能你的电脑会显示多个查找结果,不妨先随意选择一个进行下面的操作:
-d F000:6C00
会打印该地址后面 16*8 个字节的内容,可能的响应:
-d F000:6C00 F000:6C00 5F 53 4D 5F 16 1F 02 1F-4B 01 00 00 00 00 00 00 _SM_....K....... F000:6C10 5F 44 4D 49 5F 3D 98 09-10 30 0E 00 3C 00 00 00 _DMI_=...0..<... F000:6C20 52 53 44 20 50 54 52 20-DE 4E 45 43 20 20 20 00 RSD PTR .NEC . F000:6C30 34 95 F7 0D 00 00 00 00-00 00 00 00 00 00 00 00 4............... F000:6C40 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ F000:6C50 24 46 53 58 56 0C F8 00-B1 00 E0 00 00 00 00 00 $FSXV........... F000:6C60 5F 33 32 5F 20 D7 0F 00-00 01 D6 00 00 00 00 00 _32_ ........... F000:6C70 24 50 44 4D 01 0B 1A 5D-88 00 F0 00 00 00 00 00 $PDM...]........如果嫌显示的行数太少 , 可以用 -d 继续打印后面的信息。注意到了吗, _SM_ (其 ASCII 编码为 5F 53 4D 5F ),还有 10H 偏移处(即偏移 16 个字节)的 _DMI_ 。如果你在上一步中有多个结果,那么只要某个结果通过 d 命令查看的结果符合这两个特征,我们就可以用它来继续下面的操作。至这一步, EPS 已经找到了。
EPS 的偏移 18H 处的 4 字节为 10 30 0E 00 ,是 SMBIOS 结构表的 32 位地址。这 4 个字节是从低字节到高字节,因为 x86 采用小端字节序,低字节存放的是高位的值,因此重新写成 " 高位 低位 " 的格式就是 000E 3010 ,转换成 20 位格式就把高位左移 12 位,变成 E000:3010 的形式。打印该地址处的内容:
-d e000:3010
可能的响应:
-d e000:3010 E000:3010 00 14 00 00 01 02 F5 E4-03 07 90 DF 99 7C 00 00 .............|.. E000:3020 00 00 05 03 4E 45 43 20-20 20 20 20 00 4E 4F 54 ....NEC .NOT E000:3030 45 20 42 49 4F 53 20 56-65 72 73 69 6F 6E 20 2F E BIOS Version / E000:3040 33 36 39 41 30 36 30 30-20 00 30 39 2F 31 30 2F 369A0600 .09/10/ E000:3050 32 30 30 33 00 00 01 19-01 00 01 02 03 04 8B B0 2003............ E000:3060 DE 65 92 46 40 EB 86 FE-56 28 BD 80 79 13 06 4 E [email protected](..y..N E000:3070 45 43 20 43 6F 6D 70 75-74 65 72 73 20 49 6E 74 EC Computers Int E000:3080 65 72 6E 61 74 69 6F 6E-61 6C 00 50 43 2D 43 56 ernational.PC-CV首先,第一个字节 ( 零偏移处 ) 是 00 ,根据规范,这说明从这里开始的信息为 TYPE 0 结构,也即 BIOS 信息区。第二个字节 ( 偏移 01H 处 ) 是 14 ,说明 TYPE 0 区域的基本大小是 14H 。根据规范,偏移 02H 处的两个字节为 00 00 ,表示句柄。偏移 04H 处的字节为 01 ,表示 BIOS 厂商这个字符串在 Type 0 字符串区域中的编号,即字符串区域中的第 01 个字符串就 BIOS 厂商。偏移 05H 处的字节为 02 ,表示 BIOS 版本字符串在字符串区域中的编号。偏移 08H 处的字节为 03 ,表示 BIOS 发布日期字符串在字符串区域中的编号。
TYPE 0 区的基本大小为 14H ,则偏移 14H 处是 TYPE 0 字符串区域的开始。偏移 14H 处的值是 4E (右面对应的 ASCII 字符是 N ),这就是 TYPE 0 字符串区域的开始,它们是不包括在 TYPE 的大小计算中的。每个字符串不定长,由 00H 作为结束标志。熟悉 C/C++ 的人应该对这一标志符比较亲切吧。从第一个字符串依次编号为 01 , 02... 。第 01 个字符串为 ”NEC” (这里结束标志 00 前还插入了若干个控制字符 20 ),是 BIOS 厂商,第 02 个字符串为 "NOTE BIOS Version / 369A0600 " ,是 BIOS 版本,第 03 个字符串为 ”09/10/2003” ,是 BIOS 发布日期,这样我们的第一个目标 BIOS 版本就找到了。
整个字符串区的结束用 00 来标志,又根据字符串的结束符为 00 ,所以找到 00 00 ,从 00 00 的下一个地址开始就是下一个信息区了。这个例子中,就是 E000:3056 处,内容是 01 ,作为下一个区域的第一个字节,它标志着此 TYPE 的类型 , 这里就是 TYPE 1, 也即 System Information 。
根据与 TYPE 0 类似的方法 , 我们从 TYPE 1 的偏移 01H 处得到 TYPE 1 的大小为 19H , 而 SMBIOS 规范中说明, TYPE 1 的偏移 04H 处即为 Manufacturer 字符串的编号,这里即为 01 。转到 TYPE 1 的偏移 19H 处,即为 TYPE 1 的字符串区的开始,由于我们要找的信息编号为 01 ,所以从这里开始的信息 "NEC Computers International" 即为厂商的信息。
SMBIOS 支持在保护模式下工作,因此,在 Windows 中使用 16 位汇编进行程序编写将使得对物理内存进行访问变得非常容易。