Step to UEFI (227)VS2019 EDK202008 下的 Libc 编译

LibC 是 EDK2 的一个库,提供了标准 C 库的支持。这个库在 VS2015 + EDK202008 的组合下工作正常,但是在 VS2019 下就会遇到一些问题,本文将介绍如何解决之。完成的目标是:完整编译 LibC 提供的 AppPkg 。LibC 可以在 https://github.com/tianocore/edk2-libc 下载到,打开之后内容如下:

Libc 解压之后文件

解压三个目标到 EDK202008 后即可开始编译。

使用 “x86 Native Tools Command Prompt for VS2019”

VS2019 x86 窗口

命令

  1. edksetup
  2. build -a X64 -p AppPkg\AppPkg.dsc -t VS2019  (特别注意, VS2019 必须大写)

之后就遇到了第一个错误:

“Socket.c
c:\buildbs\edk202008\StdLib\EfiSocketLib\Socket.c(573): error C2220: the following warning is treated as an error
c:\buildbs\edk202008\StdLib\EfiSocketLib\Socket.c(573): warning C4459: declaration of 'errno' hides global declaration
c:\buildbs\edk202008\StdLib\Include\errno.h(43): note: see declaration of 'errno'
c:\buildbs\edk202008\StdLib\EfiSocketLib\Socket.c(1337): warning C4459: declaration of 'errno' hides global declaration
c:\buildbs\edk202008\StdLib\Include\errno.h(43): note: see declaration of 'errno'
c:\buildbs\edk202008\StdLib\EfiSocketLib\Socket.c(1480): warning C4459: declaration of 'errno' hides global declaration
c:\buildbs\edk202008\StdLib\Include\errno.h(43): note: see declaration of 'errno'
c:\buildbs\edk202008\StdLib\EfiSocketLib\Socket.c(1892): warning C4459: declaration of 'errno' hides global declaration
c:\buildbs\edk202008\StdLib\Include\errno.h(43): note: see declaration of 'errno'
c:\buildbs\edk202008\StdLib\EfiSocketLib\Socket.c(2754): warning C4459: declaration of 'errno' hides global declaration
c:\buildbs\edk202008\StdLib\Include\errno.h(43): note: see declaration of 'errno'
c:\buildbs\edk202008\StdLib\EfiSocketLib\Socket.c(2974): warning C4459: declaration of 'errno' hides global declaration
c:\buildbs\edk202008\StdLib\Include\errno.h(43): note: see declaration of 'errno'
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.26.28801\bin\Hostx86\x64\cl.exe"' : return code '0x2'
Stop.”

解决方法是:在 StdLib.inc 末尾添加下面的内容:

# Temporarily restrict compiler warnings to those produced by VS2012.
  # Code that fails when these flags are removed will have to be rewritten
  # in order to pass.  This may be as simple as renaming an object, but may
  # require more significant changes.
    MSFT:*_VS2015_*_CC_FLAGS          = /Wv:11
    MSFT:*_VS2015x86_*_CC_FLAGS       = /Wv:11
    MSFT:*_VS2015xASL_*_CC_FLAGS      = /Wv:11
    MSFT:*_VS2015x86xASL_*_CC_FLAGS   = /Wv:11

#LABZ_Start
  # Temporarily restrict compiler warnings to those produced by VS2012.
  # Code that fails when these flags are removed will have to be rewritten
  # in order to pass.  This may be as simple as renaming an object, but may
  # require more significant changes.
    MSFT:*_VS2019_*_CC_FLAGS          = /Wv:11
    MSFT:*_VS2019x86_*_CC_FLAGS       = /Wv:11
    MSFT:*_VS2019xASL_*_CC_FLAGS      = /Wv:11
    MSFT:*_VS2019x86xASL_*_CC_FLAGS   = /Wv:11
#LABZ_End

再次编译会遇到下面这个这个错误:

        "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.26.28801\bin\Hostx86\x64\link.exe" /OUT:c:\buildbs\edk202008\Build\AppPkg\DEBUG_VS2019\X64\AppPkg\Applications\Sockets\OobTx\OobTx\DEBUG\OobTx.dll /NOLOGO /NODEFAULTLIB /IGNORE:4001 /IGNORE:4281 /OPT:REF /OPT:ICF=10 /MAP /ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /Machine:X64 /LTCG /DLL /ENTRY:_ModuleEntryPoint /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO /BASE:0 /DRIVER /DEBUG /WHOLEARCHIVE  @c:\buildbs\edk202008\Build\AppPkg\DEBUG_VS2019\X64\AppPkg\Applications\Sockets\OobTx\OobTx\OUTPUT\static_library_files.lst
