innospect
A pure-Rust parser for Inno Setup installer
binaries. Provides typed access to the loader stub overlay, setup headers, every
typed record stream, and on-demand file extraction across Inno Setup 5.0 through
the 7.x preview series — including the modern XChaCha20 / euFiles / euFull
encryption modes and the legacy ARC4 + SHA-1 / MD5 password-verifier path.
Built for malware analysis and reverse engineering. The crate is
adversarial-input-safe: unsafe_code, panicking unwraps, slice indexing, and
arithmetic-with-side-effects are all denied at the lib root.
Features
- PE locator — both the modern signature-scan path (5.1.5+) and the legacy
0x30file-offset pointer used by pre-5.1.5 installers; tolerates BlackBox / GOG re-packagers. - Setup-0 decompression — Stored / Zlib / LZMA1 / LZMA2 across every documented version cutover, with the standalone-encryption-header path for 6.5+ and the inline-verifier path for 6.4.x.
- Setup-1 file extraction — solid-LZMA chunks with per-file BCJ inverse filtering (4108 / 5200 / 5.3.9-flip variants) and SHA-256 / SHA-1 / MD5 / CRC-32 / Adler-32 checksum verification at EOF.
- Per-version
TSetupHeader— everyString/AnsiString/ count / tail field across the 1.x..7.x history, with[Files]/[Run]/[Icons]/[Registry]/[INI]/[Components]/[Tasks]/[Types]/[Languages]/[CustomMessages]/[Permissions]records typed individually. - Encryption — XChaCha20 with PBKDF2-SHA-256 key derivation (Inno 6.4+);
ARC4 with salted SHA-1 (5.3.9..6.4) or MD5 (4.2..5.3.9); CRC32 verifier
(pre-4.2). Password trial via
from_bytes_with_passwords. - Embedded
[Code]/ IFPS — re-exports thepascalscriptcontainer parser; theinno_api_descriptiontable maps Inno-runtime imports (RegWriteStringValue,Exec,ShellExec, …) to one-line summaries for triage. - Analysis API —
exec_commands()/registry_ops()/shortcuts()walk the relevant record streams and tag each entry with install-vs-uninstall phase, registry-write classification, or icon-target resolution. - Uninstaller reconstruction —
extract_uninstaller()patches the loader stub bytes back to the canonicalInUnform.
Quick start
use ;
let bytes = read?;
let inst = from_bytes?;
let v = inst.version;
println!;
if let Some = inst.header
// Extract every file (skipping the uninstaller-stub entry).
for in inst.extract_files.filter_map
// Analysis: exec commands, registry writes, resolved shortcut targets.
for cmd in inst.exec_commands
let writes = inst
.registry_ops
.filter
.count;
println!;
For password-protected installers:
use ;
let bytes = read?;
match from_bytes_with_passwords
A full inspection tool ships in examples/dump.rs:
Coverage
End-to-end tested against:
- Real-world: HeidiSQL 12.17 (6.4.0.1), ImageMagick 7.1.2 (6.1.0).
- Synthetic plain matrix: 5.0.8, 5.1.14, 5.2.3, 5.3.11, 5.4.3, 5.5.5, 5.5.7, 6.0.0u, 6.3.0, 6.4.3, 6.5.2, 6.5.2-alt, 6.6.1, 6.7.0, 7.0.0-preview-3.
- Synthetic encrypted matrix (password
test123): same version ladder forenc-files-*(per-chunk encryption) plusenc-full-*for 6.5.2+ (euFullwhole-stream encryption).
Format paths recognized but not yet exercised by the public sample matrix:
4.x, 3.x / 2.x, 16-bit 1.2.x, ISX (My Inno Setup Extensions), and multi-slice
(DiskSpanning=yes).
Minimum Rust version
1.88 (edition 2024). Pinned by rust-version in Cargo.toml; the CI matrix
exercises both 1.88 and stable.
License
Apache-2.0. See LICENSE.