[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC PATCH 2/4] kern/unwrap: File filter to unwrap files wrapped by grub
From: |
Zhang Boyang |
Subject: |
[RFC PATCH 2/4] kern/unwrap: File filter to unwrap files wrapped by grub-wrap |
Date: |
Mon, 5 Dec 2022 21:06:03 +0800 |
This file filter can unwrap a file wrapped by grub-wrap. This process is
done automatically and transparently, thus no additional code is
required at user's side.
First, it checks for file type flags and ignore irrelevant files. Then,
it find target PE section and unwraps the file. Subsequent read
operations will operate on unwrapped data. If error occurred during
unwrapping, e.g. file is not wrapped, it passthroughs original file.
Currently, this filter only operate on font files (GRUB_FILE_TYPE_FONT),
and they must be wrapped with section name set to ".GRUBpf2".
Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
---
grub-core/Makefile.core.def | 1 +
grub-core/kern/main.c | 3 +
grub-core/kern/unwrap.c | 188 ++++++++++++++++++++++++++++++++++++
include/grub/efi/pe32.h | 75 ++++++++++++++
include/grub/file.h | 1 +
include/grub/unwrap.h | 25 +++++
6 files changed, 293 insertions(+)
create mode 100644 grub-core/kern/unwrap.c
create mode 100644 include/grub/unwrap.h
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 95942fc8c..fb6a25ed1 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -148,6 +148,7 @@ kernel = {
common = kern/rescue_reader.c;
common = kern/term.c;
common = kern/verifiers.c;
+ common = kern/unwrap.c;
noemu = kern/compiler-rt.c;
noemu = kern/mm.c;
diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
index 731c07c29..fa08c0086 100644
--- a/grub-core/kern/main.c
+++ b/grub-core/kern/main.c
@@ -30,6 +30,7 @@
#include <grub/reader.h>
#include <grub/parser.h>
#include <grub/verify.h>
+#include <grub/unwrap.h>
#ifdef GRUB_MACHINE_PCBIOS
#include <grub/machine/memory.h>
@@ -281,6 +282,8 @@ grub_main (void)
/* Init verifiers API. */
grub_verifiers_init ();
+ grub_unwrap_init ();
+
grub_load_config ();
grub_boot_time ("Before loading embedded modules.");
diff --git a/grub-core/kern/unwrap.c b/grub-core/kern/unwrap.c
new file mode 100644
index 000000000..dc32ccdad
--- /dev/null
+++ b/grub-core/kern/unwrap.c
@@ -0,0 +1,188 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2022 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * PE unwrapper.
+ */
+
+#include <grub/file.h>
+#include <grub/unwrap.h>
+#include <grub/safemath.h>
+#include <grub/efi/pe32.h>
+
+struct grub_unwrapped
+{
+ grub_file_t file;
+ void *buf;
+ void *real_data;
+ grub_size_t real_size;
+};
+typedef struct grub_unwrapped *grub_unwrapped_t;
+
+static void
+unwrapped_free (grub_unwrapped_t unwrapped)
+{
+ if (unwrapped)
+ {
+ grub_free (unwrapped->buf);
+ grub_free (unwrapped);
+ }
+}
+
+static grub_ssize_t
+unwrapped_read (struct grub_file *file, char *buf, grub_size_t len)
+{
+ grub_unwrapped_t unwrapped = file->data;
+
+ grub_memcpy (buf, (char *) unwrapped->real_data + file->offset, len);
+ return len;
+}
+
+static grub_err_t
+unwrapped_close (struct grub_file *file)
+{
+ grub_unwrapped_t unwrapped = file->data;
+
+ grub_file_close (unwrapped->file);
+ unwrapped_free (unwrapped);
+ file->data = 0;
+
+ /* Device and name are freed by parent. */
+ file->device = 0;
+ file->name = 0;
+
+ return grub_errno;
+}
+
+struct grub_fs unwrapped_fs =
+{
+ .name = "unwrapped_read",
+ .fs_read = unwrapped_read,
+ .fs_close = unwrapped_close
+};
+
+static void
+try_unwrap (grub_unwrapped_t unwrapped, const char (*name)[8])
+{
+ void *buffer = unwrapped->real_data;
+ grub_size_t size = unwrapped->real_size;
+ int i;
+ grub_uint32_t pe_image_header, section_table;
+ grub_uint16_t num_sections;
+ struct grub_pe32_section_table *sections;
+
+ pe_image_header = grub_pe32_get_pe_image_header (buffer, size);
+ if (pe_image_header == 0)
+ goto fail;
+ grub_dprintf ("unwrap", "pe image header at 0x%" PRIxGRUB_UINT32_T ".\n",
pe_image_header);
+
+ section_table = grub_pe32_get_section_table (buffer, size, pe_image_header,
&num_sections);
+ if (section_table == 0)
+ goto fail;
+ grub_dprintf ("unwrap", "section table at 0x%" PRIxGRUB_UINT32_T ".\n",
section_table);
+
+ sections = (void *) ((char *) buffer + section_table);
+ for (i = 0; i < (int) num_sections; i++)
+ {
+ struct grub_pe32_section_table *section = §ions[i];
+ if (grub_memcmp (section->name, *name, 8) == 0)
+ {
+ grub_uint32_t offset, length, end;
+ grub_memcpy (&offset, §ion->raw_data_offset, sizeof (offset));
+ grub_memcpy (&length, §ion->virtual_size, sizeof (length));
+ if (grub_add (offset, length, &end) || end > size)
+ goto fail;
+
+ grub_dprintf ("unwrap", "unwrap ok, offset 0x%" PRIxGRUB_UINT32_T ",
length 0x%" PRIxGRUB_UINT32_T ".\n", offset, length);
+ unwrapped->real_data = (char *) buffer + offset;
+ unwrapped->real_size = length;
+ return;
+ }
+ }
+ grub_dprintf ("unwrap", "section %.8s not found.\n", *name);
+
+fail:
+ grub_dprintf ("unwrap", "unwrap failed, passthrough original file.\n");
+}
+
+static grub_file_t
+grub_unwrap_open (grub_file_t io, enum grub_file_type type)
+{
+ grub_unwrapped_t unwrapped = NULL;
+ grub_file_t ret = NULL;
+ char name[8] = { 0 };
+
+ switch (type & GRUB_FILE_TYPE_MASK)
+ {
+ /* Only process file types we known. */
+ case GRUB_FILE_TYPE_FONT:
+ grub_strncpy(name, ".GRUBpf2", 8);
+ break;
+
+ /* Don't touch other files. */
+ default:
+ return io;
+ }
+
+ ret = grub_malloc (sizeof (*ret));
+ if (ret == NULL)
+ goto fail;
+ *ret = *io;
+
+ ret->fs = &unwrapped_fs;
+ ret->not_easily_seekable = 0;
+ if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1))
+ {
+ grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+ N_("big file unwrapping isn't implemented yet"));
+ goto fail;
+ }
+
+ unwrapped = grub_malloc (sizeof (*unwrapped));
+ if (unwrapped == NULL)
+ goto fail;
+
+ unwrapped->buf = grub_malloc (ret->size);
+ if (unwrapped->buf == NULL)
+ goto fail;
+ if (grub_file_read (io, unwrapped->buf, ret->size) != (grub_ssize_t)
ret->size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+ io->name);
+ goto fail;
+ }
+ unwrapped->real_data = unwrapped->buf;
+ unwrapped->real_size = ret->size;
+
+ try_unwrap (unwrapped, &name);
+
+ unwrapped->file = io;
+ ret->size = unwrapped->real_size;
+ ret->data = unwrapped;
+ return ret;
+
+fail:
+ unwrapped_free (unwrapped);
+ grub_free (ret);
+ return NULL;
+}
+
+void
+grub_unwrap_init (void)
+{
+ grub_file_filter_register (GRUB_FILE_FILTER_UNWRAP, grub_unwrap_open);
+}
diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
index 7609b5198..a52de0ed2 100644
--- a/include/grub/efi/pe32.h
+++ b/include/grub/efi/pe32.h
@@ -20,6 +20,8 @@
#define GRUB_EFI_PE32_HEADER 1
#include <grub/types.h>
+#include <grub/mm.h>
+#include <grub/safemath.h>
#include <grub/efi/memory.h>
/* The MSDOS compatibility stub. This was copied from the output of
@@ -280,9 +282,11 @@ struct grub_pe_image_header
#if GRUB_TARGET_SIZEOF_VOID_P == 8
/* The Optional header. */
struct grub_pe64_optional_header optional_header;
+#define GRUB_PE32_OHDR_MAGIC GRUB_PE32_PE64_MAGIC
#else
/* The Optional header. */
struct grub_pe32_optional_header optional_header;
+#define GRUB_PE32_OHDR_MAGIC GRUB_PE32_PE32_MAGIC
#endif
};
@@ -343,4 +347,75 @@ struct grub_pe32_reloc
#define GRUB_PE32_REL_I386_DIR32 0x6
#define GRUB_PE32_REL_I386_REL32 0x14
+/*
+ * Get offset of PE image header of given in-memory file, as grub_uint32_t.
+ * Returns 0 if magic numbers are not found or header location is illegal.
+ */
+static inline grub_uint32_t
+grub_pe32_get_pe_image_header (const void *buffer, grub_size_t size)
+{
+ grub_uint16_t magic;
+ grub_uint32_t offset, end;
+ const struct grub_msdos_image_header *msdos_hdr;
+ const struct grub_pe_image_header *pe_hdr;
+
+ if (size < sizeof (*msdos_hdr))
+ return 0;
+ msdos_hdr = buffer;
+
+ magic = grub_cpu_to_le16 (GRUB_PE32_MAGIC);
+ if (grub_memcmp (&msdos_hdr->msdos_magic, &magic, sizeof (magic)) != 0)
+ return 0;
+
+ grub_memcpy (&offset, &msdos_hdr->pe_image_header_offset, sizeof (offset));
+ offset = grub_le_to_cpu32 (offset);
+ if (grub_add (offset, sizeof (*pe_hdr), &end) || end > size)
+ return 0;
+ pe_hdr = (const void *) ((const char *) buffer + offset);
+
+ if (grub_memcmp (&pe_hdr->signature, "PE\0\0", GRUB_PE32_SIGNATURE_SIZE) !=
0)
+ return 0;
+
+ magic = grub_cpu_to_le16 (GRUB_PE32_OHDR_MAGIC);
+ if (grub_memcmp (&pe_hdr->optional_header.magic, &magic, sizeof (magic)) !=
0)
+ return 0;
+
+ return offset;
+}
+
+/*
+ * Get offset of PE section table of given in-memory file, as grub_uint32_t.
+ * `pe_image_header` should be the value returned by
grub_pe32_get_pe_image_header().
+ * Number of sections is stored at `*num_sections`, as grub_uint16_t.
+ * Returns 0 if section table's location is illegal.
+ */
+static inline grub_uint32_t
+grub_pe32_get_section_table (const void *buffer, grub_size_t size,
+ grub_uint32_t pe_image_header,
+ grub_uint16_t *num_sections)
+{
+ grub_uint32_t offset, length, end;
+ const struct grub_pe_image_header *pe_hdr;
+ grub_uint16_t ohdr_size, section_count;
+
+ /* grub_pe32_get_pe_image_header() has already checked boundaries. */
+ pe_hdr = (const void *) ((const char *) buffer + pe_image_header);
+ offset = pe_image_header + (grub_uint32_t) ((const char *)
&pe_hdr->optional_header - (const char *) pe_hdr);
+
+ grub_memcpy (&ohdr_size, &pe_hdr->coff_header.optional_header_size, sizeof
(ohdr_size));
+ ohdr_size = grub_le_to_cpu16 (ohdr_size);
+ if (grub_add (offset, ohdr_size, &offset))
+ return 0;
+
+ grub_memcpy (§ion_count, &pe_hdr->coff_header.num_sections, sizeof
(section_count));
+ section_count = grub_le_to_cpu16 (section_count);
+ if (grub_mul (section_count, sizeof (struct grub_pe32_section_table),
&length))
+ return 0;
+ if (grub_add (offset, length, &end) || end > size)
+ return 0;
+
+ *num_sections = section_count;
+ return offset;
+}
+
#endif /* ! GRUB_EFI_PE32_HEADER */
diff --git a/include/grub/file.h b/include/grub/file.h
index a5bf3a792..5fae2a8fc 100644
--- a/include/grub/file.h
+++ b/include/grub/file.h
@@ -183,6 +183,7 @@ extern grub_disk_read_hook_t
EXPORT_VAR(grub_file_progress_hook);
typedef enum grub_file_filter_id
{
GRUB_FILE_FILTER_VERIFY,
+ GRUB_FILE_FILTER_UNWRAP,
GRUB_FILE_FILTER_GZIO,
GRUB_FILE_FILTER_XZIO,
GRUB_FILE_FILTER_LZOPIO,
diff --git a/include/grub/unwrap.h b/include/grub/unwrap.h
new file mode 100644
index 000000000..c5155fc6d
--- /dev/null
+++ b/include/grub/unwrap.h
@@ -0,0 +1,25 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2022 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_UNWRAP_HEADER
+#define GRUB_UNWRAP_HEADER 1
+
+extern void
+grub_unwrap_init (void);
+
+#endif /* ! GRUB_UNWRAP_HEADER */
--
2.30.2