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