Coverage Report

Created: 2026-02-26 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-pefile-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2023 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
13.8k
#define G_LOG_DOMAIN "FuFirmware"
8
9
#include "config.h"
10
11
#include "fu-byte-array.h"
12
#include "fu-bytes.h"
13
#include "fu-common.h"
14
#include "fu-composite-input-stream.h"
15
#include "fu-coswid-firmware.h"
16
#include "fu-csv-firmware.h"
17
#include "fu-input-stream.h"
18
#include "fu-linear-firmware.h"
19
#include "fu-partial-input-stream.h"
20
#include "fu-pefile-firmware.h"
21
#include "fu-pefile-struct.h"
22
#include "fu-sbatlevel-section.h"
23
#include "fu-string.h"
24
25
/**
26
 * FuPefileFirmware:
27
 *
28
 * A PE file consists of a Microsoft MS-DOS stub, the PE signature, the COFF file header, and an
29
 * optional header, followed by section data.
30
 *
31
 * Documented:
32
 * https://learn.microsoft.com/en-gb/windows/win32/debug/pe-format
33
 */
34
35
typedef struct {
36
  gchar *authenticode_hash;
37
  guint16 subsystem_id;
38
} FuPefileFirmwarePrivate;
39
40
14.5k
G_DEFINE_TYPE_WITH_PRIVATE(FuPefileFirmware, fu_pefile_firmware, FU_TYPE_FIRMWARE)
41
14.5k
#define GET_PRIVATE(o) (fu_pefile_firmware_get_instance_private(o))
42
43
36
#define FU_PEFILE_SECTION_ID_STRTAB_SIZE 16
44
45
static void
46
fu_pefile_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
47
0
{
48
0
  FuPefileFirmware *self = FU_PEFILE_FIRMWARE(firmware);
49
0
  FuPefileFirmwarePrivate *priv = GET_PRIVATE(self);
50
0
  fu_xmlb_builder_insert_kv(bn, "authenticode_hash", priv->authenticode_hash);
51
0
  fu_xmlb_builder_insert_kv(bn, "subsystem", fu_coff_subsystem_to_string(priv->subsystem_id));
52
0
}
53
54
static gboolean
55
fu_pefile_firmware_validate(FuFirmware *firmware,
56
          GInputStream *stream,
57
          gsize offset,
58
          GError **error)
59
4.87k
{
60
4.87k
  return fu_struct_pe_dos_header_validate_stream(stream, offset, error);
61
4.87k
}
62
63
typedef struct {
64
  gsize offset;
65
  gsize size;
66
  gchar *name;
67
} FuPefileFirmwareRegion;
68
69
static void
70
fu_pefile_firmware_add_region(GPtrArray *regions, const gchar *name, gsize offset, gsize size)
71
16.7k
{
72
16.7k
  FuPefileFirmwareRegion *r = g_new0(FuPefileFirmwareRegion, 1);
73
16.7k
  r->name = g_strdup(name);
74
16.7k
  r->offset = offset;
75
16.7k
  r->size = size;
76
16.7k
  g_ptr_array_add(regions, r);
77
16.7k
}
78
79
static void
80
fu_pefile_firmware_region_free(FuPefileFirmwareRegion *r)
81
16.7k
{
82
16.7k
  g_free(r->name);
83
16.7k
  g_free(r);
84
16.7k
}
85
86
static gint
87
fu_pefile_firmware_region_sort_cb(gconstpointer a, gconstpointer b)
88
12.1k
{
89
12.1k
  const FuPefileFirmwareRegion *r1 = *((const FuPefileFirmwareRegion **)a);
90
12.1k
  const FuPefileFirmwareRegion *r2 = *((const FuPefileFirmwareRegion **)b);
91
12.1k
  if (r1->offset < r2->offset)
92
1.85k
    return -1;
93
10.3k
  if (r1->offset > r2->offset)
94
3.96k
    return 1;
95
6.38k
  return 0;
96
10.3k
}
97
98
static gboolean
99
fu_pefile_firmware_parse_section(FuPefileFirmware *self,
100
         GInputStream *stream,
101
         guint idx,
102
         gsize hdr_offset,
103
         gsize strtab_offset,
104
         GPtrArray *regions,
105
         FuFirmwareParseFlags flags,
106
         GError **error)
