Step to UEFI (300)改写SMBIOS

通过 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;
}

基本流程:

  1. ParseSmbios21Table() 找到 SMBIOS Table
  2. switch (Header->Type) case 1: 确认是System Information ,交给ProcessType1SystemInfo()函数
  3. 这里面找到ProductName,用我们的字符串替代之前的

实体机上测试结果如下:

原本是 Lenovo 的小新:

修改为 lab-z.com 的

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

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注