The ultimate goal is comparing 2 binaries built from exact same source in exact same environment and being able to tell that they indeed are functionally equivalent.

One application for this would be focusing QA time on things that were actually changed between releases, as well as change monitoring in general.

MSVC in tandem with PE format naturally makes this very hard to do.

So far I found and neutralized those things:

  • PE timestamp and checksum
  • Digital signature directory entry
  • Debugger section timestamp
  • PDB signature, age and file path
  • Resources timestamp
  • All file/product versions in VS_VERSION_INFO resource
  • Digital signature section

I parse PE, find offsets and sizes for all those things and ignore byte ranges when comparing binaries. Works like charm (well, for the few tests I've run it). I can tell that signed executable with version 1.0.2.0 built on Win Server 2008 is equal to unsigned one, of version 10.6.6.6, build on my Win XP dev box, as long as compiler version and all sources and headers are the same. This seems to work for VC 7.1 -- 9.0. (For release builds)

With one caveat.

Absolute paths for both builds must be the same must have the same length.

cl.exe converts relative paths to absolute ones, and puts them right into objects along with compiler flags and so on. This has unproportional effects on whole binary. One character change in path will result in one byte changed here and there several times over whole .text section (however many objects were linked I suspect). Changing length of the path results in significantly more differences. Both in obj files and in linked binary.

Feels like file path with compile flags is used as some kind of hash, which makes it into linked binary or even affects placement order of unrelated pieces of compiled

Edit
Report