UEFI固件安全 · 2023年5月23日 0

OS Loader到内核的控制权转移

研究了一下在Winload和grub2中都是怎么实现控制权到内核的转移的。

winload

在winload中负责这一操作的主要是OslExecuteTransition函数,其中调用了两个关键函数:OslFwpKernelSetupPhase1OslArchTransferToKernel

OslFwpKernelSetupPhase1调用ExitBootService来结束启动服务,将控制权从UEFI转移给OS Loader

执行完gBS->ExitBootServices之后,EFI_BOOT_SERVICES表和控制器设备句柄将会清零,同时EfiBootServicesCodeEfiBootServicesData类型的内存资源将被释放。保留运行时服务、EFI_Configuration_Table以及固件版本号和固件开发商名称字符串以供OS Loader和操作系统使用。

image-20230523150040829

而之后调用OslArchTransferToKernel则是为了将控制权从OS Loader转移到内核入口点KiSystemStartup,通过Hook该函数可以在调用KiSystemStartup之前获得控制权,并借此注入恶意代码。

image-20230523152836590

Grub2

在grub2里主要由grub_relocator32_boot()来实现跳转到内核执行。

grub_err_t
grub_relocator32_boot (struct grub_relocator *rel,
               struct grub_relocator32_state state,
               int avoid_efi_bootservices)
{
  grub_err_t err;
  void *relst;
  grub_relocator_chunk_t ch;

  /* Specific memory range due to Global Descriptor Table for use by payload
     that we will store in returned chunk.  The address range and preference
     are based on "THE LINUX/x86 BOOT PROTOCOL" specification.  */
  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x1000, 0x9a000,
                           RELOCATOR_SIZEOF (32), 16,
                           GRUB_RELOCATOR_PREFERENCE_LOW,
                           avoid_efi_bootservices);
  if (err)
    return err;

  grub_relocator32_eax = state.eax;
  grub_relocator32_ebx = state.ebx;
  grub_relocator32_ecx = state.ecx;
  grub_relocator32_edx = state.edx;
  grub_relocator32_eip = state.eip;
  grub_relocator32_esp = state.esp;
  grub_relocator32_ebp = state.ebp;
  grub_relocator32_esi = state.esi;
  grub_relocator32_edi = state.edi;

  grub_memmove (get_virtual_current_address (ch), &grub_relocator32_start,
        RELOCATOR_SIZEOF (32));

  err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch),
                       &relst, NULL);
  if (err)
    return err;

  asm volatile ("cli");
  ((void (*) (void)) relst) ();

  /* Not reached.  */
  return GRUB_ERR_NONE;
}

首先将state的各个寄存器复制到ch指向的地址。

再调用grub_relocator_prepare_relocs将ch内容转换为重定位的指令,由relst指向该重定位的指令地址。

asm volatile ("cli")关闭外部中断。

((void (*) (void)) relst) ();执行跳转到内核的入口点。