uefi学习笔记
本文最后更新于 48 天前,其中的信息可能已经有所发展或是发生改变。

chap 01 UEFI/BIOS Introdution

BIOSBasic Input Output System,而UEFI则是其的替代品。

  • BIOS

BIOS是固化在主板ROM里的程序代码,它主要就是用于对于连接到主板的上的各个硬件进行初始化配置,同时封装BIOS的中断程序(实模式开发下,我们调用int XX就可以读写磁盘…其实这些CPU肯定是需要设置的,主板上电的时候,bios会设置这些东西,一切就绪,才会跑到MBR,0x7fxxxx的那个位置)。

BIOS的主要功能如下

  • 加电自检程序,在开机时负责检测硬件设备是否正常工作。
  • 系统初始化程序,其中包括硬件设备的初始化以及创建BIOS中断向量 等。
  • 适配外围即插即用设备。
  • CMOS设置程序,负责读写保存在CMOS中的系统设置信息。
  • UEFI

这里需要提前说明的,UEFI是一种规范,他是纯接口规范,一般我们说UEFI,是指UEFI规范。具体而言,UEFI规范可以做到描述OS和平台固件之间的接口,最终定义平台固件和操作系统的通信方法,而且可以忽略平台。

UEFI规范仅提供操作系统引导过程所需的信息,旨在无需对平台或操作系 统进行深入定制便可在处理器规范兼容的平台上运行操作系统。

UEFI规范 还允许平台引入创新的特性和功能,在无需为OS引导程序重新编程的情况 下增强平台功能。UEFI规范适用于从移动系统到服务器的各种硬件平台, 并允许原始设备制造商具有最大的扩展性和定制能力,以实现差异化。

总结,就是一句话,

  • UEFI规范是纯接口,没代码,实现的时候写代码按照他的规范就行.
  • UEFI屏蔽了OS和不同平台固件的差异化(AMD ARM x86…)
  • UEFI同时给予了平台固件可扩展的接口,即可以通过UEFI的数据表来增加新的平台固件功能。
  • UEFI组成是接口表系统分区引导服务运行时服务

image-20240824101954993

UEFI要求实现的时候自带引导管理器,平台固件通过引导管理器可以从UEFI定义的系 统分区中加载任何文件,也可以通过UEFI定义的镜像加载服务来加载文件。

OS在引导的过程中,也可以使用UEFI的引导服务和接口来完成管理平台的各个固件,就类似BIOS的中断服务程序

UEFI vs BIOS

BIOS start-up

BIOS的启动流程如下

image-20240824103328164

UEFI Start-up

而UEFI的启动非常复杂,而且UEFI固件启动完毕的时候,是直接会进入IA32E或者是保护模式

image-20240824103756477

如上图,UEFI的启动流程大致为分七个阶段

  • Securtity Check,初期验证阶段,此阶段的任务是系统上电,此时内存无法使用,建立临时的内存。
  • Pre EFI Init Env,预初始化环境,主要是对内存、CPU、芯片组进行初始化,这个部分代码必须精简。还需要确认OS的loader的文件系统路径,以及初始化UEFI驱动和固件内存。
  • Driver Execution Env,DXE,是UEFI最重要的阶段,大部分驱动、固件加载工作再次完成。
  • BDS(Boot Device Select)
  • TSL(Transient System Load),进入临时UEFI Shell(如果没有Boot Device 加载,进入此模式)
  • RT(Runtime),OS调用EFI_BOOT_SERVICES.ExitBootServices,DXE和引导服务销毁,只有EFI运行时服务和EFI系统表可以使用
  • AL(After life),OS调用EFI_RUNTIME_SERVICES.ResetSystem

UEFI允许通过加载UEFI驱动程序和UEFI应用程序镜像来扩展平台固件。当 UEFI驱动程序和UEFI应用程序加载时,他们可以访问所有UEFI定义的引导 服务和运行时服务。

UEFI要求可引导的块设备必须含 有一个ESP(EFI System Partition)系统分区。UEFI不需要对ESP系统分 区的第一个扇区进行任何更改,因此可以在引导媒介中同时引导传统体系 结构和UEFI平台。

BIOS -> UEFI