107
12.3k
{
108
12.3k
  g_autofree gchar *sect_id = NULL;
109
12.3k
  g_autofree gchar *sect_id_tmp = NULL;
110
12.3k
  g_autoptr(FuFirmware) img = NULL;
111
12.3k
  g_autoptr(FuStructPeCoffSection) st = NULL;
112
113
12.3k
  st = fu_struct_pe_coff_section_parse_stream(stream, hdr_offset, error);
114
12.3k
  if (st == NULL) {
115
143
    g_prefix_error_literal(error, "failed to read section: ");
116
143
    return FALSE;
117
143
  }
118
12.2k
  sect_id_tmp = fu_struct_pe_coff_section_get_name(st);
119
12.2k
  if (sect_id_tmp == NULL) {
120
5.35k
    sect_id = g_strdup_printf(".nul%04x", idx);
121
6.87k
  } else if (sect_id_tmp[0] == '/') {
122
788
    guint64 str_idx = 0x0;
123
788
    guint8 buf[FU_PEFILE_SECTION_ID_STRTAB_SIZE] = {0};
124
125
788
    if (!fu_strtoull(sect_id_tmp + 1,
126
788
         &str_idx,
127
788
         0,
128
788
         G_MAXUINT32,
129
788
         FU_INTEGER_BASE_10,
130
788
         error)) {
131
23
      g_prefix_error(error, "failed to parse section ID '%s': ", sect_id_tmp + 1);
132
23
      return FALSE;
133
23
    }
134
765
    if (!fu_input_stream_read_safe(stream,
135
765
                 buf,
136
765
                 sizeof(buf),
137
765
                 0x0,
138
765
                 strtab_offset + str_idx, /* seek */
139
765
                 sizeof(buf),
140
765
                 error))
141
30
      return FALSE;
142
735
    sect_id = fu_strsafe((const gchar *)buf, sizeof(buf));
143
735
    if (sect_id == NULL) {
144
4
      g_set_error_literal(error,
145
4
              FWUPD_ERROR,
146
4
              FWUPD_ERROR_INVALID_DATA,
147
4
              "no section name");
148
4
      return FALSE;
149
4
    }
150
6.08k
  } else {
151
6.08k
    sect_id = g_steal_pointer(&sect_id_tmp);
152
6.08k
  }
153
154
  /* create new firmware */
155
12.1k
  if (g_strcmp0(sect_id, ".sbom") == 0) {
156
3.20k
    img = fu_linear_firmware_new(FU_TYPE_COSWID_FIRMWARE);
157
8.96k
  } else if (g_strcmp0(sect_id, ".sbat") == 0 || g_strcmp0(sect_id, ".sbata") == 0 ||
158
6.92k
       g_strcmp0(sect_id, ".sbatl") == 0) {
159
2.12k
    img = fu_csv_firmware_new();
160
2.12k
    fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(img), "$id");
161
2.12k
    fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(img), "$version_raw");
162
2.12k
    fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(img), "vendor_name");
163
2.12k
    fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(img), "vendor_package_name");
164
2.12k
    fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(img), "$version");
165
2.12k
    fu_csv_firmware_add_column_id(FU_CSV_FIRMWARE(img), "vendor_url");
166
2.12k
    fu_csv_firmware_set_write_column_ids(FU_CSV_FIRMWARE(img), FALSE);
167
6.83k
  } else if (g_strcmp0(sect_id, ".sbatlevel") == 0) {
168
206
    img = fu_sbatlevel_section_new();
169
6.63k
  } else {
170
6.63k
    img = fu_firmware_new();
171
6.63k
  }
