pe.c revision 279315
1279315Strasz/*-
2279315Strasz * Copyright (c) 2014 The FreeBSD Foundation
3279315Strasz * All rights reserved.
4279315Strasz *
5279315Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6279315Strasz * from the FreeBSD Foundation.
7279315Strasz *
8279315Strasz * Redistribution and use in source and binary forms, with or without
9279315Strasz * modification, are permitted provided that the following conditions
10279315Strasz * are met:
11279315Strasz * 1. Redistributions of source code must retain the above copyright
12279315Strasz *    notice, this list of conditions and the following disclaimer.
13279315Strasz * 2. Redistributions in binary form must reproduce the above copyright
14279315Strasz *    notice, this list of conditions and the following disclaimer in the
15279315Strasz *    documentation and/or other materials provided with the distribution.
16279315Strasz *
17279315Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18279315Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19279315Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20279315Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21279315Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22279315Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23279315Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24279315Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25279315Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26279315Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27279315Strasz * SUCH DAMAGE.
28279315Strasz *
29279315Strasz */
30279315Strasz
31279315Strasz/*
32279315Strasz * PE format reference:
33279315Strasz * http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
34279315Strasz */
35279315Strasz
36279315Strasz#include <sys/cdefs.h>
37279315Strasz__FBSDID("$FreeBSD: head/usr.sbin/uefisign/pe.c 279315 2015-02-26 09:15:24Z trasz $");
38279315Strasz
39279315Strasz#include <assert.h>
40279315Strasz#include <err.h>
41279315Strasz#include <errno.h>
42279315Strasz#include <stddef.h>
43279315Strasz#include <stdio.h>
44279315Strasz#include <stdint.h>
45279315Strasz#include <stdlib.h>
46279315Strasz#include <string.h>
47279315Strasz#include <unistd.h>
48279315Strasz
49279315Strasz#include "uefisign.h"
50279315Strasz
51279315Strasz#ifndef CTASSERT
52279315Strasz#define CTASSERT(x)		_CTASSERT(x, __LINE__)
53279315Strasz#define _CTASSERT(x, y)		__CTASSERT(x, y)
54279315Strasz#define __CTASSERT(x, y)	typedef char __assert_ ## y [(x) ? 1 : -1]
55279315Strasz#endif
56279315Strasz
57279315Straszstruct mz_header {
58279315Strasz	uint8_t			mz_signature[2];
59279315Strasz	uint8_t			mz_dont_care[58];
60279315Strasz	uint16_t		mz_lfanew;
61279315Strasz} __attribute__((packed));
62279315Strasz
63279315Straszstruct coff_header {
64279315Strasz	uint8_t			coff_dont_care[2];
65279315Strasz	uint16_t		coff_number_of_sections;
66279315Strasz	uint8_t			coff_dont_care_either[16];
67279315Strasz} __attribute__((packed));
68279315Strasz
69279315Strasz#define	PE_SIGNATURE		0x00004550
70279315Strasz
71279315Straszstruct pe_header {
72279315Strasz	uint32_t		pe_signature;
73279315Strasz	struct coff_header	pe_coff;
74279315Strasz} __attribute__((packed));
75279315Strasz
76279315Strasz#define	PE_OPTIONAL_MAGIC_32		0x010B
77279315Strasz#define	PE_OPTIONAL_MAGIC_32_PLUS	0x020B
78279315Strasz
79279315Strasz#define	PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION	10
80279315Strasz#define	PE_OPTIONAL_SUBSYSTEM_EFI_BOOT		11
81279315Strasz#define	PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME	12
82279315Strasz
83279315Straszstruct pe_optional_header_32 {
84279315Strasz	uint16_t		po_magic;
85279315Strasz	uint8_t			po_dont_care[58];
86279315Strasz	uint32_t		po_size_of_headers;
87279315Strasz	uint32_t		po_checksum;
88279315Strasz	uint16_t		po_subsystem;
89279315Strasz	uint8_t			po_dont_care_either[22];
90279315Strasz	uint32_t		po_number_of_rva_and_sizes;
91279315Strasz} __attribute__((packed));
92279315Strasz
93279315StraszCTASSERT(offsetof(struct pe_optional_header_32, po_size_of_headers) == 60);
94279315StraszCTASSERT(offsetof(struct pe_optional_header_32, po_checksum) == 64);
95279315StraszCTASSERT(offsetof(struct pe_optional_header_32, po_subsystem) == 68);
96279315StraszCTASSERT(offsetof(struct pe_optional_header_32, po_number_of_rva_and_sizes) == 92);
97279315Strasz
98279315Straszstruct pe_optional_header_32_plus {
99279315Strasz	uint16_t		po_magic;
100279315Strasz	uint8_t			po_dont_care[58];
101279315Strasz	uint32_t		po_size_of_headers;
102279315Strasz	uint32_t		po_checksum;
103279315Strasz	uint16_t		po_subsystem;
104279315Strasz	uint8_t			po_dont_care_either[38];
105279315Strasz	uint32_t		po_number_of_rva_and_sizes;
106279315Strasz} __attribute__((packed));
107279315Strasz
108279315StraszCTASSERT(offsetof(struct pe_optional_header_32_plus, po_size_of_headers) == 60);
109279315StraszCTASSERT(offsetof(struct pe_optional_header_32_plus, po_checksum) == 64);
110279315StraszCTASSERT(offsetof(struct pe_optional_header_32_plus, po_subsystem) == 68);
111279315StraszCTASSERT(offsetof(struct pe_optional_header_32_plus, po_number_of_rva_and_sizes) == 108);
112279315Strasz
113279315Strasz#define	PE_DIRECTORY_ENTRY_CERTIFICATE	4
114279315Strasz
115279315Straszstruct pe_directory_entry {
116279315Strasz	uint32_t	pde_rva;
117279315Strasz	uint32_t	pde_size;
118279315Strasz} __attribute__((packed));
119279315Strasz
120279315Straszstruct pe_section_header {
121279315Strasz	uint8_t			psh_dont_care[16];
122279315Strasz	uint32_t		psh_size_of_raw_data;
123279315Strasz	uint32_t		psh_pointer_to_raw_data;
124279315Strasz	uint8_t			psh_dont_care_either[16];
125279315Strasz} __attribute__((packed));
126279315Strasz
127279315StraszCTASSERT(offsetof(struct pe_section_header, psh_size_of_raw_data) == 16);
128279315StraszCTASSERT(offsetof(struct pe_section_header, psh_pointer_to_raw_data) == 20);
129279315Strasz
130279315Strasz#define	PE_CERTIFICATE_REVISION		0x0200
131279315Strasz#define	PE_CERTIFICATE_TYPE		0x0002
132279315Strasz
133279315Straszstruct pe_certificate {
134279315Strasz	uint32_t	pc_len;
135279315Strasz	uint16_t	pc_revision;
136279315Strasz	uint16_t	pc_type;
137279315Strasz	char		pc_signature[0];
138279315Strasz} __attribute__((packed));
139279315Strasz
140279315Straszvoid
141279315Straszrange_check(const struct executable *x, off_t off, size_t len,
142279315Strasz    const char *name)
143279315Strasz{
144279315Strasz
145279315Strasz	if (off < 0) {
146279315Strasz		errx(1, "%s starts at negative offset %jd",
147279315Strasz		    name, (intmax_t)off);
148279315Strasz	}
149279315Strasz	if (off >= (off_t)x->x_len) {
150279315Strasz		errx(1, "%s starts at %jd, past the end of executable at %zd",
151279315Strasz		    name, (intmax_t)off, x->x_len);
152279315Strasz	}
153279315Strasz	if (len >= x->x_len) {
154279315Strasz		errx(1, "%s size %zd is larger than the executable size %zd",
155279315Strasz		    name, len, x->x_len);
156279315Strasz	}
157279315Strasz	if (off + len > x->x_len) {
158279315Strasz		errx(1, "%s extends to %jd, past the end of executable at %zd",
159279315Strasz		    name, (intmax_t)(off + len), x->x_len);
160279315Strasz	}
161279315Strasz}
162279315Strasz
163279315Straszsize_t
164279315Straszsignature_size(const struct executable *x)
165279315Strasz{
166279315Strasz	const struct pe_directory_entry *pde;
167279315Strasz
168279315Strasz	range_check(x, x->x_certificate_entry_off,
169279315Strasz	    x->x_certificate_entry_len, "Certificate Directory");
170279315Strasz
171279315Strasz	pde = (struct pe_directory_entry *)
172279315Strasz	    (x->x_buf + x->x_certificate_entry_off);
173279315Strasz
174279315Strasz	if (pde->pde_rva != 0 && pde->pde_size == 0)
175279315Strasz		warnx("signature size is 0, but its RVA is %d", pde->pde_rva);
176279315Strasz	if (pde->pde_rva == 0 && pde->pde_size != 0)
177279315Strasz		warnx("signature RVA is 0, but its size is %d", pde->pde_size);
178279315Strasz
179279315Strasz	return (pde->pde_size);
180279315Strasz}
181279315Strasz
182279315Straszvoid
183279315Straszshow_certificate(const struct executable *x)
184279315Strasz{
185279315Strasz	struct pe_certificate *pc;
186279315Strasz	const struct pe_directory_entry *pde;
187279315Strasz
188279315Strasz	range_check(x, x->x_certificate_entry_off,
189279315Strasz	    x->x_certificate_entry_len, "Certificate Directory");
190279315Strasz
191279315Strasz	pde = (struct pe_directory_entry *)
192279315Strasz	    (x->x_buf + x->x_certificate_entry_off);
193279315Strasz
194279315Strasz	if (signature_size(x) == 0) {
195279315Strasz		printf("file not signed\n");
196279315Strasz		return;
197279315Strasz	}
198279315Strasz
199279315Strasz#if 0
200279315Strasz	printf("certificate chunk at offset %zd, size %zd\n",
201279315Strasz	    pde->pde_rva, pde->pde_size);
202279315Strasz#endif
203279315Strasz
204279315Strasz	range_check(x, pde->pde_rva, pde->pde_size, "Certificate chunk");
205279315Strasz
206279315Strasz	pc = (struct pe_certificate *)(x->x_buf + pde->pde_rva);
207279315Strasz	if (pc->pc_revision != PE_CERTIFICATE_REVISION) {
208279315Strasz		errx(1, "wrong certificate chunk revision, is %d, should be %d",
209279315Strasz		    pc->pc_revision, PE_CERTIFICATE_REVISION);
210279315Strasz	}
211279315Strasz	if (pc->pc_type != PE_CERTIFICATE_TYPE) {
212279315Strasz		errx(1, "wrong certificate chunk type, is %d, should be %d",
213279315Strasz		    pc->pc_type, PE_CERTIFICATE_TYPE);
214279315Strasz	}
215279315Strasz	printf("to dump PKCS7:\n    "
216279315Strasz	    "dd if='%s' bs=1 skip=%zd | openssl pkcs7 -inform DER -print\n",
217279315Strasz	    x->x_path, pde->pde_rva + offsetof(struct pe_certificate, pc_signature));
218279315Strasz	printf("to dump raw ASN.1:\n    "
219279315Strasz	    "openssl asn1parse -i -inform DER -offset %zd -in '%s'\n",
220279315Strasz	    pde->pde_rva + offsetof(struct pe_certificate, pc_signature), x->x_path);
221279315Strasz}
222279315Strasz
223279315Straszstatic void
224279315Straszparse_section_table(struct executable *x, off_t off, int number_of_sections)
225279315Strasz{
226279315Strasz	const struct pe_section_header *psh;
227279315Strasz	int i;
228279315Strasz
229279315Strasz	range_check(x, off, sizeof(*psh) * number_of_sections,
230279315Strasz	    "section table");
231279315Strasz
232279315Strasz	if (x->x_headers_len <= off + sizeof(*psh) * number_of_sections)
233279315Strasz		errx(1, "section table outside of headers");
234279315Strasz
235279315Strasz	psh = (const struct pe_section_header *)(x->x_buf + off);
236279315Strasz
237279315Strasz	if (number_of_sections >= MAX_SECTIONS) {
238279315Strasz		errx(1, "too many sections: got %d, should be %d",
239279315Strasz		    number_of_sections, MAX_SECTIONS);
240279315Strasz	}
241279315Strasz	x->x_nsections = number_of_sections;
242279315Strasz
243279315Strasz	for (i = 0; i < number_of_sections; i++) {
244279315Strasz		if (psh->psh_pointer_to_raw_data < x->x_headers_len)
245279315Strasz			errx(1, "section points inside the headers");
246279315Strasz
247279315Strasz		range_check(x, psh->psh_pointer_to_raw_data,
248279315Strasz		    psh->psh_size_of_raw_data, "section");
249279315Strasz#if 0
250279315Strasz		printf("section %d: start %d, size %d\n",
251279315Strasz		    i, psh->psh_pointer_to_raw_data, psh->psh_size_of_raw_data);
252279315Strasz#endif
253279315Strasz		x->x_section_off[i] = psh->psh_pointer_to_raw_data;
254279315Strasz		x->x_section_len[i] = psh->psh_size_of_raw_data;
255279315Strasz		psh++;
256279315Strasz	}
257279315Strasz}
258279315Strasz
259279315Straszstatic void
260279315Straszparse_directory(struct executable *x, off_t off,
261279315Strasz    int number_of_rva_and_sizes, int number_of_sections)
262279315Strasz{
263279315Strasz	//int i;
264279315Strasz	const struct pe_directory_entry *pde;
265279315Strasz
266279315Strasz	//printf("Data Directory at offset %zd\n", off);
267279315Strasz
268279315Strasz	if (number_of_rva_and_sizes <= PE_DIRECTORY_ENTRY_CERTIFICATE) {
269279315Strasz		errx(1, "wrong NumberOfRvaAndSizes %d; should be at least %d",
270279315Strasz		    number_of_rva_and_sizes, PE_DIRECTORY_ENTRY_CERTIFICATE);
271279315Strasz	}
272279315Strasz
273279315Strasz	range_check(x, off, sizeof(*pde) * number_of_rva_and_sizes,
274279315Strasz	    "PE Data Directory");
275279315Strasz	if (x->x_headers_len <= off + sizeof(*pde) * number_of_rva_and_sizes)
276279315Strasz		errx(1, "PE Data Directory outside of headers");
277279315Strasz
278279315Strasz	x->x_certificate_entry_off =
279279315Strasz	    off + sizeof(*pde) * PE_DIRECTORY_ENTRY_CERTIFICATE;
280279315Strasz	x->x_certificate_entry_len = sizeof(*pde);
281279315Strasz#if 0
282279315Strasz	printf("certificate directory entry at offset %zd, len %zd\n",
283279315Strasz	    x->x_certificate_entry_off, x->x_certificate_entry_len);
284279315Strasz
285279315Strasz	pde = (struct pe_directory_entry *)(x->x_buf + off);
286279315Strasz	for (i = 0; i < number_of_rva_and_sizes; i++) {
287279315Strasz		printf("rva %zd, size %zd\n", pde->pde_rva, pde->pde_size);
288279315Strasz		pde++;
289279315Strasz	}
290279315Strasz#endif
291279315Strasz
292279315Strasz	return (parse_section_table(x,
293279315Strasz	    off + sizeof(*pde) * number_of_rva_and_sizes, number_of_sections));
294279315Strasz}
295279315Strasz
296279315Strasz/*
297279315Strasz * The PE checksum algorithm is undocumented; this code is mostly based on
298279315Strasz * http://forum.sysinternals.com/optional-header-checksum-calculation_topic24214.html
299279315Strasz *
300279315Strasz * "Sum the entire image file, excluding the CheckSum field in the optional
301279315Strasz * header, as an array of USHORTs, allowing any carry above 16 bits to be added
302279315Strasz * back onto the low 16 bits. Then add the file size to get a 32-bit value."
303279315Strasz *
304279315Strasz * Note that most software does not care about the checksum at all; perhaps
305279315Strasz * we could just set it to 0 instead.
306279315Strasz *
307279315Strasz * XXX: Endianess?
308279315Strasz */
309279315Straszstatic uint32_t
310279315Straszcompute_checksum(const struct executable *x)
311279315Strasz{
312279315Strasz	uint32_t cksum = 0;
313279315Strasz	uint16_t tmp;
314279315Strasz	int i;
315279315Strasz
316279315Strasz	range_check(x, x->x_checksum_off, x->x_checksum_len, "PE checksum");
317279315Strasz
318279315Strasz	assert(x->x_checksum_off % 2 == 0);
319279315Strasz
320279315Strasz	for (i = 0; i + sizeof(tmp) < x->x_len; i += 2) {
321279315Strasz		/*
322279315Strasz		 * Don't checksum the checksum.  The +2 is because the checksum
323279315Strasz		 * is 4 bytes, and here we're iterating over 2 byte chunks.
324279315Strasz		 */
325279315Strasz		if (i == x->x_checksum_off || i == x->x_checksum_off + 2) {
326279315Strasz			tmp = 0;
327279315Strasz		} else {
328279315Strasz			assert(i + sizeof(tmp) <= x->x_len);
329279315Strasz			memcpy(&tmp, x->x_buf + i, sizeof(tmp));
330279315Strasz		}
331279315Strasz
332279315Strasz		cksum += tmp;
333279315Strasz		cksum += cksum >> 16;
334279315Strasz		cksum &= 0xffff;
335279315Strasz	}
336279315Strasz
337279315Strasz	cksum += cksum >> 16;
338279315Strasz	cksum &= 0xffff;
339279315Strasz
340279315Strasz	cksum += x->x_len;
341279315Strasz
342279315Strasz	return (cksum);
343279315Strasz}
344279315Strasz
345279315Straszstatic void
346279315Straszparse_optional_32_plus(struct executable *x, off_t off,
347279315Strasz    int number_of_sections)
348279315Strasz{
349279315Strasz	uint32_t computed_checksum;
350279315Strasz	const struct pe_optional_header_32_plus	*po;
351279315Strasz
352279315Strasz	range_check(x, off, sizeof(*po), "PE Optional Header");
353279315Strasz
354279315Strasz	po = (struct pe_optional_header_32_plus *)(x->x_buf + off);
355279315Strasz	switch (po->po_subsystem) {
356279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION:
357279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT:
358279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME:
359279315Strasz		break;
360279315Strasz	default:
361279315Strasz		errx(1, "wrong PE Optional Header subsystem 0x%x",
362279315Strasz		    po->po_subsystem);
363279315Strasz	}
364279315Strasz
365279315Strasz#if 0
366279315Strasz	printf("subsystem %d, checksum 0x%x, %d data directories\n",
367279315Strasz	    po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes);
368279315Strasz#endif
369279315Strasz
370279315Strasz	x->x_checksum_off = off +
371279315Strasz	    offsetof(struct pe_optional_header_32_plus, po_checksum);
372279315Strasz	x->x_checksum_len = sizeof(po->po_checksum);
373279315Strasz#if 0
374279315Strasz	printf("checksum 0x%x at offset %zd, len %zd\n",
375279315Strasz	    po->po_checksum, x->x_checksum_off, x->x_checksum_len);
376279315Strasz#endif
377279315Strasz
378279315Strasz	computed_checksum = compute_checksum(x);
379279315Strasz	if (computed_checksum != po->po_checksum) {
380279315Strasz		warnx("invalid PE+ checksum; is 0x%x, should be 0x%x",
381279315Strasz		    po->po_checksum, computed_checksum);
382279315Strasz	}
383279315Strasz
384279315Strasz	if (x->x_len < x->x_headers_len)
385279315Strasz		errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers);
386279315Strasz	x->x_headers_len = po->po_size_of_headers;
387279315Strasz	//printf("Size of Headers: %d\n", po->po_size_of_headers);
388279315Strasz
389279315Strasz	return (parse_directory(x, off + sizeof(*po),
390279315Strasz	    po->po_number_of_rva_and_sizes, number_of_sections));
391279315Strasz}
392279315Strasz
393279315Straszstatic void
394279315Straszparse_optional_32(struct executable *x, off_t off, int number_of_sections)
395279315Strasz{
396279315Strasz	uint32_t computed_checksum;
397279315Strasz	const struct pe_optional_header_32 *po;
398279315Strasz
399279315Strasz	range_check(x, off, sizeof(*po), "PE Optional Header");
400279315Strasz
401279315Strasz	po = (struct pe_optional_header_32 *)(x->x_buf + off);
402279315Strasz	switch (po->po_subsystem) {
403279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION:
404279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT:
405279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME:
406279315Strasz		break;
407279315Strasz	default:
408279315Strasz		errx(1, "wrong PE Optional Header subsystem 0x%x",
409279315Strasz		    po->po_subsystem);
410279315Strasz	}
411279315Strasz
412279315Strasz#if 0
413279315Strasz	printf("subsystem %d, checksum 0x%x, %d data directories\n",
414279315Strasz	    po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes);
415279315Strasz#endif
416279315Strasz
417279315Strasz	x->x_checksum_off = off +
418279315Strasz	    offsetof(struct pe_optional_header_32, po_checksum);
419279315Strasz	x->x_checksum_len = sizeof(po->po_checksum);
420279315Strasz#if 0
421279315Strasz	printf("checksum at offset %zd, len %zd\n",
422279315Strasz	    x->x_checksum_off, x->x_checksum_len);
423279315Strasz#endif
424279315Strasz
425279315Strasz	computed_checksum = compute_checksum(x);
426279315Strasz	if (computed_checksum != po->po_checksum) {
427279315Strasz		warnx("invalid PE checksum; is 0x%x, should be 0x%x",
428279315Strasz		    po->po_checksum, computed_checksum);
429279315Strasz	}
430279315Strasz
431279315Strasz	if (x->x_len < x->x_headers_len)
432279315Strasz		errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers);
433279315Strasz	x->x_headers_len = po->po_size_of_headers;
434279315Strasz	//printf("Size of Headers: %d\n", po->po_size_of_headers);
435279315Strasz
436279315Strasz	return (parse_directory(x, off + sizeof(*po),
437279315Strasz	    po->po_number_of_rva_and_sizes, number_of_sections));
438279315Strasz}
439279315Strasz
440279315Straszstatic void
441279315Straszparse_optional(struct executable *x, off_t off, int number_of_sections)
442279315Strasz{
443279315Strasz	const struct pe_optional_header_32 *po;
444279315Strasz
445279315Strasz	//printf("Optional header offset %zd\n", off);
446279315Strasz
447279315Strasz	range_check(x, off, sizeof(*po), "PE Optional Header");
448279315Strasz
449279315Strasz	po = (struct pe_optional_header_32 *)(x->x_buf + off);
450279315Strasz
451279315Strasz	switch (po->po_magic) {
452279315Strasz	case PE_OPTIONAL_MAGIC_32:
453279315Strasz		return (parse_optional_32(x, off, number_of_sections));
454279315Strasz	case PE_OPTIONAL_MAGIC_32_PLUS:
455279315Strasz		return (parse_optional_32_plus(x, off, number_of_sections));
456279315Strasz	default:
457279315Strasz		errx(1, "wrong PE Optional Header magic 0x%x", po->po_magic);
458279315Strasz	}
459279315Strasz}
460279315Strasz
461279315Straszstatic void
462279315Straszparse_pe(struct executable *x, off_t off)
463279315Strasz{
464279315Strasz	const struct pe_header *pe;
465279315Strasz
466279315Strasz	//printf("PE offset %zd, PE size %zd\n", off, sizeof(*pe));
467279315Strasz
468279315Strasz	range_check(x, off, sizeof(*pe), "PE header");
469279315Strasz
470279315Strasz	pe = (struct pe_header *)(x->x_buf + off);
471279315Strasz	if (pe->pe_signature != PE_SIGNATURE)
472279315Strasz		errx(1, "wrong PE signature 0x%x", pe->pe_signature);
473279315Strasz
474279315Strasz	//printf("Number of sections: %d\n", pe->pe_coff.coff_number_of_sections);
475279315Strasz
476279315Strasz	parse_optional(x, off + sizeof(*pe),
477279315Strasz	    pe->pe_coff.coff_number_of_sections);
478279315Strasz}
479279315Strasz
480279315Straszvoid
481279315Straszparse(struct executable *x)
482279315Strasz{
483279315Strasz	const struct mz_header *mz;
484279315Strasz
485279315Strasz	range_check(x, 0, sizeof(*mz), "MZ header");
486279315Strasz
487279315Strasz	mz = (struct mz_header *)x->x_buf;
488279315Strasz	if (mz->mz_signature[0] != 'M' || mz->mz_signature[1] != 'Z')
489279315Strasz		errx(1, "MZ header not found");
490279315Strasz
491279315Strasz	return (parse_pe(x, mz->mz_lfanew));
492279315Strasz}
493279315Strasz
494279315Straszstatic off_t
495279315Straszappend(struct executable *x, void *ptr, size_t len)
496279315Strasz{
497279315Strasz	off_t off;
498279315Strasz
499279315Strasz	/*
500279315Strasz	 * XXX: Alignment.
501279315Strasz	 */
502279315Strasz	off = x->x_len;
503279315Strasz	x->x_buf = realloc(x->x_buf, x->x_len + len);
504279315Strasz	if (x->x_buf == NULL)
505279315Strasz		err(1, "realloc");
506279315Strasz	memcpy(x->x_buf + x->x_len, ptr, len);
507279315Strasz	x->x_len += len;
508279315Strasz
509279315Strasz	return (off);
510279315Strasz}
511279315Strasz
512279315Straszvoid
513279315Straszupdate(struct executable *x)
514279315Strasz{
515279315Strasz	uint32_t checksum;
516279315Strasz	struct pe_certificate *pc;
517279315Strasz	struct pe_directory_entry pde;
518279315Strasz	size_t pc_len;
519279315Strasz	off_t pc_off;
520279315Strasz
521279315Strasz	pc_len = sizeof(*pc) + x->x_signature_len;
522279315Strasz	pc = calloc(1, pc_len);
523279315Strasz	if (pc == NULL)
524279315Strasz		err(1, "calloc");
525279315Strasz
526279315Strasz#if 0
527279315Strasz	/*
528279315Strasz	 * Note that pc_len is the length of pc_certificate,
529279315Strasz	 * not the whole structure.
530279315Strasz	 *
531279315Strasz	 * XXX: That's what the spec says - but it breaks at least
532279315Strasz	 *      sbverify and "pesign -S", so the spec is probably wrong.
533279315Strasz	 */
534279315Strasz	pc->pc_len = x->x_signature_len;
535279315Strasz#else
536279315Strasz	pc->pc_len = pc_len;
537279315Strasz#endif
538279315Strasz	pc->pc_revision = PE_CERTIFICATE_REVISION;
539279315Strasz	pc->pc_type = PE_CERTIFICATE_TYPE;
540279315Strasz	memcpy(&pc->pc_signature, x->x_signature, x->x_signature_len);
541279315Strasz
542279315Strasz	pc_off = append(x, pc, pc_len);
543279315Strasz#if 0
544279315Strasz	printf("added signature chunk at offset %zd, len %zd\n",
545279315Strasz	    pc_off, pc_len);
546279315Strasz#endif
547279315Strasz
548279315Strasz	free(pc);
549279315Strasz
550279315Strasz	pde.pde_rva = pc_off;
551279315Strasz	pde.pde_size = pc_len;
552279315Strasz	memcpy(x->x_buf + x->x_certificate_entry_off, &pde, sizeof(pde));
553279315Strasz
554279315Strasz	checksum = compute_checksum(x);
555279315Strasz	assert(sizeof(checksum) == x->x_checksum_len);
556279315Strasz	memcpy(x->x_buf + x->x_checksum_off, &checksum, sizeof(checksum));
557279315Strasz#if 0
558279315Strasz	printf("new checksum 0x%x\n", checksum);
559279315Strasz#endif
560279315Strasz}
561