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