BIOS被UEFI取代,有下面几个原因

image-20240824105813239

其实UEFI最大的一个突破是安全性,开了安全启动,UEFI会检测执行的应用程序和驱动证书。UEFI驱动和应用程序都是PE/COFF格式(windows牌面)

UEFI Architecture

UEFI的架构分为六个部分,他们有些部分只能在特定的UEFI启动阶段使用,分别是:

  1. UEFI Boot Services,UEFI Boot Services 用于提供启动操作系统和初始化硬件组件(包括内存管理、磁盘访问等)的基本功能,启动服务在预启动阶段可用,通常由引导加载程序调用(将操作系统内核加载到内存中并将控制权转移给它)。
  2. UEFI 运行时服务,运行时服务是一组在操作系统启动后保持可用的固件功能,这些服务为应用程序和驱动程序提供运行时支持,使它们能够与固件功能进行交互。 (它们通常包括管理系统时间、访问非易失性变量和查询系统信息的函数)。其实HAL最底层的实现就是和UEFI运行时打交道。
  3. UEFI驱动程序,UEFI驱动程序是模块化组件,通过提供对特定硬件设备或系统资源的支持来扩展固件的功能,UEFI驱动程序和内核驱动程序之间的主要区别在于它们重新实现为EFI可执行文件,并且可以由 UEFI 启动管理器在启动过程中执行(这意味着它们可以在操作系统启动之前加载)。
  4. UEFI Boot Manager,UEFI Boot Manager 负责管理引导过程并从可用的固件引导条目中选择适当的引导选项(它提供从不同设备或操作系统加载程序引导的选项)。
  5. EFI系统分区(ESP),EFI系统分区是存储设备上的一个特殊分区,其中包含启动操作系统所需的引导加载程序、固件可执行文件和配置文件。 ESP 通常使用 FAT 文件系统进行格式化,以确保与 UEFI 固件兼容(这就是为什么大多数从 U 盘加载的 UEFI 作弊程序会要求您在使用前格式化为 FAT-32 格式)
  6. 串行外设接口(SPI),SPI 是 UEFI 固件中常用的同步串行通信接口,用于各种目的,例如访问外部存储器、配置外设和更新固件,在我们的上下文中,SPI 在访问和更新固件方面起着至关重要的作用。管理存储固件和配置数据的 SPI 闪存芯片。一些反作弊程序可能会使用 SPI 转储您的 BIOS 映像等

image-20240826100013578

同样,UEFI固件还有一些组件,运行于上述架构中

  • UEFI SHELL,顾名思义,uefi可以理解为一个小os,他固件内置了uefishell,是固件的UI系统
  • Option ROM,

Build TianoCore EDK2 Env

UEFI开发很泛泛,有很多方面,比如

  1. UEFI应用程序开发
  2. UEFI驱动程序开发
  3. UEFI引导加载程序开发
  4. UEFI固件开发和定制
  5. UEFI协议实现
  6. UEFI工具和实用程序开发

对于我们这些搞逆向的来说,一般是UEFI驱动、应用程序、引导程序的开发。

注意,这里的驱动和应用程序和我们windows开发的驱动和应用程序完全不一样!不过倒是都遵循PE32+/COFF格式。

UEFI开发一般需要用到EDK,即Efi Developement Kits,而Tiano Core是托管的EDK2的开源项目,一般都是在这个框架下开发。

Not Use EDK2 To Build An UEFI Img

其实UEFI开发完全可以不用到EDK2,我们只需要按照UEFI的协议规定,使用专门的链接器链接成PE32+格式,驱动和应用程序按照规定有专门的入门和exit即可。

其实可以发现,UEFI非常像一个操作系统,他有API,可以执行应用程序和驱动,甚至有文件系统。

只不过UEFI是一个专门负责初始化硬件的操作系统。

参考UEFI白皮书4.1,对于任何UEFI镜像,都有一个入口点。镜像被UEFI加载的时候,第一个执行的就是这个入口点,同时两个参数被传递。

image-20240825085615390

同时,如果对于UEFI定义的数据结构,可以去2.3.1去查询

EFI_HADNLE与windows的HANDLE类似,