172
12.1k
  fu_firmware_set_id(img, sect_id);
173
12.1k
  fu_firmware_set_idx(img, idx);
174
175
  /* add data */
176
12.1k
  if (fu_struct_pe_coff_section_get_virtual_size(st) > 0) {
177
9.70k
    guint32 sect_offset = fu_struct_pe_coff_section_get_pointer_to_raw_data(st);
178
9.70k
    guint32 sect_size = fu_struct_pe_coff_section_get_virtual_size(st);
179
9.70k
    g_autoptr(GInputStream) img_stream = NULL;
180
181
    /* use the raw data size if the section is compressed */
182
9.70k
    if (fu_struct_pe_coff_section_get_virtual_size(st) >
183
9.70k
        fu_struct_pe_coff_section_get_size_of_raw_data(st)) {
184
8.57k
      g_debug("virtual size 0x%x bigger than raw data, truncating to 0x%x",
185
8.57k
        sect_size,
186
8.57k
        fu_struct_pe_coff_section_get_size_of_raw_data(st));
187
8.57k
      sect_size = fu_struct_pe_coff_section_get_size_of_raw_data(st);
188
8.57k
    }
189
190
9.70k
    fu_firmware_set_offset(img, sect_offset);
191
9.70k
    img_stream = fu_partial_input_stream_new(stream, sect_offset, sect_size, error);
192
9.70k
    if (img_stream == NULL) {
193
449
      g_prefix_error_literal(error, "failed to cut raw PE data: ");
194
449
      return FALSE;
195
449
    }
196
9.25k
    if (!fu_firmware_parse_stream(img, img_stream, 0x0, flags, error)) {
197
3.35k
      g_prefix_error(error, "failed to parse raw data %s: ", sect_id);
198
3.35k
      return FALSE;
199
3.35k
    }
200
201
    /* add region for Authenticode checksum */
202
5.90k
    fu_pefile_firmware_add_region(regions,
203
5.90k
                sect_id,
204
5.90k
                sect_offset,
205
5.90k
                fu_struct_pe_coff_section_get_size_of_raw_data(st));
206
5.90k
  }
207
208
  /* success */
209
8.37k
  return fu_firmware_add_image(FU_FIRMWARE(self), img, error);
210
12.1k
}
211
212
static gboolean
213
fu_pefile_firmware_parse(FuFirmware *firmware,
214
       GInputStream *stream,
215
       FuFirmwareParseFlags flags,
216
       GError **error)
