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: releng/11.0/usr.sbin/uefisign/pe.c 289677 2015-10-21 05:37:09Z eadler $");
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 *
307289677Seadler * XXX: Endianness?
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{
349283141Strasz#if 0
350279315Strasz	uint32_t computed_checksum;
351283141Strasz#endif
352279315Strasz	const struct pe_optional_header_32_plus	*po;
353279315Strasz
354279315Strasz	range_check(x, off, sizeof(*po), "PE Optional Header");
355279315Strasz
356279315Strasz	po = (struct pe_optional_header_32_plus *)(x->x_buf + off);
357279315Strasz	switch (po->po_subsystem) {
358279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION:
359279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT:
360279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME:
361279315Strasz		break;
362279315Strasz	default:
363279315Strasz		errx(1, "wrong PE Optional Header subsystem 0x%x",
364279315Strasz		    po->po_subsystem);
365279315Strasz	}
366279315Strasz
367279315Strasz#if 0
368279315Strasz	printf("subsystem %d, checksum 0x%x, %d data directories\n",
369279315Strasz	    po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes);
370279315Strasz#endif
371279315Strasz
372279315Strasz	x->x_checksum_off = off +
373279315Strasz	    offsetof(struct pe_optional_header_32_plus, po_checksum);
374279315Strasz	x->x_checksum_len = sizeof(po->po_checksum);
375279315Strasz#if 0
376279315Strasz	printf("checksum 0x%x at offset %zd, len %zd\n",
377279315Strasz	    po->po_checksum, x->x_checksum_off, x->x_checksum_len);
378279315Strasz
379279315Strasz	computed_checksum = compute_checksum(x);
380279315Strasz	if (computed_checksum != po->po_checksum) {
381279315Strasz		warnx("invalid PE+ checksum; is 0x%x, should be 0x%x",
382279315Strasz		    po->po_checksum, computed_checksum);
383279315Strasz	}
384283141Strasz#endif
385279315Strasz
386279315Strasz	if (x->x_len < x->x_headers_len)
387279315Strasz		errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers);
388279315Strasz	x->x_headers_len = po->po_size_of_headers;
389279315Strasz	//printf("Size of Headers: %d\n", po->po_size_of_headers);
390279315Strasz
391279315Strasz	return (parse_directory(x, off + sizeof(*po),
392279315Strasz	    po->po_number_of_rva_and_sizes, number_of_sections));
393279315Strasz}
394279315Strasz
395279315Straszstatic void
396279315Straszparse_optional_32(struct executable *x, off_t off, int number_of_sections)
397279315Strasz{
398283141Strasz#if 0
399279315Strasz	uint32_t computed_checksum;
400283141Strasz#endif
401279315Strasz	const struct pe_optional_header_32 *po;
402279315Strasz
403279315Strasz	range_check(x, off, sizeof(*po), "PE Optional Header");
404279315Strasz
405279315Strasz	po = (struct pe_optional_header_32 *)(x->x_buf + off);
406279315Strasz	switch (po->po_subsystem) {
407279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_APPLICATION:
408279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_BOOT:
409279315Strasz	case PE_OPTIONAL_SUBSYSTEM_EFI_RUNTIME:
410279315Strasz		break;
411279315Strasz	default:
412279315Strasz		errx(1, "wrong PE Optional Header subsystem 0x%x",
413279315Strasz		    po->po_subsystem);
414279315Strasz	}
415279315Strasz
416279315Strasz#if 0
417279315Strasz	printf("subsystem %d, checksum 0x%x, %d data directories\n",
418279315Strasz	    po->po_subsystem, po->po_checksum, po->po_number_of_rva_and_sizes);
419279315Strasz#endif
420279315Strasz
421279315Strasz	x->x_checksum_off = off +
422279315Strasz	    offsetof(struct pe_optional_header_32, po_checksum);
423279315Strasz	x->x_checksum_len = sizeof(po->po_checksum);
424279315Strasz#if 0
425279315Strasz	printf("checksum at offset %zd, len %zd\n",
426279315Strasz	    x->x_checksum_off, x->x_checksum_len);
427279315Strasz
428279315Strasz	computed_checksum = compute_checksum(x);
429279315Strasz	if (computed_checksum != po->po_checksum) {
430279315Strasz		warnx("invalid PE checksum; is 0x%x, should be 0x%x",
431279315Strasz		    po->po_checksum, computed_checksum);
432279315Strasz	}
433283141Strasz#endif
434279315Strasz
435279315Strasz	if (x->x_len < x->x_headers_len)
436279315Strasz		errx(1, "invalid SizeOfHeaders %d", po->po_size_of_headers);
437279315Strasz	x->x_headers_len = po->po_size_of_headers;
438279315Strasz	//printf("Size of Headers: %d\n", po->po_size_of_headers);
439279315Strasz
440279315Strasz	return (parse_directory(x, off + sizeof(*po),
441279315Strasz	    po->po_number_of_rva_and_sizes, number_of_sections));
442279315Strasz}
443279315Strasz
444279315Straszstatic void
445279315Straszparse_optional(struct executable *x, off_t off, int number_of_sections)
446279315Strasz{
447279315Strasz	const struct pe_optional_header_32 *po;
448279315Strasz
449279315Strasz	//printf("Optional header offset %zd\n", off);
450279315Strasz
451279315Strasz	range_check(x, off, sizeof(*po), "PE Optional Header");
452279315Strasz
453279315Strasz	po = (struct pe_optional_header_32 *)(x->x_buf + off);
454279315Strasz
455279315Strasz	switch (po->po_magic) {
456279315Strasz	case PE_OPTIONAL_MAGIC_32:
457279315Strasz		return (parse_optional_32(x, off, number_of_sections));
458279315Strasz	case PE_OPTIONAL_MAGIC_32_PLUS:
459279315Strasz		return (parse_optional_32_plus(x, off, number_of_sections));
460279315Strasz	default:
461279315Strasz		errx(1, "wrong PE Optional Header magic 0x%x", po->po_magic);
462279315Strasz	}
463279315Strasz}
464279315Strasz
465279315Straszstatic void
466279315Straszparse_pe(struct executable *x, off_t off)
467279315Strasz{
468279315Strasz	const struct pe_header *pe;
469279315Strasz
470279315Strasz	//printf("PE offset %zd, PE size %zd\n", off, sizeof(*pe));
471279315Strasz
472279315Strasz	range_check(x, off, sizeof(*pe), "PE header");
473279315Strasz
474279315Strasz	pe = (struct pe_header *)(x->x_buf + off);
475279315Strasz	if (pe->pe_signature != PE_SIGNATURE)
476279315Strasz		errx(1, "wrong PE signature 0x%x", pe->pe_signature);
477279315Strasz
478279315Strasz	//printf("Number of sections: %d\n", pe->pe_coff.coff_number_of_sections);
479279315Strasz
480279315Strasz	parse_optional(x, off + sizeof(*pe),
481279315Strasz	    pe->pe_coff.coff_number_of_sections);
482279315Strasz}
483279315Strasz
484279315Straszvoid
485279315Straszparse(struct executable *x)
486279315Strasz{
487279315Strasz	const struct mz_header *mz;
488279315Strasz
489279315Strasz	range_check(x, 0, sizeof(*mz), "MZ header");
490279315Strasz
491279315Strasz	mz = (struct mz_header *)x->x_buf;
492279315Strasz	if (mz->mz_signature[0] != 'M' || mz->mz_signature[1] != 'Z')
493279315Strasz		errx(1, "MZ header not found");
494279315Strasz
495279315Strasz	return (parse_pe(x, mz->mz_lfanew));
496279315Strasz}
497279315Strasz
498279315Straszstatic off_t
499279315Straszappend(struct executable *x, void *ptr, size_t len)
500279315Strasz{
501279315Strasz	off_t off;
502279315Strasz
503279315Strasz	/*
504279315Strasz	 * XXX: Alignment.
505279315Strasz	 */
506279315Strasz	off = x->x_len;
507279315Strasz	x->x_buf = realloc(x->x_buf, x->x_len + len);
508279315Strasz	if (x->x_buf == NULL)
509279315Strasz		err(1, "realloc");
510279315Strasz	memcpy(x->x_buf + x->x_len, ptr, len);
511279315Strasz	x->x_len += len;
512279315Strasz
513279315Strasz	return (off);
514279315Strasz}
515279315Strasz
516279315Straszvoid
517279315Straszupdate(struct executable *x)
518279315Strasz{
519279315Strasz	uint32_t checksum;
520279315Strasz	struct pe_certificate *pc;
521279315Strasz	struct pe_directory_entry pde;
522279315Strasz	size_t pc_len;
523279315Strasz	off_t pc_off;
524279315Strasz
525279315Strasz	pc_len = sizeof(*pc) + x->x_signature_len;
526279315Strasz	pc = calloc(1, pc_len);
527279315Strasz	if (pc == NULL)
528279315Strasz		err(1, "calloc");
529279315Strasz
530279315Strasz#if 0
531279315Strasz	/*
532279315Strasz	 * Note that pc_len is the length of pc_certificate,
533279315Strasz	 * not the whole structure.
534279315Strasz	 *
535279315Strasz	 * XXX: That's what the spec says - but it breaks at least
536279315Strasz	 *      sbverify and "pesign -S", so the spec is probably wrong.
537279315Strasz	 */
538279315Strasz	pc->pc_len = x->x_signature_len;
539279315Strasz#else
540279315Strasz	pc->pc_len = pc_len;
541279315Strasz#endif
542279315Strasz	pc->pc_revision = PE_CERTIFICATE_REVISION;
543279315Strasz	pc->pc_type = PE_CERTIFICATE_TYPE;
544279315Strasz	memcpy(&pc->pc_signature, x->x_signature, x->x_signature_len);
545279315Strasz
546279315Strasz	pc_off = append(x, pc, pc_len);
547279315Strasz#if 0
548279315Strasz	printf("added signature chunk at offset %zd, len %zd\n",
549279315Strasz	    pc_off, pc_len);
550279315Strasz#endif
551279315Strasz
552279315Strasz	free(pc);
553279315Strasz
554279315Strasz	pde.pde_rva = pc_off;
555279315Strasz	pde.pde_size = pc_len;
556279315Strasz	memcpy(x->x_buf + x->x_certificate_entry_off, &pde, sizeof(pde));
557279315Strasz
558279315Strasz	checksum = compute_checksum(x);
559279315Strasz	assert(sizeof(checksum) == x->x_checksum_len);
560279315Strasz	memcpy(x->x_buf + x->x_checksum_off, &checksum, sizeof(checksum));
561279315Strasz#if 0
562279315Strasz	printf("new checksum 0x%x\n", checksum);
563279315Strasz#endif
564279315Strasz}
565