VariableTool

VariableTool

        这是一个基于pycharm开发的变量编辑工具,旨在对指定的fd文件中的variable值进行修改。

导论

Variable的存储结构        

        下图为Variable在fdf文件中的描述,可以看出在fdf文件中Variable的存储结构层次为EFI_FIRMWARE_VOLUME_HEADER --> VARIABLE_STORE_HEADER -->

        其中EFI_FIRMWARE_VOLUME_HEADER为存储Variable的固件卷头,存储着固件卷的各种属性,VARIABLE_STORE_HEADER声明了Variable的存储区域属性。

        上述两部分基本为固定值,在VARIABLE_STORE_HEADER之后,便是存储变量的存储区了,存储区中的某一个Variable的存储结构为:

VARIABLE_HEADER (GUID在里面)--> VARIABLE_NAME --> VALUE

        其中VARIABLE_HEADER中包含了VARIABLE的各种属性,如State、Attributes、Datasize等。而多个变量的存储结构为:

VARIABLE_HEADER --> VARIABLE_NAME --> VALUE

--> VARIABLE_HEADER --> VARIABLE_NAME --> VALUE

--> VARIABLE_HEADER --> VARIABLE_NAME --> VALUE

.........

        基于此,想要修改fd文件中的Variable值,那么首先需要找到这块VARIABLE_STORE_HEADER,而GUID与Vairable_Name唯一标识了一个Variable,因此,从头部向后检索GUID与Vairable_Name便可以找到需要修改的Vairable。

python中的二进制文本格式

        fd文件是一个二进制文件,这个二进制文件内容如下所示,可以看到都是以1个字节为单位显示,但是,在python中,利用python的open函数,以rb+形式打开一个fd文件,最终读到的fd文件的内容格式为b'\x**\x**\x**\x**\x**\x**........',即下图第二行的78E58C8C,在python中是b'\x78\xE5x8C\x8C'。

        基于此,想要寻找fd文件中的指定内容,需要先将输入内容转换为固定的b'\x**\x**\x**\x**\x**\x**........'格式,然后再到fd文件中遍历。

对齐问题

        在Variable的存储中会涉及到对齐问题,关于Variable的对齐问题,一样通过研究源码来看,可以看到关于对齐主要有两个宏定义,一个是ALIGNMENT、另一个是HEADER_ALIGNMENT。

        两个宏的主要使用集中在下图,通过注释可以看到该部分主要操作是计算Variable的总大小,将代码倒着看,从TotalNeededSize开始看,可以看到Variable的总大小为VariableSize的值,而VariableSize的值调用了HEADER_ALIGN宏,该宏的作用是将输入参数对齐到HEADER_ALIGNMENT的倍数,此处HEADER_ALIGNMENT为4,因此就是将输入参数对齐到4的倍数,输入参数有三个Variable_Header_Size,VarNameSize,VarDataSize,根据前文导论,可以看出,是相呼应的,表明了Variable的存储结构。

        再往上看VarNameSize与VarDataSize都经过了一个GET_PAD_SIZE处理,此处看到上图由于ALIGNMENT = 1,因此GET_PAD_SIZE为0,可以推测如果ALIGNMENT为其他值,此处其实也是对Name与Data进行对齐处理,对齐到ALIGNMENT的倍数。

程序流程图

函数介绍

        整个工具的函数由三部分组成,test(),custom_string_rearrange(),string_to_hex()

custom_string_rearrange

        custom_string_rearrange主要用于GUID转换顺序,GUID由8-4-4-16格式组成,但在EmulatorPkg.fd文件中的gEfiVariableGuid排布形式可以看出格式中的8是以每两位倒序排布的,接着是第一个4,同样是以每两位倒序排布,第二个4同理,而最后的16却是以顺序排布。

        但是从用户端读取GUID的习惯自然是全顺序输入,例如ABCDEFGH,但程序却只能处理GHEFCDAB,因此custom_string_rearrange便是做了这件事,通过切片操作,将GUID分段处理,最终得到程序能够读取的GUID顺序。

string_to_hex

        string_to_hex用于将变量名进行硬编码,将输入的变量名转换为\x**类型,根据导论提到的python中二进制文本格式,输入的字符串,必须最后变成b'\x**\x**\x**\x**\x**\x**........'格式,才能被识别出,而string_to_hex正是将变量名进行硬编码,先转换为\x**\x**\x**\x**\x**\x**........,随后利用ast模块中的literal_eval将\x**\x**\x**\x**\x**\x**........转化为b'\x**\x**\x**\x**\x**\x**........',如此,变量名便可被识别。

        在程序实现中,还涉及到一个问题,即在fd文件中,变量名以2个字节表示1个字符,且结束符也算在变量名中,例如setup,16进制为7365747570a,存储形式为0x73,0x00,0x65,0x00,0x74,0x00,0x75,0x00,0x0a,0x00,0x00,0x00,因此该函数还需要在每个字符后添加\x00,以确保2个字节表示一个字符,而在结尾加上2个\x00确保结束符也被转化。

        此处提一嘴输入的GUID、变量值同样需要转化为b'\x**\x**\x**\x**\x**\x**........'格式,但是由于其在fd中存储并非以2个字节为1单位,因此无需如此繁琐,只需要先硬编码,在ast即可。

test

        test函数为整个工具的主题部分,所做的主要工作有读取文件、读取GUID、判断GUID是否存在、读取变量名、读取变量值,修改变量值。

