1e595e65bSEdward Tomasz Napierala /*- 21aa6f9aeSEdward Tomasz Napierala * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 31aa6f9aeSEdward Tomasz Napierala * 4e595e65bSEdward Tomasz Napierala * Copyright (c) 2014 The FreeBSD Foundation 5e595e65bSEdward Tomasz Napierala * 6e595e65bSEdward Tomasz Napierala * This software was developed by Edward Tomasz Napierala under sponsorship 7e595e65bSEdward Tomasz Napierala * from the FreeBSD Foundation. 8e595e65bSEdward Tomasz Napierala * 9e595e65bSEdward Tomasz Napierala * Redistribution and use in source and binary forms, with or without 10e595e65bSEdward Tomasz Napierala * modification, are permitted provided that the following conditions 11e595e65bSEdward Tomasz Napierala * are met: 12e595e65bSEdward Tomasz Napierala * 1. Redistributions of source code must retain the above copyright 13e595e65bSEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer. 14e595e65bSEdward Tomasz Napierala * 2. Redistributions in binary form must reproduce the above copyright 15e595e65bSEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer in the 16e595e65bSEdward Tomasz Napierala * documentation and/or other materials provided with the distribution. 17e595e65bSEdward Tomasz Napierala * 18e595e65bSEdward Tomasz Napierala * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19e595e65bSEdward Tomasz Napierala * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20e595e65bSEdward Tomasz Napierala * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21e595e65bSEdward Tomasz Napierala * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22e595e65bSEdward Tomasz Napierala * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23e595e65bSEdward Tomasz Napierala * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24e595e65bSEdward Tomasz Napierala * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25e595e65bSEdward Tomasz Napierala * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26e595e65bSEdward Tomasz Napierala * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27e595e65bSEdward Tomasz Napierala * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28e595e65bSEdward Tomasz Napierala * SUCH DAMAGE. 29e595e65bSEdward Tomasz Napierala * 30e595e65bSEdward Tomasz Napierala */ 31e595e65bSEdward Tomasz Napierala 32e595e65bSEdward Tomasz Napierala /* 33e595e65bSEdward Tomasz Napierala * PE format reference: 34e595e65bSEdward Tomasz Napierala * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx 35e595e65bSEdward Tomasz Napierala */ 36e595e65bSEdward Tomasz Napierala 37e595e65bSEdward Tomasz Napierala #include <sys/cdefs.h> 38e595e65bSEdward Tomasz Napierala __FBSDID("$FreeBSD$"); 39e595e65bSEdward Tomasz Napierala 40e595e65bSEdward Tomasz Napierala #include <assert.h> 41e595e65bSEdward Tomasz Napierala #include <err.h> 42e595e65bSEdward Tomasz Napierala #include <errno.h> 43e595e65bSEdward Tomasz Napierala #include <stddef.h> 44e595e65bSEdward Tomasz Napierala #include <stdio.h> 45e595e65bSEdward Tomasz Napierala #include <stdint.h> 46e595e65bSEdward Tomasz Napierala #include <stdlib.h> 47e595e65bSEdward Tomasz Napierala #include <string.h> 48e595e65bSEdward Tomasz Napierala #include <unistd.h> 49e595e65bSEdward Tomasz Napierala 50e595e65bSEdward Tomasz Napierala #include "uefisign.h" 51e595e65bSEdward Tomasz Napierala 52e595e65bSEdward Tomasz Napierala #ifndef CTASSERT 53e595e65bSEdward Tomasz Napierala #define CTASSERT(x) _CTASSERT(x, __LINE__) 54e595e65bSEdward Tomasz Napierala #define _CTASSERT(x, y) __CTASSERT(x, y) 55e595e65bSEdward Tomasz Napierala #define __CTASSERT(x, y) typedef char __assert_ ## y [(x) ? 1 : -1] 56e595e65bSEdward Tomasz Napierala #endif 57e595e65bSEdward Tomasz Napierala 58c97ed06aSMarcin Wojtas #define PE_ALIGMENT_SIZE 8 59c97ed06aSMarcin Wojtas 60e595e65bSEdward Tomasz Napierala struct mz_header { 61e595e65bSEdward Tomasz Napierala uint8_t mz_signature[2]; 62e595e65bSEdward Tomasz Napierala uint8_t mz_dont_care[58]; 63e595e65bSEdward Tomasz Napierala uint16_t mz_lfanew; 64e595e65bSEdward Tomasz Napierala } __attribute__((packed)); 65e595e65bSEdward Tomasz Napierala 66e595e65bSEdward Tomasz Napierala struct coff_header { 67e595e65bSEdward Tomasz Napierala uint8_t coff_dont_care[2]; 68e595e65bSEdward Tomasz Napierala uint16_t coff_number_of_sections; 69e595e65bSEdward Tomasz Napierala uint8_t coff_dont_care_either[16]; 70e595e65bSEdward Tomasz Napierala } __attribute__((packed)); 71e595e65bSEdward Tomasz Napierala 72e595e65bSEdward Tomasz Napierala #define PE_SIGNATURE 0x00004550 73e595e65bSEdward Tomasz Napierala 74e595e65bSEdward Tomasz Napierala struct pe_header { 75e595e65bSEdward Tomasz Napierala uint32_t pe_signature; 76e595e65bSEdward Tomasz Napierala struct coff_header pe_coff; 77e595e65bSEdward Tomasz Napierala } __attribute__((packed)); 78e595e65bSEdward Tomasz Napierala 79e595e65bSEdward Tomasz Napierala #define PE_OPTIONAL_MAGIC_32 0x010B 80e595e65bSEdward Tomasz Napierala #define PE_OPTIONAL_MAGIC_32_PLUS 0x020B 81e595e65bSEdward Tomasz Napierala 82e595e65bSEdward Tomasz Napierala #define PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION 10 83e595e65bSEdward Tomasz Napierala #define PE_OPTIONAL_SUBSYSTEM_EFI_BOOT 11 84e595e65bSEdward Tomasz Napierala #define PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME 12 85e595e65bSEdward Tomasz Napierala 86e595e65bSEdward Tomasz Napierala struct pe_optional_header_32 { 87e595e65bSEdward Tomasz Napierala uint16_t po_magic; 88e595e65bSEdward Tomasz Napierala uint8_t po_dont_care[58]; 89e595e65bSEdward Tomasz Napierala uint32_t po_size_of_headers; 90e595e65bSEdward Tomasz Napierala uint32_t po_checksum; 91e595e65bSEdward Tomasz Napierala uint16_t po_subsystem; 92e595e65bSEdward Tomasz Napierala uint8_t po_dont_care_either[22]; 93e595e65bSEdward Tomasz Napierala uint32_t po_number_of_rva_and_sizes; 94e595e65bSEdward Tomasz Napierala } __attribute__((packed)); 95e595e65bSEdward Tomasz Napierala 96e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_optional_header_32, po_size_of_headers) == 60); 97e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_optional_header_32, po_checksum) == 64); 98e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_optional_header_32, po_subsystem) == 68); 99e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_optional_header_32, po_number_of_rva_and_sizes) == 92); 100e595e65bSEdward Tomasz Napierala 101e595e65bSEdward Tomasz Napierala struct pe_optional_header_32_plus { 102e595e65bSEdward Tomasz Napierala uint16_t po_magic; 103e595e65bSEdward Tomasz Napierala uint8_t po_dont_care[58]; 104e595e65bSEdward Tomasz Napierala uint32_t po_size_of_headers; 105e595e65bSEdward Tomasz Napierala uint32_t po_checksum; 106e595e65bSEdward Tomasz Napierala uint16_t po_subsystem; 107e595e65bSEdward Tomasz Napierala uint8_t po_dont_care_either[38]; 108e595e65bSEdward Tomasz Napierala uint32_t po_number_of_rva_and_sizes; 109e595e65bSEdward Tomasz Napierala } __attribute__((packed)); 110e595e65bSEdward Tomasz Napierala 111e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_optional_header_32_plus, po_size_of_headers) == 60); 112e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_optional_header_32_plus, po_checksum) == 64); 113e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_optional_header_32_plus, po_subsystem) == 68); 114e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_optional_header_32_plus, po_number_of_rva_and_sizes) == 108); 115e595e65bSEdward Tomasz Napierala 116e595e65bSEdward Tomasz Napierala #define PE_DIRECTORY_ENTRY_CERTIFICATE 4 117e595e65bSEdward Tomasz Napierala 118e595e65bSEdward Tomasz Napierala struct pe_directory_entry { 119e595e65bSEdward Tomasz Napierala uint32_t pde_rva; 120e595e65bSEdward Tomasz Napierala uint32_t pde_size; 121e595e65bSEdward Tomasz Napierala } __attribute__((packed)); 122e595e65bSEdward Tomasz Napierala 123e595e65bSEdward Tomasz Napierala struct pe_section_header { 124e595e65bSEdward Tomasz Napierala uint8_t psh_dont_care[16]; 125e595e65bSEdward Tomasz Napierala uint32_t psh_size_of_raw_data; 126e595e65bSEdward Tomasz Napierala uint32_t psh_pointer_to_raw_data; 127e595e65bSEdward Tomasz Napierala uint8_t psh_dont_care_either[16]; 128e595e65bSEdward Tomasz Napierala } __attribute__((packed)); 129e595e65bSEdward Tomasz Napierala 130e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_section_header, psh_size_of_raw_data) == 16); 131e595e65bSEdward Tomasz Napierala CTASSERT(offsetof(struct pe_section_header, psh_pointer_to_raw_data) == 20); 132e595e65bSEdward Tomasz Napierala 133e595e65bSEdward Tomasz Napierala #define PE_CERTIFICATE_REVISION 0x0200 134e595e65bSEdward Tomasz Napierala #define PE_CERTIFICATE_TYPE 0x0002 135e595e65bSEdward Tomasz Napierala 136e595e65bSEdward Tomasz Napierala struct pe_certificate { 137e595e65bSEdward Tomasz Napierala uint32_t pc_len; 138e595e65bSEdward Tomasz Napierala uint16_t pc_revision; 139e595e65bSEdward Tomasz Napierala uint16_t pc_type; 140e595e65bSEdward Tomasz Napierala char pc_signature[0]; 141e595e65bSEdward Tomasz Napierala } __attribute__((packed)); 142e595e65bSEdward Tomasz Napierala 143e595e65bSEdward Tomasz Napierala void 144e595e65bSEdward Tomasz Napierala range_check(const struct executable *x, off_t off, size_t len, 145e595e65bSEdward Tomasz Napierala const char *name) 146e595e65bSEdward Tomasz Napierala { 147e595e65bSEdward Tomasz Napierala 148e595e65bSEdward Tomasz Napierala if (off < 0) { 149e595e65bSEdward Tomasz Napierala errx(1, "%s starts at negative offset %jd", 150e595e65bSEdward Tomasz Napierala name, (intmax_t)off); 151e595e65bSEdward Tomasz Napierala } 152e595e65bSEdward Tomasz Napierala if (off >= (off_t)x->x_len) { 153e595e65bSEdward Tomasz Napierala errx(1, "%s starts at %jd, past the end of executable at %zd", 154e595e65bSEdward Tomasz Napierala name, (intmax_t)off, x->x_len); 155e595e65bSEdward Tomasz Napierala } 156e595e65bSEdward Tomasz Napierala if (len >= x->x_len) { 157e595e65bSEdward Tomasz Napierala errx(1, "%s size %zd is larger than the executable size %zd", 158e595e65bSEdward Tomasz Napierala name, len, x->x_len); 159e595e65bSEdward Tomasz Napierala } 160e595e65bSEdward Tomasz Napierala if (off + len > x->x_len) { 161e595e65bSEdward Tomasz Napierala errx(1, "%s extends to %jd, past the end of executable at %zd", 162e595e65bSEdward Tomasz Napierala name, (intmax_t)(off + len), x->x_len); 163e595e65bSEdward Tomasz Napierala } 164e595e65bSEdward Tomasz Napierala } 165e595e65bSEdward Tomasz Napierala 166e595e65bSEdward Tomasz Napierala size_t 167e595e65bSEdward Tomasz Napierala signature_size(const struct executable *x) 168e595e65bSEdward Tomasz Napierala { 169e595e65bSEdward Tomasz Napierala const struct pe_directory_entry *pde; 170e595e65bSEdward Tomasz Napierala 171e595e65bSEdward Tomasz Napierala range_check(x, x->x_certificate_entry_off, 172e595e65bSEdward Tomasz Napierala x->x_certificate_entry_len, "Certificate Directory"); 173e595e65bSEdward Tomasz Napierala 174e595e65bSEdward Tomasz Napierala pde = (struct pe_directory_entry *) 175e595e65bSEdward Tomasz Napierala (x->x_buf + x->x_certificate_entry_off); 176e595e65bSEdward Tomasz Napierala 177e595e65bSEdward Tomasz Napierala if (pde->pde_rva != 0 && pde->pde_size == 0) 178e595e65bSEdward Tomasz Napierala warnx("signature size is 0, but its RVA is %d", pde->pde_rva); 179e595e65bSEdward Tomasz Napierala if (pde->pde_rva == 0 && pde->pde_size != 0) 180e595e65bSEdward Tomasz Napierala warnx("signature RVA is 0, but its size is %d", pde->pde_size); 181e595e65bSEdward Tomasz Napierala 182e595e65bSEdward Tomasz Napierala return (pde->pde_size); 183e595e65bSEdward Tomasz Napierala } 184e595e65bSEdward Tomasz Napierala 185e595e65bSEdward Tomasz Napierala void 186e595e65bSEdward Tomasz Napierala show_certificate(const struct executable *x) 187e595e65bSEdward Tomasz Napierala { 188e595e65bSEdward Tomasz Napierala struct pe_certificate *pc; 189e595e65bSEdward Tomasz Napierala const struct pe_directory_entry *pde; 190e595e65bSEdward Tomasz Napierala 191e595e65bSEdward Tomasz Napierala range_check(x, x->x_certificate_entry_off, 192e595e65bSEdward Tomasz Napierala x->x_certificate_entry_len, "Certificate Directory"); 193e595e65bSEdward Tomasz Napierala 194e595e65bSEdward Tomasz Napierala pde = (struct pe_directory_entry *) 195e595e65bSEdward Tomasz Napierala (x->x_buf + x->x_certificate_entry_off); 196e595e65bSEdward Tomasz Napierala 197e595e65bSEdward Tomasz Napierala if (signature_size(x) == 0) { 198e595e65bSEdward Tomasz Napierala printf("file not signed\n"); 199e595e65bSEdward Tomasz Napierala return; 200e595e65bSEdward Tomasz Napierala } 201e595e65bSEdward Tomasz Napierala 202e595e65bSEdward Tomasz Napierala #if 0 203e595e65bSEdward Tomasz Napierala printf("certificate chunk at offset %zd, size %zd\n", 204e595e65bSEdward Tomasz Napierala pde->pde_rva, pde->pde_size); 205e595e65bSEdward Tomasz Napierala #endif 206e595e65bSEdward Tomasz Napierala 207e595e65bSEdward Tomasz Napierala range_check(x, pde->pde_rva, pde->pde_size, "Certificate chunk"); 208e595e65bSEdward Tomasz Napierala 209e595e65bSEdward Tomasz Napierala pc = (struct pe_certificate *)(x->x_buf + pde->pde_rva); 210e595e65bSEdward Tomasz Napierala if (pc->pc_revision != PE_CERTIFICATE_REVISION) { 211e595e65bSEdward Tomasz Napierala errx(1, "wrong certificate chunk revision, is %d, should be %d", 212e595e65bSEdward Tomasz Napierala pc->pc_revision, PE_CERTIFICATE_REVISION); 213e595e65bSEdward Tomasz Napierala } 214e595e65bSEdward Tomasz Napierala if (pc->pc_type != PE_CERTIFICATE_TYPE) { 215e595e65bSEdward Tomasz Napierala errx(1, "wrong certificate chunk type, is %d, should be %d", 216e595e65bSEdward Tomasz Napierala pc->pc_type, PE_CERTIFICATE_TYPE); 217e595e65bSEdward Tomasz Napierala } 218e595e65bSEdward Tomasz Napierala printf("to dump PKCS7:\n " 219e595e65bSEdward Tomasz Napierala "dd if='%s' bs=1 skip=%zd | openssl pkcs7 -inform DER -print\n", 220e595e65bSEdward Tomasz Napierala x->x_path, pde->pde_rva + offsetof(struct pe_certificate, pc_signature)); 221e595e65bSEdward Tomasz Napierala printf("to dump raw ASN.1:\n " 222e595e65bSEdward Tomasz Napierala "openssl asn1parse -i -inform DER -offset %zd -in '%s'\n", 223e595e65bSEdward Tomasz Napierala pde->pde_rva + offsetof(struct pe_certificate, pc_signature), x->x_path); 224e595e65bSEdward Tomasz Napierala } 225e595e65bSEdward Tomasz Napierala 226e595e65bSEdward Tomasz Napierala static void 227e595e65bSEdward Tomasz Napierala parse_section_table(struct executable *x, off_t off, int number_of_sections) 228e595e65bSEdward Tomasz Napierala { 229e595e65bSEdward Tomasz Napierala const struct pe_section_header *psh; 230e595e65bSEdward Tomasz Napierala int i; 231e595e65bSEdward Tomasz Napierala 232e595e65bSEdward Tomasz Napierala range_check(x, off, sizeof(*psh) * number_of_sections, 233e595e65bSEdward Tomasz Napierala "section table"); 234e595e65bSEdward Tomasz Napierala 235e595e65bSEdward Tomasz Napierala if (x->x_headers_len <= off + sizeof(*psh) * number_of_sections) 236e595e65bSEdward Tomasz Napierala errx(1, "section table outside of headers"); 237e595e65bSEdward Tomasz Napierala 238e595e65bSEdward Tomasz Napierala psh = (const struct pe_section_header *)(x->x_buf + off); 239e595e65bSEdward Tomasz Napierala 240e595e65bSEdward Tomasz Napierala if (number_of_sections >= MAX_SECTIONS) { 241e595e65bSEdward Tomasz Napierala errx(1, "too many sections: got %d, should be %d", 242e595e65bSEdward Tomasz Napierala number_of_sections, MAX_SECTIONS); 243e595e65bSEdward Tomasz Napierala } 244e595e65bSEdward Tomasz Napierala x->x_nsections = number_of_sections; 245e595e65bSEdward Tomasz Napierala 246e595e65bSEdward Tomasz Napierala for (i = 0; i < number_of_sections; i++) { 247*12db51d2SEric van Gyzen if (psh->psh_size_of_raw_data > 0 && 248*12db51d2SEric van Gyzen psh->psh_pointer_to_raw_data < x->x_headers_len) 249e595e65bSEdward Tomasz Napierala errx(1, "section points inside the headers"); 250e595e65bSEdward Tomasz Napierala 251e595e65bSEdward Tomasz Napierala range_check(x, psh->psh_pointer_to_raw_data, 252e595e65bSEdward Tomasz Napierala psh->psh_size_of_raw_data, "section"); 253e595e65bSEdward Tomasz Napierala #if 0 254e595e65bSEdward Tomasz Napierala printf("section %d: start %d, size %d\n", 255e595e65bSEdward Tomasz Napierala i, psh->psh_pointer_to_raw_data, psh->psh_size_of_raw_data); 256e595e65bSEdward Tomasz Napierala #endif 257e595e65bSEdward Tomasz Napierala x->x_section_off[i] = psh->psh_pointer_to_raw_data; 258e595e65bSEdward Tomasz Napierala x->x_section_len[i] = psh->psh_size_of_raw_data; 259e595e65bSEdward Tomasz Napierala psh++; 260e595e65bSEdward Tomasz Napierala } 261e595e65bSEdward Tomasz Napierala } 262e595e65bSEdward Tomasz Napierala 263e595e65bSEdward Tomasz Napierala static void 264e595e65bSEdward Tomasz Napierala parse_directory(struct executable *x, off_t off, 265e595e65bSEdward Tomasz Napierala int number_of_rva_and_sizes, int number_of_sections) 266e595e65bSEdward Tomasz Napierala { 267e595e65bSEdward Tomasz Napierala //int i; 268e595e65bSEdward Tomasz Napierala const struct pe_directory_entry *pde; 269e595e65bSEdward Tomasz Napierala 270e595e65bSEdward Tomasz Napierala //printf("Data Directory at offset %zd\n", off); 271e595e65bSEdward Tomasz Napierala 272e595e65bSEdward Tomasz Napierala if (number_of_rva_and_sizes <= PE_DIRECTORY_ENTRY_CERTIFICATE) { 273e595e65bSEdward Tomasz Napierala errx(1, "wrong NumberOfRvaAndSizes %d; should be at least %d", 274e595e65bSEdward Tomasz Napierala number_of_rva_and_sizes, PE_DIRECTORY_ENTRY_CERTIFICATE); 275e595e65bSEdward Tomasz Napierala } 276e595e65bSEdward Tomasz Napierala 277e595e65bSEdward Tomasz Napierala range_check(x, off, sizeof(*pde) * number_of_rva_and_sizes, 278e595e65bSEdward Tomasz Napierala "PE Data Directory"); 279e595e65bSEdward Tomasz Napierala if (x->x_headers_len <= off + sizeof(*pde) * number_of_rva_and_sizes) 280e595e65bSEdward Tomasz Napierala errx(1, "PE Data Directory outside of headers"); 281e595e65bSEdward Tomasz Napierala 282e595e65bSEdward Tomasz Napierala x->x_certificate_entry_off = 283e595e65bSEdward Tomasz Napierala off + sizeof(*pde) * PE_DIRECTORY_ENTRY_CERTIFICATE; 284e595e65bSEdward Tomasz Napierala x->x_certificate_entry_len = sizeof(*pde); 285e595e65bSEdward Tomasz Napierala #if 0 286e595e65bSEdward Tomasz Napierala printf("certificate directory entry at offset %zd, len %zd\n", 287e595e65bSEdward Tomasz Napierala x->x_certificate_entry_off, x->x_certificate_entry_len); 288e595e65bSEdward Tomasz Napierala 289e595e65bSEdward Tomasz Napierala pde = (struct pe_directory_entry *)(x->x_buf + off); 290e595e65bSEdward Tomasz Napierala for (i = 0; i < number_of_rva_and_sizes; i++) { 291e595e65bSEdward Tomasz Napierala printf("rva %zd, size %zd\n", pde->pde_rva, pde->pde_size); 292e595e65bSEdward Tomasz Napierala pde++; 293e595e65bSEdward Tomasz Napierala } 294e595e65bSEdward Tomasz Napierala #endif 295e595e65bSEdward Tomasz Napierala 296e595e65bSEdward Tomasz Napierala return (parse_section_table(x, 297e595e65bSEdward Tomasz Napierala off + sizeof(*pde) * number_of_rva_and_sizes, number_of_sections)); 298e595e65bSEdward Tomasz Napierala } 299e595e65bSEdward Tomasz Napierala 300e595e65bSEdward Tomasz Napierala /* 301e595e65bSEdward Tomasz Napierala * The PE checksum algorithm is undocumented; this code is mostly based on 302e595e65bSEdward Tomasz Napierala * http://forum.sysinternals.com/optional-header-checksum-calculation_topic24214.html 303e595e65bSEdward Tomasz Napierala * 304e595e65bSEdward Tomasz Napierala * "Sum the entire image file, excluding the CheckSum field in the optional 305e595e65bSEdward Tomasz Napierala * header, as an array of USHORTs, allowing any carry above 16 bits to be added 306e595e65bSEdward Tomasz Napierala * back onto the low 16 bits. Then add the file size to get a 32-bit value." 307e595e65bSEdward Tomasz Napierala * 308e595e65bSEdward Tomasz Napierala * Note that most software does not care about the checksum at all; perhaps 309e595e65bSEdward Tomasz Napierala * we could just set it to 0 instead. 310e595e65bSEdward Tomasz Napierala * 311463a577bSEitan Adler * XXX: Endianness? 312e595e65bSEdward Tomasz Napierala */ 313e595e65bSEdward Tomasz Napierala static uint32_t 314e595e65bSEdward Tomasz Napierala compute_checksum(const struct executable *x) 315e595e65bSEdward Tomasz Napierala { 316e595e65bSEdward Tomasz Napierala uint32_t cksum = 0; 317e595e65bSEdward Tomasz Napierala uint16_t tmp; 318e595e65bSEdward Tomasz Napierala int i; 319e595e65bSEdward Tomasz Napierala 320e595e65bSEdward Tomasz Napierala range_check(x, x->x_checksum_off, x->x_checksum_len, "PE checksum"); 321e595e65bSEdward Tomasz Napierala 322e595e65bSEdward Tomasz Napierala assert(x->x_checksum_off % 2 == 0); 323e595e65bSEdward Tomasz Napierala 324e595e65bSEdward Tomasz Napierala for (i = 0; i + sizeof(tmp) < x->x_len; i += 2) { 325e595e65bSEdward Tomasz Napierala /* 326e595e65bSEdward Tomasz Napierala * Don't checksum the checksum. The +2 is because the checksum 327e595e65bSEdward Tomasz Napierala * is 4 bytes, and here we're iterating over 2 byte chunks. 328e595e65bSEdward Tomasz Napierala */ 329e595e65bSEdward Tomasz Napierala if (i == x->x_checksum_off || i == x->x_checksum_off + 2) { 330e595e65bSEdward Tomasz Napierala tmp = 0; 331e595e65bSEdward Tomasz Napierala } else { 332e595e65bSEdward Tomasz Napierala assert(i + sizeof(tmp) <= x->x_len); 333e595e65bSEdward Tomasz Napierala memcpy(&tmp, x->x_buf + i, sizeof(tmp)); 334e595e65bSEdward Tomasz Napierala } 335e595e65bSEdward Tomasz Napierala 336e595e65bSEdward Tomasz Napierala cksum += tmp; 337e595e65bSEdward Tomasz Napierala cksum += cksum >> 16; 338e595e65bSEdward Tomasz Napierala cksum &= 0xffff; 339e595e65bSEdward Tomasz Napierala } 340e595e65bSEdward Tomasz Napierala 341e595e65bSEdward Tomasz Napierala cksum += cksum >> 16; 342e595e65bSEdward Tomasz Napierala cksum &= 0xffff; 343e595e65bSEdward Tomasz Napierala 344e595e65bSEdward Tomasz Napierala cksum += x->x_len; 345e595e65bSEdward Tomasz Napierala 346e595e65bSEdward Tomasz Napierala return (cksum); 347e595e65bSEdward Tomasz Napierala } 348e595e65bSEdward Tomasz Napierala 349e595e65bSEdward Tomasz Napierala static void 350e595e65bSEdward Tomasz Napierala parse_optional_32_plus(struct executable *x, off_t off, 351e595e65bSEdward Tomasz Napierala int number_of_sections) 352e595e65bSEdward Tomasz Napierala { 3538dca98d7SEdward Tomasz Napierala #if 0 354e595e65bSEdward Tomasz Napierala uint32_t computed_checksum; 3558dca98d7SEdward Tomasz Napierala #endif 356e595e65bSEdward Tomasz Napierala const struct pe_optional_header_32_plus *po; 357e595e65bSEdward Tomasz Napierala 358e595e65bSEdward Tomasz Napierala range_check(x, off, sizeof(*po), "PE Optional Header"); 359e595e65bSEdward Tomasz Napierala 360e595e65bSEdward Tomasz Napierala po = (struct pe_optional_header_32_plus *)(x->x_buf + off); 361e595e65bSEdward Tomasz Napierala switch (po->po_subsystem) { 362e595e65bSEdward Tomasz Napierala case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION: 363e595e65bSEdward Tomasz Napierala case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT: 364e595e65bSEdward Tomasz Napierala case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME: 365e595e65bSEdward Tomasz Napierala break; 366e595e65bSEdward Tomasz Napierala default: 367e595e65bSEdward Tomasz Napierala errx(1, "wrong PE Optional Header subsystem 0x%x", 368e595e65bSEdward Tomasz Napierala po->po_subsystem); 369e595e65bSEdward Tomasz Napierala } 370e595e65bSEdward Tomasz Napierala 371e595e65bSEdward Tomasz Napierala #if 0 372e595e65bSEdward Tomasz Napierala printf("subsystem %d, checksum 0x%x, %d data directories\n", 373e595e65bSEdward Tomasz Napierala po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes); 374e595e65bSEdward Tomasz Napierala #endif 375e595e65bSEdward Tomasz Napierala 376e595e65bSEdward Tomasz Napierala x->x_checksum_off = off + 377e595e65bSEdward Tomasz Napierala offsetof(struct pe_optional_header_32_plus, po_checksum); 378e595e65bSEdward Tomasz Napierala x->x_checksum_len = sizeof(po->po_checksum); 379e595e65bSEdward Tomasz Napierala #if 0 380e595e65bSEdward Tomasz Napierala printf("checksum 0x%x at offset %zd, len %zd\n", 381e595e65bSEdward Tomasz Napierala po->po_checksum, x->x_checksum_off, x->x_checksum_len); 382e595e65bSEdward Tomasz Napierala 383e595e65bSEdward Tomasz Napierala computed_checksum = compute_checksum(x); 384e595e65bSEdward Tomasz Napierala if (computed_checksum != po->po_checksum) { 385e595e65bSEdward Tomasz Napierala warnx("invalid PE+ checksum; is 0x%x, should be 0x%x", 386e595e65bSEdward Tomasz Napierala po->po_checksum, computed_checksum); 387e595e65bSEdward Tomasz Napierala } 3888dca98d7SEdward Tomasz Napierala #endif 389e595e65bSEdward Tomasz Napierala 390e595e65bSEdward Tomasz Napierala if (x->x_len < x->x_headers_len) 391e595e65bSEdward Tomasz Napierala errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers); 392e595e65bSEdward Tomasz Napierala x->x_headers_len = po->po_size_of_headers; 393e595e65bSEdward Tomasz Napierala //printf("Size of Headers: %d\n", po->po_size_of_headers); 394e595e65bSEdward Tomasz Napierala 395e595e65bSEdward Tomasz Napierala return (parse_directory(x, off + sizeof(*po), 396e595e65bSEdward Tomasz Napierala po->po_number_of_rva_and_sizes, number_of_sections)); 397e595e65bSEdward Tomasz Napierala } 398e595e65bSEdward Tomasz Napierala 399e595e65bSEdward Tomasz Napierala static void 400e595e65bSEdward Tomasz Napierala parse_optional_32(struct executable *x, off_t off, int number_of_sections) 401e595e65bSEdward Tomasz Napierala { 4028dca98d7SEdward Tomasz Napierala #if 0 403e595e65bSEdward Tomasz Napierala uint32_t computed_checksum; 4048dca98d7SEdward Tomasz Napierala #endif 405e595e65bSEdward Tomasz Napierala const struct pe_optional_header_32 *po; 406e595e65bSEdward Tomasz Napierala 407e595e65bSEdward Tomasz Napierala range_check(x, off, sizeof(*po), "PE Optional Header"); 408e595e65bSEdward Tomasz Napierala 409e595e65bSEdward Tomasz Napierala po = (struct pe_optional_header_32 *)(x->x_buf + off); 410e595e65bSEdward Tomasz Napierala switch (po->po_subsystem) { 411e595e65bSEdward Tomasz Napierala case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION: 412e595e65bSEdward Tomasz Napierala case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT: 413e595e65bSEdward Tomasz Napierala case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME: 414e595e65bSEdward Tomasz Napierala break; 415e595e65bSEdward Tomasz Napierala default: 416e595e65bSEdward Tomasz Napierala errx(1, "wrong PE Optional Header subsystem 0x%x", 417e595e65bSEdward Tomasz Napierala po->po_subsystem); 418e595e65bSEdward Tomasz Napierala } 419e595e65bSEdward Tomasz Napierala 420e595e65bSEdward Tomasz Napierala #if 0 421e595e65bSEdward Tomasz Napierala printf("subsystem %d, checksum 0x%x, %d data directories\n", 422e595e65bSEdward Tomasz Napierala po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes); 423e595e65bSEdward Tomasz Napierala #endif 424e595e65bSEdward Tomasz Napierala 425e595e65bSEdward Tomasz Napierala x->x_checksum_off = off + 426e595e65bSEdward Tomasz Napierala offsetof(struct pe_optional_header_32, po_checksum); 427e595e65bSEdward Tomasz Napierala x->x_checksum_len = sizeof(po->po_checksum); 428e595e65bSEdward Tomasz Napierala #if 0 429e595e65bSEdward Tomasz Napierala printf("checksum at offset %zd, len %zd\n", 430e595e65bSEdward Tomasz Napierala x->x_checksum_off, x->x_checksum_len); 431e595e65bSEdward Tomasz Napierala 432e595e65bSEdward Tomasz Napierala computed_checksum = compute_checksum(x); 433e595e65bSEdward Tomasz Napierala if (computed_checksum != po->po_checksum) { 434e595e65bSEdward Tomasz Napierala warnx("invalid PE checksum; is 0x%x, should be 0x%x", 435e595e65bSEdward Tomasz Napierala po->po_checksum, computed_checksum); 436e595e65bSEdward Tomasz Napierala } 4378dca98d7SEdward Tomasz Napierala #endif 438e595e65bSEdward Tomasz Napierala 439e595e65bSEdward Tomasz Napierala if (x->x_len < x->x_headers_len) 440e595e65bSEdward Tomasz Napierala errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers); 441e595e65bSEdward Tomasz Napierala x->x_headers_len = po->po_size_of_headers; 442e595e65bSEdward Tomasz Napierala //printf("Size of Headers: %d\n", po->po_size_of_headers); 443e595e65bSEdward Tomasz Napierala 444e595e65bSEdward Tomasz Napierala return (parse_directory(x, off + sizeof(*po), 445e595e65bSEdward Tomasz Napierala po->po_number_of_rva_and_sizes, number_of_sections)); 446e595e65bSEdward Tomasz Napierala } 447e595e65bSEdward Tomasz Napierala 448e595e65bSEdward Tomasz Napierala static void 449e595e65bSEdward Tomasz Napierala parse_optional(struct executable *x, off_t off, int number_of_sections) 450e595e65bSEdward Tomasz Napierala { 451e595e65bSEdward Tomasz Napierala const struct pe_optional_header_32 *po; 452e595e65bSEdward Tomasz Napierala 453e595e65bSEdward Tomasz Napierala //printf("Optional header offset %zd\n", off); 454e595e65bSEdward Tomasz Napierala 455e595e65bSEdward Tomasz Napierala range_check(x, off, sizeof(*po), "PE Optional Header"); 456e595e65bSEdward Tomasz Napierala 457e595e65bSEdward Tomasz Napierala po = (struct pe_optional_header_32 *)(x->x_buf + off); 458e595e65bSEdward Tomasz Napierala 459e595e65bSEdward Tomasz Napierala switch (po->po_magic) { 460e595e65bSEdward Tomasz Napierala case PE_OPTIONAL_MAGIC_32: 461e595e65bSEdward Tomasz Napierala return (parse_optional_32(x, off, number_of_sections)); 462e595e65bSEdward Tomasz Napierala case PE_OPTIONAL_MAGIC_32_PLUS: 463e595e65bSEdward Tomasz Napierala return (parse_optional_32_plus(x, off, number_of_sections)); 464e595e65bSEdward Tomasz Napierala default: 465e595e65bSEdward Tomasz Napierala errx(1, "wrong PE Optional Header magic 0x%x", po->po_magic); 466e595e65bSEdward Tomasz Napierala } 467e595e65bSEdward Tomasz Napierala } 468e595e65bSEdward Tomasz Napierala 469e595e65bSEdward Tomasz Napierala static void 470e595e65bSEdward Tomasz Napierala parse_pe(struct executable *x, off_t off) 471e595e65bSEdward Tomasz Napierala { 472e595e65bSEdward Tomasz Napierala const struct pe_header *pe; 473e595e65bSEdward Tomasz Napierala 474e595e65bSEdward Tomasz Napierala //printf("PE offset %zd, PE size %zd\n", off, sizeof(*pe)); 475e595e65bSEdward Tomasz Napierala 476e595e65bSEdward Tomasz Napierala range_check(x, off, sizeof(*pe), "PE header"); 477e595e65bSEdward Tomasz Napierala 478e595e65bSEdward Tomasz Napierala pe = (struct pe_header *)(x->x_buf + off); 479e595e65bSEdward Tomasz Napierala if (pe->pe_signature != PE_SIGNATURE) 480e595e65bSEdward Tomasz Napierala errx(1, "wrong PE signature 0x%x", pe->pe_signature); 481e595e65bSEdward Tomasz Napierala 482e595e65bSEdward Tomasz Napierala //printf("Number of sections: %d\n", pe->pe_coff.coff_number_of_sections); 483e595e65bSEdward Tomasz Napierala 484e595e65bSEdward Tomasz Napierala parse_optional(x, off + sizeof(*pe), 485e595e65bSEdward Tomasz Napierala pe->pe_coff.coff_number_of_sections); 486e595e65bSEdward Tomasz Napierala } 487e595e65bSEdward Tomasz Napierala 488e595e65bSEdward Tomasz Napierala void 489e595e65bSEdward Tomasz Napierala parse(struct executable *x) 490e595e65bSEdward Tomasz Napierala { 491e595e65bSEdward Tomasz Napierala const struct mz_header *mz; 492e595e65bSEdward Tomasz Napierala 493e595e65bSEdward Tomasz Napierala range_check(x, 0, sizeof(*mz), "MZ header"); 494e595e65bSEdward Tomasz Napierala 495e595e65bSEdward Tomasz Napierala mz = (struct mz_header *)x->x_buf; 496e595e65bSEdward Tomasz Napierala if (mz->mz_signature[0] != 'M' || mz->mz_signature[1] != 'Z') 497e595e65bSEdward Tomasz Napierala errx(1, "MZ header not found"); 498e595e65bSEdward Tomasz Napierala 499e595e65bSEdward Tomasz Napierala return (parse_pe(x, mz->mz_lfanew)); 500e595e65bSEdward Tomasz Napierala } 501e595e65bSEdward Tomasz Napierala 502e595e65bSEdward Tomasz Napierala static off_t 503c97ed06aSMarcin Wojtas append(struct executable *x, void *ptr, size_t len, size_t aligment) 504e595e65bSEdward Tomasz Napierala { 505e595e65bSEdward Tomasz Napierala off_t off; 506e595e65bSEdward Tomasz Napierala 507e595e65bSEdward Tomasz Napierala off = x->x_len; 508c97ed06aSMarcin Wojtas x->x_buf = realloc(x->x_buf, x->x_len + len + aligment); 509e595e65bSEdward Tomasz Napierala if (x->x_buf == NULL) 510e595e65bSEdward Tomasz Napierala err(1, "realloc"); 511e595e65bSEdward Tomasz Napierala memcpy(x->x_buf + x->x_len, ptr, len); 512c97ed06aSMarcin Wojtas memset(x->x_buf + x->x_len + len, 0, aligment); 513c97ed06aSMarcin Wojtas x->x_len += len + aligment; 514e595e65bSEdward Tomasz Napierala 515e595e65bSEdward Tomasz Napierala return (off); 516e595e65bSEdward Tomasz Napierala } 517e595e65bSEdward Tomasz Napierala 518e595e65bSEdward Tomasz Napierala void 519e595e65bSEdward Tomasz Napierala update(struct executable *x) 520e595e65bSEdward Tomasz Napierala { 521e595e65bSEdward Tomasz Napierala uint32_t checksum; 522e595e65bSEdward Tomasz Napierala struct pe_certificate *pc; 523e595e65bSEdward Tomasz Napierala struct pe_directory_entry pde; 524e595e65bSEdward Tomasz Napierala size_t pc_len; 525c97ed06aSMarcin Wojtas size_t pc_aligment; 526e595e65bSEdward Tomasz Napierala off_t pc_off; 527e595e65bSEdward Tomasz Napierala 528e595e65bSEdward Tomasz Napierala pc_len = sizeof(*pc) + x->x_signature_len; 529e595e65bSEdward Tomasz Napierala pc = calloc(1, pc_len); 530e595e65bSEdward Tomasz Napierala if (pc == NULL) 531e595e65bSEdward Tomasz Napierala err(1, "calloc"); 532e595e65bSEdward Tomasz Napierala 533c97ed06aSMarcin Wojtas if (pc_len % PE_ALIGMENT_SIZE > 0) 534c97ed06aSMarcin Wojtas pc_aligment = PE_ALIGMENT_SIZE - (pc_len % PE_ALIGMENT_SIZE); 535c97ed06aSMarcin Wojtas else 536c97ed06aSMarcin Wojtas pc_aligment = 0; 537c97ed06aSMarcin Wojtas 538e595e65bSEdward Tomasz Napierala #if 0 539e595e65bSEdward Tomasz Napierala /* 540e595e65bSEdward Tomasz Napierala * Note that pc_len is the length of pc_certificate, 541e595e65bSEdward Tomasz Napierala * not the whole structure. 542e595e65bSEdward Tomasz Napierala * 543e595e65bSEdward Tomasz Napierala * XXX: That's what the spec says - but it breaks at least 544e595e65bSEdward Tomasz Napierala * sbverify and "pesign -S", so the spec is probably wrong. 545e595e65bSEdward Tomasz Napierala */ 546e595e65bSEdward Tomasz Napierala pc->pc_len = x->x_signature_len; 547e595e65bSEdward Tomasz Napierala #else 548e595e65bSEdward Tomasz Napierala pc->pc_len = pc_len; 549e595e65bSEdward Tomasz Napierala #endif 550e595e65bSEdward Tomasz Napierala pc->pc_revision = PE_CERTIFICATE_REVISION; 551e595e65bSEdward Tomasz Napierala pc->pc_type = PE_CERTIFICATE_TYPE; 552e595e65bSEdward Tomasz Napierala memcpy(&pc->pc_signature, x->x_signature, x->x_signature_len); 553e595e65bSEdward Tomasz Napierala 554c97ed06aSMarcin Wojtas pc_off = append(x, pc, pc_len, pc_aligment); 555e595e65bSEdward Tomasz Napierala #if 0 556e595e65bSEdward Tomasz Napierala printf("added signature chunk at offset %zd, len %zd\n", 557e595e65bSEdward Tomasz Napierala pc_off, pc_len); 558e595e65bSEdward Tomasz Napierala #endif 559e595e65bSEdward Tomasz Napierala 560e595e65bSEdward Tomasz Napierala free(pc); 561e595e65bSEdward Tomasz Napierala 562e595e65bSEdward Tomasz Napierala pde.pde_rva = pc_off; 563c97ed06aSMarcin Wojtas pde.pde_size = pc_len + pc_aligment; 564e595e65bSEdward Tomasz Napierala memcpy(x->x_buf + x->x_certificate_entry_off, &pde, sizeof(pde)); 565e595e65bSEdward Tomasz Napierala 566e595e65bSEdward Tomasz Napierala checksum = compute_checksum(x); 567e595e65bSEdward Tomasz Napierala assert(sizeof(checksum) == x->x_checksum_len); 568e595e65bSEdward Tomasz Napierala memcpy(x->x_buf + x->x_checksum_off, &checksum, sizeof(checksum)); 569e595e65bSEdward Tomasz Napierala #if 0 570e595e65bSEdward Tomasz Napierala printf("new checksum 0x%x\n", checksum); 571e595e65bSEdward Tomasz Napierala #endif 572e595e65bSEdward Tomasz Napierala } 573