Development Windows Hardening - ShadobaDev/Platemaker-qt GitHub Wiki
Windows security hardening
What the application does about DLL search-path injection, what it defers, and what it does not address.
Platemaker is an unsigned desktop app that ships a large bundled DLL closure (Qt, platemaker.dll,
the whole libvips graph, the toolchain's runtime), all copied next to the executable
(cross-platform notes). Third-party global hooks do inject themselves
into the process (seen: LG OnScreen Control's ScreenSplitterHook64X.dll, RGB software) โ benign, but a
reminder that the process has to take its own precautions. This page records the security posture and,
importantly, what is deliberately not done and why.
What we do โ restrict the DLL search path
app/main.cpp calls SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32) as the first statement in main() โ before QApplication, before
any post-main dynamic DLL load. This drops the current working directory and PATH from the default
LoadLibrary search, closing the classic DLL search-order hijack / planting vector (drop a
malicious zlib1/libpng on PATH or beside a data file and have our graph pick it up).
- Why it's safe here: the entire dependency closure is co-located in the application directory, which
LOAD_LIBRARY_SEARCH_APPLICATION_DIRstill covers, and the exe's static imports are resolved beforemain()runs โ so startup linkage is unaffected. Only post-maindynamic loads are narrowed (Qt plugins, loaded by absolute path with their deps in the app dir; libvips operation DLLs at render time). Verified by launching + running a full render under both the Qt Creator run environment (which injects the Qt bin viaPATH) and the installed build. - Robustness: the API is resolved dynamically via
GetProcAddress, so it degrades to a no-op on any pre-Windows-8 host instead of failing to load, and sidesteps MinGW header/_WIN32_WINNTquirks. It logs a one-line startup notice (DLL hardening: โฆ) so the code path is verifiable in DebugView / the Qt Creator Application Output. - The one consequence: SVG icons. Qt's SVG plugins (
qsvgicon,qsvg) importQt6Svg.dll, which nothing in the executable referenced, so it would load lazily โ after the search path no longer includesPATH, where a build-directory run finds the Qt bin. Every SVG then rendered blank.main()constructs a throwawayQSvgRenderer, which makesQt6Svg.dlla load-time import resolved before the hardening runs. Deployed builds carry it in the app directory anyway.
What we defer โ blocking extension-point injection
The natural companion is SetProcessMitigationPolicy(ProcessExtensionPointDisablePolicy, โฆ), which would
block exactly the hook injection we observed, plus AppInit_DLLs and legacy IME DLLs. We deliberately
do not ship it. It also disables legacy IMM32 IMEs and some accessibility / assistive tools.
Broken IME would hurt non-Latin text entry for Korean / Japanese / Chinese authors โ a core webcomic
audience โ and we have no way to validate that without a CJK IME test rig (modern TSF IMEs are usually
unaffected; legacy ones are not). The benign, low real-world risk of hook injection (the attacker already
needs hook-registration privileges on the machine โ not a remote vector) does not justify that
regression. It stays an idea, not a plan: it costs more than it buys, and it is not on the TODO list.
What this does not address
Neither mitigation affects SmartScreen / AV reputation โ for an unsigned app that is per-file-hash and resets each release. Code signing is the real integrity/reputation fix; the search-path restriction is defence-in-depth layered under it, not a substitute.