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