217
4.82k
{
218
4.82k
  FuPefileFirmware *self = FU_PEFILE_FIRMWARE(firmware);
219
4.82k
  FuPefileFirmwarePrivate *priv = GET_PRIVATE(self);
220
4.82k
  guint32 cert_table_sz = 0;
221
4.82k
  gsize offset = 0;
222
4.82k
  gsize streamsz = 0;
223
4.82k
  gsize strtab_offset;
224
4.82k
  guint32 nr_sections;
225
4.82k
  g_autoptr(FuStructPeCoffFileHeader) st_coff = NULL;
226
4.82k
  g_autoptr(FuStructPeDosHeader) st_doshdr = NULL;
227
4.82k
  g_autoptr(GPtrArray) regions = NULL;
228
4.82k
  g_autoptr(GInputStream) composite_stream = fu_composite_input_stream_new();
229
230
  /* get size */
231
4.82k
  if (!fu_input_stream_size(stream, &streamsz, error))
232
0
    return FALSE;
233
234
  /* parse the DOS header to get the COFF header */
235
4.82k
  st_doshdr = fu_struct_pe_dos_header_parse_stream(stream, offset, error);
236
4.82k
  if (st_doshdr == NULL) {
237
0
    g_prefix_error_literal(error, "failed to read DOS header: ");
238
0
    return FALSE;
239
0
  }
240
4.82k
  offset += fu_struct_pe_dos_header_get_lfanew(st_doshdr);
241
4.82k
  st_coff = fu_struct_pe_coff_file_header_parse_stream(stream, offset, error);
242
4.82k
  if (st_coff == NULL) {
243
84
    g_prefix_error_literal(error, "failed to read COFF header: ");
244
84
    return FALSE;
245
84
  }
246
4.74k
  offset += st_coff->buf->len;
247
248
4.74k
  regions = g_ptr_array_new_with_free_func((GDestroyNotify)fu_pefile_firmware_region_free);
249
250
  /* 1st Authenticode region */
251
4.74k
  fu_pefile_firmware_add_region(regions,
252
4.74k
              "pre-cksum",
253
4.74k
              0x0,
254
4.74k
              offset + FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_CHECKSUM);
255
256
4.74k
  if (!fu_input_stream_read_safe(
257
4.74k
    stream,
258
4.74k
    (guint8 *)&priv->subsystem_id,
259
4.74k
    sizeof(priv->subsystem_id),
260
4.74k
    0x0,
261
4.74k
    offset + FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_SUBSYSTEM, /* seek */
262
4.74k
    sizeof(priv->subsystem_id),
263
4.74k
    error))
264
2
    return FALSE;
265
266
  /* 2nd Authenticode region */
267
4.74k
  fu_pefile_firmware_add_region(
268
4.74k
      regions,
269
4.74k
      "chksum->cert-table",
270
4.74k
      offset + FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_SUBSYSTEM,
271
4.74k
      FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_CERTIFICATE_TABLE -
272
4.74k
    FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_SUBSYSTEM); /* end */
273
274
  /* verify optional extra header */
275
4.74k
  if (fu_struct_pe_coff_file_header_get_size_of_optional_header(st_coff) > 0) {
276
910
    g_autoptr(FuStructPeCoffOptionalHeader64) st_opt =
277
910
        fu_struct_pe_coff_optional_header64_parse_stream(stream, offset, error);
278
910
    if (st_opt == NULL) {
279
13
      g_prefix_error_literal(error, "failed to read optional header: ");
280
13
      return FALSE;
281
13
    }
282
283
    /* 3rd Authenticode region */
284
897
    if (fu_struct_pe_coff_optional_header64_get_size_of_headers(st_opt) > 0) {
285
654
      fu_pefile_firmware_add_region(
286
654
          regions,
287
654
          "cert-table->end-of-headers",
288
654
          offset + FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_DEBUG_TABLE,
289
654
          fu_struct_pe_coff_optional_header64_get_size_of_headers(st_opt) -
290
654
        (offset + FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_DEBUG_TABLE));
291
654
    }
292
293
    /* 4th Authenticode region */
294
897
    cert_table_sz =
295
897
        fu_struct_pe_coff_optional_header64_get_size_of_certificate_table(st_opt);
296
297
897
    offset += fu_struct_pe_coff_file_header_get_size_of_optional_header(st_coff);
298
897
  }
299
300
  /* read number of sections */
301
4.72k
  nr_sections = fu_struct_pe_coff_file_header_get_number_of_sections(st_coff);
302
4.72k
  if (nr_sections == 0) {
303
1
    g_set_error_literal(error,
304
1
            FWUPD_ERROR,
305
1
            FWUPD_ERROR_INVALID_FILE,
306
1
            "invalid number of sections");
307
1
    return FALSE;
308
1
  }
309
4.72k
  strtab_offset = fu_struct_pe_coff_file_header_get_pointer_to_symbol_table(st_coff) +
310
4.72k
      fu_struct_pe_coff_file_header_get_number_of_symbols(st_coff) *
311
4.72k
          FU_STRUCT_PE_COFF_SYMBOL_SIZE;
312
313
  /* read out each section */
314
13.0k
  for (guint idx = 0; idx < nr_sections; idx++) {
315
12.3k
    if (!fu_pefile_firmware_parse_section(self,
316
12.3k
                  stream,
317
12.3k
                  idx,
318
12.3k
                  offset,
319
12.3k
                  strtab_offset,
320
12.3k
                  regions,
321
12.3k
                  flags,
322
12.3k
                  error)) {
323
4.00k
      g_prefix_error(error, "failed to read section 0x%x: ", idx);
324
4.00k
      return FALSE;
325
4.00k
    }
326
8.36k
    offset += FU_STRUCT_PE_COFF_SECTION_SIZE;
327
8.36k
  }
328
329
  /* make sure ordered by address */
330
721
  g_ptr_array_sort(regions, fu_pefile_firmware_region_sort_cb);
331
332
  /* for the data at the end of the image */
333
721
  if (regions->len > 0) {
334
721
    FuPefileFirmwareRegion *r = g_ptr_array_index(regions, regions->len - 1);
335
721
    gsize offset_end = r->offset + r->size;
336
721
    fu_pefile_firmware_add_region(regions,
337
721
                "tabledata->cert-table",
338
721
                offset_end,
339
721
                streamsz - (offset_end + cert_table_sz));
340
721
  }
341
342
  /* calculate the checksum we would find in the dbx */
343
5.57k
  for (guint i = 0; i < regions->len; i++) {
344
5.22k
    FuPefileFirmwareRegion *r = g_ptr_array_index(regions, i);
345
5.22k
    g_autoptr(GInputStream) partial_stream = NULL;
346
347
5.22k
    if (r->size == 0)
348
59
      continue;
349
5.16k
    g_debug("authenticode region %s: 0x%04x -> 0x%04x [0x%04x]",
350
5.16k
      r->name,
351
5.16k
      (guint)r->offset,
352
5.16k
      (guint)(r->offset + r->size),
353
5.16k
      (guint)r->size);
354
5.16k
    partial_stream = fu_partial_input_stream_new(stream, r->offset, r->size, error);
355
5.16k
    if (partial_stream == NULL) {
356
365
      g_prefix_error_literal(error, "failed to cut Authenticode region: ");
357
365
      return FALSE;
358
365
    }
359
4.79k
    fu_composite_input_stream_add_partial_stream(
360
4.79k
        FU_COMPOSITE_INPUT_STREAM(composite_stream),
361
4.79k
        FU_PARTIAL_INPUT_STREAM(partial_stream));
362
4.79k
  }
363
356
  priv->authenticode_hash =
364
356
      fu_input_stream_compute_checksum(composite_stream, G_CHECKSUM_SHA256, error);
365
356
  if (priv->authenticode_hash == NULL)
366
0
    return FALSE;
367
368
  /* success */
369
356
  return TRUE;
370
356
}
371
372
typedef struct {
373
  GBytes *blob;
374
  gchar *id;
375
  gsize offset;
376
  gsize blobsz_aligned;
377
} FuPefileSection;
378
379
static void
380
fu_pefile_firmware_section_free(FuPefileSection *section)
381
646
{
382
646
  if (section->blob != NULL)
383
460
    g_bytes_unref(section->blob);
384
646
  g_free(section->id);
385
646
  g_free(section);
386
646
}
387
388
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuPefileSection, fu_pefile_firmware_section_free)
389
390
static GByteArray *
391
fu_pefile_firmware_write(FuFirmware *firmware, GError **error)
392
356
{
393
356
  gsize offset = 0;
394
356
  g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware);
