2 * Copyright (c) 2014 The FreeBSD Foundation
5 * This software was developed by Edward Tomasz Napierala under sponsorship
6 * from the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
32 * PE format reference:
33 * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD: head/usr.sbin/uefisign/pe.c 289677 2015-10-21 05:37:09Z eadler $");
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]
58 uint8_t mz_signature
[2];
59 uint8_t mz_dont_care
[58];
61 } __attribute__((packed
));
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
));
69 #define PE_SIGNATURE 0x00004550
72 uint32_t pe_signature
;
73 struct coff_header pe_coff
;
74 } __attribute__((packed
));
76 #define PE_OPTIONAL_MAGIC_32 0x010B
77 #define PE_OPTIONAL_MAGIC_32_PLUS 0x020B
79 #define PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION 10
80 #define PE_OPTIONAL_SUBSYSTEM_EFI_BOOT 11
81 #define PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME 12
83 struct pe_optional_header_32
{
85 uint8_t po_dont_care
[58];
86 uint32_t po_size_of_headers
;
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
));
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);
98 struct pe_optional_header_32_plus
{
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
));
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);
113 #define PE_DIRECTORY_ENTRY_CERTIFICATE 4
115 struct pe_directory_entry
{
118 } __attribute__((packed
));
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
));
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);
130 #define PE_CERTIFICATE_REVISION 0x0200
131 #define PE_CERTIFICATE_TYPE 0x0002
133 struct pe_certificate
{
135 uint16_t pc_revision
;
137 char pc_signature
[0];
138 } __attribute__((packed
));
141 range_check(const struct executable
*x
, off_t off
, size_t len
,
146 errx(1, "%s starts at negative offset %jd",
147 name
, (intmax_t)off
);
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
);
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
);
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
);
164 signature_size(const struct executable
*x
)
166 const struct pe_directory_entry
*pde
;
168 range_check(x
, x
->x_certificate_entry_off
,
169 x
->x_certificate_entry_len
, "Certificate Directory");
171 pde
= (struct pe_directory_entry
*)
172 (x
->x_buf
+ x
->x_certificate_entry_off
);
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
);
179 return (pde
->pde_size
);
183 show_certificate(const struct executable
*x
)
185 struct pe_certificate
*pc
;
186 const struct pe_directory_entry
*pde
;
188 range_check(x
, x
->x_certificate_entry_off
,
189 x
->x_certificate_entry_len
, "Certificate Directory");
191 pde
= (struct pe_directory_entry
*)
192 (x
->x_buf
+ x
->x_certificate_entry_off
);
194 if (signature_size(x
) == 0) {
195 printf("file not signed\n");
200 printf("certificate chunk at offset %zd, size %zd\n",
201 pde
->pde_rva
, pde
->pde_size
);
204 range_check(x
, pde
->pde_rva
, pde
->pde_size
, "Certificate chunk");
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
);
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
);
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
);
224 parse_section_table(struct executable
*x
, off_t off
, int number_of_sections
)
226 const struct pe_section_header
*psh
;
229 range_check(x
, off
, sizeof(*psh
) * number_of_sections
,
232 if (x
->x_headers_len
<= off
+ sizeof(*psh
) * number_of_sections
)
233 errx(1, "section table outside of headers");
235 psh
= (const struct pe_section_header
*)(x
->x_buf
+ off
);
237 if (number_of_sections
>= MAX_SECTIONS
) {
238 errx(1, "too many sections: got %d, should be %d",
239 number_of_sections
, MAX_SECTIONS
);
241 x
->x_nsections
= number_of_sections
;
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");
247 range_check(x
, psh
->psh_pointer_to_raw_data
,
248 psh
->psh_size_of_raw_data
, "section");
250 printf("section %d: start %d, size %d\n",
251 i
, psh
->psh_pointer_to_raw_data
, psh
->psh_size_of_raw_data
);
253 x
->x_section_off
[i
] = psh
->psh_pointer_to_raw_data
;
254 x
->x_section_len
[i
] = psh
->psh_size_of_raw_data
;
260 parse_directory(struct executable
*x
, off_t off
,
261 int number_of_rva_and_sizes
, int number_of_sections
)
264 const struct pe_directory_entry
*pde
;
266 //printf("Data Directory at offset %zd\n", off);
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
);
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");
278 x
->x_certificate_entry_off
=
279 off
+ sizeof(*pde
) * PE_DIRECTORY_ENTRY_CERTIFICATE
;
280 x
->x_certificate_entry_len
= sizeof(*pde
);
282 printf("certificate directory entry at offset %zd, len %zd\n",
283 x
->x_certificate_entry_off
, x
->x_certificate_entry_len
);
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
);
292 return (parse_section_table(x
,
293 off
+ sizeof(*pde
) * number_of_rva_and_sizes
, number_of_sections
));
297 * The PE checksum algorithm is undocumented; this code is mostly based on
298 * http://forum.sysinternals.com/optional-header-checksum-calculation_topic24214.html
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."
304 * Note that most software does not care about the checksum at all; perhaps
305 * we could just set it to 0 instead.
310 compute_checksum(const struct executable
*x
)
316 range_check(x
, x
->x_checksum_off
, x
->x_checksum_len
, "PE checksum");
318 assert(x
->x_checksum_off
% 2 == 0);
320 for (i
= 0; i
+ sizeof(tmp
) < x
->x_len
; i
+= 2) {
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.
325 if (i
== x
->x_checksum_off
|| i
== x
->x_checksum_off
+ 2) {
328 assert(i
+ sizeof(tmp
) <= x
->x_len
);
329 memcpy(&tmp
, x
->x_buf
+ i
, sizeof(tmp
));
333 cksum
+= cksum
>> 16;
337 cksum
+= cksum
>> 16;
346 parse_optional_32_plus(struct executable
*x
, off_t off
,
347 int number_of_sections
)
350 uint32_t computed_checksum
;
352 const struct pe_optional_header_32_plus
*po
;
354 range_check(x
, off
, sizeof(*po
), "PE Optional Header");
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
:
363 errx(1, "wrong PE Optional Header subsystem 0x%x",
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
);
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
);
376 printf("checksum 0x%x at offset %zd, len %zd\n",
377 po
->po_checksum
, x
->x_checksum_off
, x
->x_checksum_len
);
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
);
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);
391 return (parse_directory(x
, off
+ sizeof(*po
),
392 po
->po_number_of_rva_and_sizes
, number_of_sections
));
396 parse_optional_32(struct executable
*x
, off_t off
, int number_of_sections
)
399 uint32_t computed_checksum
;
401 const struct pe_optional_header_32
*po
;
403 range_check(x
, off
, sizeof(*po
), "PE Optional Header");
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
:
412 errx(1, "wrong PE Optional Header subsystem 0x%x",
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
);
421 x
->x_checksum_off
= off
+
422 offsetof(struct pe_optional_header_32
, po_checksum
);
423 x
->x_checksum_len
= sizeof(po
->po_checksum
);
425 printf("checksum at offset %zd, len %zd\n",
426 x
->x_checksum_off
, x
->x_checksum_len
);
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
);
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);
440 return (parse_directory(x
, off
+ sizeof(*po
),
441 po
->po_number_of_rva_and_sizes
, number_of_sections
));
445 parse_optional(struct executable
*x
, off_t off
, int number_of_sections
)
447 const struct pe_optional_header_32
*po
;
449 //printf("Optional header offset %zd\n", off);
451 range_check(x
, off
, sizeof(*po
), "PE Optional Header");
453 po
= (struct pe_optional_header_32
*)(x
->x_buf
+ off
);
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
));
461 errx(1, "wrong PE Optional Header magic 0x%x", po
->po_magic
);
466 parse_pe(struct executable
*x
, off_t off
)
468 const struct pe_header
*pe
;
470 //printf("PE offset %zd, PE size %zd\n", off, sizeof(*pe));
472 range_check(x
, off
, sizeof(*pe
), "PE header");
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
);
478 //printf("Number of sections: %d\n", pe->pe_coff.coff_number_of_sections);
480 parse_optional(x
, off
+ sizeof(*pe
),
481 pe
->pe_coff
.coff_number_of_sections
);
485 parse(struct executable
*x
)
487 const struct mz_header
*mz
;
489 range_check(x
, 0, sizeof(*mz
), "MZ header");
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");
495 return (parse_pe(x
, mz
->mz_lfanew
));
499 append(struct executable
*x
, void *ptr
, size_t len
)
507 x
->x_buf
= realloc(x
->x_buf
, x
->x_len
+ len
);
508 if (x
->x_buf
== NULL
)
510 memcpy(x
->x_buf
+ x
->x_len
, ptr
, len
);
517 update(struct executable
*x
)
520 struct pe_certificate
*pc
;
521 struct pe_directory_entry pde
;
525 pc_len
= sizeof(*pc
) + x
->x_signature_len
;
526 pc
= calloc(1, pc_len
);
532 * Note that pc_len is the length of pc_certificate,
533 * not the whole structure.
535 * XXX: That's what the spec says - but it breaks at least
536 * sbverify and "pesign -S", so the spec is probably wrong.
538 pc
->pc_len
= x
->x_signature_len
;
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
);
546 pc_off
= append(x
, pc
, pc_len
);
548 printf("added signature chunk at offset %zd, len %zd\n",
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
));
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
));
562 printf("new checksum 0x%x\n", checksum
);