UEFI 文件类型 .efi (二)
上一篇博客講了一個例子關于在一個C 程序里如何動態去load 一個可執行文件,并且讓這個文件能夠跑起來,代碼里有幾處是hard code 寫的,沒有去根據讀取的可執行文件類型去分析它。上篇也說了,如果那個例子理解透了,那么對于efi 文件加載和執行理解起來就不成問題了。
這篇博客介紹一下,efi 文件內容。其實不用去看代碼,也不用怎么翻spec, 看看下面的幾個圖片,應該就會很清晰,efi 文件的重定位,裝載,執行了。
舉個具體例子看一下吧。我這邊有一個build 好的efi driver, 文件格式呢是pe32。
先來看看這個driver 是怎么個結構,我們可以用Windows 下的命令dumpbin /ALL xxx.efi > xx.txt
FILE HEADER VALUES 
 14C machine (x86) 
 3 number of sections 
 0 time date stamp 
 0 file pointer to symbol table 
 0 number of symbols 
 E0 size of optional header 
 2102 characteristics 
 Executable 
 32 bit word machine 
 DLL
OPTIONAL HEADER VALUES 
 10B magic # (PE32) 
 10.00 linker version 
 4DA0 size of code 
 4400 size of initialized data 
 0 size of uninitialized data 
 240 entry point (00000240) 
 240 base of code 
 4FE0 base of data 
 0 image base (00000000 to 000093DF) 
 20 section alignment 
 20 file alignment 
 0.00 operating system version 
 0.00 image version 
 0.00 subsystem version 
 0 Win32 version 
 93E0 size of image 
 240 size of headers 
 0 checksum 
 B subsystem (EFI Boot Service Driver) 
 0 DLL characteristics 
 0 size of stack reserve 
 0 size of stack commit 
 0 size of heap reserve 
 0 size of heap commit 
 0 loader flags 
 10 number of directories 
 0 [ 0] RVA [size] of Export Directory 
 0 [ 0] RVA [size] of Import Directory 
 0 [ 0] RVA [size] of Resource Directory 
 0 [ 0] RVA [size] of Exception Directory 
 0 [ 0] RVA [size] of Certificates Directory 
 8FA0 [ 428] RVA [size] of Base Relocation Directory 
 0 [ 0] RVA [size] of Debug Directory 
 0 [ 0] RVA [size] of Architecture Directory 
 0 [ 0] RVA [size] of Global Pointer Directory 
 0 [ 0] RVA [size] of Thread Storage Directory 
 0 [ 0] RVA [size] of Load Configuration Directory 
 0 [ 0] RVA [size] of Bound Import Directory 
 0 [ 0] RVA [size] of Import Address Table Directory 
 0 [ 0] RVA [size] of Delay Import Directory 
 0 [ 0] RVA [size] of COM Descriptor Directory 
 0 [ 0] RVA [size] of Reserved Directory
只是截取一部分,大概就這個樣子,我們只關心加載和執行,所以其中
都在option header 里。所以無論我們的build tool 還是core 去分析這個driver 找到option header 就可以把需要的信息提取出來。提取出來,我們image 需要重定向,關于重定向之前也講過,需要分析重定向表,好吧,看看重定向表的信息。
SECTION HEADER #3.reloc name428 virtual size8FA0 virtual address (00008FA0 to 000093C7)440 size of raw data8FA0 file pointer to raw data (00008FA0 to 000093DF)0 file pointer to relocation table0 file pointer to line numbers0 number of relocations0 number of line numbers 42000040 flagsInitialized DataDiscardableRead OnlyRAW DATA #300008FA0: 00 00 00 00 4C 00 00 00 54 32 6F 32 80 32 85 32 00008FB0: A1 32 B6 33 CC 33 E2 33 F8 33 04 36 2C 36 36 38 00008FC0: 47 38 4D 38 59 38 9C 38 97 39 10 3A 37 3A 52 3A 00008FD0: 57 3A 09 3B 35 3B 95 3B 2D 3C 4C 3C 6B 3C 8A 3C 00008FE0: A9 3C 2B 3E 32 3E E6 3E 4B 3F 5D 3F 00 10 00 00 00008FF0: 68 00 00 00 87 33 D2 33 95 35 9B 35 DE 35 E4 35 00009000: 3A 36 40 36 BA 36 BF 36 12 37 17 37 63 37 69 37 00009010: 8D 37 92 37 AB 37 B1 37 C7 37 CD 37 EA 37 F0 37 00009020: 06 38 0C 38 22 38 28 38 36 38 3B 38 5B 38 61 38 00009030: 77 38 7D 38 91 38 97 38 AB 38 B1 38 CD 38 D5 38 00009040: 33 39 3A 39 BD 39 C3 39 82 3B 4B 3D 25 3F 44 3F // 重定向表的解析BASE RELOCATIONS #30 RVA, 4C SizeOfBlock254 HIGHLOW 000051B026F HIGHLOW 000070B8280 HIGHLOW 00005020285 HIGHLOW 000070BC2A1 HIGHLOW 000070A03B6 HIGHLOW 000052A03CC HIGHLOW 000052B03E2 HIGHLOW 000052C03F8 HIGHLOW 000052D0604 HIGHLOW 00005010我們也稍微簡單分析一下這個可重定向表,我們可以從表頭可以看到這個表載入內存大小是0x428, 并且virtual address 0x8FA0(Image base 0x0000),所以0x8FA0 也可以看成載入內存時候,可重定向表距離image base 偏移是0x8FA0。 OK 找到表了,接下來就是對表的分析了
00008FA0: 00 00 00 00 4C 00 00 00 54 32 6F 32 80 32 85 32
重定向表還是按照block 去劃分,每個block 會有一個header ,里面有兩個內容 1 UINT32 RVP, 2 UINT32 blocksize。 從上面的數據,我們可以得出 RVP 是 0x00000000, blocksize =0x4c ,然后每個可重定位項是16bits 高4位是類型,低12bit 是偏移。我們可以看到上面的 54 32 就會解析成,偏移是0x254, 類型是0x03(HIGHLOW)。這個0x254 需要加上所在Block 的 RVP, 意思就是0x254 + 0x000000 = 0x254,這個偏移地方需要修改,修改多大,我們的類型是0x03,就是32bit。一起看個圖片就知道了。兩張圖一個是重定向后的(左),一個沒有重定向(右)。 
 
可以看到偏移0xF4 的位置被修改了,那是Image Base ,被修改成實際image 載入內存的地址。剩下的就是需要重定向位置,我們用可以看到上面需要重定向的位置,有0x254, 0x26F, 等等,我們對著圖片看看,不一樣的地方正好和這些重定向偏移的地址吻合。好了,重定向之后,我們就可以根據表頭的Entry point 偏移找到入口地址,然后跳入這個image 里。
關于PE 結構,它有它方便的一面,也有復雜的一面,對于學習uefi 的同學,PE 結構,我們了解這么多,也可以了。其他方面關于導入表,導出表,動態延時綁定等等,有興趣自己去找另外的資料研究學習。
看著簡單吧,其實自己寫還不一定能寫好,因為也有很多細節問題,比如內存對齊等等,細節的地方只能自己去摸索了,別人講太多都是記不住,程序員還是應該務實一點,寫幾段代碼調試幾遍。比只看不寫強太多。
總結
以上是生活随笔為你收集整理的UEFI 文件类型 .efi (二)的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: JavaScript——执行环境、变量对
 - 下一篇: 对X264/FFMPEG架构探讨---感