2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2014 The FreeBSD Foundation
6 * This software was developed by Edward Tomasz Napierala under sponsorship
7 * from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
33 * PE format reference:
34 * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
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]
58 #define PE_ALIGMENT_SIZE 8
61 uint8_t mz_signature[2];
62 uint8_t mz_dont_care[58];
64 } __attribute__((packed));
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));
72 #define PE_SIGNATURE 0x00004550
75 uint32_t pe_signature;
76 struct coff_header pe_coff;
77 } __attribute__((packed));
79 #define PE_OPTIONAL_MAGIC_32 0x010B
80 #define PE_OPTIONAL_MAGIC_32_PLUS 0x020B
82 #define PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION 10
83 #define PE_OPTIONAL_SUBSYSTEM_EFI_BOOT 11
84 #define PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME 12
86 struct pe_optional_header_32 {
88 uint8_t po_dont_care[58];
89 uint32_t po_size_of_headers;
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));
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);
101 struct pe_optional_header_32_plus {
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));
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);
116 #define PE_DIRECTORY_ENTRY_CERTIFICATE 4
118 struct pe_directory_entry {
121 } __attribute__((packed));
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));
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);
133 #define PE_CERTIFICATE_REVISION 0x0200
134 #define PE_CERTIFICATE_TYPE 0x0002
136 struct pe_certificate {
138 uint16_t pc_revision;
140 char pc_signature[0];
141 } __attribute__((packed));
144 range_check(const struct executable *x, off_t off, size_t len,
149 errx(1, "%s starts at negative offset %jd",
150 name, (intmax_t)off);
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);
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);
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);
167 signature_size(const struct executable *x)
169 const struct pe_directory_entry *pde;
171 range_check(x, x->x_certificate_entry_off,
172 x->x_certificate_entry_len, "Certificate Directory");
174 pde = (struct pe_directory_entry *)
175 (x->x_buf + x->x_certificate_entry_off);
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);
182 return (pde->pde_size);
186 show_certificate(const struct executable *x)
188 struct pe_certificate *pc;
189 const struct pe_directory_entry *pde;
191 range_check(x, x->x_certificate_entry_off,
192 x->x_certificate_entry_len, "Certificate Directory");
194 pde = (struct pe_directory_entry *)
195 (x->x_buf + x->x_certificate_entry_off);
197 if (signature_size(x) == 0) {
198 printf("file not signed\n");
203 printf("certificate chunk at offset %zd, size %zd\n",
204 pde->pde_rva, pde->pde_size);
207 range_check(x, pde->pde_rva, pde->pde_size, "Certificate chunk");
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);
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);
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);
227 parse_section_table(struct executable *x, off_t off, int number_of_sections)
229 const struct pe_section_header *psh;
232 range_check(x, off, sizeof(*psh) * number_of_sections,
235 if (x->x_headers_len <= off + sizeof(*psh) * number_of_sections)
236 errx(1, "section table outside of headers");
238 psh = (const struct pe_section_header *)(x->x_buf + off);
240 if (number_of_sections >= MAX_SECTIONS) {
241 errx(1, "too many sections: got %d, should be %d",
242 number_of_sections, MAX_SECTIONS);
244 x->x_nsections = number_of_sections;
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");
251 range_check(x, psh->psh_pointer_to_raw_data,
252 psh->psh_size_of_raw_data, "section");
254 printf("section %d: start %d, size %d\n",
255 i, psh->psh_pointer_to_raw_data, psh->psh_size_of_raw_data);
257 x->x_section_off[i] = psh->psh_pointer_to_raw_data;
258 x->x_section_len[i] = psh->psh_size_of_raw_data;
264 parse_directory(struct executable *x, off_t off,
265 int number_of_rva_and_sizes, int number_of_sections)
268 const struct pe_directory_entry *pde;
270 //printf("Data Directory at offset %zd\n", off);
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);
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");
282 x->x_certificate_entry_off =
283 off + sizeof(*pde) * PE_DIRECTORY_ENTRY_CERTIFICATE;
284 x->x_certificate_entry_len = sizeof(*pde);
286 printf("certificate directory entry at offset %zd, len %zd\n",
287 x->x_certificate_entry_off, x->x_certificate_entry_len);
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);
296 return (parse_section_table(x,
297 off + sizeof(*pde) * number_of_rva_and_sizes, number_of_sections));
301 * The PE checksum algorithm is undocumented; this code is mostly based on
302 * http://forum.sysinternals.com/optional-header-checksum-calculation_topic24214.html
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."
308 * Note that most software does not care about the checksum at all; perhaps
309 * we could just set it to 0 instead.
314 compute_checksum(const struct executable *x)
320 range_check(x, x->x_checksum_off, x->x_checksum_len, "PE checksum");
322 assert(x->x_checksum_off % 2 == 0);
324 for (i = 0; i + sizeof(tmp) < x->x_len; i += 2) {
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.
329 if (i == x->x_checksum_off || i == x->x_checksum_off + 2) {
332 assert(i + sizeof(tmp) <= x->x_len);
333 memcpy(&tmp, x->x_buf + i, sizeof(tmp));
337 cksum += cksum >> 16;
341 cksum += cksum >> 16;
350 parse_optional_32_plus(struct executable *x, off_t off,
351 int number_of_sections)
354 uint32_t computed_checksum;
356 const struct pe_optional_header_32_plus *po;
358 range_check(x, off, sizeof(*po), "PE Optional Header");
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:
367 errx(1, "wrong PE Optional Header subsystem 0x%x",
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);
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);
380 printf("checksum 0x%x at offset %zd, len %zd\n",
381 po->po_checksum, x->x_checksum_off, x->x_checksum_len);
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);
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);
395 return (parse_directory(x, off + sizeof(*po),
396 po->po_number_of_rva_and_sizes, number_of_sections));
400 parse_optional_32(struct executable *x, off_t off, int number_of_sections)
403 uint32_t computed_checksum;
405 const struct pe_optional_header_32 *po;
407 range_check(x, off, sizeof(*po), "PE Optional Header");
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:
416 errx(1, "wrong PE Optional Header subsystem 0x%x",
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);
425 x->x_checksum_off = off +
426 offsetof(struct pe_optional_header_32, po_checksum);
427 x->x_checksum_len = sizeof(po->po_checksum);
429 printf("checksum at offset %zd, len %zd\n",
430 x->x_checksum_off, x->x_checksum_len);
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);
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);
444 return (parse_directory(x, off + sizeof(*po),
445 po->po_number_of_rva_and_sizes, number_of_sections));
449 parse_optional(struct executable *x, off_t off, int number_of_sections)
451 const struct pe_optional_header_32 *po;
453 //printf("Optional header offset %zd\n", off);
455 range_check(x, off, sizeof(*po), "PE Optional Header");
457 po = (struct pe_optional_header_32 *)(x->x_buf + off);
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));
465 errx(1, "wrong PE Optional Header magic 0x%x", po->po_magic);
470 parse_pe(struct executable *x, off_t off)
472 const struct pe_header *pe;
474 //printf("PE offset %zd, PE size %zd\n", off, sizeof(*pe));
476 range_check(x, off, sizeof(*pe), "PE header");
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);
482 //printf("Number of sections: %d\n", pe->pe_coff.coff_number_of_sections);
484 parse_optional(x, off + sizeof(*pe),
485 pe->pe_coff.coff_number_of_sections);
489 parse(struct executable *x)
491 const struct mz_header *mz;
493 range_check(x, 0, sizeof(*mz), "MZ header");
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");
499 return (parse_pe(x, mz->mz_lfanew));
503 append(struct executable *x, void *ptr, size_t len, size_t aligment)
508 x->x_buf = realloc(x->x_buf, x->x_len + len + aligment);
509 if (x->x_buf == NULL)
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;
519 update(struct executable *x)
522 struct pe_certificate *pc;
523 struct pe_directory_entry pde;
528 pc_len = sizeof(*pc) + x->x_signature_len;
529 pc = calloc(1, pc_len);
533 if (pc_len % PE_ALIGMENT_SIZE > 0)
534 pc_aligment = PE_ALIGMENT_SIZE - (pc_len % PE_ALIGMENT_SIZE);
540 * Note that pc_len is the length of pc_certificate,
541 * not the whole structure.
543 * XXX: That's what the spec says - but it breaks at least
544 * sbverify and "pesign -S", so the spec is probably wrong.
546 pc->pc_len = x->x_signature_len;
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);
554 pc_off = append(x, pc, pc_len, pc_aligment);
556 printf("added signature chunk at offset %zd, len %zd\n",
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));
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));
570 printf("new checksum 0x%x\n", checksum);