xref: /dragonfly/usr.sbin/uefisign/pe.c (revision 7608722c)
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: head/usr.sbin/uefisign/pe.c 289677 2015-10-21 05:37:09Z eadler $");
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