1
0
mirror of https://github.com/systemd/systemd.git synced 2025-08-25 13:49:55 +03:00

Merge pull request #28955 from poettering/generic-pe

unify two disparate PE executable parsers we currently have into one
This commit is contained in:
Luca Boccassi
2023-08-29 16:09:54 +01:00
committed by GitHub
6 changed files with 471 additions and 323 deletions

View File

@ -15,7 +15,7 @@
#include "fileio.h"
#include "find-esp.h"
#include "path-util.h"
#include "pe-header.h"
#include "pe-binary.h"
#include "pretty-print.h"
#include "recurse-dir.h"
#include "sort-util.h"
@ -757,92 +757,36 @@ static int find_sections(
char **ret_osrelease,
char **ret_cmdline) {
_cleanup_free_ struct PeSectionHeader *sections = NULL;
_cleanup_free_ char *osrelease = NULL, *cmdline = NULL;
ssize_t n;
_cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
_cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
_cleanup_free_ char *osrel = NULL, *cmdline = NULL;
_cleanup_free_ PeHeader *pe_header = NULL;
int r;
struct DosFileHeader dos;
n = pread(fd, &dos, sizeof(dos), 0);
if (n < 0)
return log_warning_errno(errno, "%s: Failed to read DOS header, ignoring: %m", path);
if (n != sizeof(dos))
return log_warning_errno(SYNTHETIC_ERRNO(EIO), "%s: Short read while reading DOS header, ignoring.", path);
assert(fd >= 0);
assert(path);
if (dos.Magic[0] != 'M' || dos.Magic[1] != 'Z')
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "%s: DOS executable magic missing, ignoring.", path);
r = pe_load_headers(fd, &dos_header, &pe_header);
if (r < 0)
return log_warning_errno(r, "Failed to parse PE file '%s': %m", path);
uint64_t start = unaligned_read_le32(&dos.ExeHeader);
r = pe_load_sections(fd, dos_header, pe_header, &sections);
if (r < 0)
return log_warning_errno(r, "Failed to parse PE sections of '%s': %m", path);
struct PeHeader pe;
n = pread(fd, &pe, sizeof(pe), start);
if (n < 0)
return log_warning_errno(errno, "%s: Failed to read PE header, ignoring: %m", path);
if (n != sizeof(pe))
return log_warning_errno(SYNTHETIC_ERRNO(EIO), "%s: Short read while reading PE header, ignoring.", path);
if (!pe_is_uki(pe_header, sections))
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "Parsed PE file '%s' is not a UKI.", path);
if (pe.Magic[0] != 'P' || pe.Magic[1] != 'E' || pe.Magic[2] != 0 || pe.Magic[3] != 0)
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "%s: PE executable magic missing, ignoring.", path);
r = pe_read_section_data(fd, pe_header, sections, ".osrel", PE_SECTION_SIZE_MAX, (void**) &osrel, NULL);
if (r < 0)
return log_warning_errno(r, "Failed to read .osrel section of '%s': %m", path);
size_t n_sections = unaligned_read_le16(&pe.FileHeader.NumberOfSections);
if (n_sections > 96)
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "%s: PE header has too many sections, ignoring.", path);
sections = new(struct PeSectionHeader, n_sections);
if (!sections)
return log_oom();
n = pread(fd, sections,
n_sections * sizeof(struct PeSectionHeader),
start + sizeof(pe) + unaligned_read_le16(&pe.FileHeader.SizeOfOptionalHeader));
if (n < 0)
return log_warning_errno(errno, "%s: Failed to read section data, ignoring: %m", path);
if ((size_t) n != n_sections * sizeof(struct PeSectionHeader))
return log_warning_errno(SYNTHETIC_ERRNO(EIO), "%s: Short read while reading sections, ignoring.", path);
for (size_t i = 0; i < n_sections; i++) {
_cleanup_free_ char *k = NULL;
uint32_t offset, size;
char **b;
if (strneq((char*) sections[i].Name, ".osrel", sizeof(sections[i].Name)))
b = &osrelease;
else if (strneq((char*) sections[i].Name, ".cmdline", sizeof(sections[i].Name)))
b = &cmdline;
else
continue;
if (*b)
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "%s: Duplicate section %s, ignoring.", path, sections[i].Name);
offset = unaligned_read_le32(&sections[i].PointerToRawData);
size = unaligned_read_le32(&sections[i].VirtualSize);
if (size > PE_SECTION_SIZE_MAX)
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "%s: Section %s too large, ignoring.", path, sections[i].Name);
k = new(char, size+1);
if (!k)
return log_oom();
n = pread(fd, k, size, offset);
if (n < 0)
return log_warning_errno(errno, "%s: Failed to read section payload, ignoring: %m", path);
if ((size_t) n != size)
return log_warning_errno(SYNTHETIC_ERRNO(EIO), "%s: Short read while reading section payload, ignoring:", path);
/* Allow one trailing NUL byte, but nothing more. */
if (size > 0 && memchr(k, 0, size - 1))
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "%s: Section contains embedded NUL byte, ignoring.", path);
k[size] = 0;
*b = TAKE_PTR(k);
}
if (!osrelease)
return log_warning_errno(SYNTHETIC_ERRNO(EBADMSG), "%s: Image lacks .osrel section, ignoring.", path);
r = pe_read_section_data(fd, pe_header, sections, ".cmdline", PE_SECTION_SIZE_MAX, (void**) &cmdline, NULL);
if (r < 0 && r != -ENXIO) /* cmdline is optional */
return log_warning_errno(r, "Failed to read .cmdline section of '%s': %m", path);
if (ret_osrelease)
*ret_osrelease = TAKE_PTR(osrelease);
*ret_osrelease = TAKE_PTR(osrel);
if (ret_cmdline)
*ret_cmdline = TAKE_PTR(cmdline);

