Writing the Application
Learning to write EDK2 applications in general is well outside the scope of this tutorial, but we will cover the general workflow.
First, we will create a src
directory with the following files:
PlatformBuild.py
- the stuart build description file for our target software. You can read more about the pytool extensions here.Tutorial.dsc
- The EDK2 description file for building our target softwareTutorial.inf
- The EDK2 info file for building our target softwareTutorial.c
- Our C source filetsffs.h
- The header file from theharness
directory in the repository for our target architecture
We'll cover the auxiliary and build files first, then we'll cover the source code.
PlatformBuild.py
As mentioned above, this file is used by the EDK2 PyTools (also known as Stuart) to configure tools for building our target software. You can read about stuart and the PyTool Extensions .
We specify our workspace, scopes, packages, and so forth:
from os.path import abspath, dirname, join
from typing import Iterable, List
from edk2toolext.environment.uefi_build import UefiBuilder
from edk2toolext.invocables.edk2_platform_build import BuildSettingsManager
from edk2toolext.invocables.edk2_setup import RequiredSubmodule, SetupSettingsManager
from edk2toolext.invocables.edk2_update import UpdateSettingsManager
class TutorialSettingsManager(
UpdateSettingsManager, SetupSettingsManager, BuildSettingsManager
):
def __init__(self) -> None:
script_path = dirname(abspath(__file__))
self.ws = script_path
def GetWorkspaceRoot(self) -> str:
return self.ws
def GetActiveScopes(self) -> List[str]:
return ["Tutorial"]
def GetPackagesSupported(self) -> Iterable[str]:
return ("Tutorial",)
def GetRequiredSubmodules(self) -> Iterable[RequiredSubmodule]:
return []
def GetArchitecturesSupported(self) -> Iterable[str]:
return ("X64",)
def GetTargetsSupported(self) -> Iterable[str]:
return ("DEBUG",)
def GetPackagesPath(self) -> Iterable[str]:
return [abspath(join(self.GetWorkspaceRoot(), ".."))]
class PlatformBuilder(UefiBuilder):
def SetPlatformEnv(self) -> int:
self.env.SetValue(
"ACTIVE_PLATFORM", "Tutorial/Tutorial.dsc", "Platform hardcoded"
)
self.env.SetValue("PRODUCT_NAME", "Tutorial", "Platform hardcoded")
self.env.SetValue("TARGET_ARCH", "X64", "Platform hardcoded")
self.env.SetValue("TOOL_CHAIN_TAG", "GCC", "Platform Hardcoded", True)
return 0
Tutorial.inf
The exact meaning of all the entries in the Tutorial.inf
file is out of scope of this
tutorial, but in general this file declares the packages and libraries our application
needs.
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = Tutorial
FILE_GUID = 6987936E-ED34-44db-AE97-1FA5E4ED2116
MODULE_TYPE = UEFI_APPLICATION
VERSION_STRING = 1.0
ENTRY_POINT = UefiMain
UEFI_HII_RESOURCE_SECTION = TRUE
[Sources]
Tutorial.c
[Packages]
CryptoPkg/CryptoPkg.dec
MdeModulePkg/MdeModulePkg.dec
MdePkg/MdePkg.dec
[LibraryClasses]
BaseCryptLib
SynchronizationLib
UefiApplicationEntryPoint
UefiLib
Tutorial.dsc
The descriptor file also declares classes and libraries that are needed to build the whole platform including our application and requisite additional libraries.
[Defines]
PLATFORM_NAME = Tutorial
PLATFORM_GUID = 0458dade-8b6e-4e45-b773-1b27cbda3e06
PLATFORM_VERSION = 0.01
DSC_SPECIFICATION = 0x00010006
OUTPUT_DIRECTORY = Build/Tutorial
SUPPORTED_ARCHITECTURES = X64
BUILD_TARGETS = DEBUG|RELEASE|NOOPT
SKUID_IDENTIFIER = DEFAULT
!include MdePkg/MdeLibs.dsc.inc
!include CryptoPkg/CryptoPkg.dsc
[LibraryClasses]
BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf
BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
IntrinsicLib|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf
IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLib.inf
PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf
UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
TimerLib|UefiCpuPkg/Library/CpuTimerLib/BaseCpuTimerLib.inf
[Components]
Tutorial/Tutorial.inf
tsffs.h
Copy this file from the TSFFS repository's harness
directory. It provides macros for
compiling in the harness so the target software can communicate with and receive
test cases from the fuzzer.
Tutorial.c
This is our actual source file. We'll be fuzzing a real EDK2 API: X509VerifyCert
,
which tries to verify a certificate was issued by a given certificate authority.
#include <Library/BaseCryptLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Uefi.h>
#include "tsffs.h"
void hexdump(UINT8 *buf, UINTN size) {
for (UINTN i = 0; i < size; i++) {
if (i != 0 && i % 26 == 0) {
Print(L"\n");
} else if (i != 0 && i % 2 == 0) {
Print(L" ");
}
Print(L"%02x", buf[i]);
}
Print(L"\n");
}
EFI_STATUS
EFIAPI
UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
UINTN MaxInputSize = 0x1000;
UINTN InputSize = MaxInputSize;
UINT8 *Input = (UINT8 *)AllocatePages(EFI_SIZE_TO_PAGES(MaxInputSize));
if (!Input) {
return EFI_OUT_OF_RESOURCES;
}
Print(L"Input: %p Size: %d\n", Input, InputSize);
UINT8 *Cert = Input;
UINTN CertSize = InputSize / 2;
UINT8 *CACert = (Input + CertSize);
UINTN CACertSize = CertSize;
Print(L"Certificate:\n");
hexdump(Cert, CertSize);
Print(L"CA Certificate:\n");
hexdump(CACert, CACertSize);
BOOLEAN Status = X509VerifyCert(Cert, CertSize, CACert, CACertSize);
if (Input) {
FreePages(Input, EFI_SIZE_TO_PAGES(MaxInputSize));
}
return EFI_SUCCESS;
}
Now that we have some code, we'll move on to building.