Update the PE checksum field using the somewhat-underdocumented
algorithm, so that we match the Microsoft implementation in our signature generation. [jejb: add endian to autogen.sh and fix for multi-sign] Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
parent
706bec1a95
commit
be1f3d8350
2 changed files with 62 additions and 1 deletions
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
ccan_modules="talloc read_write_all build_assert array_size"
|
||||
ccan_modules="talloc read_write_all build_assert array_size endian"
|
||||
|
||||
# Add ccan upstream sources
|
||||
if [ ! -e lib/ccan.git/Makefile ]
|
||||
|
|
61
src/image.c
61
src/image.c
|
@ -38,6 +38,7 @@
|
|||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <ccan/endian/endian.h>
|
||||
#include <ccan/talloc/talloc.h>
|
||||
#include <ccan/read_write_all/read_write_all.h>
|
||||
#include <ccan/build_assert/build_assert.h>
|
||||
|
@ -134,6 +135,62 @@ static int align_up(int size, int align)
|
|||
return (size + align - 1) & ~(align - 1);
|
||||
}
|
||||
|
||||
static uint16_t csum_update_fold(uint16_t csum, uint16_t x)
|
||||
{
|
||||
uint32_t new = csum + x;
|
||||
new = (new >> 16) + (new & 0xffff);
|
||||
return new;
|
||||
}
|
||||
|
||||
static uint16_t csum_bytes(uint16_t checksum, void *buf, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
uint16_t *p;
|
||||
|
||||
for (i = 0; i < len; i += sizeof(*p)) {
|
||||
p = buf + i;
|
||||
checksum = csum_update_fold(checksum, *p);
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static void image_pecoff_update_checksum(struct image *image)
|
||||
{
|
||||
bool is_signed = image->sigsize && image->sigbuf;
|
||||
uint32_t checksum;
|
||||
struct cert_table_header *cert_table = image->cert_table;
|
||||
|
||||
/* We carefully only include the signature data in the checksum (and
|
||||
* in the file length) if we're outputting the signature. Otherwise,
|
||||
* in case of signature removal, the signature data is in the buffer
|
||||
* we read in (as indicated by image->size), but we do *not* want to
|
||||
* checksum it.
|
||||
*
|
||||
* We also skip the 32-bits of checksum data in the PE/COFF header.
|
||||
*/
|
||||
checksum = csum_bytes(0, image->buf,
|
||||
(void *)image->checksum - (void *)image->buf);
|
||||
checksum = csum_bytes(checksum,
|
||||
image->checksum + 1,
|
||||
(void *)(image->buf + image->data_size) -
|
||||
(void *)(image->checksum + 1));
|
||||
|
||||
if (is_signed) {
|
||||
checksum = csum_bytes(checksum,
|
||||
cert_table, sizeof(*cert_table));
|
||||
|
||||
checksum = csum_bytes(checksum, image->sigbuf, image->sigsize);
|
||||
}
|
||||
|
||||
checksum += image->data_size;
|
||||
|
||||
if (is_signed)
|
||||
checksum += sizeof(*cert_table) + image->sigsize;
|
||||
|
||||
*(image->checksum) = cpu_to_le32(checksum);
|
||||
}
|
||||
|
||||
static int image_pecoff_parse(struct image *image)
|
||||
{
|
||||
struct cert_table_header *cert_table;
|
||||
|
@ -508,6 +565,8 @@ int image_add_signature(struct image *image, void *sig, int size)
|
|||
if (aligned_size != tot_size)
|
||||
memset(start + size, 0, aligned_size - tot_size);
|
||||
|
||||
image->cert_table = cth;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -587,6 +646,8 @@ int image_write(struct image *image, const char *filename)
|
|||
image->data_dir_sigtable->size = 0;
|
||||
}
|
||||
|
||||
image_pecoff_update_checksum(image);
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
|
|
Loading…
Reference in a new issue