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