innospect/lib.rs
1//! Parse and inspect Inno Setup installer binaries.
2//!
3//! This crate provides typed access to the internal structures of an
4//! [Inno Setup](https://jrsoftware.org/isinfo.php) installer executable,
5//! from the PE loader stub through the compressed setup header down to
6//! individual records and embedded files.
7//!
8//! Format research is tracked in `RESEARCH.md` at the crate root and is
9//! the source of truth for what each layer of the parser implements.
10//!
11//! # Stable Identifiers
12//!
13//! Public discriminator enums expose stable [`core::fmt::Display`]
14//! strings, and most also expose `as_str()`. Consumers may persist
15//! those strings in databases and compare them across crate releases.
16//! New variants on `#[non_exhaustive]` enums are added in minor-version
17//! releases; once a variant's display string is published, that string
18//! is a compatibility surface and will not be renamed.
19//!
20//! # Architecture
21//!
22//! The crate is organized in layers that mirror the on-disk format:
23//!
24//! - **PE overlay detection** ([`overlay`]): Locates the Inno Setup loader
25//! table (`SetupLdr`) and the setup payload appended after the PE
26//! sections.
27//! - **Decompression** ([`decompress`]): Handles `zlib`, LZMA, and LZMA2
28//! decompression of the setup header and data streams.
29//! - **Low-level structures** ([`header`], [`records`]): View types for
30//! each structure in the setup header (`TSetupHeader`, file/registry/
31//! ini/run records, language tables, Pascal script, and the file
32//! location table).
33//! - **High-level API** ([`InnoInstaller`]): Ties everything together
34//! into a convenient exploration interface for analysis use cases.
35
36// The `missing_docs`, `unsafe_code`, `clippy::unwrap_used`,
37// `clippy::expect_used`, `clippy::panic`,
38// `clippy::arithmetic_side_effects`, and `clippy::indexing_slicing` lints
39// are declared in `Cargo.toml` under `[lints]` so they enforce on every
40// build regardless of the consuming workspace. innospect is used in
41// malware-analysis pipelines where every input byte is adversarial and
42// the parser must not panic.
43#![cfg_attr(
44 test,
45 allow(
46 clippy::unwrap_used,
47 clippy::expect_used,
48 clippy::panic,
49 clippy::arithmetic_side_effects,
50 clippy::indexing_slicing
51 )
52)]
53
54macro_rules! stable_name_enum {
55 ($ty:ty, { $($pat:pat => $name:literal),+ $(,)? }) => {
56 impl $ty {
57 /// Returns this value's stable identifier.
58 #[must_use]
59 pub fn as_str(&self) -> &'static str {
60 match self {
61 $($pat => $name,)+
62 }
63 }
64 }
65
66 impl core::fmt::Display for $ty {
67 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68 f.write_str(self.as_str())
69 }
70 }
71 };
72}
73
74macro_rules! stable_flag_enum {
75 ($ty:ty, { $($variant:ident => $name:literal),+ $(,)? }) => {
76 stable_name_enum!($ty, { $(Self::$variant => $name,)+ });
77
78 impl $ty {
79 /// Canonical storage order for this flag enum.
80 pub const ORDER: &'static [Self] = &[$(Self::$variant,)+];
81
82 /// Canonical stable-name order for this flag enum.
83 pub const NAME_ORDER: &'static [&'static str] = &[$($name,)+];
84
85 /// Returns this flag's canonical bit position.
86 #[must_use]
87 pub fn bit(self) -> u64 {
88 let shift = Self::ORDER
89 .iter()
90 .position(|flag| *flag == self)
91 .and_then(|idx| u32::try_from(idx).ok());
92 shift.and_then(|idx| 1_u64.checked_shl(idx)).unwrap_or(0)
93 }
94
95 /// Converts a set of flags into the canonical bitmask.
96 #[must_use]
97 pub fn set_to_bits(set: &std::collections::HashSet<Self>) -> u64 {
98 set.iter().fold(0_u64, |acc, flag| acc | flag.bit())
99 }
100 }
101 };
102}
103
104pub mod analysis;
105pub mod decompress;
106pub mod error;
107pub mod extract;
108pub mod header;
109pub mod installer;
110pub mod overlay;
111pub mod records;
112pub mod version;
113
114/// Re-export of the `pascalscript` crate so existing
115/// `innospect::pascalscript::*` paths resolve through `innospect`. The
116/// parser itself is the standalone
117/// [`pascalscript`](https://github.com/BinFlip/pascalscript-rs)
118/// crate; this re-export is for caller convenience.
119pub use ::pascalscript;
120
121mod crypto;
122mod util;
123
124pub use error::Error;
125pub use extract::FileReader;
126pub use header::{
127 Architecture, AutoNoYes, CompressMethod, EntryCounts, HeaderAnsi, HeaderOption, HeaderString,
128 HeaderTail, ImageAlphaFormat, LanguageDetectionMethod, PrivilegesRequired,
129 PrivilegesRequiredOverride, SetupHeader, UninstallLogMode, WizardStyle, YesNoAuto,
130};
131pub use installer::{Compression, EncryptionInfo, EncryptionMode, InnoInstaller};
132pub use overlay::{
133 OffsetTable, OffsetTableSource, SetupLdrFamily, pe::LocatorMode as PeLocatorMode,
134};
135pub use records::{
136 component::{ComponentEntry, ComponentFlag},
137 dataentry::{DataChecksum, DataEntry, DataFlag, SignMode},
138 delete::{DeleteEntry, DeleteTargetType},
139 directory::{DirectoryEntry, DirectoryFlag},
140 file::{FileEntry, FileEntryType, FileFlag, FileVerification, FileVerificationKind},
141 icon::{CloseOnExit, IconEntry, IconFlag},
142 ini::{IniEntry, IniFlag},
143 isssigkey::ISSigKeyEntry,
144 language::{LanguageCodepage, LanguageEntry},
145 message::MessageEntry,
146 permission::PermissionEntry,
147 registry::{RegistryEntry, RegistryFlag, RegistryHive, RegistryValueType},
148 run::{RunEntry, RunFlag, RunWait},
149 task::{TaskEntry, TaskFlag},
150 type_::{SetupTypeKind, TypeEntry},
151 windows::Bitness,
152};
153pub use version::{Variant, Version, VersionFlags};
154
155// Thread-safety guarantee: InnoInstaller borrows from a `&[u8]` input
156// buffer, holds no interior mutability, and contains no raw pointers
157// or `Cell`/`RefCell`. It is therefore both `Send` and `Sync`. This
158// static assertion makes the guarantee a compile-time invariant: a
159// future change that adds a non-Send/non-Sync field will break the
160// build here, not silently at a downstream `.await` point.
161const _: fn() = || {
162 fn assert_send_sync<T: Send + Sync>() {}
163 assert_send_sync::<InnoInstaller<'static>>();
164};