reactos/boot/environ/lib/misc/image.c
Pierre Schweitzer 321bcc056d Create the AHCI branch for Aman's work
svn path=/branches/GSoC_2016/AHCI/; revision=71203
2016-04-24 20:17:09 +00:00

1755 lines
50 KiB
C

/*
* COPYRIGHT: See COPYING.ARM in the top level directory
* PROJECT: ReactOS UEFI Boot Library
* FILE: boot/environ/lib/misc/image.c
* PURPOSE: Boot Library Image Routines
* PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include "bl.h"
#include <bcd.h>
/* DATA VARIABLES ************************************************************/
ULONG IapAllocatedTableEntries;
ULONG IapTableEntries;
PVOID* IapImageTable;
/* FUNCTIONS *****************************************************************/
NTSTATUS
ImgpGetFileSize (
_In_ PBL_IMG_FILE File,
_Out_ PULONG FileSize
)
{
NTSTATUS Status;
ULONG Size;
BL_FILE_INFORMATION FileInformation;
/* Check if the file was memory mapped */
if (File->Flags & BL_IMG_MEMORY_FILE)
{
/* Just read the size of the mapping */
Size = File->FileSize;
}
else
{
/* Do file I/O to get the file size */
Status = BlFileGetInformation(File->FileId,
&FileInformation);
if (!NT_SUCCESS(Status))
{
return Status;
}
/* We only support files less than 4GB in the Image Mapped */
Size = FileInformation.Size;
if (FileInformation.Size > ULONG_MAX)
{
return STATUS_NOT_SUPPORTED;
}
}
/* Return the size and success */
*FileSize = Size;
return STATUS_SUCCESS;
}
NTSTATUS
ImgpReadAtFileOffset (
_In_ PBL_IMG_FILE File,
_In_ ULONG Size,
_In_ ULONGLONG ByteOffset,
_In_ PVOID Buffer,
_Out_ PULONG BytesReturned
)
{
NTSTATUS Status;
/* Check what if this is a mapped file or not */
if (File->Flags & BL_IMG_MEMORY_FILE)
{
/* Check if the boundaries are within the file size */
if ((ByteOffset + Size) <= File->FileSize)
{
/* Yep, copy into the caller-supplied buffer */
RtlCopyMemory(Buffer,
(PVOID)((ULONG_PTR)File->BaseAddress + (ULONG_PTR)ByteOffset),
Size);
/* If caller wanted to know, return the size copied */
if (BytesReturned)
{
*BytesReturned = Size;
}
/* All good */
Status = STATUS_SUCCESS;
}
else
{
/* Doesn't fit */
Status = STATUS_INVALID_PARAMETER;
}
}
else
{
/* Issue the file I/O instead */
Status = BlFileReadAtOffsetEx(File->FileId,
Size,
ByteOffset,
Buffer,
BytesReturned,
0);
}
/* Return the final status */
return Status;
}
NTSTATUS
ImgpOpenFile (
_In_ ULONG DeviceId,
_In_ PWCHAR FileName,
_In_ ULONG Flags,
_Out_ PBL_IMG_FILE NewFile
)
{
NTSTATUS Status;
ULONG FileSize;
ULONGLONG RemoteFileSize;
PVOID RemoteFileAddress;
ULONG FileId;
/* First, try to see if BD has this file remotely */
Status = BlBdPullRemoteFile(FileName,
&RemoteFileAddress,
&RemoteFileSize);
if (NT_SUCCESS(Status))
{
/* Yep, get the file size and make sure it's < 4GB */
FileSize = RemoteFileSize;
if (RemoteFileSize <= ULONG_MAX)
{
/* Remember this is a memory mapped remote file */
NewFile->Flags |= (BL_IMG_MEMORY_FILE | BL_IMG_REMOTE_FILE);
NewFile->FileSize = FileSize;
NewFile->BaseAddress = RemoteFileAddress;
goto Quickie;
}
}
/* Use File I/O instead */
Status = BlFileOpen(DeviceId,
FileName,
BL_FILE_READ_ACCESS,
&FileId);
if (!NT_SUCCESS(Status))
{
/* Bail out on failure */
return Status;
}
/* Make sure nobody thinks this is a memory file */
NewFile->Flags &= ~BL_IMG_MEMORY_FILE;
NewFile->FileId = FileId;
Quickie:
/* Set common data for both memory and I/O based file */
NewFile->Flags |= BL_IMG_VALID_FILE;
NewFile->FileName = FileName;
return Status;
}
NTSTATUS
ImgpCloseFile (
_In_ PBL_IMG_FILE File
)
{
NTSTATUS Status;
/* Make sure this is a valid file, otherwise no-op */
Status = STATUS_SUCCESS;
if (File->Flags & BL_IMG_VALID_FILE)
{
/* Is this a memory mapped file? */
if (!(File->Flags & BL_IMG_MEMORY_FILE))
{
/* Nope, close the file handle */
return BlFileClose(File->FileId);
}
/* Is this a remote file? */
if (File->Flags & BL_IMG_REMOTE_FILE)
{
/* Then only free the memory in that scenario */
EfiPrintf(L"TODO\r\n");
//return MmPapFreePages(File->BaseAddress, TRUE);
}
}
/* Return the final status */
return Status;
}
NTSTATUS
BlImgUnallocateImageBuffer (
_In_ PVOID ImageBase,
_In_ ULONG ImageSize,
_In_ ULONG ImageFlags
)
{
EfiPrintf(L"leaking the shit out of %p\r\n", ImageBase);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
BlImgAllocateImageBuffer (
_Inout_ PVOID* ImageBuffer,
_In_ ULONG MemoryType,
_In_ ULONGLONG ImageSize,
_In_ ULONG Flags
)
{
ULONG Attributes;
ULONGLONG Pages, Size;
PVOID MappedBase, CurrentBuffer;
NTSTATUS Status;
PHYSICAL_ADDRESS PhysicalAddress;
/* Read and reset the current buffer address */
CurrentBuffer = *ImageBuffer;
*ImageBuffer = NULL;
/* Align the image size to page */
Size = ROUND_TO_PAGES(ImageSize);
/* Not sure what this attribute does yet */
Attributes = 0;
if (Flags & BL_LOAD_IMG_UNKNOWN_BUFFER_FLAG)
{
Attributes = 0x10000;
}
/* Check if the caller wants a virtual buffer */
if (Flags & BL_LOAD_IMG_VIRTUAL_BUFFER)
{
/* Set the physical address to the current buffer */
PhysicalAddress.QuadPart = (ULONG_PTR)CurrentBuffer;
Pages = Size >> PAGE_SHIFT;
/* Allocate the physical pages */
Status = BlMmAllocatePhysicalPages(&PhysicalAddress,
Pages,
MemoryType,
Attributes,
0);
if (!NT_SUCCESS(Status))
{
/* If that failed, remove allocation attributes */
PhysicalAddress.QuadPart = 0;
Attributes &= ~BlMemoryValidAllocationAttributeMask,
Status = BlMmAllocatePhysicalPages(&PhysicalAddress,
Pages,
MemoryType,
Attributes,
0);
}
/* Check if either attempts succeeded */
if (!NT_SUCCESS(Status))
{
return Status;
}
/* Now map the physical buffer at the address requested */
MappedBase = (PVOID)PhysicalAddress.LowPart;
Status = BlMmMapPhysicalAddressEx(&MappedBase,
BlMemoryFixed,
Size,
PhysicalAddress);
if (!NT_SUCCESS(Status))
{
/* Free on failure if needed */
BlMmFreePhysicalPages(PhysicalAddress);
return Status;
}
}
else
{
/* Otherwise, allocate raw physical pages */
MappedBase = CurrentBuffer;
Pages = Size >> PAGE_SHIFT;
Status = MmPapAllocatePagesInRange(&MappedBase,
MemoryType,
Pages,
Attributes,
0,
NULL,
0);
if (!NT_SUCCESS(Status))
{
/* If that failed, try without allocation attributes */
MappedBase = NULL;
Attributes &= ~BlMemoryValidAllocationAttributeMask,
Status = MmPapAllocatePagesInRange(&MappedBase,
MemoryType,
Pages,
Attributes,
0,
NULL,
0);
}
/* Check if either attempts succeeded */
if (!NT_SUCCESS(Status))
{
return Status;
}
}
/* Success path, returned allocated address */
*ImageBuffer = MappedBase;
return STATUS_SUCCESS;
}
NTSTATUS
BlImgLoadImageWithProgress2 (
_In_ ULONG DeviceId,
_In_ BL_MEMORY_TYPE MemoryType,
_In_ PWCHAR FileName,
_Inout_ PVOID* MappedBase,
_Inout_ PULONG MappedSize,
_In_ ULONG ImageFlags,
_In_ BOOLEAN ShowProgress,
_Out_opt_ PUCHAR* HashBuffer,
_Out_opt_ PULONG HashSize
)
{
NTSTATUS Status;
PVOID BaseAddress, Buffer;
ULONG RemainingLength, CurrentSize, ImageSize, ReadSize;
BOOLEAN ComputeSignature, ComputeHash, Completed;
BL_IMG_FILE FileHandle;
ULONGLONG ByteOffset;
PHYSICAL_ADDRESS PhysicalAddress;
/* Initialize variables */
BaseAddress = 0;
ImageSize = 0;
Completed = FALSE;
RtlZeroMemory(&FileHandle, sizeof(FileHandle));
/* Check for missing parameters */
if (!MappedBase)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
if (!FileName)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
if (!MappedSize)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
/* Check if the image buffer is being provided */
if (ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER)
{
/* An existing base must already exist */
if (!(*MappedBase))
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
}
/* Check of a hash is being requested */
if (ImageFlags & BL_LOAD_IMG_COMPUTE_HASH)
{
/* Make sure we can return the hash */
if (!HashBuffer)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
if (!HashSize)
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
}
/* Check for invalid combination of parameters */
if ((ImageFlags & BL_LOAD_IMG_COMPUTE_HASH) && (ImageFlags & 0x270))
{
Status = STATUS_INVALID_PARAMETER;
goto Quickie;
}
/* Initialize hash if requested by caller */
if (HashBuffer)
{
*HashBuffer = 0;
}
/* Do the same for the hash size */
if (HashSize)
{
*HashSize = 0;
}
/* Open the image file */
Status = ImgpOpenFile(DeviceId, FileName, DeviceId, &FileHandle);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"Error opening file: %lx\r\n", Status);
goto Quickie;
}
/* Get the size of the image */
Status = ImgpGetFileSize(&FileHandle, &ImageSize);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"Error getting file size: %lx\r\n", Status);
goto Quickie;
}
/* Read the current base address */
BaseAddress = *MappedBase;
if (ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER)
{
/* Check if the current buffer is too small */
if (*MappedSize < ImageSize)
{
/* Return the required size of the buffer */
*MappedSize = ImageSize;
Status = STATUS_BUFFER_TOO_SMALL;
}
}
else
{
/* A buffer was not provided, allocate one ourselves */
Status = BlImgAllocateImageBuffer(&BaseAddress,
MemoryType,
ImageSize,
ImageFlags);
}
/* Bail out if allocation failed */
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Set the initial byte offset and length to read */
RemainingLength = ImageSize;
ByteOffset = 0;
Buffer = BaseAddress;
/* Update the initial progress */
Completed = FALSE;
if (ShowProgress)
{
BlUtlUpdateProgress(0, &Completed);
ShowProgress &= (Completed != 0) - 1;
}
/* Set the chunk size for each read */
ReadSize = 0x100000;
if (ReadSize > ImageSize)
{
ReadSize = ImageSize;
}
/* Check if we should compute hash and/or signatures */
ComputeSignature = ImageFlags & BL_LOAD_IMG_COMPUTE_SIGNATURE;
if ((ComputeSignature) || (ImageFlags & BL_LOAD_IMG_COMPUTE_HASH))
{
ComputeHash = TRUE;
// todo: crypto is hard
}
/* Begin the read loop */
while (RemainingLength)
{
/* Check if we've got more than a chunk left to read */
if (RemainingLength > ReadSize)
{
/* Read a chunk*/
CurrentSize = ReadSize;
}
else
{
/* Read only what's left */
CurrentSize = RemainingLength;
}
/* Read the chunk */
Status = ImgpReadAtFileOffset(&FileHandle,
CurrentSize,
ByteOffset,
Buffer,
0);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Check if we need to compute the hash of this chunk */
if (ComputeHash)
{
// todo: crypto is hard
}
/* Update our position and read information */
Buffer = (PVOID)((ULONG_PTR)Buffer + CurrentSize);
RemainingLength -= CurrentSize;
ByteOffset += CurrentSize;
/* Check if we should update the progress bar */
if (ShowProgress)
{
/* Compute new percentage completed, check if we're done */
BlUtlUpdateProgress(100 - 100 * RemainingLength / ImageSize,
&Completed);
ShowProgress &= (Completed != 0) - 1;
}
}
/* Is the read fully complete? We need to finalize the hash if requested */
if (ComputeHash != RemainingLength)
{
// todo: CRYPTO IS HARD
}
/* Success path, return back the buffer and the size of the image */
*MappedBase = BaseAddress;
*MappedSize = ImageSize;
Quickie:
/* Close the file handle */
ImgpCloseFile(&FileHandle);
/* Check if we failed and had allocated a buffer */
if (!(NT_SUCCESS(Status)) &&
(BaseAddress) &&
!(ImageFlags & BL_LOAD_IMG_EXISTING_BUFFER))
{
/* Check what kind of buffer we had allocated */
if (ImageFlags & BL_LOAD_IMG_VIRTUAL_BUFFER)
{
/* Unmap and free the virtual buffer */
PhysicalAddress.QuadPart = (ULONG_PTR)BaseAddress;
BlMmUnmapVirtualAddressEx(BaseAddress, ImageSize);
BlMmFreePhysicalPages(PhysicalAddress);
}
else
{
/* Free the physical buffer */
//MmPapFreePages(VirtualAddress, 1);
EfiPrintf(L"Leaking memory\r\n");
}
}
/* If we hadn't gotten to 100% yet, do it now */
if (ShowProgress)
{
BlUtlUpdateProgress(100, &Completed);
}
/* Return the final status */
return Status;
}
PIMAGE_SECTION_HEADER
BlImgFindSection (
_In_ PVOID ImageBase,
_In_ ULONG ImageSize
)
{
PIMAGE_SECTION_HEADER FoundSection;
ULONG i;
PIMAGE_SECTION_HEADER SectionHeader;
PIMAGE_NT_HEADERS NtHeader;
NTSTATUS Status;
/* Assume failure */
FoundSection = NULL;
/* Make sure the image is valid */
Status = RtlImageNtHeaderEx(0, ImageBase, ImageSize, &NtHeader);
if (NT_SUCCESS(Status))
{
/* Get the first section and loop through them all */
SectionHeader = IMAGE_FIRST_SECTION(NtHeader);
for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
{
/* Check if this is the resource section */
if (!_stricmp((PCCH)SectionHeader->Name, ".rsrc"))
{
/* Yep, we're done */
FoundSection = SectionHeader;
break;
}
/* Nope, keep going */
SectionHeader++;
}
}
/* Return the matching section */
return FoundSection;
}
VOID
BlImgQueryCodeIntegrityBootOptions (
_In_ PBL_LOADED_APPLICATION_ENTRY ApplicationEntry,
_Out_ PBOOLEAN IntegrityChecksDisabled,
_Out_ PBOOLEAN TestSigning
)
{
NTSTATUS Status;
BOOLEAN Value;
/* Check if /DISABLEINTEGRITYCHECKS is on */
Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
BcdLibraryBoolean_DisableIntegrityChecks,
&Value);
*IntegrityChecksDisabled = NT_SUCCESS(Status) && (Value);
/* Check if /TESTSIGNING is on */
Status = BlGetBootOptionBoolean(ApplicationEntry->BcdData,
BcdLibraryBoolean_AllowPrereleaseSignatures,
&Value);
*TestSigning = NT_SUCCESS(Status) && (Value);
}
NTSTATUS
BlImgUnLoadImage (
_In_ PVOID ImageBase,
_In_ ULONG ImageSize,
_In_ ULONG ImageFlags
)
{
/* Check for missing parameters */
if (!(ImageSize) || !(ImageBase))
{
/* Bail out */
return STATUS_INVALID_PARAMETER;
}
/* Unallocate the image buffer */
return BlImgUnallocateImageBuffer(ImageBase, ImageSize, ImageFlags);
}
NTSTATUS
ImgpLoadPEImage (
_In_ PBL_IMG_FILE ImageFile,
_In_ BL_MEMORY_TYPE MemoryType,
_Inout_ PVOID* ImageBase,
_Out_opt_ PULONG ImageSize,
_Inout_opt_ PVOID Hash,
_In_ ULONG Flags
)
{
NTSTATUS Status;
ULONG FileSize, HeaderSize;
BL_IMG_FILE LocalFileBuffer;
PBL_IMG_FILE LocalFile;
PVOID VirtualAddress, PreferredBase, ImageBuffer, CertBuffer, HashBuffer;
ULONGLONG VirtualSize;
PIMAGE_DATA_DIRECTORY CertDirectory;
PHYSICAL_ADDRESS PhysicalAddress;
PIMAGE_NT_HEADERS NtHeaders;
USHORT SectionCount, CheckSum, PartialSum, FinalSum;
PIMAGE_SECTION_HEADER Section;
ULONG_PTR EndOfHeaders, SectionStart, Slack, SectionEnd;
ULONG i, SectionSize, RawSize, BytesRead, RemainingLength, Offset, AlignSize;
BOOLEAN First, ImageHashValid;
UCHAR LocalBuffer[1024];
UCHAR TrustedBootInformation[52];
ULONG WorkaroundForBinutils;
/* Initialize locals */
WorkaroundForBinutils = 0;
LocalFile = NULL;
ImageBuffer = NULL;
FileSize = 0;
First = FALSE;
VirtualAddress = NULL;
CertBuffer = NULL;
CertDirectory = NULL;
HashBuffer = NULL;
Offset = 0;
VirtualSize = 0;
ImageHashValid = FALSE;
RtlZeroMemory(&TrustedBootInformation, sizeof(TrustedBootInformation));
/* Get the size of the image */
Status = ImgpGetFileSize(ImageFile, &FileSize);
if (!NT_SUCCESS(Status))
{
return STATUS_FILE_INVALID;
}
/* Allocate a flat buffer for it */
Status = BlImgAllocateImageBuffer(&ImageBuffer, BlLoaderData, FileSize, 0);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Read the whole file flat for now */
Status = ImgpReadAtFileOffset(ImageFile, FileSize, 0, ImageBuffer, NULL);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Build a local file handle */
LocalFile = &LocalFileBuffer;
LocalFileBuffer.FileName = ImageFile->FileName;
LocalFileBuffer.Flags = BL_IMG_MEMORY_FILE | BL_IMG_VALID_FILE;
LocalFileBuffer.BaseAddress = ImageBuffer;
LocalFileBuffer.FileSize = FileSize;
/* Get the NT headers of the file */
Status = RtlImageNtHeaderEx(0, ImageBuffer, FileSize, &NtHeaders);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Check if we should validate the machine type */
if (Flags & BL_LOAD_PE_IMG_CHECK_MACHINE)
{
/* Is it different than our current machine type? */
#if _M_AMD64
if (NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64)
#else
if (NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
#endif
{
/* Is it x86 (implying we are x64) ? */
if (NtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
{
/* Return special error code */
Status = STATUS_INVALID_IMAGE_WIN_32;
}
else if (NtHeaders->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
{
/* Otherwise, it's x64 but we are x86 */
Status = STATUS_INVALID_IMAGE_WIN_64;
}
else
{
/* Or it's ARM or something... */
Status = STATUS_INVALID_IMAGE_FORMAT;
}
/* Return with the distinguished error code */
goto Quickie;
}
}
/* Check if we should validate the subsystem */
if (Flags & BL_LOAD_PE_IMG_CHECK_SUBSYSTEM)
{
/* It must be a Windows boot Application */
if (NtHeaders->OptionalHeader.Subsystem !=
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
{
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
}
/* Check if we should validate the /INTEGRITYCHECK flag */
if (Flags & BL_LOAD_PE_IMG_CHECK_FORCED_INTEGRITY)
{
/* Check if it's there */
if (!(NtHeaders->OptionalHeader.DllCharacteristics &
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY))
{
/* Nope, fail otherwise */
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
}
/* Check if we should compute the image hash */
if ((Flags & BL_LOAD_PE_IMG_COMPUTE_HASH) || (Hash))
{
EfiPrintf(L"No hash support\r\n");
}
/* Read the current base address, if any */
VirtualAddress = *ImageBase;
/* Get the virtual size of the image */
VirtualSize = NtHeaders->OptionalHeader.SizeOfImage;
/* Safely align the virtual size to a page */
Status = RtlULongLongAdd(VirtualSize,
PAGE_SIZE - 1,
&VirtualSize);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
VirtualSize = ALIGN_DOWN_BY(VirtualSize, PAGE_SIZE);
/* Make sure the image isn't larger than 4GB */
if (VirtualSize > ULONG_MAX)
{
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Check if we have a buffer already */
if (Flags & BL_LOAD_IMG_EXISTING_BUFFER)
{
/* Check if it's too small */
if (*ImageSize < VirtualSize)
{
/* Fail, letting the caller know how big to make it */
*ImageSize = VirtualSize;
Status = STATUS_BUFFER_TOO_SMALL;
}
}
else
{
/* Allocate the buffer with the flags and type the caller wants */
Status = BlImgAllocateImageBuffer(&VirtualAddress,
MemoryType,
VirtualSize,
Flags);
}
/* Bail out if allocation failed, or existing buffer is too small */
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Read the size of the headers */
HeaderSize = NtHeaders->OptionalHeader.SizeOfHeaders;
if (VirtualSize < HeaderSize)
{
/* Bail out if they're bigger than the image! */
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Now read the header into the buffer */
Status = ImgpReadAtFileOffset(LocalFile, HeaderSize, 0, VirtualAddress, NULL);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Get the NT headers of the file */
Status = RtlImageNtHeaderEx(0, VirtualAddress, HeaderSize, &NtHeaders);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
First = FALSE;
/* Record how many sections we have */
SectionCount = NtHeaders->FileHeader.NumberOfSections;
/* Capture the current checksum and reset it */
CheckSum = NtHeaders->OptionalHeader.CheckSum;
NtHeaders->OptionalHeader.CheckSum = 0;
/* Calculate the checksum of the header, and restore the original one */
PartialSum = BlUtlCheckSum(0,
VirtualAddress,
HeaderSize,
BL_UTL_CHECKSUM_COMPLEMENT |
BL_UTL_CHECKSUM_USHORT_BUFFER);
NtHeaders->OptionalHeader.CheckSum = CheckSum;
/* Record our current position (right after the headers) */
EndOfHeaders = (ULONG_PTR)VirtualAddress + HeaderSize;
EfiPrintf(L"here\r\n");
/* Get the first section and iterate through each one */
Section = IMAGE_FIRST_SECTION(NtHeaders);
for (i = 0; i < SectionCount; i++)
{
/* Compute where this section starts */
SectionStart = (ULONG_PTR)VirtualAddress + Section->VirtualAddress;
/* Make sure that the section fits within the image */
if ((VirtualSize < Section->VirtualAddress) ||
((PVOID)SectionStart < VirtualAddress))
{
EfiPrintf(L"fail 1\r\n");
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Check if there's slack space between header end and the section */
if (!(First) && (EndOfHeaders < SectionStart))
{
/* Zero it out */
Slack = SectionStart - EndOfHeaders;
RtlZeroMemory((PVOID)EndOfHeaders, Slack);
}
/* Get the section virtual size and the raw size */
SectionSize = Section->Misc.VirtualSize;
RawSize = Section->SizeOfRawData;
/* Safely align the raw size by 2 */
Status = RtlULongAdd(RawSize, 1, &AlignSize);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
AlignSize = ALIGN_DOWN_BY(AlignSize, 2);
/* IF we don't have a virtual size, use the raw size */
if (!SectionSize)
{
SectionSize = RawSize;
}
/* If we don't have raw data, ignore the raw size */
if (!Section->PointerToRawData)
{
RawSize = 0;
}
else if (SectionSize < RawSize)
{
/* And if the virtual size is smaller, use it as the final size */
RawSize = SectionSize;
}
/* Make sure that the section doesn't overflow in memory */
Status = RtlULongAdd(Section->VirtualAddress,
SectionSize,
&SectionEnd);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"fail 21\r\n");
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Make sure that it fits within the image */
if (VirtualSize < SectionEnd)
{
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Make sure it doesn't overflow on disk */
Status = RtlULongAdd(Section->VirtualAddress,
AlignSize,
&SectionEnd);
if (!NT_SUCCESS(Status))
{
EfiPrintf(L"fail 31\r\n");
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* Make sure that it fits within the disk image as well */
if (VirtualSize < SectionEnd)
{
Status = STATUS_INVALID_IMAGE_FORMAT;
goto Quickie;
}
/* So does this section have a valid size after all? */
if (RawSize)
{
/* Are we in the first iteration? */
if (!First)
{
/* FUCK YOU BINUTILS */
if ((*(PULONG)&Section->Name == 'ler.') && (RawSize < AlignSize))
{
/* Piece of shit won't build relocations when you tell it to,
* either by using --emit-relocs or --dynamicbase. People online
* have found out that by using -pie-executable you can get this
* to happen, but then it turns out that the .reloc section is
* incorrectly sized, and results in a corrupt PE. However, they
* still compute the checksum using the correct value. What idiots.
*/
WorkaroundForBinutils = AlignSize - RawSize;
AlignSize -= WorkaroundForBinutils;
}
/* Yes, read the section data */
Status = ImgpReadAtFileOffset(LocalFile,
AlignSize,
Section->PointerToRawData,
(PVOID)SectionStart,
NULL);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Update our current offset */
Offset = AlignSize + Section->PointerToRawData;
/* Update the checksum to include this section */
PartialSum = BlUtlCheckSum(PartialSum,
(PUCHAR)SectionStart,
AlignSize,
BL_UTL_CHECKSUM_COMPLEMENT |
BL_UTL_CHECKSUM_USHORT_BUFFER);
AlignSize += WorkaroundForBinutils;
}
}
/* Are we in the first iteration? */
if (!First)
{
/* Is there space at the end of the section? */
if (RawSize < SectionSize)
{
/* Zero out the slack space that's there */
Slack = SectionSize - RawSize;
RtlZeroMemory((PVOID)(SectionStart + RawSize), Slack);
}
/* Update our tail offset */
EndOfHeaders = SectionStart + SectionSize;
}
/* Move to the next section */
Section++;
}
/* Are we in the first iteration? */
if (!First)
{
/* Go to the end of the file */
SectionStart = (ULONG_PTR)VirtualAddress + VirtualSize;
/* Is there still some slack space left? */
if (EndOfHeaders < SectionStart)
{
/* Zero it out */
Slack = SectionStart - EndOfHeaders;
RtlZeroMemory((PVOID)EndOfHeaders, Slack);
}
}
/* Did the first iteration complete OK? */
if ((NT_SUCCESS(Status)) && !(First))
{
/* Check how many non-image bytes are left in the file */
RemainingLength = FileSize - Offset;
while (RemainingLength)
{
/* See if the read will fit into our local buffer */
if (RemainingLength >= sizeof(LocalBuffer))
{
/* Nope, cap it */
BytesRead = sizeof(LocalBuffer);
}
else
{
/* Yes, but there's less to read */
BytesRead = RemainingLength;
}
/* Read 1024 bytes into the local buffer */
Status = ImgpReadAtFileOffset(LocalFile,
BytesRead,
Offset,
LocalBuffer,
&BytesRead);
if (!(NT_SUCCESS(Status)) || !(BytesRead))
{
Status = STATUS_FILE_INVALID;
goto Quickie;
}
/* Advance the offset and reduce the length */
RemainingLength -= BytesRead;
Offset += BytesRead;
/* Compute the checksum of this leftover space */
PartialSum = BlUtlCheckSum(PartialSum,
LocalBuffer,
BytesRead,
BL_UTL_CHECKSUM_COMPLEMENT |
BL_UTL_CHECKSUM_USHORT_BUFFER);
}
/* Finally, calculate the final checksum and compare it */
FinalSum = FileSize + PartialSum + WorkaroundForBinutils;
if ((FinalSum != CheckSum) && (PartialSum == 0xFFFF))
{
/* It hit overflow, so set it to the file size */
FinalSum = FileSize;
}
/* If the checksum doesn't match, and caller is enforcing, bail out */
if ((FinalSum != CheckSum) &&
!(Flags & BL_LOAD_PE_IMG_IGNORE_CHECKSUM_MISMATCH))
{
Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
goto Quickie;
}
}
/* Check if the .rsrc section should be checked with the filename */
if (Flags & BL_LOAD_PE_IMG_VALIDATE_ORIGINAL_FILENAME)
{
EfiPrintf(L"Not yet supported\r\n");
Status = 0xC0430007; // STATUS_SECUREBOOT_FILE_REPLACED
goto Quickie;
}
/* Check if we should relocate */
if (!(Flags & BL_LOAD_PE_IMG_SKIP_RELOCATIONS))
{
/* Check if we loaded at a different address */
PreferredBase = (PVOID)NtHeaders->OptionalHeader.ImageBase;
if (VirtualAddress != PreferredBase)
{
/* Yep -- do relocations */
Status = LdrRelocateImage(VirtualAddress,
"Boot Environment Library",
STATUS_SUCCESS,
STATUS_UNSUCCESSFUL,
STATUS_INVALID_IMAGE_FORMAT);
if (!NT_SUCCESS(Status))
{
/* That's bad */
goto Quickie;
}
}
}
#if BL_TPM_SUPPORT
/* Check if the image hash was valid */
if (!ImageHashValid)
{
/* Send a TPM/SI notification without a context */
BlEnNotifyEvent(0x10000002, NULL);
}
/* Now send a TPM/SI notification with the hash of the loaded image */
BlMmTranslateVirtualAddress(VirtualAddress, &Context.ImageBase);
Context.HashAlgorithm = HashAlgorithm;
Context.HashSize = HashSize;
Context.FileName = ImageFile->FileName;
Context.ImageSize = VirtualSize;
Context.HashValid = ImageHashValid;
Context.Hash = Hash;
BlEnNotifyEvent(0x10000002, &Context);
#endif
/* Return the loaded address to the caller */
*ImageBase = VirtualAddress;
/* If the caller wanted the image size, return it too */
if (ImageSize)
{
*ImageSize = VirtualSize;
}
Quickie:
/* Check if we computed the image hash OK */
if (ImageHashValid)
{
/* Then free the information that ImgpValidateImageHash set up */
EfiPrintf(L"leadking trusted boot\r\n");
//ImgpDestroyTrustedBootInformation(&TrustedBootInformation);
}
/* Check if we had a hash buffer */
if (HashBuffer)
{
/* Free it */
EfiPrintf(L"Leadking hash: %p\r\n", HashBuffer);
//MmPapFreePages(HashBuffer, TRUE);
}
/* Check if we have a certificate diretory */
if ((CertBuffer) && (CertDirectory))
{
/* Free it */
BlImgUnallocateImageBuffer(CertBuffer, CertDirectory->Size, 0);
}
/* Check if we had an image buffer allocated */
if ((ImageBuffer) && (FileSize))
{
/* Free it */
BlImgUnallocateImageBuffer(ImageBuffer, FileSize, 0);
}
/* Check if we had a local file handle */
if (LocalFile)
{
/* Close it */
ImgpCloseFile(LocalFile);
}
/* Check if this is the failure path */
if (!NT_SUCCESS(Status))
{
/* Check if we had started mapping in the image already */
if ((VirtualAddress) && !(Flags & BL_LOAD_PE_IMG_EXISTING_BUFFER))
{
/* Into a virtual buffer? */
if (Flags & BL_LOAD_PE_IMG_VIRTUAL_BUFFER)
{
/* Unmap and free it */
BlMmUnmapVirtualAddressEx(VirtualAddress, VirtualSize);
PhysicalAddress.QuadPart = (ULONG_PTR)VirtualAddress;
BlMmFreePhysicalPages(PhysicalAddress);
}
else
{
/* Into a physical buffer -- free it */
EfiPrintf(L"Leaking physical pages\r\n");
// MmPapFreePages(VirtualAddress, TRUE);
}
}
}
/* Return back to caller */
return Status;
}
NTSTATUS
BlImgLoadPEImageEx (
_In_ ULONG DeviceId,
_In_ BL_MEMORY_TYPE MemoryType,
_In_ PWCHAR Path,
_Out_ PVOID* ImageBase,
_Out_ PULONG ImageSize,
_Out_ PVOID Hash,
_In_ ULONG Flags
)
{
BL_IMG_FILE ImageFile;
NTSTATUS Status;
/* Initialize the image file structure */
ImageFile.Flags = 0;
ImageFile.FileName = NULL;
/* Check if the required parameter are missing */
if (!(ImageBase) || !(Path))
{
return STATUS_INVALID_PARAMETER;
}
/* If we are loading a pre-allocated image, make sure we have it */
if ((Flags & BL_LOAD_IMG_EXISTING_BUFFER) && (!(*ImageBase) || !(ImageSize)))
{
return STATUS_INVALID_PARAMETER;
}
/* Load the file from disk */
Status = ImgpOpenFile(DeviceId, Path, 0, &ImageFile);
if (NT_SUCCESS(Status))
{
/* If that worked, do the PE parsing */
Status = ImgpLoadPEImage(&ImageFile,
MemoryType,
ImageBase,
ImageSize,
Hash,
Flags);
}
/* Close the image file and return back to caller */
ImgpCloseFile(&ImageFile);
return Status;
}
NTSTATUS
BlImgLoadBootApplication (
_In_ PBL_LOADED_APPLICATION_ENTRY BootEntry,
_Out_ PULONG AppHandle
)
{
NTSTATUS Status;
PULONGLONG AllowedList;
ULONGLONG AllowedCount;
ULONG i, DeviceId, ImageSize, Flags, ListSize;
LARGE_INTEGER Frequency;
PVOID UnlockCode, ImageBase;
PBL_DEVICE_DESCRIPTOR Device, BitLockerDevice;
PWCHAR Path;
PBL_APPLICATION_ENTRY AppEntry;
PBL_IMG_FILE ImageFile;
BOOLEAN DisableIntegrity, TestSigning;
UCHAR Hash[64];
PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
/* Initialize all locals */
BitLockerDevice = NULL;
UnlockCode = NULL;
ImageFile = NULL;
DeviceId = -1;
Device = NULL;
ImageAppEntry = NULL;
Path = NULL;
ImageSize = 0;
ImageBase = NULL;
/* Check for "allowed in-memory settings" */
Status = BlpGetBootOptionIntegerList(BootEntry->BcdData,
BcdLibraryIntegerList_AllowedInMemorySettings,
&AllowedList,
&AllowedCount,
TRUE);
if (Status == STATUS_SUCCESS)
{
/* Loop through the list of allowed setting */
for (i = 0; i < AllowedCount; i++)
{
/* Find the super undocumented one */
if (AllowedList[i] == BcdLibraryInteger_UndocumentedMagic)
{
/* If it's present, append the current perf frequence to it */
BlTimeQueryPerformanceCounter(&Frequency);
BlAppendBootOptionInteger(BootEntry,
BcdLibraryInteger_UndocumentedMagic,
Frequency.QuadPart);
}
}
}
#if BL_BITLOCKER_SUPPORT
/* Do bitlocker stuff */
Status = BlFveSecureBootUnlockBootDevice(BootEntry, &BitLockerDevice, &UnlockCode);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
#endif
/* Get the device on which this application is on*/
Status = BlGetBootOptionDevice(BootEntry->BcdData,
BcdLibraryDevice_ApplicationDevice,
&Device,
NULL);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Get the path of the application */
Status = BlGetBootOptionString(BootEntry->BcdData,
BcdLibraryString_ApplicationPath,
&Path);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Open the device */
Status = BlpDeviceOpen(Device,
BL_DEVICE_READ_ACCESS,
0,
&DeviceId);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
/* Check for integrity BCD options */
BlImgQueryCodeIntegrityBootOptions(BootEntry,
&DisableIntegrity,
&TestSigning);
#if BL_TPM_SUPPORT
RtlZeroMemory(&Context, sizeof(Context);
Context.BootEntry = BootEntry;
BlEnNotifyEvent(0x10000003, &Context);
#endif
/* Enable signing and hashing checks if integrity is enabled */
Flags = 0;
if (!DisableIntegrity)
{
Flags = 0x8070;
}
/* Now call the PE loader to load the image */
Status = BlImgLoadPEImageEx(DeviceId,
BlLoaderMemory,
Path,
&ImageBase,
&ImageSize,
Hash,
Flags);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
#if BL_KD_SUPPORT
/* Check if we should notify the debugger of load */
if (BdDebugTransitions)
{
/* Initialize it */
BdForceDebug = 1;
Status = BlBdInitialize();
if (NT_SUCCESS(Status))
{
/* Check if it's enabled */
if (BlBdDebuggerEnabled())
{
/* Send it an image load notification */
BdDebuggerNotPresent = FALSE;
RtlInitUnicodeString(&PathString, Path);
BlBdLoadImageSymbols(&PathString, ImageBase);
}
}
}
#endif
#if BL_BITLOCKER_SUPPORT
/* Do bitlocker stuff */
Status = BlSecureBootCheckPolicyOnFveDevice(BitLockerDevice);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
#endif
#if BL_BITLOCKER_SUPPORT
/* Do bitlocker stuff */
Status = BlFveSecureBootCheckpointBootApp(BootEntry, BitLockerDevice, Hash, UnlockCode);
if (!NT_SUCCESS(Status))
{
goto Quickie;
}
#endif
/* Get the BCD option size */
ListSize = BlGetBootOptionListSize(BootEntry->BcdData);
/* Allocate an entry with all the BCD options */
AppEntry = BlMmAllocateHeap(ListSize + sizeof(*AppEntry));
if (!AppEntry)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Zero it out */
RtlZeroMemory(AppEntry, sizeof(*AppEntry));
/* Initialize it */
strcpy(AppEntry->Signature, "BTAPENT");
AppEntry->Guid = BootEntry->Guid;
AppEntry->Flags = BootEntry->Flags;
/* Copy the BCD options */
RtlCopyMemory(&AppEntry->BcdData, BootEntry->BcdData, ListSize);
/* Allocate the image entry */
ImageAppEntry = BlMmAllocateHeap(sizeof(*ImageAppEntry));
if (!ImageAppEntry)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Initialize it */
ImageAppEntry->ImageBase = ImageBase;
ImageAppEntry->ImageSize = ImageSize;
ImageAppEntry->AppEntry = AppEntry;
/* Check if this is the first entry */
if (!IapTableEntries)
{
/* Allocate two entries */
IapAllocatedTableEntries = 0;
IapTableEntries = 2;
IapImageTable = BlMmAllocateHeap(IapTableEntries * sizeof(PVOID));
if (!IapImageTable)
{
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* Zero out the entries for now */
RtlZeroMemory(IapImageTable, IapTableEntries * sizeof(PVOID));
}
/* Set this entry into the table */
Status = BlTblSetEntry(&IapImageTable,
&IapTableEntries,
ImageAppEntry,
AppHandle,
TblDoNotPurgeEntry);
Quickie:
/* Is the device open? Close it if so */
if (DeviceId != 1)
{
BlDeviceClose(DeviceId);
}
/* Is there an allocated device? Free it */
if (Device)
{
BlMmFreeHeap(Device);
}
/* Is there an allocated path? Free it */
if (Path)
{
BlMmFreeHeap(Path);
}
/* Is there a bitlocker device? Free it */
if (BitLockerDevice)
{
BlMmFreeHeap(BitLockerDevice);
}
/* Is there a bitlocker unlock code? Free it */
if (UnlockCode)
{
BlMmFreeHeap(UnlockCode);
}
/* Did we succeed in creating an entry? */
if (NT_SUCCESS(Status))
{
/* Remember there's one more in the table */
IapAllocatedTableEntries++;
/* Return success */
return Status;
}
/* Did we load an image after all? */
if (ImageBase)
{
/* Unload it */
BlImgUnLoadImage(ImageBase, ImageSize, 0);
}
/* Did we allocate an app entry? Free it */
if (AppEntry)
{
BlMmFreeHeap(AppEntry);
}
/* Do we have an image file entry? Free it */
if (ImageFile)
{
BlMmFreeHeap(ImageFile);
}
/* Do we no longer have a single entry in the table? */
if (!(IapAllocatedTableEntries) && (IapImageTable))
{
/* Free and destroy the table */
BlMmFreeHeap(IapImageTable);
IapTableEntries = 0;
IapImageTable = NULL;
}
/* Return the failure code */
return Status;
}
NTSTATUS
BlpPdParseReturnArguments (
_In_ PBL_RETURN_ARGUMENTS ReturnArguments
)
{
/* Check if any custom data was returned */
if (ReturnArguments->DataPage == 0)
{
/* Nope, nothing to do */
return STATUS_SUCCESS;
}
/* Yes, we have to parse it */
EfiPrintf(L"Return arguments not supported\r\n");
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
ImgArchEfiStartBootApplication (
_In_ PBL_APPLICATION_ENTRY AppEntry,
_In_ PVOID ImageBase,
_In_ ULONG ImageSize,
_In_ PBL_RETURN_ARGUMENTS ReturnArguments
)
{
/* Not yet implemented. This is the last step! */
EfiPrintf(L"EFI APPLICATION START!!!\r\n");
EfiStall(100000000);
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
BlImgStartBootApplication (
_In_ ULONG AppHandle,
_Inout_opt_ PBL_RETURN_ARGUMENTS ReturnArguments
)
{
PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
BL_RETURN_ARGUMENTS LocalReturnArgs;
PBL_FILE_SYSTEM_ENTRY FileSystem;
PLIST_ENTRY NextEntry, ListHead;
NTSTATUS Status;
/* Check if we don't have an argument structure */
if (!ReturnArguments)
{
/* Initialize a local copy and use it instead */
LocalReturnArgs.Version = BL_RETURN_ARGUMENTS_VERSION;
LocalReturnArgs.Status = STATUS_SUCCESS;
LocalReturnArgs.Flags = 0;
LocalReturnArgs.DataPage = 0;
LocalReturnArgs.DataSize = 0;
ReturnArguments = &LocalReturnArgs;
}
/* Make sure the handle index is valid */
if (IapTableEntries <= AppHandle)
{
return STATUS_INVALID_PARAMETER;
}
/* Get the entry for this handle, making sure it exists */
ImageAppEntry = IapImageTable[AppHandle];
if (!ImageAppEntry)
{
return STATUS_INVALID_PARAMETER;
}
/* Loop the registered file systems */
ListHead = &RegisteredFileSystems;
NextEntry = RegisteredFileSystems.Flink;
while (NextEntry != ListHead)
{
/* Get the filesystem entry */
FileSystem = CONTAINING_RECORD(NextEntry,
BL_FILE_SYSTEM_ENTRY,
ListEntry);
/* See if it has a purge callback */
if (FileSystem->PurgeCallback)
{
/* Call it */
FileSystem->PurgeCallback();
}
/* Move to the next entry */
NextEntry = NextEntry->Flink;
}
/* TODO -- flush the block I/O cache too */
//BlockIoPurgeCache();
/* Call into EFI land to start the boot application */
Status = ImgArchEfiStartBootApplication(ImageAppEntry->AppEntry,
ImageAppEntry->ImageBase,
ImageAppEntry->ImageSize,
ReturnArguments);
/* Parse any arguments we got on the way back */
BlpPdParseReturnArguments(ReturnArguments);
#if BL_BITLOCKER_SUPPORT
/* Bitlocker stuff */
FvebpCheckAllPartitions(TRUE);
#endif
#if BL_TPM_SUPPORT
/* Notify a TPM/SI event */
BlEnNotifyEvent(0x10000005, NULL);
#endif
/* Reset the display */
BlpDisplayReinitialize();
/* TODO -- reset ETW */
//BlpLogInitialize();
/* All done */
return Status;
}
NTSTATUS
BlImgUnloadBootApplication (
_In_ ULONG AppHandle
)
{
PBL_IMAGE_APPLICATION_ENTRY ImageAppEntry;
NTSTATUS Status;
/* Make sure the handle index is valid */
if (IapTableEntries <= AppHandle)
{
return STATUS_INVALID_PARAMETER;
}
/* Get the entry for this handle, making sure it exists */
ImageAppEntry = IapImageTable[AppHandle];
if (!ImageAppEntry)
{
return STATUS_INVALID_PARAMETER;
}
/* Unload the image */
Status = BlImgUnLoadImage(ImageAppEntry->ImageBase,
ImageAppEntry->ImageSize,
0);
if (NT_SUCCESS(Status))
{
/* Normalize the success code */
Status = STATUS_SUCCESS;
}
else
{
/* Normalize the failure code */
Status = STATUS_MEMORY_NOT_ALLOCATED;
}
/* Free the entry and the image entry as well */
BlMmFreeHeap(ImageAppEntry->AppEntry);
BlMmFreeHeap(ImageAppEntry);
/* Clear the handle */
IapImageTable[AppHandle] = NULL;
/* Free one entry */
if (!(--IapAllocatedTableEntries))
{
/* There are no more, so get rid of the table itself */
BlMmFreeHeap(IapImageTable);
IapImageTable = NULL;
IapTableEntries = 0;
}
/* All good */
return Status;
}