395
356
  g_autoptr(FuStructPeDosHeader) st = fu_struct_pe_dos_header_new();
396
356
  g_autoptr(FuStructPeCoffFileHeader) st_hdr = fu_struct_pe_coff_file_header_new();
397
356
  g_autoptr(FuStructPeCoffOptionalHeader64) st_opt =
398
356
      fu_struct_pe_coff_optional_header64_new();
399
356
  g_autoptr(GByteArray) strtab = g_byte_array_new();
400
356
  g_autoptr(GPtrArray) sections =
401
356
      g_ptr_array_new_with_free_func((GDestroyNotify)fu_pefile_firmware_section_free);
402
403
  /* calculate the offset for each of the sections */
404
356
  offset += st->buf->len + st_hdr->buf->len + st_opt->buf->len;
405
356
  offset += FU_STRUCT_PE_COFF_SECTION_SIZE * imgs->len;
406
816
  for (guint i = 0; i < imgs->len; i++) {
407
646
    g_autoptr(FuPefileSection) section = g_new0(FuPefileSection, 1);
408
646
    FuFirmware *img = g_ptr_array_index(imgs, i);
409
410
646
    section->offset = offset;
411
646
    section->blob = fu_firmware_write(img, error);
412
646
    if (section->blob == NULL)
413
186
      return NULL;
414
460
    if (g_bytes_get_size(section->blob) == 0) {
415
138
      g_debug("skipping zero length section %u", i);
416
138
      continue;
417
138
    }
418
322
    section->id = g_strdup(fu_firmware_get_id(img));
419
322
    section->blobsz_aligned = fu_common_align_up(g_bytes_get_size(section->blob), 4);
420
322
    offset += section->blobsz_aligned;
421
322
    g_ptr_array_add(sections, g_steal_pointer(&section));
422
322
  }
