Re: [PATCH rebase 2/2] peflags: Add -k, --checksum option

2023-08-07 Thread Corinna Vinschen via Cygwin-apps
On Aug  7 16:26, Christian Franke via Cygwin-apps wrote:
> This patch is on top of the --timestamp patch. Could not be applied to
> current HEAD without conflicts.
> 
> -- 
> Regards,
> Christian
> 

> From 9ecaf86bff5d229bf5b2a1ba1ff4674526fc1b68 Mon Sep 17 00:00:00 2001
> From: Christian Franke 
> Date: Mon, 7 Aug 2023 15:52:14 +0200
> Subject: [PATCH] peflags: Add -k, --checksum option
> 
> This allows to fix the file checksum in the PE header.
> An invalid checksum may break reproducible builds or may
> increase the risk of false positive malware detections.
> The checksum calculation is done by a new self-contained module
> 'pechecksum.c' which could also be built as a stand-alone tool
> or later added to rebase.
> 
> Signed-off-by: Christian Franke 
> ---
>  Makefile.in  |   6 +-
>  pechecksum.c | 195 +++
>  pechecksum.h |  25 +++
>  peflags.c| 129 --
>  4 files changed, 347 insertions(+), 8 deletions(-)
>  create mode 100644 pechecksum.c
>  create mode 100644 pechecksum.h

Pushed.


Thanks,
Corinna



[PATCH rebase 2/2] peflags: Add -k, --checksum option

2023-08-07 Thread Christian Franke via Cygwin-apps
This patch is on top of the --timestamp patch. Could not be applied to 
current HEAD without conflicts.


--
Regards,
Christian

From 9ecaf86bff5d229bf5b2a1ba1ff4674526fc1b68 Mon Sep 17 00:00:00 2001
From: Christian Franke 
Date: Mon, 7 Aug 2023 15:52:14 +0200
Subject: [PATCH] peflags: Add -k, --checksum option

This allows to fix the file checksum in the PE header.
An invalid checksum may break reproducible builds or may
increase the risk of false positive malware detections.
The checksum calculation is done by a new self-contained module
'pechecksum.c' which could also be built as a stand-alone tool
or later added to rebase.

Signed-off-by: Christian Franke 
---
 Makefile.in  |   6 +-
 pechecksum.c | 195 +++
 pechecksum.h |  25 +++
 peflags.c| 129 --
 4 files changed, 347 insertions(+), 8 deletions(-)
 create mode 100644 pechecksum.c
 create mode 100644 pechecksum.h

diff --git a/Makefile.in b/Makefile.in
index 34c4684..46df1d5 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -80,7 +80,7 @@ REBASE_LIBS = $(LIBIMAGEHELPER)
 REBASE_DUMP_OBJS = rebase-dump.$(O) rebase-db.$(O) $(LIBOBJS)
 REBASE_DUMP_LIBS =
 
-PEFLAGS_OBJS = peflags.$(O) $(LIBOBJS)
+PEFLAGS_OBJS = peflags.$(O) pechecksum.$(O) $(LIBOBJS)
 PEFLAGS_LIBS =
 
 SRC_DISTFILES = configure.ac configure Makefile.in \