EFI_SYSTEM_TABLE的接口如下

typedef struct {
  ///
  /// The table header for the EFI System Table.
  ///
  EFI_TABLE_HEADER                  Hdr;
  ///
  /// A pointer to a null terminated string that identifies the vendor
  /// that produces the system firmware for the platform.
  ///
  CHAR16                            *FirmwareVendor;
  ///
  /// A firmware vendor specific value that identifies the revision
  /// of the system firmware for the platform.
  ///
  UINT32                            FirmwareRevision;
  ///
  /// The handle for the active console input device. This handle must support
  /// EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.
  ///
  EFI_HANDLE                        ConsoleInHandle;
  ///
  /// A pointer to the EFI_SIMPLE_TEXT_INPUT_PROTOCOL interface that is
  /// associated with ConsoleInHandle.
  ///
  EFI_SIMPLE_TEXT_INPUT_PROTOCOL    *ConIn;
  ///
  /// The handle for the active console output device.
  ///
  EFI_HANDLE                        ConsoleOutHandle;
  ///
  /// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface
  /// that is associated with ConsoleOutHandle.
  ///
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL   *ConOut;
  ///
  /// The handle for the active standard error console device.
  /// This handle must support the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.
  ///
  EFI_HANDLE                        StandardErrorHandle;
  ///
  /// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface
  /// that is associated with StandardErrorHandle.
  ///
  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL   *StdErr;
  ///
  /// A pointer to the EFI Runtime Services Table.
  ///
  EFI_RUNTIME_SERVICES              *RuntimeServices;
  ///
  /// A pointer to the EFI Boot Services Table.
  ///
  EFI_BOOT_SERVICES                 *BootServices;
  ///
  /// The number of system configuration tables in the buffer ConfigurationTable.
  ///
  UINTN                             NumberOfTableEntries;
  ///
  /// A pointer to the system configuration tables.
  /// The number of entries in the table is NumberOfTableEntries.
  ///
  EFI_CONFIGURATION_TABLE           *ConfigurationTable;
} EFI_SYSTEM_TABLE;

在UEFI中,功能被分成了一个一个Protocol协议,定义成以结尾的_PROTOCOL结构,比如上面可以找到一个

EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr;这个协议,上面写着这是一个接口的指针。UEFI手册有这个接口的详细信息

image-20240825090424092

可以看到,结构体里面的OutputString是一个函数指针,它可以用作向屏幕展示字符串。

image-20240825090544857

This就是PROTOCOL的指针了,String就是要展示的字符串了。当然,他这个里面还要别的函数指针,比如什么cleanScreen,用来清除屏幕…

比如我们就可以这样写一个hello uefi

struct EFI_SYSTEM_TABLE {
    char _buf[60];
    struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {
        unsigned long long _buf;
        unsigned long long (*OutputString)(
            struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This,
            unsigned short *String);
        unsigned long long _buf2[4];
        unsigned long long (*ClearScreen)(
             struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This);
    } *ConOut;
};

void efi_main(void *ImageHandle __attribute__ ((unused)),
    struct EFI_SYSTEM_TABLE *SystemTable)
{
    SystemTable->ConOut->ClearScreen(SystemTable->ConOut);
    SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello UEFI!n");
    while (1);
}

一般而言,还需要用到交叉编译的技术,因为一般UEFI开发都是linux,linux的gcc会把他们编译成EFL格式,所以可以用到gcc-mingw-w64-x86-64,这里就不赘述演示了。编译完成后,需要将其放在UEFI固件可以找到的地方。

UEFI可以识别FAT文件系统,因此我们需要找个U盘,然后格式化成FAT32,然后将其放在EFI/Boot/目录下,

$ sudo mount /dev/sdb1 /mnt
$ sudo mkdir -p /mnt/EFI/BOOT
$ sudo cp main.efi /mnt/EFI/BOOT/BOOTX64.EFI
$ sudo umount /mnt

接着,从U盘启动,发现屏幕会打印Hello UEFI

image-20240825092148101

这便是纯粹通过UEFI规范不借助任何开发包来完成一个UEFI应用程序的实现。

EDK2 Env Build

