通过 SMBIOS 识别当前系统型号是最简单的方法。
通常情况下 SMBIOS是直接写在BIOS中的,可以使用一些工具重新写入,但是因为 IBV 的差别,没有通用工具。
最近我忽然意识到,对于 UEFI 来说,这个信息是存储在内存中的,如果在启动之前直接修改内存,那么就可以实现修改的目的。
代码如下:
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/Smbios.h>
#include <IndustryStandard/SmBios.h>
#include <Guid/SmBios.h>
#include <string.h>
extern EFI_BOOT_SERVICES *gBS;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_RUNTIME_SERVICES *gRT;
/**
* 获取 SMBIOS 字符串
*/
CHAR8*
GetSmbiosStringByOffset (
IN UINT8 *StructureStart,
IN UINT8 StructureLength,
IN UINT8 StringNumber
)
{
CHAR8 *String;
UINTN Index;
if (StringNumber == 0) {
return NULL;
}
// 跳过结构体,到达字符串区域
String = (CHAR8 *)(StructureStart + StructureLength);
// 查找指定编号的字符串
for (Index = 1; Index < StringNumber; Index++) {
while (*String != 0) {
String++;
}
String++;
if (*String == 0) {
return NULL; // 没有更多字符串
}
}
return String;
}
/**
* 获取下一个 SMBIOS 结构的地址
*/
UINT8*
GetNextSmbiosStructure (
IN UINT8 *CurrentStructure
)
{
SMBIOS_STRUCTURE *Header;
UINT8 *StringPtr;
Header = (SMBIOS_STRUCTURE *)CurrentStructure;
// 跳过结构体到字符串区域
StringPtr = CurrentStructure + Header->Length;
// 跳过所有字符串,直到遇到双 NULL 终止符
while (!(StringPtr[0] == 0 && StringPtr[1] == 0)) {
StringPtr++;
}
// 跳过双 NULL 终止符
return StringPtr + 2;
}
/**
* 处理 SMBIOS Type 1 (System Information)
*/
VOID
ProcessType1SystemInfo (
IN UINT8 *Structure
)
{
SMBIOS_TABLE_TYPE1 *Type1;
CHAR8 *Manufacturer;
CHAR8 *ProductName;
CHAR8 *Version;
CHAR8 *SerialNumber;
Type1 = (SMBIOS_TABLE_TYPE1 *)Structure;
Print(L"=== System Information (Type 1) ===\n");
Manufacturer = GetSmbiosStringByOffset(Structure, Type1->Hdr.Length, Type1->Manufacturer);
if (Manufacturer != NULL) {
}
ProductName = GetSmbiosStringByOffset(Structure, Type1->Hdr.Length, Type1->ProductName);
if (ProductName != NULL) {
Print(L"Product Name: %a\n", ProductName);
strcpy(ProductName, "lab-z.com");
}
Version = GetSmbiosStringByOffset(Structure, Type1->Hdr.Length, Type1->Version);
if (Version != NULL) {
}
SerialNumber = GetSmbiosStringByOffset(Structure, Type1->Hdr.Length, Type1->SerialNumber);
if (SerialNumber != NULL) {
}
Print(L"UUID: %g\n", &Type1->Uuid);
Print(L"\n");
}
/**
* 解析 SMBIOS 2.1 表
*/
EFI_STATUS
ParseSmbios21Table (
IN SMBIOS_TABLE_ENTRY_POINT *Smbios21Entry
)
{
UINT8 *TableAddress;
UINT8 *CurrentStructure;
UINT8 *TableEnd;
SMBIOS_STRUCTURE *Header;
UINTN StructureCount = 0;
Print(L"=== SMBIOS 2.1 Entry Point ===\n");
Print(L"Anchor String: %.4a\n", Smbios21Entry->AnchorString);
Print(L"Major Version: %d\n", Smbios21Entry->MajorVersion);
Print(L"Minor Version: %d\n", Smbios21Entry->MinorVersion);
Print(L"Table Length: %d bytes\n", Smbios21Entry->TableLength);
Print(L"Table Address: 0x%x\n", Smbios21Entry->TableAddress);
Print(L"Number of Structures: %d\n", Smbios21Entry->NumberOfSmbiosStructures);
Print(L"\n");
TableAddress = (UINT8 *)(UINTN)Smbios21Entry->TableAddress;
TableEnd = TableAddress + Smbios21Entry->TableLength;
CurrentStructure = TableAddress;
// 遍历所有 SMBIOS 结构
while (CurrentStructure < TableEnd &&
StructureCount < Smbios21Entry->NumberOfSmbiosStructures) {
Header = (SMBIOS_STRUCTURE *)CurrentStructure;
// 检查是否到达表尾 (Type 127)
if (Header->Type == 127) {
break;
}
StructureCount++;
//Print(L"Structure %d: Type %d, Length %d, Handle 0x%04x\n",
// StructureCount, Header->Type, Header->Length, Header->Handle);
// 处理特定类型的结构
switch (Header->Type) {
case 1: // System Information
ProcessType1SystemInfo(CurrentStructure);
break;
default:
// 其他类型暂时只显示基本信息
break;
}
// 移动到下一个结构
CurrentStructure = GetNextSmbiosStructure(CurrentStructure);
}
Print(L"Total structures processed: %d\n", StructureCount);
return EFI_SUCCESS;
}
int
EFIAPI
main (
IN int Argc,
IN CHAR16 **Argv
)
{
EFI_STATUS Status;
SMBIOS_TABLE_ENTRY_POINT *SmbiosTable = NULL;
Status = EfiGetSystemConfigurationTable (
&gEfiSmbiosTableGuid,
(VOID **)&SmbiosTable
);
if (!EFI_ERROR (Status)) {
Print (L"SmbiosTable Version %d.%d\n",
SmbiosTable->MajorVersion,
SmbiosTable->MinorVersion);
Print (L"SmbiosTable Address: %x \n",
SmbiosTable->TableAddress);
ParseSmbios21Table(SmbiosTable);
} else {
Print (L"Can't read SMBIOS\n");
}
return EFI_SUCCESS;
}
基本流程:
- ParseSmbios21Table() 找到 SMBIOS Table
- switch (Header->Type) case 1: 确认是System Information ,交给ProcessType1SystemInfo()函数
- 这里面找到ProductName,用我们的字符串替代之前的
实体机上测试结果如下:
原本是 Lenovo 的小新:

修改为 lab-z.com 的

在使用的时候需要特别注意可能发生覆盖之前字符串的问题,不要溢出,不要破坏之前的结构。