LibGdtoa.lib(ldtoa.obj) : error LNK2001: unresolved external symbol __fpclassifyd
BsdSocketLib.lib(res_send.obj) : error LNK2001: unresolved external symbol inet_ntoa
BsdSocketLib.lib(res_mkupdate.obj) : error LNK2001: unresolved external symbol inet_aton
BsdSocketLib.lib(ns_print.obj) : error LNK2001: unresolved external symbol inet_ntop
BsdSocketLib.lib(getnetbyht.obj) : error LNK2001: unresolved external symbol inet_network
c:\buildbs\edk202008\Build\AppPkg\DEBUG_VS2019\X64\AppPkg\Applications\Sockets\OobTx\OobTx\DEBUG\OobTx.dll : fatal error LNK1120: 5 unresolved externals
NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.26.28801\bin\Hostx86\x64\link.exe"' : return code '0x460'
Stop.
build.py...
 : error 7000: Failed to execute command
        C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.26.28801\bin\Hostx86\x86\nmake.exe /nologo tbuild [c:\buildbs\edk202008\Build\AppPkg\DEBUG_VS2019\X64\AppPkg\Applications\Sockets\OobTx\OobTx]


build.py...
 : error 7000: Failed to execute command
        C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.26.28801\bin\Hostx86\x86\nmake.exe /nologo tbuild [c:\buildbs\edk202008\Build\AppPkg\DEBUG_VS2019\X64\AppPkg\Applications\Sockets\DataSink\DataSink]


build.py...
 : error F002: Failed to build module
        c:\buildbs\edk202008\AppPkg\Applications\Sockets\OobTx\OobTx.inf [X64, VS2019, DEBUG]

解决方法是修改Conf/tools_def.txt 文件,注释掉一行。对于/WHOLEARCHIVE 这个参数的解释,可以在【参考1】看到,似乎是要求编译过程中把所有的可能的 OBJ 都编译一次?

*_VS2019_*_MAKE_FLAGS      = /nologo
*_VS2019_*_SLINK_FLAGS     = /NOLOGO /LTCG
*_VS2019_*_APP_FLAGS       = /nologo /E /TC
*_VS2019_*_PP_FLAGS        = /nologo /E /TC /FIAutoGen.h
*_VS2019_*_VFRPP_FLAGS     = /nologo /E /TC /DVFRCOMPILE /FI$(MODULE_NAME)StrDefs.h
#LABZ *_VS2019_*_DLINK2_FLAGS    = /WHOLEARCHIVE
*_VS2019_*_ASM16_PATH      = DEF(VS2019_BIN_IA32)\ml.exe
*_VS2019_*_DEPS_FLAGS      = DEF(MSFT_DEPS_FLAGS)
##################
# ASL definitions
##################

再次编译,就可以正常通过得到结果:

编译结果

修改后的 Libc 可以在这里下载

链接: https://pan.baidu.com/s/1xCjiZ6_Shsa0tWOdRCjGrg 提取码: k6n2

感觉目前 EDK2 对于 VS2019 的支持还是有问题的,对于有编译需求的朋友,我依然建议使用 VS2015。

参考:

/WHOLEARCHIVE (Include All Library Object Files)

Force the linker to include all object files in the static library in the linked executable.

Syntax

/WHOLEARCHIVE
/WHOLEARCHIVE:library

Arguments

library
An optional pathname to a static library. The linker includes every object file from this library.

Remarks

The /WHOLEARCHIVE option forces the linker to include every object file from either a specified static library, or if no library is specified, from all static libraries specified to the LINK command. To specify the /WHOLEARCHIVE option for multiple libraries, you can use more than one /WHOLEARCHIVE switch on the linker command line. By default, the linker includes object files in the linked output only if they export symbols referenced by other object files in the executable. The /WHOLEARCHIVE option makes the linker treat all object files archived in a static library as if they were specified individually on the linker command line.

