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