@@ -111,7 +111,9 @@ rebase-dump.$(O):: rebase-dump.c rebase-db.h Makefile
 peflags$(EXEEXT): $(PEFLAGS_OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(PEFLAGS_OBJS)
 
-peflags.$(O):: peflags.c Makefile
+peflags.$(O):: peflags.c pechecksum.h Makefile
+
+pechecksum.$(O):: pechecksum.c pechecksum.h Makefile
 
 getopt.h: getopt.h_
cp $^ $@
diff --git a/pechecksum.c b/pechecksum.c
new file mode 100644
index 000..8695138
--- /dev/null
+++ b/pechecksum.c
@@ -0,0 +1,195 @@
+/*
+ * PE32 checksum
+ *
+ * Copyright (C) 2023 Christian Franke
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "pechecksum.h"
+
+#include 
+#include 
+#include 
+
+static inline unsigned get_word(const unsigned char * p)
+{
+  return p[0] | (p[1] << 8);
+}
+
+static inline unsigned get_dword(const unsigned char * p)
+{
+  return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static inline void put_dword(unsigned char * p, unsigned val)
+{
+  p[0] = (unsigned char) val;
+  p[1] = (unsigned char)(val >>  8);
+  p[2] = (unsigned char)(val >> 16);
+  p[3] = (unsigned char)(val >> 24);
+}
+
+unsigned pe32_checksum(int fd, int set, unsigned * old_checksum)
+{
+  // Read headers.
+  const unsigned bufsiz = 4096;
+  unsigned char buf[bufsiz];
+  if (lseek(fd, 0, SEEK_SET) != 0)
+return 0;
+  int nr = read(fd, buf, bufsiz);
+  if (nr < 0)
+return 0;
+  if (nr <= 0x200) {
+errno = EINVAL;
+return 0;
+  }
+
+  // IMAGE_DOS_HEADER.pe_magic == "MZ" ?
+  if (get_word(buf) != 0x5a4d) {
+errno = EINVAL;
+return 0;
+  }
+  // pehdr_offs = IMAGE_DOS_HEADER.lfa_new
+  unsigned pehdr_offs = get_dword(buf + 0x3c);
+  if (!(0x40 <= pehdr_offs && pehdr_offs <= nr - 0x100)) {
+errno = EINVAL;
+return 0;
+  }
+  // IMAGE_NT_HEADERS.Signature == "PE" ?
+  if (get_word(buf + pehdr_offs) != 0x4550) {
+errno = EINVAL;
+return 0;
+  }
+  // old_sum = IMAGE_OPTIONAL_HEADER(32|64).CheckSum
+  unsigned sum_offs = pehdr_offs + 0x58;
+  unsigned old_sum = get_dword(buf + sum_offs);
+  if (old_checksum)
+*old_checksum = old_sum;
+
+  // Clear old checksum because it is included below.
+  put_dword(buf + sum_offs, 0);
+
+  // Calc new checksum.
+  unsigned sum = 0, size = 0;
+  int i = 0;
+  for (;;) {
+sum += get_word(buf + i);
+sum = (sum + (sum >> 16)) & 0x;
+
+if ((size += 2) >= 0x4000) {
+  // 1GiB, assume something is wrong.
+  errno = EINVAL;
+  return 0;
+}
+if ((i += 2) < nr - 1)
+  continue; // ... with next 2 bytes.
+
+// Assume that there are no short reads.
+if (i < nr)
+  break; // Last byte.
+i = 0;
+if ((nr = read(fd, buf, bufsiz)) < 0)
+  return 0;
+if (nr < 2)
+  break; // Last byte or EOF.
+// Continue with next block.
+  }
+
+  // Handle last byte of file with uneven size.
+  if (i < nr) {
+sum += buf[i];
+sum = (sum + (sum >> 16)) & 0x;
+size++;
+  }
+
+  // Add filesize to use some of the upper 16 bits.
+  sum += size;
+
+  // Fix the checksum if requested and required.
+  if (set && old_sum != sum) {
+put_dword(buf, sum);
+if (lseek(fd, sum_offs, SEEK_SET) == -1)
+  return 0;
+if (write(fd, buf, 4) == -1)
+  return 0;
+  }
+
+  return sum;
+}
+
+#if STANDALONE
+//
+// Test program
+//
+#include 
+#include 
+
+// Optionally check result using native imagehlp.dll function.
+#if STANDALONE > 1
+#include 
+#include 
+#endif
+
+int main(int argc, char ** argv)
+{
+  int i = 1, set = 0;
+  if (i < argc && !strcmp(argv[i], "-s")) {
+set = 1;
+i++;
+  }
+  if (i >= argc) {
+printf("Usage: %s [-s]