The /WHOLEARCHIVE option can be used to re-export all the symbols from a static library. This allows you to make sure that all of your library code, resources, and metadata are included when you create a component from more than one static library. If you see warning LNK4264 when you create a static library that contains Windows Runtime components for export, use the /WHOLEARCHIVE option when linking that library into another component or app.

The /WHOLEARCHIVE option was introduced in Visual Studio 2015 Update 2.

C# SerialPort 发送8Bits的问题

最近在使用 SerialPort 类发送串口数据,惊奇的发现PC端程序发送 0xFF 对面(ESP32)只能收到 0x7F。但是用串口工具是可以正常发送的。于是针对这个问题进行研究。Baidu上说是因为奇偶校验导致的,我设置了无校验现象依旧。

查看 SerialPort 类发现它有一个 Encoding 属性,默认是ASCIIEncoding,一般来说,正经的 ASCII 是 0-128 就是0-0x7F,因此,直觉上这个问题是它导致的。最终在下面的页面搜索到了答案:

http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm

By default, strings and char’s are transmitted (not coded) as 7-bit ASCII where all characters above 127 are illegal and will be replaced with “?” (code 63) – even though you have defined 8 data bits! Therefore, you cannot use a string or char related method to send or read binary information (0-255) unless you change the encoding to something, which include all values up to 255 like:

   YourSerialPort.Encoding = System.Text.Encoding.GetEncoding(28591)  ' or
   YourSerialPort.Encoding = System.Text.Encoding.GetEncoding(1252)

于是按照上面的方法修改代码问题就得以解决。

Step to UEFI (226)EDK2 生成 ROM 最后的打包动作(上)

编译产生 BIOS ROM的最后一个步骤是将 FV 打包,那么这个操作是在哪里?带着这个问题,以 OVMF 的 PKG 为例进行研究。

我们平时编译使用的是 BaseTools\Bin\Win32下面的 build.exe ,这个文件是使用 Python 转 EXE工具生成的。如果想研究 Build.exe 具体的代码,可以将 build.exe 改名或者删除,然后保证当前 path 中含有BaseTools\BinWrappers\WindowsLike 路径,这样调用 Built 时会直接使用目录下的 Build.bat,该批处理内容如下:

@setlocal
@set ToolName=%~n0%
@%PYTHON_HOME%\python.exe %BASE_TOOLS_PATH%\Source\Python\%ToolName%\%ToolName%.py %*

如果你调用 build就会使用 Python.exe 来执行BaseTools\Source\Python\build\build.py。特别提醒,我实验发现 Python38 会出现如下的错误:

except BaseException, X:
^
SyntaxError: invalid syntax

修正方法是更换为 Python27 的版本(他们可以共存)。另外,这个问题也可能是我使用的 EDK2 版本的问题(我实验环境是EDK2201911).

代码入口在 main中,可以通过加入下面2条语句做一个标记:

print("www.lab-z.com\n")
subprocess.call("pause",shell=True)

添加在代码中入口位置

## Tool entrance method
#
# This method mainly dispatch specific methods per the command line options.
# If no error found, return zero value so the caller of this tool can know
# if it's executed successfully or not.
#
#   @retval 0     Tool was successful
#   @retval 1     Tool failed
#
def Main():

测试结果:

这里证明了上述代码就是程序入口

代码中MyBuild.Launch()是具体执行 build 动作的地方:

        MyBuild = Build(Target, Workspace, Option)
        GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)
        if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):
            MyBuild.Launch()

实验确定对应代码:

    ## Launch the module or platform build
    #
    def Launch(self):
        if not self.ModuleFile:
            if not self.SpawnMode or self.Target not in ["", "all"]:
                self.SpawnMode = False
                self._BuildPlatform()
            else:
                self._MultiThreadBuildPlatform()
            self.CreateGuidedSectionToolsFile()
        else:
            self.SpawnMode = False
            self._BuildModule()

self._MultiThreadBuildPlatform() 这里会完成所有的动作.这个函数在同一个文件中:

    ## Build a platform in multi-thread mode
    #
    def _MultiThreadBuildPlatform(self):
        SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)

我们的目标是找到最后将 FV 打包的过程,在代码中下面的位置加入 Pause 可以正常停下来,所以确定这个是我们目标动作的位置。

                    if self.Fdf:
    		print("www.lab-z.com\n")
