image: Allow manipulation of i386 PE/COFF files

Replace struct image->aouthdr with a union of the 32- and 64-bit a.out
header definitions, and abstract the relevant parsing code into the
image_pecoff_parse_{32,64} functions.

We also move all references of data in the a.out header to these
functions, so we don't need to lookup the machine types elsewhere.

Based on a patch by Maxim Kammerer <mk@dee.su>.

Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
This commit is contained in:
Jeremy Kerr 2012-08-10 16:26:14 +08:00
parent e027b87cff
commit e1b58d6ccb
2 changed files with 89 additions and 23 deletions

100
image.c
View file

@ -77,13 +77,65 @@ static uint16_t __pehdr_u16(char field[])
#define pehdr_u32(f) __pehdr_u32(f + BUILD_ASSERT_OR_ZERO(sizeof(f) == 4)) #define pehdr_u32(f) __pehdr_u32(f + BUILD_ASSERT_OR_ZERO(sizeof(f) == 4))
#define pehdr_u16(f) __pehdr_u16(f + BUILD_ASSERT_OR_ZERO(sizeof(f) == 2)) #define pehdr_u16(f) __pehdr_u16(f + BUILD_ASSERT_OR_ZERO(sizeof(f) == 2))
/* Machine-specific PE/COFF parse functions. These parse the relevant a.out
* header for the machine type, and set the following members of struct image:
* - aouthdr_size
* - file_alignment
* - header_size
* - data_dir
* - checksum
*
* These functions require image->aouthdr to be set by the caller.
*/
static int image_pecoff_parse_32(struct image *image)
{
if (image->aouthdr.aout_32->standard.magic[0] != 0x0b ||
image->aouthdr.aout_32->standard.magic[1] != 0x01) {
fprintf(stderr, "Invalid a.out machine type\n");
return -1;
}
image->aouthdr_size = sizeof(*image->aouthdr.aout_32);
image->file_alignment =
pehdr_u32(image->aouthdr.aout_32->FileAlignment);
image->header_size =
pehdr_u32(image->aouthdr.aout_32->SizeOfHeaders);
image->data_dir = (void *)image->aouthdr.aout_32->DataDirectory;
image->checksum = (uint32_t *)image->aouthdr.aout_32->CheckSum;
return 0;
}
static int image_pecoff_parse_64(struct image *image)
{
if (image->aouthdr.aout_64->standard.magic[0] != 0x0b ||
image->aouthdr.aout_64->standard.magic[1] != 0x02) {
fprintf(stderr, "Invalid a.out machine type\n");
return -1;
}
image->aouthdr_size = sizeof(*image->aouthdr.aout_64);
image->file_alignment =
pehdr_u32(image->aouthdr.aout_64->FileAlignment);
image->header_size =
pehdr_u32(image->aouthdr.aout_64->SizeOfHeaders);
image->data_dir = (void *)image->aouthdr.aout_64->DataDirectory;
image->checksum = (uint32_t *)image->aouthdr.aout_64->CheckSum;
return 0;
}
static int image_pecoff_parse(struct image *image) static int image_pecoff_parse(struct image *image)
{ {
struct cert_table_header *cert_table; struct cert_table_header *cert_table;
char nt_sig[] = {'P', 'E', 0, 0}; char nt_sig[] = {'P', 'E', 0, 0};
size_t size = image->size; size_t size = image->size;
void *buf = image->buf; void *buf = image->buf;
uint16_t magic;
uint32_t addr; uint32_t addr;
int rc;
/* sanity checks */ /* sanity checks */
if (size < sizeof(*image->doshdr)) { if (size < sizeof(*image->doshdr)) {
@ -117,35 +169,40 @@ static int image_pecoff_parse(struct image *image)
return -1; return -1;
} }
if (pehdr_u16(image->pehdr->f_magic) != IMAGE_FILE_MACHINE_AMD64) { /* a.out header directly follows PE header */
fprintf(stderr, "Invalid PE header magic for x86_64\n"); image->aouthdr.addr = image->pehdr + 1;
magic = pehdr_u16(image->pehdr->f_magic);
if (magic == IMAGE_FILE_MACHINE_AMD64) {
rc = image_pecoff_parse_64(image);
} else if (magic == IMAGE_FILE_MACHINE_I386) {
rc = image_pecoff_parse_32(image);
} else {
fprintf(stderr, "Invalid PE header magic\n");
return -1; return -1;
} }
if (pehdr_u16(image->pehdr->f_opthdr) != sizeof(*image->aouthdr)) { if (rc) {
fprintf(stderr, "Error parsing a.out header\n");
return -1;
}
/* we have the data_dir now, from parsing the a.out header */
image->data_dir_sigtable = &image->data_dir[DATA_DIR_CERT_TABLE];
if (pehdr_u16(image->pehdr->f_opthdr) != image->aouthdr_size) {
fprintf(stderr, "Invalid a.out header size\n"); fprintf(stderr, "Invalid a.out header size\n");
return -1; return -1;
} }
if (image->size < sizeof(*image->doshdr) + sizeof(*image->pehdr) if (image->size < sizeof(*image->doshdr) + sizeof(*image->pehdr)
+ sizeof(*image->aouthdr)) { + image->aouthdr_size) {
fprintf(stderr, "file is too small for a.out header\n"); fprintf(stderr, "file is too small for a.out header\n");
return -1; return -1;
} }
/* a.out header directly follows PE header */
image->aouthdr = (void *)(image->pehdr+1);
if (image->aouthdr->standard.magic[0] != 0x0b ||
image->aouthdr->standard.magic[1] != 0x02) {
fprintf(stderr, "Invalid a.out machine type\n");
return -1;
}
image->data_dir = (void *)image->aouthdr->DataDirectory;
image->data_dir_sigtable = &image->data_dir[DATA_DIR_CERT_TABLE];
image->checksum = (uint32_t *)image->aouthdr->CheckSum;
image->cert_table_size = image->data_dir_sigtable->size; image->cert_table_size = image->data_dir_sigtable->size;
if (image->cert_table_size) if (image->cert_table_size)
cert_table = buf + image->data_dir_sigtable->addr; cert_table = buf + image->data_dir_sigtable->addr;
@ -165,7 +222,7 @@ static int image_pecoff_parse(struct image *image)
} }
image->sections = pehdr_u16(image->pehdr->f_nscns); image->sections = pehdr_u16(image->pehdr->f_nscns);
image->scnhdr = (void *)(image->aouthdr+1); image->scnhdr = image->aouthdr.addr + image->aouthdr_size;
return 0; return 0;
} }
@ -222,11 +279,9 @@ int image_find_regions(struct image *image)
struct region *regions; struct region *regions;
void *buf = image->buf; void *buf = image->buf;
int i, gap_warn; int i, gap_warn;
uint32_t align;
size_t bytes; size_t bytes;
gap_warn = 0; gap_warn = 0;
align = pehdr_u32(image->aouthdr->FileAlignment);
/* now we know where the checksum and cert table data is, we can /* now we know where the checksum and cert table data is, we can
* construct regions that need to be signed */ * construct regions that need to be signed */
@ -260,7 +315,7 @@ int image_find_regions(struct image *image)
set_region_from_range(&regions[2], set_region_from_range(&regions[2],
(void *)image->data_dir_sigtable (void *)image->data_dir_sigtable
+ sizeof(struct data_dir_entry), + sizeof(struct data_dir_entry),
buf + pehdr_u32(image->aouthdr->SizeOfHeaders)); buf + image->header_size);
regions[2].name = "datadir[CERT]->headers"; regions[2].name = "datadir[CERT]->headers";
bytes += regions[2].size; bytes += regions[2].size;
@ -282,7 +337,8 @@ int image_find_regions(struct image *image)
regions = image->checksum_regions; regions = image->checksum_regions;
regions[i + 3].data = buf + file_offset; regions[i + 3].data = buf + file_offset;
regions[i + 3].size = align_up(file_size, align); regions[i + 3].size = align_up(file_size,
image->file_alignment);
regions[i + 3].name = talloc_strndup(image->checksum_regions, regions[i + 3].name = talloc_strndup(image->checksum_regions,
image->scnhdr[i].s_name, 8); image->scnhdr[i].s_name, 8);
bytes += regions[i + 3].size; bytes += regions[i + 3].size;

12
image.h
View file

@ -54,7 +54,12 @@ struct image {
uint32_t *checksum; uint32_t *checksum;
struct external_PEI_DOS_hdr *doshdr; struct external_PEI_DOS_hdr *doshdr;
struct external_PEI_IMAGE_hdr *pehdr; struct external_PEI_IMAGE_hdr *pehdr;
PEPAOUTHDR *aouthdr; union {
PEPAOUTHDR *aout_64;
PEAOUTHDR *aout_32;
void *addr;
} aouthdr;
unsigned int aouthdr_size;
struct data_dir_entry *data_dir; struct data_dir_entry *data_dir;
struct data_dir_entry *data_dir_sigtable; struct data_dir_entry *data_dir_sigtable;
struct external_scnhdr *scnhdr; struct external_scnhdr *scnhdr;
@ -63,6 +68,11 @@ struct image {
void *cert_table; void *cert_table;
int cert_table_size; int cert_table_size;
/* We cache a few values from the aout header, so we don't have to
* keep checking whether to use the 32- or 64-bit version */
uint32_t file_alignment;
uint32_t header_size;
/* Regions that are included in the image hash: populated /* Regions that are included in the image hash: populated
* during image parsing, then used during the hash process. * during image parsing, then used during the hash process.
*/ */