EDK2前面以及介绍过了,其实就是类似WDK那样的开发包。他里面定义了大量的UEFI实例、库、结构、协议的实现。

也就是说假如你编译了他的MdePck,那么你就可以在UEFI中使用C的lib了!

具体环境搭建可以参考这篇文章,总之windows下坑太多,环境变量啥的弄不好,所以还是在linux下搭建环境吧:

https://martins3.github.io/uefi/uefi-linux.html

下载好EDK2,并搭建好环境后,可以发现,它可以编译的库非常多

oxygen@oxygen-virtual-machine:~/Desktop/edk2/edk2$ ls -l
total 228
drwxrwxr-x  7 oxygen oxygen  4096  8月 24 16:56 ArmPkg
drwxrwxr-x 12 oxygen oxygen  4096  8月 24 16:56 ArmPlatformPkg
drwxrwxr-x 14 oxygen oxygen  4096  8月 24 16:56 ArmVirtPkg
drwxrwxr-x 11 oxygen oxygen  4096  8月 24 16:56 BaseTools
drwxrwxr-x  4 oxygen oxygen  4096  8月 24 17:10 Build
drwxrwxr-x  3 oxygen oxygen  4096  8月 24 17:06 Conf
-rw-rw-r--  1 oxygen oxygen   161  8月 24 16:56 CONTRIBUTING.md
drwxrwxr-x  7 oxygen oxygen  4096  8月 24 16:56 CryptoPkg
drwxrwxr-x  5 oxygen oxygen  4096  8月 24 16:56 DynamicTablesPkg
-rwxrwxr-x  1 oxygen oxygen  4955  8月 24 16:56 edksetup.bat
-rwxrwxr-x  1 oxygen oxygen  3487  8月 24 16:56 edksetup.sh
drwxrwxr-x 14 oxygen oxygen  4096  8月 24 16:56 EmbeddedPkg
drwxrwxr-x 26 oxygen oxygen  4096  8月 24 16:56 EmulatorPkg
drwxrwxr-x  4 oxygen oxygen  4096  8月 24 16:56 FatPkg
drwxrwxr-x  8 oxygen oxygen  4096  8月 24 16:56 FmpDevicePkg
drwxrwxr-x  7 oxygen oxygen  4096  8月 24 16:56 IntelFsp2Pkg
drwxrwxr-x  7 oxygen oxygen  4096  8月 24 16:56 IntelFsp2WrapperPkg
-rw-rw-r--  1 oxygen oxygen 28674  8月 24 16:56 License-History.txt
-rw-rw-r--  1 oxygen oxygen  2732  8月 24 16:56 License.txt
-rw-rw-r--  1 oxygen oxygen 25524  8月 24 16:56 Maintainers.txt
drwxrwxr-x 10 oxygen oxygen  4096  8月 24 16:56 MdeModulePkg
drwxrwxr-x  5 oxygen oxygen  4096  8月 24 16:56 MdePkg
drwxrwxr-x 29 oxygen oxygen  4096  8月 24 16:56 NetworkPkg
drwxrwxr-x 61 oxygen oxygen  4096  8月 24 16:56 OvmfPkg
drwxrwxr-x  7 oxygen oxygen  4096  8月 24 16:56 PcAtChipsetPkg
-rw-rw-r--  1 oxygen oxygen   625  8月 24 16:56 pip-requirements.txt
drwxrwxr-x 10 oxygen oxygen  4096  8月 24 16:56 PrmPkg
-rw-rw-r--  1 oxygen oxygen 27215  8月 24 16:56 ReadMe.rst
drwxrwxr-x 15 oxygen oxygen  4096  8月 24 16:56 RedfishPkg
drwxrwxr-x 14 oxygen oxygen  4096  8月 24 16:56 SecurityPkg
drwxrwxr-x  6 oxygen oxygen  4096  8月 24 16:56 ShellPkg
drwxrwxr-x  5 oxygen oxygen  4096  8月 24 16:56 SignedCapsulePkg
drwxrwxr-x  6 oxygen oxygen  4096  8月 24 16:56 SourceLevelDebugPkg
drwxrwxr-x  6 oxygen oxygen  4096  8月 24 16:56 StandaloneMmPkg
drwxrwxr-x 23 oxygen oxygen  4096  8月 24 16:56 UefiCpuPkg
drwxrwxr-x 14 oxygen oxygen  4096  8月 24 16:56 UefiPayloadPkg
drwxrwxr-x  6 oxygen oxygen  4096  8月 24 16:56 UnitTestFrameworkPkg