subprocess.call("pause",shell=True)

                        #
                        # Generate FD image if there's a FDF file found
                        #
                        GenFdsStart = time.time()
                        LaunchCommand(Wa.GenFdsCommand, os.getcwd())

                        #
                        # Create MAP file for all platform FVs after GenFds.
                        #
                        self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
                        self.GenFdsTime += int(round((time.time() - GenFdsStart)))
                    #
                    # Save MAP buffer into MAP file.
                    #
                    self._SaveMapFile(MapBuffer, Wa)

代码中加入下面两句输出具体调用的命令:

                        print("[%s]\n" % Wa.GenFdsCommand)
                        print("[%s]\n" % os.getcwd())
                        subprocess.call("pause",shell=True)

测试,在编译环境下输入下面的 command, 可以实现生成 OVMF.fd:

GenFds -f c:\buildbs\i2c\OvmfPkg\OvmfPkgX64.fdf --conf=c:\buildbs\i2c\conf -o c:\buildbs\i2c\Build\OvmfX64\DEBUG_VS2015x86 -t VS2015x86 -b DEBUG -p c:\buildbs\i2c\OvmfPkg\OvmfPkgX64.dsc -a X64  -D "EFI_SOURCE=c:\\buildbs\\i2c\\edkcompatibilitypkg"  -D "EDK_SOURCE=c:\\buildbs\\i2c\\edkcompatibilitypkg"  -D "TOOL_CHAIN_TAG=VS2015x86"  -D "TOOLCHAIN=VS2015x86"  -D "TARGET=DEBUG"  -D "FAMILY=MSFT"  -D "WORKSPACE=c:\\buildbs\\i2c"  -D "EDK_TOOLS_PATH=c:\\buildbs\\i2c\\basetools"  -D "ARCH=X64"  -D "ECP_SOURCE=c:\\buildbs\\i2c\\edkcompatibilitypkg"

结论:build 过程中的最后一段是调用 GenFds 来生成 BIOS 的 FD (或者叫做 ROM)文件。

推荐一个十六进制编辑器 HXD

之前我一直使用 WinHex作为十六进制编译器,但是WinHex 是收费的,经过研究找到了 HxD 这款软件,它是免费的十六进制编辑软件,主页在 https://mh-nexus.de/en/hxd/  经过一段时间的使用(主要是用于 ROM 文件的分析),感觉挺好用,有需要的朋友可以试试。

HXD 界面

