前面介绍了静态条件下的分析,下面研究一下EFI 文件加载在内存中的情况。
用一个图来说明情况:
左侧是文件,右侧是加载到内存后的情况,可以看到对于头是照搬到内存中。对于Section 的话,看起来就比较麻烦,一般情况下内存的对齐要求会比PE文件要求的大。比如:PE 中按照 16Bytes对齐,在内存中可能要求按照 64Bytes对齐,相比是因为 PE文件希望紧凑一些,内存的数据希望读取更快所以要做成这样的。
用一个GenCRC32.exe为例:
加载到内存中的是按照 0x1000对齐,数据在文件中存放是按照 0x200对齐。
继续查看 .text Section 可以看到 RVA=0x1000,意思是:当这个PE被加载到内存后,会放在 BaseAddress+0x1000的内存地址;Pointer to Raw Data 给出 0x400意思是这个段在文件中的位置是从 0x400开始的。
下面我们查看之前的 es.efi ,可以看到文件对齐和内存对齐是相同的。
再查看 Section,可以看到 RVA 和 Pointer to Raw Data 是相同的:
有兴趣的可以多查看几个EFI和 Section ,和上面是相同的。为此,我们再做一个实验,在\MdePkg\Library\UefiApplicationEntryPoint\ApplicationEntryPoint.c 加入一个中断:
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
if (_gUefiDriverRevision != 0) {
//
// Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the application.
//
if (SystemTable->Hdr.Revision < _gUefiDriverRevision) {
return EFI_INCOMPATIBLE_VERSION;
}
}
CpuBreakpoint(); //LABZ_Debug
//
// Call constructor for all libraries.
//
ProcessLibraryConstructorList (ImageHandle, SystemTable);
重新编译一次es.efi(注意不要重新编译 NT32Pkg)。执行之后报错可以用 VS2015断下来:
跳出int中断就来到我们EFI 代码的领空:
对照ApplicationEntryPoint.cod 查看停在CpuBreakpoint() 之后的语句了:
_ModuleEntryPoint PROC ; COMDAT
; 45 : {
$LN25:
00000 48 89 5c 24 08 mov QWORD PTR [rsp+8], rbx
00005 48 89 74 24 10 mov QWORD PTR [rsp+16], rsi
0000a 57 push rdi
0000b 48 83 ec 20 sub rsp, 32 ; 00000020H
0000f 48 8b fa mov rdi, rdx
00012 48 8b f1 mov rsi, rcx
; 46 : EFI_STATUS Status;
; 47 :
; 48 : if (_gUefiDriverRevision != 0) {
; 49 : //
; 50 : // Make sure that the EFI/UEFI spec revision of the platform is >= EFI/UEFI spec revision of the application.
; 51 : //
; 52 : if (SystemTable->Hdr.Revision < _gUefiDriverRevision) {
; 53 : return EFI_INCOMPATIBLE_VERSION;
; 54 : }
; 55 : }
; 56 : CpuBreakpoint(); //LABZ_Debug
00015 e8 00 00 00 00 call CpuBreakpoint
; 57 : //
; 58 : // Call constructor for all libraries.
; 59 : //
; 60 : ProcessLibraryConstructorList (ImageHandle, SystemTable);
0001a 48 8b d7 mov rdx, rdi
0001d 48 8b ce mov rcx, rsi
00020 e8 00 00 00 00 call ProcessLibraryConstructorList
当前的位置是 0x 26BABD252DA,那么这个 .text 段是在26BABD252C0 开始的,我们还可以推理出Image 加载位置(BaseAddress)0x26BABD252C0 - 0x2c0=0x26BABD25000:
.text 段在 0x26BABD25000 + 0x2C0
.rdata段在0x26BABD25000 + 0x1700
.data段在0x26BABD25000 + 0x1F20
无名段在0x26BABD25000 + 0x1F60
.xdata段在0x26BABD25000 + 0x2020
.reloc段在0x26BABD25000 + 0x20C0
我们只验证一下 .reloc 段:
可见他们是完全相同的,就是说 EFI 文件加载到内存之后依然是相同的对齐。从设计上说,这样可以使得文件更加紧凑,能够化简加载动作,同时方便调试。
参考: