mirror of
https://github.com/systemd/systemd.git
synced 2025-03-21 02:50:18 +03:00
efi: teach PE parsing support for ".profile" sections
This adds helpers for: 1. Returning the PE section table of open PE files or memory 2. Scanning PE section tables for the sections that belong to a specific profile
This commit is contained in:
parent
52dd7c8131
commit
f4e081051d
@ -106,19 +106,6 @@ typedef struct PeFileHeader {
|
||||
PeOptionalHeader OptionalHeader;
|
||||
} _packed_ PeFileHeader;
|
||||
|
||||
typedef struct PeSectionHeader {
|
||||
uint8_t Name[8];
|
||||
uint32_t VirtualSize;
|
||||
uint32_t VirtualAddress;
|
||||
uint32_t SizeOfRawData;
|
||||
uint32_t PointerToRawData;
|
||||
uint32_t PointerToRelocations;
|
||||
uint32_t PointerToLinenumbers;
|
||||
uint16_t NumberOfRelocations;
|
||||
uint16_t NumberOfLinenumbers;
|
||||
uint32_t Characteristics;
|
||||
} _packed_ PeSectionHeader;
|
||||
|
||||
#define SECTION_TABLE_BYTES_MAX (16U * 1024U * 1024U)
|
||||
|
||||
static bool verify_dos(const DosFileHeader *dos) {
|
||||
@ -188,7 +175,7 @@ static void pe_locate_sections(
|
||||
|
||||
/* Searches for the sections listed in 'sections[]' within the section table. Validates the resulted
|
||||
* data. If 'validate_base' is non-zero also takes base offset when loaded into memory into account for
|
||||
* qchecking for overflows. */
|
||||
* checking for overflows. */
|
||||
|
||||
for (size_t i = 0; section_names[i]; i++)
|
||||
FOREACH_ARRAY(j, section_table, n_section_table) {
|
||||
@ -313,31 +300,49 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address, size_t
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS pe_section_table_from_base(
|
||||
const void *base,
|
||||
const PeSectionHeader **ret_section_table,
|
||||
size_t *ret_n_section_table) {
|
||||
|
||||
assert(base);
|
||||
assert(ret_section_table);
|
||||
assert(ret_n_section_table);
|
||||
|
||||
const DosFileHeader *dos = (const DosFileHeader*) base;
|
||||
if (!verify_dos(dos))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
const PeFileHeader *pe = (const PeFileHeader*) ((const uint8_t*) base + dos->ExeHeader);
|
||||
if (!verify_pe(dos, pe, /* allow_compatibility= */ false))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
*ret_section_table = (const PeSectionHeader*) ((const uint8_t*) base + section_table_offset(dos, pe));
|
||||
*ret_n_section_table = pe->FileHeader.NumberOfSections;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS pe_memory_locate_sections(
|
||||
const void *base,
|
||||
const char *const section_names[],
|
||||
PeSectionVector sections[]) {
|
||||
|
||||
const DosFileHeader *dos;
|
||||
const PeFileHeader *pe;
|
||||
size_t offset;
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(base);
|
||||
assert(section_names);
|
||||
assert(sections);
|
||||
|
||||
dos = (const DosFileHeader *) base;
|
||||
if (!verify_dos(dos))
|
||||
return EFI_LOAD_ERROR;
|
||||
const PeSectionHeader *section_table;
|
||||
size_t n_section_table;
|
||||
err = pe_section_table_from_base(base, §ion_table, &n_section_table);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader);
|
||||
if (!verify_pe(dos, pe, /* allow_compatibility= */ false))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
offset = section_table_offset(dos, pe);
|
||||
pe_locate_sections(
|
||||
(const PeSectionHeader *) ((const uint8_t *) base + offset),
|
||||
pe->FileHeader.NumberOfSections,
|
||||
section_table,
|
||||
n_section_table,
|
||||
section_names,
|
||||
PTR_TO_SIZE(base),
|
||||
sections);
|
||||
@ -345,27 +350,19 @@ EFI_STATUS pe_memory_locate_sections(
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS pe_file_locate_sections(
|
||||
EFI_FILE *dir,
|
||||
const char16_t *path,
|
||||
const char *const section_names[],
|
||||
PeSectionVector sections[]) {
|
||||
_cleanup_free_ PeSectionHeader *section_table = NULL;
|
||||
_cleanup_(file_closep) EFI_FILE *handle = NULL;
|
||||
DosFileHeader dos;
|
||||
PeFileHeader pe;
|
||||
size_t len, section_table_len;
|
||||
EFI_STATUS pe_section_table_from_file(
|
||||
EFI_FILE *handle,
|
||||
PeSectionHeader **ret_section_table,
|
||||
size_t *ret_n_section_table) {
|
||||
|
||||
EFI_STATUS err;
|
||||
size_t len;
|
||||
|
||||
assert(dir);
|
||||
assert(path);
|
||||
assert(section_names);
|
||||
assert(sections);
|
||||
|
||||
err = dir->Open(dir, &handle, (char16_t *) path, EFI_FILE_MODE_READ, 0ULL);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
assert(handle);
|
||||
assert(ret_section_table);
|
||||
assert(ret_n_section_table);
|
||||
|
||||
DosFileHeader dos;
|
||||
len = sizeof(dos);
|
||||
err = handle->Read(handle, &len, &dos);
|
||||
if (err != EFI_SUCCESS)
|
||||
@ -377,6 +374,7 @@ EFI_STATUS pe_file_locate_sections(
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
PeFileHeader pe;
|
||||
len = sizeof(pe);
|
||||
err = handle->Read(handle, &len, &pe);
|
||||
if (err != EFI_SUCCESS)
|
||||
@ -388,10 +386,11 @@ EFI_STATUS pe_file_locate_sections(
|
||||
if ((size_t) pe.FileHeader.NumberOfSections > SIZE_MAX / sizeof(PeSectionHeader))
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
REENABLE_WARNING;
|
||||
section_table_len = (size_t) pe.FileHeader.NumberOfSections * sizeof(PeSectionHeader);
|
||||
if (section_table_len > SECTION_TABLE_BYTES_MAX)
|
||||
size_t n_section_table = (size_t) pe.FileHeader.NumberOfSections;
|
||||
if (n_section_table * sizeof(PeSectionHeader) > SECTION_TABLE_BYTES_MAX)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
section_table = xmalloc(section_table_len);
|
||||
|
||||
_cleanup_free_ PeSectionHeader *section_table = xnew(PeSectionHeader, n_section_table);
|
||||
if (!section_table)
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
|
||||
@ -399,19 +398,138 @@ EFI_STATUS pe_file_locate_sections(
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
len = section_table_len;
|
||||
len = n_section_table * sizeof(PeSectionHeader);
|
||||
err = handle->Read(handle, &len, section_table);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
if (len != section_table_len)
|
||||
if (len != n_section_table * sizeof(PeSectionHeader))
|
||||
return EFI_LOAD_ERROR;
|
||||
|
||||
*ret_section_table = TAKE_PTR(section_table);
|
||||
*ret_n_section_table = n_section_table;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS pe_file_locate_sections(
|
||||
EFI_FILE *dir,
|
||||
const char16_t *path,
|
||||
const char* const section_names[],
|
||||
PeSectionVector sections[]) {
|
||||
|
||||
_cleanup_free_ PeSectionHeader *section_table = NULL;
|
||||
_cleanup_(file_closep) EFI_FILE *handle = NULL;
|
||||
size_t n_section_table;
|
||||
EFI_STATUS err;
|
||||
|
||||
assert(dir);
|
||||
assert(path);
|
||||
assert(section_names);
|
||||
assert(sections);
|
||||
|
||||
err = dir->Open(dir, &handle, (char16_t *) path, EFI_FILE_MODE_READ, 0ULL);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
err = pe_section_table_from_file(handle, §ion_table, &n_section_table);
|
||||
if (err != EFI_SUCCESS)
|
||||
return err;
|
||||
|
||||
pe_locate_sections(
|
||||
section_table,
|
||||
pe.FileHeader.NumberOfSections,
|
||||
n_section_table,
|
||||
section_names,
|
||||
/* validate_base= */ 0, /* don't validate base */
|
||||
sections);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
static const PeSectionHeader* pe_section_table_find_profile_start(
|
||||
const PeSectionHeader *section_table,
|
||||
size_t n_section_table,
|
||||
unsigned profile) {
|
||||
|
||||
assert(section_table || n_section_table == 0);
|
||||
|
||||
if (profile == UINT_MAX) /* base profile? that starts at the beginning */
|
||||
return section_table;
|
||||
|
||||
unsigned current_profile = UINT_MAX;
|
||||
FOREACH_ARRAY(p, section_table, n_section_table) {
|
||||
|
||||
if (!pe_section_name_equal((const char*) p->Name, ".profile"))
|
||||
continue;
|
||||
|
||||
if (current_profile == UINT_MAX)
|
||||
current_profile = 0;
|
||||
else
|
||||
current_profile++;
|
||||
|
||||
if (current_profile == profile) /* Found our profile! */
|
||||
return p;
|
||||
}
|
||||
|
||||
/* We reached the end of the table? Then this section does not exist */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t pe_section_table_find_profile_length(
|
||||
const PeSectionHeader *section_table,
|
||||
size_t n_section_table,
|
||||
const PeSectionHeader *start,
|
||||
unsigned profile) {
|
||||
|
||||
assert(section_table);
|
||||
assert(n_section_table > 0);
|
||||
assert(start >= section_table);
|
||||
assert(start < section_table + n_section_table);
|
||||
|
||||
/* Look for the next .profile (or the end of the table), this is where the the sections for this
|
||||
* profile end. The base profile does not start with a .profile, the others do, hence conditionally
|
||||
* skip over the first entry. */
|
||||
const PeSectionHeader *e;
|
||||
if (profile == UINT_MAX) /* Base profile */
|
||||
e = start;
|
||||
else {
|
||||
assert(pe_section_name_equal((const char *) start->Name, ".profile"));
|
||||
e = start + 1;
|
||||
}
|
||||
|
||||
for (; e < section_table + n_section_table; e++)
|
||||
if (pe_section_name_equal((const char*) e->Name, ".profile"))
|
||||
return e - start;
|
||||
|
||||
return (section_table + n_section_table) - start;
|
||||
}
|
||||
|
||||
EFI_STATUS pe_locate_profile_sections(
|
||||
const PeSectionHeader section_table[],
|
||||
size_t n_section_table,
|
||||
const char* const section_names[],
|
||||
unsigned profile,
|
||||
size_t validate_base,
|
||||
PeSectionVector sections[]) {
|
||||
|
||||
assert(section_table || n_section_table == 0);
|
||||
assert(section_names);
|
||||
assert(sections);
|
||||
|
||||
/* Now scan through the section table until we skipped over the right number of .profile sections */
|
||||
const PeSectionHeader *p = pe_section_table_find_profile_start(section_table, n_section_table, profile);
|
||||
if (!p)
|
||||
return EFI_NOT_FOUND;
|
||||
|
||||
/* Look for the next .profile (or the end of the table), this is where the the sections for this
|
||||
* profile end. */
|
||||
size_t n = pe_section_table_find_profile_length(section_table, n_section_table, p, profile);
|
||||
|
||||
/* And now parse everything between the start and end of our profile */
|
||||
pe_locate_sections(
|
||||
p,
|
||||
n,
|
||||
section_names,
|
||||
validate_base,
|
||||
sections);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
@ -3,6 +3,20 @@
|
||||
|
||||
#include "efi.h"
|
||||
|
||||
/* This is the actual PE format of the section header*/
|
||||
typedef struct PeSectionHeader {
|
||||
uint8_t Name[8];
|
||||
uint32_t VirtualSize;
|
||||
uint32_t VirtualAddress;
|
||||
uint32_t SizeOfRawData;
|
||||
uint32_t PointerToRawData;
|
||||
uint32_t PointerToRelocations;
|
||||
uint32_t PointerToLinenumbers;
|
||||
uint16_t NumberOfRelocations;
|
||||
uint16_t NumberOfLinenumbers;
|
||||
uint32_t Characteristics;
|
||||
} _packed_ PeSectionHeader;
|
||||
|
||||
/* This is a subset of the full PE section header structure, with validated values, and without
|
||||
* the noise. */
|
||||
typedef struct PeSectionVector {
|
||||
@ -15,6 +29,24 @@ static inline bool PE_SECTION_VECTOR_IS_SET(const PeSectionVector *v) {
|
||||
return v && v->size != 0;
|
||||
}
|
||||
|
||||
EFI_STATUS pe_section_table_from_base(
|
||||
const void *base,
|
||||
const PeSectionHeader **ret_section_table,
|
||||
size_t *ret_n_section_table);
|
||||
|
||||
EFI_STATUS pe_section_table_from_file(
|
||||
EFI_FILE *handle,
|
||||
PeSectionHeader **ret_section_table,
|
||||
size_t *ret_n_section_table);
|
||||
|
||||
EFI_STATUS pe_locate_profile_sections(
|
||||
const PeSectionHeader section_table[],
|
||||
size_t n_section_table,
|
||||
const char* const section_names[],
|
||||
unsigned profile,
|
||||
size_t validate_base,
|
||||
PeSectionVector sections[]);
|
||||
|
||||
EFI_STATUS pe_memory_locate_sections(
|
||||
const void *base,
|
||||
const char *const section_names[],
|
||||
|
Loading…
x
Reference in New Issue
Block a user