pe.c revision 293290
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: stable/10/usr.sbin/uefisign/pe.c 293290 2016-01-07 00:40:51Z bdrewery $");
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
57struct mz_header {
58	uint8_t			mz_signature[2];
59	uint8_t			mz_dont_care[58];
60	uint16_t		mz_lfanew;
61} __attribute__((packed));
62
63struct 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
71struct 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
83struct 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
93CTASSERT(offsetof(struct pe_optional_header_32, po_size_of_headers) == 60);
94CTASSERT(offsetof(struct pe_optional_header_32, po_checksum) == 64);
95CTASSERT(offsetof(struct pe_optional_header_32, po_subsystem) == 68);
96CTASSERT(offsetof(struct pe_optional_header_32, po_number_of_rva_and_sizes) == 92);
97
98struct 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
108CTASSERT(offsetof(struct pe_optional_header_32_plus, po_size_of_headers) == 60);
109CTASSERT(offsetof(struct pe_optional_header_32_plus, po_checksum) == 64);
110CTASSERT(offsetof(struct pe_optional_header_32_plus, po_subsystem) == 68);
111CTASSERT(offsetof(struct pe_optional_header_32_plus, po_number_of_rva_and_sizes) == 108);
112
113#define	PE_DIRECTORY_ENTRY_CERTIFICATE	4
114
115struct pe_directory_entry {
116	uint32_t	pde_rva;
117	uint32_t	pde_size;
118} __attribute__((packed));
119
120struct 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
127CTASSERT(offsetof(struct pe_section_header, psh_size_of_raw_data) == 16);
128CTASSERT(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
133struct 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
140void
141range_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
163size_t
164signature_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
182void
183show_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
223static void
224parse_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
259static void
260parse_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 */
309static uint32_t
310compute_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
345static void
346parse_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
395static void
396parse_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
444static void
445parse_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
465static void
466parse_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
484void
485parse(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
498static off_t
499append(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
516void
517update(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