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_size_of_raw_data > 0 && 248 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