View File

@ -6,19 +6,10 @@
#include "kernel-image.h"
#include "os-util.h"
#include "parse-util.h"
#include "pe-header.h"
#include "pe-binary.h"
#include "string-table.h"
#define MAX_SECTIONS 96
static const uint8_t dos_file_magic[2] = "MZ";
static const uint8_t pe_file_magic[4] = "PE\0\0";
static const uint8_t name_osrel[8] = ".osrel";
static const uint8_t name_linux[8] = ".linux";
static const uint8_t name_initrd[8] = ".initrd";
static const uint8_t name_cmdline[8] = ".cmdline";
static const uint8_t name_uname[8] = ".uname";
#define PE_SECTION_READ_MAX (16U*1024U)
static const char * const kernel_image_type_table[_KERNEL_IMAGE_TYPE_MAX] = {
[KERNEL_IMAGE_TYPE_UNKNOWN] = "unknown",
@ -28,172 +19,44 @@ static const char * const kernel_image_type_table[_KERNEL_IMAGE_TYPE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(kernel_image_type, KernelImageType);
static int pe_sections(FILE *f, struct PeSectionHeader **ret, size_t *ret_n) {
_cleanup_free_ struct PeSectionHeader *sections = NULL;
struct DosFileHeader dos;
struct PeHeader pe;
size_t scount;
uint64_t soff, items;
assert(f);
assert(ret);
assert(ret_n);
items = fread(&dos, 1, sizeof(dos), f);
if (items < sizeof(dos.Magic))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS magic (got %"PRIu64" of %zu bytes)",
items, sizeof(dos.Magic));
if (memcmp(dos.Magic, dos_file_magic, sizeof(dos_file_magic)) != 0)
goto no_sections;
if (items != sizeof(dos))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS header (got %"PRIu64" of %zu bytes)",
items, sizeof(dos));
if (fseek(f, le32toh(dos.ExeHeader), SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to PE header: %m");
items = fread(&pe, 1, sizeof(pe), f);
if (items != sizeof(pe))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read PE header.");
if (memcmp(pe.Magic, pe_file_magic, sizeof(pe_file_magic)) != 0)
goto no_sections;
soff = le32toh(dos.ExeHeader) + sizeof(pe) + le16toh(pe.FileHeader.SizeOfOptionalHeader);
if (fseek(f, soff, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to PE section headers: %m");
scount = le16toh(pe.FileHeader.NumberOfSections);
if (scount > MAX_SECTIONS)
goto no_sections;
sections = new(struct PeSectionHeader, scount);
if (!sections)
return log_oom();
items = fread(sections, sizeof(*sections), scount, f);
if (items != scount)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read PE section header.");
*ret = TAKE_PTR(sections);
*ret_n = scount;
return 0;
no_sections:
*ret = NULL;
*ret_n = 0;
return 0;
}
static bool find_pe_section(
struct PeSectionHeader *sections,
size_t scount,
const uint8_t *name,
size_t namelen,
size_t *ret) {
assert(sections || scount == 0);
assert(name || namelen == 0);
for (size_t s = 0; s < scount; s++)
if (memcmp_nn(sections[s].Name, sizeof(sections[s].Name), name, namelen) == 0) {
if (ret)
*ret = s;
return true;
}
return false;
}
static bool is_uki(struct PeSectionHeader *sections, size_t scount) {
assert(sections || scount == 0);
return
find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), NULL) &&
find_pe_section(sections, scount, name_linux, sizeof(name_linux), NULL) &&
find_pe_section(sections, scount, name_initrd, sizeof(name_initrd), NULL);
}
static int read_pe_section(
FILE *f,
struct PeSectionHeader *sections,
size_t scount,
const uint8_t *name,
size_t name_len,
void **ret,
size_t *ret_n) {
struct PeSectionHeader *section;
_cleanup_free_ void *data = NULL;
uint32_t size, bytes;
uint64_t soff;
size_t idx;
assert(f);
assert(sections || scount == 0);
assert(ret);
if (!find_pe_section(sections, scount, name, name_len, &idx)) {
*ret = NULL;
if (ret_n)
*ret_n = 0;
return 0;
}
section = sections + idx;
soff = le32toh(section->PointerToRawData);
size = le32toh(section->VirtualSize);
if (size > 16 * 1024)
return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "PE section too big.");
if (fseek(f, soff, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to PE section: %m");
data = malloc(size+1);
if (!data)
return log_oom();
((uint8_t*) data)[size] = 0; /* safety NUL byte */
bytes = fread(data, 1, size, f);
if (bytes != size)
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read PE section.");
*ret = TAKE_PTR(data);
if (ret_n)
*ret_n = size;
return 1;
}
static int uki_read_pretty_name(
FILE *f,
struct PeSectionHeader *sections,
size_t scount,
int fd,
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
char **ret) {
_cleanup_free_ char *pname = NULL, *name = NULL;
_cleanup_fclose_ FILE *s = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ void *osrel = NULL;
size_t osrel_size = 0;
size_t osrel_size;
int r;
assert(f);
assert(sections || scount == 0);
assert(fd >= 0);
assert(pe_header);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
assert(ret);
r = read_pe_section(f, sections, scount, name_osrel, sizeof(name_osrel), &osrel, &osrel_size);
if (r < 0)
return r;
if (r == 0) {
r = pe_read_section_data(
fd,
pe_header,
sections,
".osrel",
/* max_size=*/ PE_SECTION_READ_MAX,
&osrel,
&osrel_size);
if (r == -ENXIO) { /* Section not found */
*ret = NULL;
return 0;
}
s = fmemopen(osrel, osrel_size, "r");
if (!s)
f = fmemopen(osrel, osrel_size, "r");
if (!f)
return log_error_errno(errno, "Failed to open embedded os-release file: %m");
r = parse_env_file(s, NULL,
"PRETTY_NAME", &pname,
"NAME", &name);
r = parse_env_file(
f, NULL,
"PRETTY_NAME", &pname,
"NAME", &name);
if (r < 0)
return log_error_errno(r, "Failed to parse embedded os-release file: %m");
@ -214,9 +77,9 @@ static int uki_read_pretty_name(
}
static int inspect_uki(
FILE *f,
struct PeSectionHeader *sections,
size_t scount,
int fd,
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
char **ret_cmdline,
char **ret_uname,
char **ret_pretty_name) {
@ -224,23 +87,23 @@ static int inspect_uki(
_cleanup_free_ char *cmdline = NULL, *uname = NULL, *pname = NULL;
int r;
assert(f);
assert(sections || scount == 0);
assert(fd >= 0);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
if (ret_cmdline) {
r = read_pe_section(f, sections, scount, name_cmdline, sizeof(name_cmdline), (void**) &cmdline, NULL);
if (r < 0)
r = pe_read_section_data(fd, pe_header, sections, ".cmdline", PE_SECTION_READ_MAX, (void**) &cmdline, NULL);
if (r < 0 && r != -ENXIO) /* If the section doesn't exist, that's fine */
return r;
}
if (ret_uname) {
r = read_pe_section(f, sections, scount, name_uname, sizeof(name_uname), (void**) &uname, NULL);
if (r < 0)
r = pe_read_section_data(fd, pe_header, sections, ".uname", PE_SECTION_READ_MAX, (void**) &uname, NULL);
if (r < 0 && r != -ENXIO) /* If the section doesn't exist, that's fine */
return r;
}
if (ret_pretty_name) {
r = uki_read_pretty_name(f, sections, scount, &pname);
r = uki_read_pretty_name(fd, pe_header, sections, &pname);
if (r < 0)
return r;
}
@ -263,33 +126,51 @@ int inspect_kernel(
char **ret_uname,
char **ret_pretty_name) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ struct PeSectionHeader *sections = NULL;
size_t scount;
KernelImageType t;
_cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
_cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
KernelImageType t = KERNEL_IMAGE_TYPE_UNKNOWN;
_cleanup_free_ PeHeader *pe_header = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
assert(filename);
r = xfopenat(dir_fd, filename, "re", 0, &f);
if (r < 0)
return log_error_errno(r, "Failed to open kernel image file '%s': %m", filename);
fd = openat(dir_fd, filename, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return log_error_errno(errno, "Failed to open kernel image file '%s': %m", filename);
r = pe_sections(f, &sections, &scount);
r = pe_load_headers(fd, &dos_header, &pe_header);
if (r == -EBADMSG) /* not a valid PE file */
goto not_uki;
if (r < 0)
return r;
return log_error_errno(r, "Failed to parse kernel image file '%s': %m", filename);
if (!sections)
t = KERNEL_IMAGE_TYPE_UNKNOWN;
else if (is_uki(sections, scount)) {
t = KERNEL_IMAGE_TYPE_UKI;
r = inspect_uki(f, sections, scount, ret_cmdline, ret_uname, ret_pretty_name);
r = pe_load_sections(fd, dos_header, pe_header, &sections);
if (r == -EBADMSG) /* not a valid PE file */
goto not_uki;
if (r < 0)
return log_error_errno(r, "Failed to load PE sections from kernel image file '%s': %m", filename);
if (pe_is_uki(pe_header, sections)) {
r = inspect_uki(fd, pe_header, sections, ret_cmdline, ret_uname, ret_pretty_name);
if (r < 0)
return r;
t = KERNEL_IMAGE_TYPE_UKI;
goto done;
} else
t = KERNEL_IMAGE_TYPE_PE;
not_uki:
if (ret_cmdline)
*ret_cmdline = NULL;
if (ret_uname)
*ret_uname = NULL;
if (ret_pretty_name)
*ret_pretty_name = NULL;
done:
if (ret_type)
*ret_type = t;

View File

@ -131,6 +131,7 @@ shared_sources = files(
'password-quality-util-passwdqc.c',
'password-quality-util-pwquality.c',
'pcre2-util.c',
'pe-binary.c',
'pkcs11-util.c',
'pretty-print.c',
'ptyfwd.c',

238
src/shared/pe-binary.c Normal file
View File

@ -0,0 +1,238 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <unistd.h>
#include "alloc-util.h"
#include "log.h"
#include "pe-binary.h"
#include "string-util.h"
bool pe_header_is_64bit(const PeHeader *h) {
assert(h);
if (le16toh(h->optional.Magic) == UINT16_C(0x010B)) /* PE32 */
return false;
if (le16toh(h->optional.Magic) == UINT16_C(0x020B)) /* PE32+ */
return true;
assert_not_reached();
}
static size_t pe_header_size(const PeHeader *pe_header) {
assert(pe_header);
return offsetof(PeHeader, optional) + le16toh(pe_header->pe.SizeOfOptionalHeader);
}
const IMAGE_DATA_DIRECTORY *pe_header_get_data_directory(
const PeHeader *h,
size_t i) {
assert(h);
if (i >= le32toh(PE_HEADER_OPTIONAL_FIELD(h, NumberOfRvaAndSizes)))
return NULL;
return PE_HEADER_OPTIONAL_FIELD(h, DataDirectory) + i;
}
const IMAGE_SECTION_HEADER *pe_header_find_section(
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
const char *name) {
size_t n;
assert(pe_header);
assert(name);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
n = strlen(name);
if (n > sizeof(sections[0].Name)) /* Too long? */
return NULL;
FOREACH_ARRAY(section, sections, le16toh(pe_header->pe.NumberOfSections))
if (memcmp(section->Name, name, n) == 0 &&
memeqzero(section->Name + n, sizeof(section->Name) - n))
return section;
return NULL;
}
int pe_load_headers(
int fd,
IMAGE_DOS_HEADER **ret_dos_header,
PeHeader **ret_pe_header) {
_cleanup_free_ IMAGE_DOS_HEADER *dos_header = NULL;
_cleanup_free_ PeHeader *pe_header = NULL;
ssize_t n;
assert(fd >= 0);
dos_header = new(IMAGE_DOS_HEADER, 1);
if (!dos_header)
return log_oom_debug();
n = pread(fd,
dos_header,
sizeof(IMAGE_DOS_HEADER),
0);
if (n < 0)
return log_debug_errno(errno, "Failed to read DOS header: %m");
if ((size_t) n != sizeof(IMAGE_DOS_HEADER))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while reading MZ executable header.");
if (le16toh(dos_header->e_magic) != UINT16_C(0x5A4D))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks MZ executable header.");
pe_header = new(PeHeader, 1);
if (!pe_header)
return log_oom_debug();
n = pread(fd,
pe_header,
offsetof(PeHeader, optional),
le32toh(dos_header->e_lfanew));
if (n < 0)
return log_debug_errno(errno, "Failed to read PE executable header: %m");
if ((size_t) n != offsetof(PeHeader, optional))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while reading PE executable header.");
if (le32toh(pe_header->signature) != UINT32_C(0x00004550))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "File lacks PE executable header.");
if (le16toh(pe_header->pe.SizeOfOptionalHeader) < sizeof_field(PeHeader, optional.Magic))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Optional header size too short for magic.");
PeHeader *pe_header_tmp = realloc(pe_header, MAX(sizeof(PeHeader), pe_header_size(pe_header)));
if (!pe_header_tmp)
return log_oom_debug();
pe_header = pe_header_tmp;
n = pread(fd,
&pe_header->optional,
le16toh(pe_header->pe.SizeOfOptionalHeader),
le32toh(dos_header->e_lfanew) + offsetof(PeHeader, optional));
if (n < 0)
return log_debug_errno(errno, "Failed to read PE executable optional header: %m");
if ((size_t) n != le16toh(pe_header->pe.SizeOfOptionalHeader))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while reading PE executable optional header.");
if (!IN_SET(le16toh(pe_header->optional.Magic), UINT16_C(0x010B), UINT16_C(0x020B)))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Optional header magic invalid.");
if (pe_header_size(pe_header) !=
PE_HEADER_OPTIONAL_FIELD_OFFSET(pe_header, DataDirectory) +
sizeof(IMAGE_DATA_DIRECTORY) * (uint64_t) le32toh(PE_HEADER_OPTIONAL_FIELD(pe_header, NumberOfRvaAndSizes)))
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Optional header size mismatch.");
if (ret_dos_header)
*ret_dos_header = TAKE_PTR(dos_header);
if (ret_pe_header)
*ret_pe_header = TAKE_PTR(pe_header);
return 0;
}
int pe_load_sections(
int fd,
const IMAGE_DOS_HEADER *dos_header,
const PeHeader *pe_header,
IMAGE_SECTION_HEADER **ret_sections) {
_cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
size_t nos;
ssize_t n;
assert(fd >= 0);
assert(dos_header);
assert(pe_header);
nos = le16toh(pe_header->pe.NumberOfSections);
sections = new(IMAGE_SECTION_HEADER, nos);
if (!sections)
return log_oom_debug();
n = pread(fd,
sections,
sizeof(IMAGE_SECTION_HEADER) * nos,
le32toh(dos_header->e_lfanew) + pe_header_size(pe_header));
if (n < 0)
return log_debug_errno(errno, "Failed to read section table: %m");
if ((size_t) n != sizeof(IMAGE_SECTION_HEADER) * nos)
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Short read while reading section table.");
if (ret_sections)
*ret_sections = TAKE_PTR(sections);
return 0;
}
int pe_read_section_data(
int fd,
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
const char *name,
size_t max_size,
void **ret,
size_t *ret_size) {
const IMAGE_SECTION_HEADER *section;
_cleanup_free_ void *data = NULL;
size_t n;
ssize_t ss;
assert(fd >= 0);
assert(pe_header);
assert(sections || pe_header->pe.NumberOfSections == 0);
assert(name);
section = pe_header_find_section(pe_header, sections, name);
if (!section)
return -ENXIO;
n = le32toh(section->VirtualSize);
if (n > MIN(max_size, (size_t) SSIZE_MAX))
return -E2BIG;
data = malloc(n+1);
if (!data)
return -ENOMEM;
ss = pread(fd, data, n, le32toh(section->PointerToRawData));
if (ss < 0)
return -errno;
if ((size_t) ss != n)
return -EIO;
((uint8_t*) data)[n] = 0; /* NUL terminate, no matter what */
if (ret_size)
*ret_size = n;
else {
/* Check that there are no embedded NUL bytes if the caller doesn't want to know the size
* (i.e. treats the blob as a string) */
const char *nul;
nul = memchr(data, 0, n);
if (nul && !memeqzero(nul, n - (nul - (const char*) data))) /* If there's a NUL it must only be NULs from there on */
return -EBADMSG;
}
if (ret)
*ret = TAKE_PTR(data);
return 0;
}
bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections) {
assert(pe_header);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
return
pe_header_find_section(pe_header, sections, ".osrel") &&
pe_header_find_section(pe_header, sections, ".linux") &&
pe_header_find_section(pe_header, sections, ".initrd");
}

142
src/shared/pe-binary.h Normal file
View File

@ -0,0 +1,142 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <sys/types.h>
#include "sparse-endian.h"
/* When naming things we try to stay close to the official Windows APIs as per:
* → https://learn.microsoft.com/en-us/windows/win32/debug/pe-format */
typedef struct _packed_ _IMAGE_DOS_HEADER {
le16_t e_magic;
le16_t e_cblp;
le16_t e_cp;
le16_t e_crlc;
le16_t e_cparhdr;
le16_t e_minalloc;
le16_t e_maxalloc;
le16_t e_ss;
le16_t e_sp;
le16_t e_csum;
le16_t e_ip;
le16_t e_cs;
le16_t e_lfarlc;
le16_t e_ovno;
le16_t e_res[4];
le16_t e_oemid;
le16_t e_oeminfo;
le16_t e_res2[10];
le32_t e_lfanew;
} IMAGE_DOS_HEADER;
typedef struct _packed_ _IMAGE_FILE_HEADER {
le16_t Machine;
le16_t NumberOfSections;
le32_t TimeDateStamp;
le32_t PointerToSymbolTable;
le32_t NumberOfSymbols;
le16_t SizeOfOptionalHeader;
le16_t Characteristics;
} IMAGE_FILE_HEADER;
typedef struct _packed_ _IMAGE_DATA_DIRECTORY {
le32_t VirtualAddress;
le32_t Size;
} IMAGE_DATA_DIRECTORY;
typedef struct _packed_ _IMAGE_OPTIONAL_HEADER {
/* Standard fields */
le16_t Magic;
uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion;
le32_t SizeOfCode;
le32_t SizeOfInitializedData;
le32_t SizeOfUninitializedData;
le32_t AddressOfEntryPoint;
le32_t BaseOfCode;
/* Here the PE32 and PE32+ headers differ: PE32+ has one 64bit field, PE32+ has two 32bit fields */
union {
struct {
le32_t BaseOfData;
le32_t pe32_ImageBase;
};
le64_t pe32plus_ImageBase;
};
/* Additional fields */
le32_t SectionAlignment;
le32_t FileAlignment;
le16_t MajorOperatingSystemVersion;
le16_t MinorOperatingSystemVersion;
le16_t MajorImageVersion;
le16_t MinorImageVersion;
le16_t MajorSubsystemVersion;
le16_t MinorSubsystemVersion;
le32_t Win32VersionValue;
le32_t SizeOfImage;
le32_t SizeOfHeaders;
le32_t CheckSum;
le16_t Subsystem;
le16_t DllCharacteristics;
/* Here similar: on PE32+ some fields are 64bit that are 32bit on PE32. */
union {
struct {
le32_t pe32_SizeOfStackReserve;
le32_t pe32_SizeOfStackCommit;
le32_t pe32_SizeOfHeapReserve;
le32_t pe32_SizeOfHeapCommit;
le32_t pe32_LoaderFlags;
le32_t pe32_NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY pe32_DataDirectory[];
};
struct {
le64_t pe32plus_SizeOfStackReserve;
le64_t pe32plus_SizeOfStackCommit;
le64_t pe32plus_SizeOfHeapReserve;
le64_t pe32plus_SizeOfHeapCommit;
le32_t pe32plus_LoaderFlags;
le32_t pe32plus_NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY pe32plus_DataDirectory[];
};
};
} IMAGE_OPTIONAL_HEADER;
typedef struct _packed_ PeHeader {
le32_t signature;
IMAGE_FILE_HEADER pe;
IMAGE_OPTIONAL_HEADER optional;
} PeHeader;
typedef struct _packed_ _IMAGE_SECTION_HEADER {
uint8_t Name[8];
le32_t VirtualSize;
le32_t VirtualAddress;
le32_t SizeOfRawData;
le32_t PointerToRawData;
le32_t PointerToRelocations;
le32_t PointerToLinenumbers;
le16_t NumberOfRelocations;
le16_t NumberOfLinenumbers;
le32_t Characteristics;
} IMAGE_SECTION_HEADER;
bool pe_header_is_64bit(const PeHeader *h);
#define PE_HEADER_OPTIONAL_FIELD(h, field) \
(pe_header_is_64bit(h) ? (h)->optional.pe32plus_##field : (h)->optional.pe32_##field)
#define PE_HEADER_OPTIONAL_FIELD_OFFSET(h, field) \
(pe_header_is_64bit(h) ? offsetof(PeHeader, optional.pe32plus_##field) : offsetof(PeHeader, optional.pe32_##field))
const IMAGE_DATA_DIRECTORY *pe_header_get_data_directory(const PeHeader *h, size_t i);
const IMAGE_SECTION_HEADER *pe_header_find_section(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections, const char *name);
int pe_load_headers(int fd, IMAGE_DOS_HEADER **ret_dos_header, PeHeader **ret_pe_header);
int pe_load_sections(int fd, const IMAGE_DOS_HEADER *dos_header, const PeHeader *pe_header, IMAGE_SECTION_HEADER **ret_sections);
int pe_read_section_data(int fd, const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections, const char *name, size_t max_size, void **ret, size_t *ret_size);
bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections);

View File

@ -1,58 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <inttypes.h>
#include "macro.h"
#include "sparse-endian.h"
struct DosFileHeader {
uint8_t Magic[2];
le16_t LastSize;
le16_t nBlocks;
le16_t nReloc;
le16_t HdrSize;
le16_t MinAlloc;
le16_t MaxAlloc;
le16_t ss;
le16_t sp;
le16_t Checksum;
le16_t ip;
le16_t cs;
le16_t RelocPos;
le16_t nOverlay;
le16_t reserved[4];
le16_t OEMId;
le16_t OEMInfo;
le16_t reserved2[10];
le32_t ExeHeader;
} _packed_;
struct PeFileHeader {
le16_t Machine;
le16_t NumberOfSections;
le32_t TimeDateStamp;
le32_t PointerToSymbolTable;
le32_t NumberOfSymbols;
le16_t SizeOfOptionalHeader;
le16_t Characteristics;
} _packed_;
struct PeHeader {
uint8_t Magic[4];
struct PeFileHeader FileHeader;
} _packed_;
struct PeSectionHeader {
uint8_t Name[8];
le32_t VirtualSize;
le32_t VirtualAddress;
le32_t SizeOfRawData;
le32_t PointerToRawData;
le32_t PointerToRelocations;
le32_t PointerToLinenumbers;
le16_t NumberOfRelocations;
le16_t NumberOfLinenumbers;
le32_t Characteristics;
} _packed_;