A high-performance, cross-platform port of the legendary Inno Setup Unpacker (innounp) written in pure Go (Golang).
This platform supports nearly three decades of Inno Setup installer structures (from vintage v1.x releases in 1997 up to modern v6.x releases) natively under a unified, schema-driven, zero-dependency codebase. It is fully compiled as a command-line interface (CLI) and exposed as a high-performance, in-process Python API wrapper via ctypes.
Warning
Active Testing & Beta Phase This project is currently in its active Beta and testing phase. While our parsing coverage is extremely high and extensively validated against years of Inno Setup layouts, we are actively looking for feedback! If you encounter any custom-packed installers, obfuscation techniques, or edge cases where parsing fails, please let us know and open an issue with the sample hash or error logs so we can continue to expand and perfect our self-healing capabilities!
innounp-go/- The Go module home.parser/- The core extraction engine package:loader.go&types.go: PE binary DOS header scanners and 32/64-bit offset table solvers. Supports legacy and modern signatures.compress.go: CustomCRCChunkReadersegment parser, alongside zlib, bzip2, LZMA, and LZMA2 stream bindings.schema.go: Declarative schema registries for v1.x (1000), v2.x (2000), v3.x (3000), v4.x (4000), v5.x (5000), and v6.x (6000) records.records.go: Sequential record deserializer with UTF-16LE decoding.decrypt.go: Cryptographic key derivation and RC4 (v5) / XChaCha20 (v6) stream decryption layers.optimizer.go: CALL/JMP absolute target address restoration filters.extract.go: Multi-slice payload extractors and checksum verifiers.orchestrator.go: High-level list and extract loops.crack.go: Concurrent, multi-threaded password recovery and brute-force engine.ifps/- Native Inno Free Pascal Script (IFPS) parser:types.go: OpCode and TypeCode constant tables.parser.go: Binary deserializer ofCompiledCode.binstructures.disasm.go: Linear sweep bytecode disassembler and assembly formatter.
main.go- Command-line interface entry point.cgo_exports.go- CGo shared library exported entry points.innounpack.py- Standard ctypes Python wrapper with recovery defaults.test_innounpack.py- Python unit-test suite.
Go provides two different build paths: a pure Go CLI binary (which can be compiled with zero external dependencies for any target on earth) and a C-shared library (which requires a host C compiler and is used by Python ctypes).
Because the CLI binary has zero C dependencies, you can cross-compile it for any operating system and architecture directly from your current machine.
Change directory into innounp-go/ and run the appropriate command:
GOOS=darwin GOARCH=arm64 go build -o innounp-go main.go cgo_exports.goGOOS=darwin GOARCH=amd64 go build -o innounp-go main.go cgo_exports.goGOOS=linux GOARCH=amd64 go build -o innounp-go main.go cgo_exports.goGOOS=windows GOARCH=amd64 go build -o innounp-go.exe main.go cgo_exports.goBuilding shared libraries requires CGo, which depends on a platform-native C compiler. Ensure you have the prerequisites installed:
- macOS: Install Xcode Command Line Tools (
xcode-select --install). - Linux: Install standard build tools (
sudo apt install build-essentialorsudo dnf groupinstall "Development Tools"). - Windows: Install MinGW-w64 (via MSYS2, Chocolatey, or w64devkit).
Inside the innounp-go/ directory, execute the native build command:
go build -buildmode=c-shared -o libinnounpack.dylibgo build -buildmode=c-shared -o libinnounpack.sogo build -buildmode=c-shared -o libinnounpack.dllRunning the compiled native CLI binary against any Inno Setup installer executable verifies its internal offset tables, performs magic boundary checking, detects the installer version, and displays its block metadata:
./innounp-go [flags] <path_to_installer_exe_or_bytecode>-l- List-Only Mode: Prints out a beautifully aligned table containing IDs, uncompressed file sizes, compression status, password protection, and target destination directories.-o <directory_path>- Output extraction folder. Automatically creates the directory (with0755permissions) if it does not exist on disk.-p <password>- Explicit decryption password.-crack- Initiates a multi-core, parallel dictionary attack using our built-in forensic dictionary of standard malware and installation passwords. If found, the password is automatically injected to list or extract the files in-place!-w <path_to_wordlist>- Initiates a concurrent parallel dictionary attack using passwords read line-by-line from a specified wordlist (e.g.rockyou.txt). If found, the password is automatically injected to complete the run.-d- Native Decompiler Mode: Extracts and disassembles compiled Pascal Script bytecode (CompiledCode.bin) in-memory. Auto-detects format: accepts both a full setup executable or a raw standalone.binfile as the path!
Analyzing Inno Setup Executable: my_installer.exe
================================================================================
Loader Offset Table verified successfully!
...
================================================================================
Detected Inno Setup Installer Version: 5500 (Format: Unicode)
Number of File Payloads: 4
================================================================================
Initiating Multi-Core Parallel Password Recovery...
Using built-in quick dictionary of 25 forensic keys
[+] PASSWORD RECOVERED SUCCESSFULLY!
Password: infected
Hash Type: SHA-1
Guesses: 25
================================================================================
Extracting payloads to: extracted_my_installer
--------------------------------------------------------------------------------
Decrypting & Extracting bin/malware.exe ... OK
Decrypting & Extracting bin/config.json ... OK
================================================================================
EXTRACTION COMPLETED SUMMARY:
--------------------------------------------------------------------------------
Total Files Processed: 4
Successfully Extracted: 4
Failed to Extract: 0
Total Bytes Decompressed: 819200 bytes
Destination Directory: extracted_my_installer
Used Password: infected
================================================================================
The Python module innounpack.py loads the compiled shared library and provides clean, thread-safe, high-performance APIs.
import innounpack
installer_path = "my_installer.exe"
try:
files = innounpack.list_files(installer_path)
print(f"Discovered {len(files)} files:")
for f in files:
print(f"ID: {f['id']} | File: {f['dest_name']} | Size: {f['original_size']} bytes | Encrypted: {f['encrypted']}")
except Exception as e:
print(f"Error inspecting installer: {e}")crack_password spawns multi-core parallel routines in-process to bruteforce/dictionary-attack the installer's protection keys:
import innounpack
installer_path = "protected_installer.exe"
# 1. Run dictionary attack (omit wordlist parameter to use the built-in forensic list)
result = innounpack.crack_password(installer_path, wordlist=["admin", "password", "infected"])
if result and result["PasswordFound"]:
print(f"[+] Decryption password found: {result['Password']} (Hash Type: {result['HashType']})")
# 2. Automatically extract files in-process using the recovered key!
innounpack.extract_all(installer_path, "./extracted_payloads", password=result["Password"])
else:
print("[-] Password was not recovered.")decompile_script reads and disassembles compiled Pascal Script bytecode. It features an incredibly versatile API, supporting both string file paths and raw binary buffers directly in-memory!
import innounpack
# Example A: Decompile from a full installer exe on disk (in-memory extraction & disasm)
assembly_from_exe = innounpack.decompile_script("my_installer.exe")
print(assembly_from_exe)
# Example B: Decompile from a raw, standalone bytecode buffer directly (No disk I/O!)
# This is perfect for advanced pipelines (like CAPE Sandbox) that load blobs from DBs/streams
raw_bytecode_bytes = b"IFPS\x17\x00..." # binary CompiledCode payload
assembly_from_memory = innounpack.decompile_script(raw_bytecode_bytes)
print(assembly_from_memory)import innounpack
success = innounpack.extract_all("installer.exe", "./output", password="ExplicitPassword")Delphi structures are notoriously packed and compiler-dependent. Instead of generating hundreds of individual record mappings, InnoUnp-Go uses a declarative, dynamic schema registry (parser/schema.go). Record layouts (WideStrings, AnsiStrings, bitsets, enums, and scalar integers) are described in schemas. The parser reads streams sequentially based on these descriptors.
This enables native, error-free support for nearly 30 years of Inno Setup compilers—including changing field sizes (like v3's 4-byte file sizes vs v4/v5/v6's 8-byte file sizes)—without writing separate parsing layers.
When password protection is enabled, encryption is applied over the compressed payload. To decompress, InnoUnp-Go feeds raw segments into CRCChunkReader (which validates chunk boundaries), wraps it inside DecryptReader (which runs in-place RC4 or XChaCha20 decryption), and passes the decrypted stream directly to standard decompressors. This ensures zero-copy streaming performance.
Inno Setup pre-filters executables (.exe, .dll) by converting relative CALL and JMP targets to absolute offsets, boosting the compression ratio. InnoUnp-Go implements byte-accurate reverse transformations (TransformCallInstructions for legacy and TransformCallInstructions5309 for modern installers), restoring files to their exact original SHA-256 byte-exact states.
Memory allocated on the Go heap or C heap must be managed carefully. To prevent memory leaks during Python execution, Go's ListInnoFiles exports the JSON-serialized file list on the C heap, and innounpack.py wraps calls in try...finally statements that automatically invoke Go's exported FreeString deallocator on the C pointer, guaranteeing leak-free in-process parsing.
This project is built upon decades of outstanding reverse-engineering and open-source contributions from the community. We express our deepest gratitude and special thanks to:
- Josef Rathlev (and the contributors of
innounp): For creating the legendary, battle-tested Object Pascal-based Inno Setup Unpacker command-line tool, which served as the foundational layout blueprint and structural reference for this Go implementation. - Daniel Scharrer (author of
innoextract): For his incredible work engineering a clean, cross-platform C++ Inno Setup extractor, which provided invaluable reference structures, and gdesmar for his fork focusing on Pascal Script bytecode (CompiledCode.bin) string-extraction concepts. - Gemini, and doomedraven: For collaborating hand-in-hand to design, port, implement, and fully verify this modern, schema-driven Go porting initiative from legacy Object Pascal to modern Go, achieving a new milestone in cross-platform forensic utility tooling.