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