它提供了如下功能:

  • Available as a portable and installable edition
  • RAM-Editor
    • To edit the main memory
    • Memory sections are tagged with data-folds
  • Disk-Editor (Hard disks, floppy disks, ZIP-disks, USB flash drives, CDs, …)
    • RAW reading and writing of disks and drives
    • for Win9x, WinNT and higher
  • Instant opening regardless of file-size
    • Up to 8EB; opening and editing is very fast
  • Liberal but safe file sharing with other programs
  • Flexible and fast searching/replacing for several data types
    • Data types: text (including Unicode), hex-values, integers and floats
    • Search direction: Forward, Backwards, All (starting from the beginning)
  • File compare (simple)
  • View data in Ansi, DOS, EBCDIC and Macintosh character sets
  • Checksum-Generator: Checksum, CRCs, Custom CRC, SHA-1, SHA-512, MD5, …
  • Exporting of data to several formats
    • Source code (Pascal, C, Java, C#, VB.NET)
    • Formatted output (plain text, HTML, Richtext, TeX)
    • Hex files (Intel HEX, Motorola S-record)
  • Insertion of byte patterns
  • File tools
    • File shredder for safe file deletion
    • Splitting or concatenating of files
  • Basic data analysis (statistics)
    • Graphical representation of the byte/character distribution
    • Helps to identify the data type of a selection
  • Byte grouping
    • 1, 2, 4, 8 or 16 bytes packed together into one column
  • “Hex only” or “text only”-modes
  • Progress-window for lengthy operations
    • Shows the remaining time
    • Button to cancel
  • Modified data is highlighted
  • Unlimited undo
  • “Find updates…”-function
  • Easy to use and modern interface
  • Goto address
  • Printing
  • Overwrite or insert mode
  • Cut, copy, paste insert, paste write
  • Clipboard support for other hex editors
    • Visual Studio/Visual C++, WinHex, HexWorkshop, …
  • Bookmarks
    • Ctrl+Shift+Number (0-9) sets a bookmark
    • Ctrl+Number (0-9) goes to a bookmark
  • Navigating to nibbles with Ctrl+Left or Ctrl+Right
  • Flicker free display and fast drawing

Step to UEFI (225)CSME版本检查工具

CSME 和BIOS 并没有多少关系,但是因为同属 Firmware,所以出了问题都会首先找BIOS工程师。对于大多数情况下,升级 CSME 都能解决问题。因此,BIOS工程师遇到的问题是:当前 CSME 是什么版本?通常的解决方法是进入BIOS Setup查看,更专业一点是在 Shell 或者Windows 下运行Intel提供的 MEInfo这个工具。但是对于单独的Binary 就无能为力了。

最近发现了一个开源工具,能够完成这个目标。项目地址是 https://github.com/platomav/MEAnalyzer

项目发布了2个版本: Python的源代码版本和转换为 EXE 的版本。对于大多数用户,个人推荐EXE版本,理由是:省事。

首先展示一下 EXE版本的使用,直接将要查看的Binary 推拽到EXE 上即可:

试验文件

运行结果:

查看到的 CSME 版本
查看到的其他Firmware 版本

Python 版本的下载之后文件如下:

运行方法是打开 CMD 窗口后输入 mea.py + 文件名,运行结果和EXE版本的相同。但是这个代码需要3个额外的库 colorama ,crccheck和PLTable,可以在 https://pypi.org/ 找到。我在附件中提供了直接下载。Colorama 和crccheck是以 WHL 格式提供的。安装方法是将下载好的 WHL 文件放在 Python 安装后的 Scripts 目录下,然后使用 pip install 命令安装,示例如下:

如果发生 pip3无法运行的情况,推荐卸载 Python 之后重新安装,安装时务必选中 PIP选项。

对于PLTable安装方法是解压后CMD窗口中使用 python setup.py install 进行安装:

本文提到的文件可以在这里下载,推荐有兴趣的朋友试试

链接: https://pan.baidu.com/s/1GaDeaRWZU-hpwAnUx4mT5w 提取码: 3rz3

Rowhammer Attacks

最近看资料了解到的一种内存的攻击方式,效果是能够翻转被攻击的内存区域 Bits,如果这个 Bits 正好是用于检查权限之类的,用这种方法就能获得更高的权限。

原理:RAM的制造精度越来越高,部件在物理层面上越来越小。既能在一块芯片上集成更大的内存容量,又能让各个内存单元之间不发生电磁干扰是非常难以做到的。这一情况使得对内存的单个区域进行的读写有可能干扰到邻近的区域,导致电流流入或流出邻近的内存单元。如果反复进行大量读写,有可能改变邻近内存单元的内容,使得0变成1,或者1变成0。这种现象被称为比特翻转(bitflipping),可被利用获取更高的权限。【参考1】

快速读取上图中的黄色 Row ,可能会导致紫色的Row 出现 bits 反转。【参考2】

当然,从原理上来看触发Rowhammer漏洞非常简单,但成功地利用该漏洞有点难,因为内存中大多数的位是与攻击目标无关的,而这些无关位的翻转可能会导致内存破坏。想要成功地利用Rowhammer,攻击者必须能够诱使系统加载目标内存页到DRAM中与攻击者所有的物理内存行邻接的行中。【参考3】

在 MemTest86 的测试中,如果有遇到写入和读取有1Bit差别的failure,可以尝试在Setep 中打开 Enable RH Prevention 这个功能。

参考:

1.http://xilinx.eetrend.com/d6-xilinx/news/2016-10/10624.html 【DDR未解之谜】Row Hammer这么严重,知道如何解决吗?

2.https://www.zhihu.com/question/28666294 Row Hammer漏洞会有多大实质性影响?

3.https://blog.csdn.net/u013806583/article/details/53103714

开源的 Windows ACPI Table 查看工具

最近发现一个开源的 Windows 下查看 ACPI table 的工具 firmwaretables 在下面的链接可以下载到:

https://github.com/vurdalakov/firmwaretables

Lists, extracts and decodes system firmware tables. Usage: firmwaretables <-list | -all | -save | -decode <table type> <table id> [filename]> [-silent] Commands: -l – list available system firmware tables -a – save all system firmware tables to files -s – save specific system firmware table to file -d – decode specific system firmware table Options: -silent – no error messages are shown; check exit code Exit codes: 0 – operation succeeded 1 – operation failed -1 – invalid command line syntax