读取文件:

        此处以rb+的形式打开fd_file_name,即以二进制读写的形式打开fd文件,将文件的数据读到binary_data。

读取GUID:

        此处调用了custom_string_rearrange函数,该函数的主要作用是将用户输入的GUID值转换为固定格式。前文已经提过,随后便是硬编码,ast,最终转为b'\x**\x**\x**\x**\x**\x**........'格式,这种数据就是binary_data中的数据存放格式,接下来,便可在binary_data进行检索GUID。

判断GUID是否存在:

GUID存在:

        判断变量名是否存在,此处调用string_to_hex,将输入的变量转换为16进制格式。即输入Phytium,得到b'\x50\x00\x68\x00\x79\x00\x74\x00\x69\x00\x75\x00\x6d\x00\x00\x00',此处的string_to_hex主要做了两个工作,首先在每个字节后插入\x00,其次在末尾加入结束符, 随后利用ast.literal_eval将其转换为b'\x******'格式的数据。将上述转换后的变量名输入binary_data检索。

        若变量名存在:将输入的变量值进行同样的操作,转换为16进制形式,此处补\X00是由用户输入的变量值的大小与原变量值的存储大小决定的,原变量值的存储大小,即DataSize,在Variable_Header中已经声明,因此,利用DataSize - 输入的变量值长度,即可得到需要补充的\x00的数量,即假设原始DataSize为6,用户输入变量值1234,转16进制并补\X00后为b'\x31\x32\x33\x34\x00\x00‘,随后将整体直接覆写之前的空间。

GUID不存在:

        执行创建新变量操作,将新输入的GUID、变量名、变量值都转换为16进制格式。首先检索到Variable_Store_Header的最后一段\x5A\xFE\x00\x00\x00\x00\x00\x00,接下来向后检索,当检索到连续4个FF,意味着此处为存储空闲区,因此在此处写入Variable_Header (含GUID)+ Variable_name + Value。

运行

修改已有变量

        文件为FV_RECOVERY_Test.fd,提前向变量存储区添加变量Phytium,值 “3D

        变量Phytium值已修改为Test123 

添加变量

        同样向FV_RECOVERY_Test.fd中添加变量AddVar,值为Add123

添加功能

        添加读取有效变量个数功能

思路

        先查找到Variable_Store_Header的末尾,在这之后存储的便是各个变量,根据Variable_Header中的startId是固定的0xAA、0x55,以及state属性为VarADDED,即0x3F位有效变量,那么只要搜寻存储区头部后有多少个\xAA\x55\x3F\x00即可找到有效的变量个数,直到找到连续4个FF,意味着已到存储区末,退出查找。

程序流程图

运行

        三个有效变量Phytium、AddVar、Test2

    1个有效变量Phytium

改进

        之前的运行其实并没有考虑对齐问题,而在导论中提到的对齐问题已经诠释了edk2源码中是如何解决Variable的存储对齐问题,而在利用UefiBiosEditor读取fd文件后,最终得到了Variable的真实对齐方式。

        下图为一个名为a,而值为12的变量,可以观察到

        00000000地址存储的是a的Variable_Header,其中AA55、3F分别Header中的startId和state,07000000为a的属性,即NV、RT、BS,04000000为a的NameSize,此处为4的原因是a为char16类型,因此转化为16进制为6100,而结束符也需要计算在内,即0000,因此a的NameSize为4字节,而第二个04000000为a的DataSize,即a含有一个4字节大小的值。

        00000010存储的是a的GUID。

        00000020存储的是a的Variable_name与Variable_name,其中61000000为Variable_name,即a,而31320000为Variable_value,即12。

        从这可以得出Vairable的对齐只需要判断Variable_name+Variable_value为4字节的最小公倍数即可,此处为8字节,其中Variable_name4字节,Variable_value4字节,抛开对齐规则,其实Variable_value只有2字节,但是为了对齐,补上了2字节的00。

思路

代码实现

        首先依旧是输入新的GUID,并调序,转换16进制形式

         输入变量名,并转换为16进制形式

         输入变量属性,转换为16进制形式

         输入变量值,并进行对齐操作

        1、计算Vairable_name的长度,Variable_value的长度

        2、得到两者之和除4的余数

        3、利用4减余数即可得到需要补0的个数

         获取输入的Vairable_name的大小与Variable_value的大小,填充到Variable_Header中

最终运行

添加新变量         

        向1.fd文件中添加多个变量

        利用UefiBiosEditor读取1.fd,可以查找到所有变量,在未加入对齐之前,是无法检测到多个变量的,而加入对其之后,已可以检测出全部变量。

         而在模拟器中也是可以找到这些变量的,此处有些变量显示不全,为模拟器的bug问题

修改已有变量        

        ab变量的初始值为123abc

         修改ab的值为test

         修改成功

总结

        此VariableTool并非真正完美实现了对Variable的编辑,仅作学习Variable使用,在针对已有变量的修改功能中,从edk2源码中的SetVariable函数可以看到,实际上并非直接修改已有变量的值,而是先在空闲区重新建立一个同名变量,将修改值放在该同名变量下,并将原来的变量的头部中的state置为delete,同名变量的头部的state置为VAR_ADDED,而在创建新变量时,也需要考虑创建的变量为Non-Volatile类型还是Volatile类型以及是否调用Reclaim操作等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值