rufus/loadcfg.py
Pete Batard 4adfa4f37e
[misc] more MinGW DLL side loading "improvements"
- Side load SetupAPI.dll, as this is the DLL that was causing the CfgMgr32.dll local load.
  This reverts part of 622e60659c since we no longer have to hook into CfgMgr32.dll directly.
- Also set the redefinition of DECLSPEC_IMPORT, which we need for MinGW32 x86, in the global AM_CFLAGS
  of configure.ac, so that we no longer have to worry about forgetting to do it in a source and experience
  crashes on 32-bit as a result (See 965759f58a).
- Also delay-load crypt32.dll while we're at it.
- Also add provision for enabling /DEPENDENTLOADFLAG:0x800 on MinGW, by leaving a properly crafted entry
  in the .rdata section that can then be used with the loadcfg.py Python script.
- Sadly, per https://github.com/pbatard/rufus/issues/2701#issuecomment-2874788564 and subsequent comment,
  having DependentLoadFlags set to LOAD_LIBRARY_SEARCH_SYSTEM32 is still not enough to take care of side
  loading issues, as, ever since the introduction of wimlib support, we are seeing CRYPTBASE.DLL being
  side-loaded in MinGW, and, even with crypt32.dll being delay-loaded there is literally *nothing* we can
  do about it!
- The end result of all the above is that we will have no choice but ditch MinGW for release executables
  as it's just impossible to properly take care of side-loading vulnerabilities with MinGW (and Microsoft
  are REALLY not helping with this whole mess either, when they don't even use LOAD_LIBRARY_SEARCH_SYSTEM32
  for Windows' system DLLs).
- In preparation for this, we add UPX compression to the x86_64 and x86_32 MSVC executables.
- Finally, we also fix one last Coverity warning in xml.c and remove duplicates in .vcxproj for ARM64.
2025-05-13 20:31:35 +01:00

63 lines
2 KiB
Python

#!/bin/env python3
# PE Load Configuration section enabler for MinGW/gcc executables.
# The PE executable should have a IMAGE_LOAD_CONFIG_DIRECTORY## section
# in .rdata with a 16-byte IMAGE_DIRECTORY_ENTRY_MARKER marker.
import os
import sys
import pefile
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10
IMAGE_DIRECTORY_ENTRY_MARKER = b"_RUFUS_LOAD_CFG"
if len(sys.argv) < 2:
raise RuntimeError("No executable path supplied")
# Create a temp file as our source
pe_dst = sys.argv[1]
pe_src = sys.argv[1] + ".tmp"
os.replace(pe_dst, pe_src)
# Open and parse PE
pe = pefile.PE(pe_src)
# Find .rdata section
rdata_section = next(
(s for s in pe.sections if s.Name.rstrip(b'\x00') == b'.rdata'),
None
)
if not rdata_section:
raise RuntimeError(".rdata section not found")
# Read the section's raw data to search for the target string
raw_data = rdata_section.get_data()
# Look for the target data in the raw section data
offset = raw_data.find(IMAGE_DIRECTORY_ENTRY_MARKER)
if offset == -1:
raise RuntimeError("Load Config marker not found")
# Move past our 16 bytes marker
offset += 0x10
# Calculate the RVA and size of the Load Config section
load_config_rva = rdata_section.VirtualAddress + offset
print(f"RVA of Load Config: 0x{load_config_rva:X}")
load_config_size = pe.get_dword_at_rva(load_config_rva)
if (load_config_size < 0x20):
raise RuntimeError("Size of Load Config section is too small")
print(f"Size of Load Config: 0x{load_config_size:X}")
# Update Load Config directory entry
pe.OPTIONAL_HEADER.DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress = load_config_rva
pe.OPTIONAL_HEADER.DATA_DIRECTORY[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size = load_config_size
# Update the checksum
pe.OPTIONAL_HEADER.CheckSum = pe.generate_checksum()
# Write the updated PE file and remove temp
pe.write(pe_dst)
os.remove(pe_src)
# Can be validated with `DUMPBIN /LOADCONFIG <.exe>` or `objdump -x <.exe> | grep "Load Configuration"`
print(f"Successfully enabled Load Config section in '{pe_dst}'")