研究了一下在Winload和grub2中都是怎么实现控制权到内核的转移的。
winload
在winload中负责这一操作的主要是OslExecuteTransition
函数,其中调用了两个关键函数:OslFwpKernelSetupPhase1
和OslArchTransferToKernel
。
OslFwpKernelSetupPhase1
调用ExitBootService来结束启动服务,将控制权从UEFI转移给OS Loader。
执行完gBS->ExitBootServices
之后,EFI_BOOT_SERVICES
表和控制器设备句柄将会清零,同时EfiBootServicesCode
和EfiBootServicesData
类型的内存资源将被释放。保留运行时服务、EFI_Configuration_Table
以及固件版本号和固件开发商名称字符串以供OS Loader和操作系统使用。
而之后调用OslArchTransferToKernel
则是为了将控制权从OS Loader转移到内核入口点KiSystemStartup
,通过Hook该函数可以在调用KiSystemStartup
之前获得控制权,并借此注入恶意代码。
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) ();
执行跳转到内核的入口点。
近期评论