From c0d4e3f33421bcba3138829bed45e011c5b80c87 Mon Sep 17 00:00:00 2001 From: bwoodsend Date: Mon, 22 Feb 2021 21:04:03 +0000 Subject: [PATCH] Building: Windows: Set EXE checksums (#5579). Windows executables contain an optional checksum to protect against corruption. It turns out that several of antiviral programs raise false positives if this checksum is missing or wrong. Setting this checksum appeases McAfee and inconsistently fixes MS Defender which are probably the most common (and also dumbest) AVs for Windows. --- PyInstaller/building/api.py | 4 ++++ PyInstaller/utils/win32/winutils.py | 14 ++++++++++++++ bootloader/wscript | 2 ++ news/5579.feature.rst | 1 + tests/functional/test_basic.py | 12 ++++++++++++ 5 files changed, 33 insertions(+) create mode 100644 news/5579.feature.rst diff --git a/PyInstaller/building/api.py b/PyInstaller/building/api.py index a83c390673f..02d5c30a9fe 100644 --- a/PyInstaller/building/api.py +++ b/PyInstaller/building/api.py @@ -640,6 +640,10 @@ def assemble(self): logger.info("Fixing EXE for code signing %s", self.name) import PyInstaller.utils.osx as osxutils osxutils.fix_exe_for_code_signing(self.name) + if is_win: + # Set checksum to appease antiviral software. + from PyInstaller.utils.win32.winutils import set_exe_checksum + set_exe_checksum(self.name) os.chmod(self.name, 0o755) # get mtime for storing into the guts diff --git a/PyInstaller/utils/win32/winutils.py b/PyInstaller/utils/win32/winutils.py index 9ec66c676ea..a2dbf5d37c9 100644 --- a/PyInstaller/utils/win32/winutils.py +++ b/PyInstaller/utils/win32/winutils.py @@ -155,3 +155,17 @@ def convert_dll_name_to_str(dll_name): return str(dll_name, encoding='UTF-8') else: return dll_name + + +def set_exe_checksum(exe_path): + """Set executable's checksum in its metadata. + + This optional checksum is supposed to protect the executable against + corruption but some anti-viral software have taken to flagging anything + without it set correctly as malware. See issue #5579. + """ + import pefile + pe = pefile.PE(exe_path) + pe.OPTIONAL_HEADER.CheckSum = pe.generate_checksum() + pe.close() + pe.write(exe_path) diff --git a/bootloader/wscript b/bootloader/wscript index 7d96c123051..0581b057c6b 100644 --- a/bootloader/wscript +++ b/bootloader/wscript @@ -311,6 +311,8 @@ def set_arch_flags(ctx): ctx.env.append_value('CFLAGS', '/D_CRT_SECURE_NO_WARNINGS') # We use SEH exceptions in winmain.c; make sure they are activated. ctx.env.append_value('CFLAGS', '/EHa') + # Set the PE checksum on resulting binary + ctx.env.append_value('LINKFLAGS', '/RELEASE') # Ensure proper architecture flags on Mac OS X. elif ctx.env.DEST_OS == 'darwin': diff --git a/news/5579.feature.rst b/news/5579.feature.rst new file mode 100644 index 00000000000..b3ee0a6e898 --- /dev/null +++ b/news/5579.feature.rst @@ -0,0 +1 @@ +Windows: Set EXE checksums. Reduces false-positive detection from antiviral software. diff --git a/tests/functional/test_basic.py b/tests/functional/test_basic.py index 59953a2bf86..738ee2d88e9 100644 --- a/tests/functional/test_basic.py +++ b/tests/functional/test_basic.py @@ -585,3 +585,15 @@ def test_several_scripts2(pyi_builder_spec): Verify each script has it's own global vars (basic test). """ pyi_builder_spec.test_spec('several-scripts2.spec') + + +@pytest.mark.win32 +def test_pe_checksum(pyi_builder): + import pefile + + pyi_builder.test_source("print('hello')") + exes = pyi_builder._find_executables('test_source') + assert exes + for exe in exes: + pe = pefile.PE(exe) + assert pe.verify_checksum()