Add support for analysing Setup Factory installers#26
Add support for analysing Setup Factory installers#26Tom Plant (pl4nty) wants to merge 4 commits into
Conversation
Detect Indigo Rose Setup Factory installers (e.g. DIALux evo). The setup stub embeds the `irsetup.exe` run-time, whose assembly manifest carries `<description>Setup Factory Run-time</description>`, and the builder stamps `Created with Setup Factory <version>` into the `Comments` version-info field. Either signal identifies the installer. Emits an `exe` installer with the `/S` silent switch and populates the ARP entry from the version info (product name, publisher, version). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012BX4kXe76SvBKY3BySbzRN
The setup stub's version info describes the Setup Factory run-time (and is often left at the builder's defaults, e.g. ProductName "Setup Factory Runtime" published by Indigo Rose Corporation), not the packaged application. The real Apps & Features / uninstall entry (DisplayName, DisplayVersion, ProductCode, ...) is written by the compiled setup script inside the payload, which isn't recoverable from the PE. Emitting DisplayName/Publisher/DisplayVersion from the version info therefore produced ARP entries that don't match the registry - the real winget manifests for these packages (e.g. DIAL.DIALux-evo) carry no such entries. Drop the extraction and emit only the reliably-known fields: the exe installer type and the /S silent switch. Version info is still read solely to detect the "Created with Setup Factory" Comments marker. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012BX4kXe76SvBKY3BySbzRN
Test resultsRan
Example output (DIALux evo), which matches the shape of the published InstallerType: exe
InstallModes:
- interactive
- silent
- silentWithProgress
InstallerSwitches:
Silent: /S
SilentWithProgress: /SNotes¹ Architecture is read from the setup stub's PE machine type. DIALux ships a 32-bit stub that installs a 64-bit app, so it reports ² The exact ³ The OutCALL asset is a GitHub release download. This session's egress policy scopes GitHub to On
|
Rather than reading the setup stub's PE version info - which describes the Setup Factory run-time and is frequently left at the builder's defaults (e.g. gloCOM/Communicator reported "Setup Factory Runtime" published by "Indigo Rose Corporation") - walk the Setup Factory 8/9 archive in the PE overlay to recover the compiled setup script (irsetup.dat) and its session variable table. That table holds the values the author entered in the designer: %ProductName%, %CompanyName% and %ProductVer%. The archive layout and XOR/compression handling follow the format documented by CybercentreCanada/sfextract. The script is LZMA- or LZMA2-compressed (decoded via the already-vendored liblzma); large embedded files are seeked past so only the small script is buffered. Detection now keys off the 16-byte overlay signature, with the run-time manifest and version-info comments kept as fallbacks. The recovered product name and publisher populate AppsAndFeaturesEntries. The version variable is author-defined and sometimes a bare major number, so it is only used when it looks like a real dotted version. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012BX4kXe76SvBKY3BySbzRN
884e97d to
aca026b
Compare
Updated results — metadata now parsed from the overlay archiveFollowing the feedback to parse the binary rather than the stub's PE version info, the detector now walks the Setup Factory archive, decompresses All emit Notes¹ The exact ² OutCALL is a GitHub release asset; this session's egress policy scopes GitHub to On the empty/omitted fields
Known limitationArchitecture still comes from the setup stub's PE machine type, so DIALux reports Generated by Claude Code |
| } | ||
| } | ||
|
|
||
| fn has_archive_signature<R: Read + Seek>(reader: &mut R, overlay_start: u64) -> bool { |
There was a problem hiding this comment.
if this is only called once, we should inline it
There was a problem hiding this comment.
Done in 484018c — inlined into new(). (The manifest/comments fallback that also lived in this file is gone too, so detection is now just the inlined signature check.)
Generated by Claude Code
| // The overlay signature is the definitive marker, but fall back to the run-time manifest and | ||
| // version-info comments so older (Setup Factory 7) or repacked stubs are still recognised. | ||
| if !has_archive | ||
| && !is_runtime_manifest(pe, &mut reader) | ||
| && !has_setup_factory_comments(pe, &mut reader) | ||
| { | ||
| return Err(SetupFactoryError::NotSetupFactoryFile); | ||
| } |
There was a problem hiding this comment.
can we remove this? which of your tests actually needs it? if we're going to keep it, we should document the tests + Factory versions that trigger it
There was a problem hiding this comment.
Removed in 484018c. No test needed it — all seven test installers (DIALux evo, LDraw AIOI, gloCOM, Communicator, and the three Locklizard Safeguard installers) carry the 16-byte overlay signature, so detection now keys off that alone. The fallback was speculative (Setup Factory 7 / repacked stubs) with no coverage.
Generated by Claude Code
| //! ..\x0d%ProductName%\x06gloCOM\x01\x00\x00\x00..\x0d%CompanyName%\x0dBicom Systems.. | ||
| //! ``` | ||
| //! | ||
| //! Entries are separated by a short, version-dependent run of bytes, so rather than decode that |
There was a problem hiding this comment.
is there any documentation or code that could decode these bytes? I wonder if they have useful data
There was a problem hiding this comment.
I dug into these. Between entries the bytes are 01 00 00 00 <XX XX 01> 00 00 00: the leading 01 is a constant type/flag, and the middle three bytes are constant within a given installer but differ between installers (6e 81 01 for Locklizard PDF Writer, b4 83 01 for gloCOM) — i.e. a per-build marker/checksum, not per-variable data. No product metadata is hidden there; the useful content is the name/value pairs we already extract. Neither sfextract nor SFUnpacker decode this table (they only handle the CSetupFileData file table), so there's no reference decoder to borrow from.
Generated by Claude Code
|
|
||
| /// A file count above this is treated as an embedded `lua5.1.dll` special file rather than a real | ||
| /// count, matching `sfextract`'s heuristic. | ||
| const MAX_PLAUSIBLE_FILE_COUNT: u32 = 1000; |
There was a problem hiding this comment.
which test cases have this? is the real issue that we're not parsing a specific Factory version properly?
There was a problem hiding this comment.
All seven test installers take this path — it's the normal Setup Factory 9 layout, not a version mis-parse. After irsetup.exe, SF9 embeds a second special file (lua5.1.dll, ~350 KB) before the file table. The u32 read here to disambiguate is the low half of that DLL's 64-bit size (~350000), hence >1000; a genuine file count is small. Expanded the doc comment in 484018c to spell this out.
Generated by Claude Code
|
|
||
| /// Upper bound on the decompressed script size we are willing to buffer (scripts are well under a | ||
| /// megabyte; this guards against a malformed size field). | ||
| const MAX_SCRIPT_SIZE: i64 = 64 << 20; |
There was a problem hiding this comment.
I don't think this is necessary
There was a problem hiding this comment.
Agreed — removed in 484018c. The script is now read with a bounded take(size).read_to_end(), which grows with the data instead of pre-allocating from the size field, so the cap isn't needed.
Generated by Claude Code
- Detect solely from the 16-byte overlay signature and inline the check; drop the run-time manifest / version-info comment fallback, which no test exercised (all samples carry the signature). - Read the script with a bounded take().read_to_end() instead of pre-allocating from the size field, removing the size-cap guard. - Document that the embedded lua5.1.dll branch is taken by every tested installer (it is the normal Setup Factory 9 layout, not a version mis-parse): the leading u32 is the low half of the DLL's 64-bit size. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012BX4kXe76SvBKY3BySbzRN
Summary
Adds detection and analysis of Indigo Rose Setup Factory installers, such as DIALux evo.
Setup Factory setup executables are a self-extracting stub wrapping the
irsetup.exerun-time, with a compressed archive in the PE overlay. This detector:Setup Factory Run-timeand theCreated with Setup Factoryversion-info comment kept as fallbacks for older/repacked stubs).irsetup.dat) and reads its session variable table — the real values the author entered in the designer (%ProductName%,%CompanyName%,%ProductVer%).exeinstaller with the documented/Ssilent switch and anAppsAndFeaturesEntriesbuilt from the recovered metadata.Why parse the binary instead of the PE version info
The setup stub's PE version info describes the run-time, not the packaged app — and authors frequently leave it at the Setup Factory defaults (gloCOM/Communicator would otherwise report
DisplayName: Setup Factory Runtime, publisherIndigo Rose Corporation). The real product name/publisher/version live in the compiled script inside the overlay archive, so that's what we read. The archive format, XOR obfuscation and compression handling follow CybercentreCanada/sfextract.Notes:
liblzma. Large embedded files are seeked past so only the small script (well under 1 MB) is buffered — no full-payload extraction.%ProductVer%is author-defined and is sometimes a bare major number (gloCOM ships"4"; the real version lives in a custom variable), so it's only used when it looks like a real dotted version.Changes
src/analysis/installers/setup_factory/module:mod.rs— detection, best-effort metadata extraction, installer emission.archive.rs— walks the overlay archive and decompressesirsetup.dat.script.rs— parses the session variable table (with unit tests).src/analysis/installers/mod.rsand wiredSetupFactoryinto theExedetection chain insrc/analysis/installers/exe.rs.Verification
Tested end-to-end with
komac analyzeagainst real Setup Factory installers — see the results comment below.cargo build,cargo clippy(-D warnings), andcargo test(session-variable parser) all pass.🤖 Generated with Claude Code
https://claude.ai/code/session_012BX4kXe76SvBKY3BySbzRN