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