比如EmulatorPkg就是模拟的,MdeModulePkg就是最基础的库,只需要改一下配置文件,就可以编译不同的库了。

1. MdePkg: 核心UEFI和PI接口定义
2. MdeModulePkg: UEFI和PI实现模块
3. UefiCpuPkg: CPU相关协议和库
4. OvmfPkg: 用于虚拟机的UEFI固件
5. SecurityPkg: 安全相关功能
6. NetworkPkg: 网络协议栈
7. ShellPkg: UEFI Shell实现
8. BaseTools: 构建工具和脚本
9. EmulatorPkg: UEFI模拟环境
10. CryptoPkg: 加密库

VisualUefi

这是一个可以无缝在Windows上使用EDK2的项目,项目链接

https://github.com/ionescu007/VisualUefi

它可以编译EDK2的各个库,同时实现了几个samples,

image-20240825094204707

image-20240825094215500

这个库clone下来后,编译所有的库,然后设置samples(或者按照samples格式),然后编译即可。

image-20240825094746911

ACPI

uefi和acpi联系紧密,一般在引导的时候也需要读解析、读取ACPI。当然,os拉起来之后也可以通过ACPI的Signature来进行查找到。

这里先简单快速的了解下ACPI的概念, ACPI (Advanced Configuration and Power Interface),他就是一个开放的标准,其实和uefi是一样的。主要是描述os和硬件之间的接口。他是很多表(比如FADT DSDT RSDT XSDT)和结构组成的。

RSDT(Root System Description Table)和XSDT(Extented System Description Table)是ACPI规范中定义的两个具体的表,他们分别是在ACPI和ACPI2.0中引入的。

都是一个根目录表的作用,用于指向其他表,其中XSDT在现代64位固件体系中用的更多,且XSDT要优先RSDT。

ACPI这么重要,所以uefi运行时服务提供了给定guid去获取XSDT、RSDT的接口:

image-20241109144444218

gst就不必多说了,这就是uefi程序入口固件给uefi传递的第二个参数。这个里面的ConfigurationTable我们可以看EFI_SYSTEM_TABLE的结构

image-20241109144848555

这个里面是有一堆配置表的,每个配置表结构有相关指针+Guid。

image-20241109144906065

因此要找到acpi的RSDP(Root System Description Pointer)就循环,然后compare guid即可。

这里要在提一嘴APIC这些表的组成结构,

  • RSDP → RSDT/XSDT → 其他 ACPI 表(如 FADT、DSDT 等)

可以看到,这个代码

image-20241109150015790

再找到Rsdp之后,判断是1.0还是2.0,然后根据1.0还是2.0来进行返回到底是Xsdt还是Rsdt。

然后就可以找到其他所有的Sdt表了。

当然,哪怕是os起来了,也是可以找到XSDT的,这些代码可以参考一个开源库

SKlib查看EfiLocateNextAcpiTable函数。

image-20241109151826813

发现可以直接调用Hal的函数就可以了,windows帮你映射好了。

而至于找什么表,比如dmar、各种表,我们只需要知道他的Signature即可在XSdt或者Rsdt找到了:

还是去看开源项目SKlibScanTableInSDT函数。其实也很简单,先获取Xsdt所有表的数量

EntryCount = (Sdt->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) / TablePointerSize;

然后直接根据这个数量进行遍历

如果是Rsdt,指针是32位,否则是64位的。注意,这些都是物理地址!

image-20241109152517927

而BasePtr就是Sdt后面紧跟的。这样就可以得到一个完整的ACPI的表了,每个表结构开头都是EFI_ACPI_COMMON_HEADER,通过对比Header的Signature,即可判断出来是不是要找到表的指针。

chap02 Voyager

chap03 Uefi debug

Qemu+GDB to debug uefi

IDA有内置的GDB调试器,从而可以远程调试Qemu。

