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