Step to UEFI (303)更改 Memmap

之前提到过,UEFI 上没有 E820 ,而是通过gBS->GetMemoryMap() 来获得当前内存分配情况,这里给出了一个驱动代码,可以在UEFI Shell 下加载,他会替换之前的GetMemoryMap() 函数,基于之前的返回值增加一个条目:0xFED00000-0xFED1FFFF ,报告为 available 。

HookMemoryMapDxe.c

/** @file
  Memory Map Hook DXE Driver
  Hooks GetMemoryMap to add a fake memory region
**/

#include <Uefi.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>

// Hook Protocol GUID
#define MEMORY_MAP_HOOK_PROTOCOL_GUID \
  { 0x87654321, 0x4321, 0x8765, { 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90 } }

EFI_GUID gMemoryMapHookProtocolGuid = MEMORY_MAP_HOOK_PROTOCOL_GUID;

// Hook数据结构
typedef struct {
  UINT32                Signature;
  EFI_GET_MEMORY_MAP    OriginalGetMemoryMap;
  EFI_PHYSICAL_ADDRESS  FakeMemoryStart;
  UINT64                FakeMemoryPages;
  BOOLEAN               HookEnabled;
} MEMORY_MAP_HOOK_DATA;

#define HOOK_SIGNATURE  SIGNATURE_32('H','M','A','P')

// 全局变量
STATIC MEMORY_MAP_HOOK_DATA *gHookData = NULL;
STATIC EFI_HANDLE gProtocolHandle = NULL;
STATIC EFI_EVENT gExitBootServicesEvent = NULL;

/**
  Hooked GetMemoryMap function
**/
EFI_STATUS
EFIAPI
HookedGetMemoryMap (
  IN OUT UINTN                  *MemoryMapSize,
  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,
  OUT UINTN                     *MapKey,
  OUT UINTN                     *DescriptorSize,
  OUT UINT32                    *DescriptorVersion
  )
{
  EFI_STATUS                Status;
  UINTN                     OriginalMapSize;
  EFI_MEMORY_DESCRIPTOR     *OriginalMap;
  UINTN                     NewMapSize;
  UINTN                     EntryCount;
  UINTN                     Index;
  BOOLEAN                   NeedToAddEntry = TRUE;
  
  // 检查Hook是否启用
  if (gHookData == NULL || !gHookData->HookEnabled || gHookData->OriginalGetMemoryMap == NULL) {
    return EFI_UNSUPPORTED;
  }
  
  // 如果MemoryMap为NULL,只是查询大小
  if (MemoryMap == NULL) {
    Status = gHookData->OriginalGetMemoryMap(MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion);
    if (Status == EFI_BUFFER_TOO_SMALL) {
      // 为新增的条目预留空间
      *MemoryMapSize += *DescriptorSize;
    }
    return Status;
  }
  
  // 分配临时缓冲区
  OriginalMapSize = *MemoryMapSize + *DescriptorSize;
  OriginalMap = AllocatePool(OriginalMapSize);
  if (OriginalMap == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  
  // 调用原始的GetMemoryMap
  Status = gHookData->OriginalGetMemoryMap(&OriginalMapSize, OriginalMap, MapKey, DescriptorSize, DescriptorVersion);
  if (EFI_ERROR(Status)) {
    FreePool(OriginalMap);
    return Status;
  }
  
  EntryCount = OriginalMapSize / *DescriptorSize;
  
  // 检查是否已经存在重叠的内存区域
  for (Index = 0; Index < EntryCount; Index++) {
    EFI_MEMORY_DESCRIPTOR *Entry = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)OriginalMap + Index * (*DescriptorSize));
    EFI_PHYSICAL_ADDRESS EntryStart = Entry->PhysicalStart;
    EFI_PHYSICAL_ADDRESS EntryEnd = EntryStart + (Entry->NumberOfPages * EFI_PAGE_SIZE) - 1;
    
    // 检查是否与新条目重叠
    if ((gHookData->FakeMemoryStart >= EntryStart && gHookData->FakeMemoryStart <= EntryEnd) ||
        (gHookData->FakeMemoryStart + (gHookData->FakeMemoryPages * EFI_PAGE_SIZE) - 1 >= EntryStart && 
         gHookData->FakeMemoryStart + (gHookData->FakeMemoryPages * EFI_PAGE_SIZE) - 1 <= EntryEnd)) {
      NeedToAddEntry = FALSE;
      break;
    }
  }
  
  // 计算新的内存映射大小
  if (NeedToAddEntry) {
    NewMapSize = OriginalMapSize + *DescriptorSize;
  } else {
    NewMapSize = OriginalMapSize;
  }
  
  // 检查提供的缓冲区是否足够大
  if (*MemoryMapSize < NewMapSize) {
    *MemoryMapSize = NewMapSize;
    FreePool(OriginalMap);
    return EFI_BUFFER_TOO_SMALL;
  }
  
  // 复制原始内存映射
  CopyMem(MemoryMap, OriginalMap, OriginalMapSize);
  
  // 如果需要,添加新的内存条目
  if (NeedToAddEntry) {
    EFI_MEMORY_DESCRIPTOR *NewEntry = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + OriginalMapSize);
    
    NewEntry->Type = EfiConventionalMemory;  // Available memory
    NewEntry->PhysicalStart = gHookData->FakeMemoryStart;
    NewEntry->VirtualStart = 0;
    NewEntry->NumberOfPages = gHookData->FakeMemoryPages;
    NewEntry->Attribute = EFI_MEMORY_WB;  // Write-back cacheable
  }
  
  *MemoryMapSize = NewMapSize;
  FreePool(OriginalMap);
  
  return EFI_SUCCESS;
}

