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