423
424
  /* export_table -> architecture_table */
425
170
  fu_struct_pe_coff_optional_header64_set_number_of_rva_and_sizes(st_opt, 7);
426
427
  /* COFF file header */
428
170
  fu_struct_pe_coff_file_header_set_size_of_optional_header(st_hdr, st_opt->buf->len);
429
170
  fu_struct_pe_coff_file_header_set_number_of_sections(st_hdr, sections->len);
430
170
  fu_struct_pe_coff_file_header_set_pointer_to_symbol_table(st_hdr, offset);
431
170
  fu_byte_array_append_array(st->buf, st_hdr->buf);
432
170
  fu_byte_array_append_array(st->buf, st_opt->buf);
433
434
  /* add sections */
435
434
  for (guint i = 0; i < sections->len; i++) {
436
264
    FuPefileSection *section = g_ptr_array_index(sections, i);
437
264
    g_autoptr(FuStructPeCoffSection) st_sect = fu_struct_pe_coff_section_new();
438
439
264
    fu_struct_pe_coff_section_set_size_of_raw_data(st_sect,
440
264
                     g_bytes_get_size(section->blob));
441
264
    fu_struct_pe_coff_section_set_virtual_address(st_sect, 0x0);
442
264
    fu_struct_pe_coff_section_set_virtual_size(st_sect, section->blobsz_aligned);
443
264
    fu_struct_pe_coff_section_set_pointer_to_raw_data(st_sect, section->offset);
444
445
    /* set the name directly, or add to the string table */
446
264
    if (section->id == NULL) {
447
0
      g_set_error(error,
448
0
            FWUPD_ERROR,
449
0
            FWUPD_ERROR_INVALID_DATA,
450
0
            "image %u has no ID",
451
0
            i);
452
0
      return NULL;
453
0
    }
454
264
    if (strlen(section->id) <= 8) {
455
246
      if (!fu_struct_pe_coff_section_set_name(st_sect, section->id, error))
456
0
        return NULL;
457
246
    } else {
458
18
      g_autofree gchar *name_tmp = g_strdup_printf("/%u", strtab->len);
459
18
      g_autoptr(GByteArray) strtab_buf = g_byte_array_new();
460
461
18
      if (!fu_struct_pe_coff_section_set_name(st_sect, name_tmp, error))
462
0
        return NULL;
463
464
      /* create a byte buffer of exactly the correct chunk size */
465
18
      g_byte_array_append(strtab_buf,
466
18
              (const guint8 *)section->id,
467
18
              strlen(section->id));
468
18
      if (strtab_buf->len > FU_PEFILE_SECTION_ID_STRTAB_SIZE) {
469
0
        g_set_error(error,
470
0
              FWUPD_ERROR,
471
0
              FWUPD_ERROR_INVALID_DATA,
472
0
              "image ID %s is too long",
473
0
              section->id);
474
0
        return NULL;
475
0
      }
476
18
      fu_byte_array_set_size(strtab_buf, FU_PEFILE_SECTION_ID_STRTAB_SIZE, 0x0);
477
18
      g_byte_array_append(strtab, strtab_buf->data, strtab_buf->len);
478
18
    }
479
264
    fu_byte_array_append_array(st->buf, st_sect->buf);
480
264
  }