/**
  ExitBootServices event handler
**/
VOID
EFIAPI
ExitBootServicesNotify (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  // 在ExitBootServices之前恢复原始函数指针
  if (gHookData != NULL && gHookData->HookEnabled && gHookData->OriginalGetMemoryMap != NULL) {
    gBS->GetMemoryMap = gHookData->OriginalGetMemoryMap;
    
    // 更新CRC32
    gBS->Hdr.CRC32 = 0;
    gBS->CalculateCrc32((UINT8 *)gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32);
    
    gHookData->HookEnabled = FALSE;
  }
}

/**
  Driver entry point
  
  @param ImageHandle     The image handle
  @param SystemTable     The system table
  
  @retval EFI_SUCCESS    Driver loaded successfully
**/
EFI_STATUS
EFIAPI
HookMemoryMapDxeEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS Status;
  
  DEBUG((DEBUG_INFO, "HookMemoryMapDxe: Driver starting...\n"));
  
  // 分配Hook数据结构
  gHookData = AllocateRuntimeZeroPool(sizeof(MEMORY_MAP_HOOK_DATA));
  if (gHookData == NULL) {
    DEBUG((DEBUG_ERROR, "HookMemoryMapDxe: Failed to allocate hook data\n"));
    return EFI_OUT_OF_RESOURCES;
  }
  
  // 初始化Hook数据
  gHookData->Signature = HOOK_SIGNATURE;
  gHookData->OriginalGetMemoryMap = gBS->GetMemoryMap;
  gHookData->FakeMemoryStart = 0xFED00000;  // 0xFED00000-0xFED1FFFF
  gHookData->FakeMemoryPages = 0x20000 / EFI_PAGE_SIZE; // 128KB = 32 pages
  gHookData->HookEnabled = FALSE;
  
  // 安装Protocol (用于标识和查找)
  Status = gBS->InstallProtocolInterface(
    &gProtocolHandle,
    &gMemoryMapHookProtocolGuid,
    EFI_NATIVE_INTERFACE,
    gHookData
  );
  
  if (EFI_ERROR(Status)) {
    DEBUG((DEBUG_ERROR, "HookMemoryMapDxe: Failed to install protocol: %r\n", Status));
    FreePool(gHookData);
    gHookData = NULL;
    return Status;
  }
  
  // 创建ExitBootServices事件
  Status = gBS->CreateEvent(
    EVT_SIGNAL_EXIT_BOOT_SERVICES,
    TPL_NOTIFY,
    ExitBootServicesNotify,
    NULL,
    &gExitBootServicesEvent
  );
  
  if (EFI_ERROR(Status)) {
    DEBUG((DEBUG_WARN, "HookMemoryMapDxe: Failed to create ExitBootServices event: %r\n", Status));
    // 不是致命错误,继续执行
  }
  
  // 安装Hook
  gBS->GetMemoryMap = HookedGetMemoryMap;
  
  // 更新CRC32
  gBS->Hdr.CRC32 = 0;
  gBS->CalculateCrc32((UINT8 *)gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32);
  
  // 启用Hook
  gHookData->HookEnabled = TRUE;
  
  DEBUG((DEBUG_INFO, "HookMemoryMapDxe: Driver loaded successfully\n"));
  DEBUG((DEBUG_INFO, "HookMemoryMapDxe: Hook installed and enabled\n"));
  DEBUG((DEBUG_INFO, "HookMemoryMapDxe: Fake memory region: 0x%lx-0x%lx\n", 
        gHookData->FakeMemoryStart,
        gHookData->FakeMemoryStart + (gHookData->FakeMemoryPages * EFI_PAGE_SIZE) - 1));
  
  return EFI_SUCCESS;
}

HookMemoryMapDxe.inf


这个代码需要放置在MdeModulePkg下面,然后在MdeModulePkg.dsc添加:

[PcdsDynamicExDefault]
  gEfiMdeModulePkgTokenSpaceGuid.PcdRecoveryFileName|L"FVMAIN.FV"

[Components]
  MdeModulePkg/HookMemoryMapDxe/HookMemoryMapDxe.inf
  MdeModulePkg/Application/HelloWorld/HelloWorld.inf
  MdeModulePkg/Application/DumpDynPcd/DumpDynPcd.inf
  MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf

之后,使用下面的命令进行编译即可:

build -a X64 -p MdeModulePkg/MdeModulePkg.dsc -t VS2019

完整的代码下载:

使用时,只需要在 UEFI Shell 下 load HookMemoryMapDxe.efi 即可,之后可以使用 memmap 命令看到改动结果。

特别注意:这里只是一个Demo ,实际使用时需要根据你的需求进行修改。

发表回复

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