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