481
482
  /* add the section data itself */
483
434
  for (guint i = 0; i < sections->len; i++) {
484
264
    FuPefileSection *section = g_ptr_array_index(sections, i);
485
264
    g_autoptr(GBytes) blob_aligned =
486
264
        fu_bytes_pad(section->blob, section->blobsz_aligned, 0xFF);
487
264
    fu_byte_array_append_bytes(st->buf, blob_aligned);
488
264
  }
489
490
  /* string table comes last */
491
170
  g_byte_array_append(st->buf, strtab->data, strtab->len);
492
493
  /* success */
494
170
  return g_steal_pointer(&st->buf);
495
170
}
496
497
static gchar *
498
fu_pefile_firmware_get_checksum(FuFirmware *firmware, GChecksumType csum_kind, GError **error)
499
0
{
500
0
  FuPefileFirmware *self = FU_PEFILE_FIRMWARE(firmware);
501
0
  FuPefileFirmwarePrivate *priv = GET_PRIVATE(self);
502
0
  if (csum_kind != G_CHECKSUM_SHA256) {
503
0
    g_set_error_literal(error,
504
0
            FWUPD_ERROR,
505
0
            FWUPD_ERROR_NOT_SUPPORTED,
506
0
            "Authenticode only supports SHA256");
507
0
    return NULL;
508
0
  }
509
0
  if (priv->authenticode_hash == NULL) {
510
0
    g_set_error_literal(error,
511
0
            FWUPD_ERROR,
512
0
            FWUPD_ERROR_INVALID_DATA,
513
0
            "Authenticode checksum not set");
514
0
    return NULL;
515
0
  }
516
0
  return g_strdup(priv->authenticode_hash);
517
0
}
518
519
static void
520
fu_pefile_firmware_init(FuPefileFirmware *self)
521
4.87k
{
522
4.87k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FIRMWARE);
523
4.87k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_CSV_FIRMWARE);
524
4.87k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_SBATLEVEL_SECTION);
525
4.87k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 100);
526
4.87k
}
527
528
static void
529
fu_pefile_firmware_finalize(GObject *object)
530
4.87k
{
531
4.87k
  FuPefileFirmware *self = FU_PEFILE_FIRMWARE(object);
532
4.87k
  FuPefileFirmwarePrivate *priv = GET_PRIVATE(self);
533
4.87k
  g_free(priv->authenticode_hash);
534
4.87k
  G_OBJECT_CLASS(fu_pefile_firmware_parent_class)->finalize(object);
535
4.87k
}
536
537
static void
538
fu_pefile_firmware_class_init(FuPefileFirmwareClass *klass)
539
1
{
540
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
541
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
542
1
  object_class->finalize = fu_pefile_firmware_finalize;
543
1
  firmware_class->validate = fu_pefile_firmware_validate;
544
1
  firmware_class->parse = fu_pefile_firmware_parse;
545
1
  firmware_class->write = fu_pefile_firmware_write;
546
1
  firmware_class->export = fu_pefile_firmware_export;
547
1
  firmware_class->get_checksum = fu_pefile_firmware_get_checksum;
548
1
}
549
550
/**
551
 * fu_pefile_firmware_new:
552
 *
553
 * Creates a new #FuPefileFirmware
554
 *
555
 * Since: 1.8.10
556
 **/
557
FuFirmware *
558
fu_pefile_firmware_new(void)
559
0
{
560
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_PEFILE_FIRMWARE, NULL));
561
0
}