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:
Steve Langasek 2016-01-27 11:06:02 -08:00 committed by James Bottomley
parent 706bec1a95
commit be1f3d8350
2 changed files with 62 additions and 1 deletions

View file

@ -1,6 +1,6 @@
#!/bin/bash #!/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 # Add ccan upstream sources
if [ ! -e lib/ccan.git/Makefile ] if [ ! -e lib/ccan.git/Makefile ]

View file

@ -38,6 +38,7 @@
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <ccan/endian/endian.h>
#include <ccan/talloc/talloc.h> #include <ccan/talloc/talloc.h>
#include <ccan/read_write_all/read_write_all.h> #include <ccan/read_write_all/read_write_all.h>
#include <ccan/build_assert/build_assert.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); 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) static int image_pecoff_parse(struct image *image)
{ {
struct cert_table_header *cert_table; 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) if (aligned_size != tot_size)
memset(start + size, 0, aligned_size - tot_size); memset(start + size, 0, aligned_size - tot_size);
image->cert_table = cth;
return 0; return 0;
} }
@ -587,6 +646,8 @@ int image_write(struct image *image, const char *filename)
image->data_dir_sigtable->size = 0; image->data_dir_sigtable->size = 0;
} }
image_pecoff_update_checksum(image);
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) { if (fd < 0) {
perror("open"); perror("open");