image-20240918164816991

一般这个会被用作远程调试qemu,因为qemu的特殊性,甚至还可以用于调试uefi程序。下面展示如何调试windows的bootmgr.efi。

如果想让qemu运行efi,首先需要编译EDK2中的OVMF组件,这个可以理解为是烧在主板uefi固件里面的程序。

qemu按照、编译OVMF的流程不赘述了,网上比较多,编译完之后会得到一个OVMF.fd的文件,这是一个完整的固件文件。

接着使用如下命令行,来让qemu加载这个uefi固件:

 qemu-system-x86_64.exe -s -drive if=pflash,format=raw,file=OVMF.fd -drive file=fat:rw:C:UsersAdministratorDesktopuefi_cheater,format=raw -net none -debugcon file:debug.log -global isa-debugcon.iobase=0x402 -vga std

简单解释下上述命令行,首先qemu-system-x86_64.exe表面了硬件平台是x86-x64.

-s则是开启了远程调试,qemu的默认调试端口开的是1234,可以通过这个来进行和GDB远程链接调试。

-drive if=pflash,format=raw,file=OVMF.fd,即添加一个驱动器,这个类型是pflash,这里不太清楚为什么是pflash,总之这样就会让qemu使用OVMF.fd作为硬件uefi固件。

-drive file=fat:rw:C:UsersAdministratorDesktopuefi_cheater,format=raw uefi只识别fat32的文件系统,上述就是把该文件夹内的文件以fat32呈现给qemu。

运行该命令,出现如下即代表成功

image-20240918171330606

这便是进入了uefi shell,主要是因为

image-20240918171911072

uefi的启动阶段,在BDS阶段,如果OVFD没找到合适的,那么就会进入shell,让用户手动选择。因为前面我们指定了一个驱动器(即cheat_uefi文件夹),那么切换到fs0:发现可以正常解析文件夹的内容,这个时候我们就可以手动去执行uefi了

image-20240918172308951

比如,我们可以尝试去执行一下windows原生的efi,即bootx64.efi,记住,一定要目录结构一致。

启动后发现蓝屏

image-20240918174900869

查看debug文件

image-20240918174724492

发现启动项目符合预期(loader.efi->bootmgfw.efi)

image-20240918175022472

只不过毕竟我们是没有真正的windows的,所以到bootmgfw.efi的时候,自然就失败了。但是这样可以简单地调试uefi,至少到bootmgfw.efi这里是没问题的。

下面将用个比较傻的方法进行调试,因为我们是没有源码的,所以下断点啥的都是失败的,那怎么办呢?

image-20240918181635468

因为uefi加载的这个地址是不变的,因此我们可以提前下一个硬件断点。

具体流程如下:

  • 首先确认要调试的efi程序的EntryPoint

image-20240918181828904

  • 重新启动qemu(这个地址是不会变化的),连接gdb

image-20240918182042436

这个很简单,调试器选择Remote GDB,端口一定要填写1234.

  • 然后再次点击debugger这个选项,里面有Attach process,选择之后即可链接成功。

image-20240918182149158

可以发现qemu已经暂停下来了

  • 对刚才得到的地址进行下断

image-20240918182302019

这里,因为ida里面的base和实际上加载的base大概率是不一样的,所以需要在

edit->segemnts->rebase program里面来重定位下

image-20240918182347130

  • 运行,中断成功,可以进行调试。

image-20240918182449073

这便是一个比较傻瓜的方法来进行调试了。实际上可能会有更好的方法,比如可以对uefi的运行时服务加载镜像下断,这样就可以拦截每一个加载的efi镜像了。

chap04 windows uefi

windows启动之后,uefi的绝大部分组件基本上都会被释放。但是还是有一些内存区域会驻留内存中,比如UEFI Runtime service和一些保留的内存区域,比如ACPI表,SMBIOS表。

比如下面,左侧是windows的hal.dll调用uefi runtime service,而右侧是我自己编译的OVMF.fd固件,可以发现不能说一模一样,只能说大差不差,也就是hal是知道uefi runtime service并保有这张表的。

只不过我对比了下,和原始runtime service还是有差别的,但是基本一模一样。

image-20240919092259910

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