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