1use std::{env, fs, process};
7
8fn main() {
9 let args: Vec<String> = env::args().collect();
10 let Some(path) = args.get(1) else {
11 eprintln!("usage: dump <setup.exe>");
12 process::exit(1);
13 };
14
15 let data = fs::read(path).unwrap_or_else(|e| {
16 eprintln!("error reading {path}: {e}");
17 process::exit(1);
18 });
19
20 let installer = innospect::InnoInstaller::from_bytes(&data).unwrap_or_else(|e| {
21 eprintln!("error parsing Inno Setup installer: {e}");
22 process::exit(1);
23 });
24
25 let v = installer.version();
26 println!("== Identification ==");
27 println!(" marker: {:?}", v.marker_str());
28 println!(" version: {}.{}.{}.{}", v.a, v.b, v.c, v.d);
29 println!(" flags: {:?}", v.flags);
30 println!(" variant: {:?}", installer.variant());
31 println!(
32 " setupldr: {:?} (locator: {:?})",
33 installer.setup_ldr_family(),
34 installer.pe_locator_mode(),
35 );
36
37 let ot = installer.offset_table();
38 println!();
39 println!("== Offset table ==");
40 println!(" generation: {:?}", ot.source.generation);
41 println!(" version_id: {}", ot.version_id);
42 println!(" start: {:#x}", ot.source.start);
43 println!(" Offset0: {:#x}", ot.offset_setup0);
44 println!(" Offset1: {:#x}", ot.offset_setup1);
45 println!(" OffsetEXE: {:#x}", ot.offset_exe);
46 println!(" TotalSize: {} bytes", ot.total_size,);
47
48 println!();
49 println!("== Setup-0 ==");
50 println!(" compression: {:?}", installer.compression());
51 println!(
52 " encryption: {:?}",
53 installer.encryption().map(|e| e.mode)
54 );
55 let setup0 = installer.decompressed_setup0();
56 println!(" decompressed: {} bytes", setup0.len());
57 if !setup0.is_empty() {
58 let preview_len = 128.min(setup0.len());
59 let preview: Vec<u8> = setup0
60 .iter()
61 .take(preview_len)
62 .map(|&b| if (32..127).contains(&b) { b } else { b'.' })
63 .collect();
64 println!(" preview: {:?}", String::from_utf8_lossy(&preview),);
65 }
66
67 if let Some(header) = installer.header() {
68 println!();
69 println!("== Setup header ==");
70 println!(" AppName: {:?}", header.app_name().unwrap_or(""));
71 println!(" AppId: {:?}", header.app_id().unwrap_or(""));
72 println!(" AppVersion: {:?}", header.app_version().unwrap_or(""));
73 println!(
74 " AppPublisher: {:?}",
75 header.app_publisher().unwrap_or(""),
76 );
77 println!(
78 " DefaultDirName: {:?}",
79 header.default_dir_name().unwrap_or(""),
80 );
81
82 let counts = header.counts();
83 println!();
84 println!("== Entry counts ==");
85 println!(" languages: {}", counts.languages);
86 println!(" custom_messages: {}", counts.custom_messages);
87 println!(" permissions: {}", counts.permissions);
88 println!(" types: {}", counts.types);
89 println!(" components: {}", counts.components);
90 println!(" tasks: {}", counts.tasks);
91 println!(" directories: {}", counts.directories);
92 if let Some(n) = counts.iss_sig_keys {
93 println!(" iss_sig_keys: {n}");
94 }
95 println!(" files: {}", counts.files);
96 println!(" file_locations: {}", counts.file_locations);
97 println!(" icons: {}", counts.icons);
98 println!(" ini_entries: {}", counts.ini_entries);
99 println!(" registry: {}", counts.registry);
100 println!(" install_deletes: {}", counts.install_deletes);
101 println!(" uninstall_deletes:{}", counts.uninstall_deletes,);
102 println!(" run: {}", counts.run);
103 println!(" uninstall_run: {}", counts.uninstall_run);
104
105 let license_len = installer
106 .header()
107 .and_then(|h| h.ansi(innospect::HeaderAnsi::LicenseText))
108 .map_or(0, <[u8]>::len);
109 let info_before_len = installer
110 .header()
111 .and_then(|h| h.ansi(innospect::HeaderAnsi::InfoBeforeText))
112 .map_or(0, <[u8]>::len);
113 let info_after_len = installer
114 .header()
115 .and_then(|h| h.ansi(innospect::HeaderAnsi::InfoAfterText))
116 .map_or(0, <[u8]>::len);
117 let compiled_len = installer
118 .header()
119 .and_then(|h| h.ansi(innospect::HeaderAnsi::CompiledCodeText))
120 .map_or(0, <[u8]>::len);
121 println!();
122 println!("== Embedded blobs ==");
123 println!(" license_text: {license_len} bytes");
124 println!(" info_before: {info_before_len} bytes");
125 println!(" info_after: {info_after_len} bytes");
126 println!(" compiled_code_text: {compiled_len} bytes");
127
128 let tail = header.tail();
129 let tail_size = header
130 .records_offset()
131 .saturating_sub(header.tail_start_offset());
132 println!();
133 println!("== Fixed numeric tail ({tail_size} bytes) ==");
134 println!(
135 " MinVersion (Win): {}.{} build {}",
136 tail.windows_version_range.min.windows.major,
137 tail.windows_version_range.min.windows.minor,
138 tail.windows_version_range.min.windows.build,
139 );
140 println!(
141 " OnlyBelowVersion (Win): {}.{} build {}",
142 tail.windows_version_range.only_below.windows.major,
143 tail.windows_version_range.only_below.windows.minor,
144 tail.windows_version_range.only_below.windows.build,
145 );
146 println!(" WizardStyle: {:?}", tail.wizard_style);
147 println!(
148 " WizardSizePercent: ({}, {})",
149 tail.wizard_size_percent_x, tail.wizard_size_percent_y,
150 );
151 println!(" PrivilegesRequired: {:?}", tail.privileges_required);
152 println!(" CompressMethod: {:?}", tail.compress_method);
153 println!(
154 " ExtraDiskSpaceRequired: {} bytes",
155 tail.extra_disk_space_required,
156 );
157 println!(
158 " UninstallDisplaySize: {} bytes",
159 tail.uninstall_display_size,
160 );
161 println!(" Options ({} set):", tail.options.len());
162 let mut sorted: Vec<_> = tail.options.iter().collect();
163 sorted.sort_by_key(|o| format!("{o:?}"));
164 for opt in sorted {
165 println!(" - {opt:?}");
166 }
167 }
168
169 println!();
170 println!("== Data block (file-location records) ==");
171 let data = installer.data_block();
172 println!(" decompressed: {} bytes", data.len());
173
174 println!();
175 println!(
176 "== Records (3c lightweight) — languages={} messages={} permissions={} types={} components={} tasks={} ==",
177 installer.languages().len(),
178 installer.messages().len(),
179 installer.permissions().len(),
180 installer.types().len(),
181 installer.components().len(),
182 installer.tasks().len(),
183 );
184 for (i, l) in installer.languages().iter().enumerate().take(5) {
185 let name = l.name_string().unwrap_or_default();
186 let pretty = l.language_name_string().unwrap_or_default();
187 println!(
188 " language[{i}]: id={:#06x} cp={:?} name={:?} ({:?})",
189 l.language_id, l.codepage, name, pretty,
190 );
191 }
192 if installer.languages().len() > 5 {
193 println!(
194 " … {} more",
195 installer.languages().len().saturating_sub(5)
196 );
197 }
198 for (i, t) in installer.tasks().iter().enumerate().take(8) {
199 println!(
200 " task[{i}]: name={:?} flags={:?} level={}",
201 t.name, t.flags, t.level,
202 );
203 }
204
205 println!();
206 println!(
207 "== Records (3d heavy) — dirs={} files={} icons={} ini={} reg={} ins_del={} unins_del={} run={} unins_run={} file_loc={} ==",
208 installer.directories().len(),
209 installer.files().len(),
210 installer.icons().len(),
211 installer.ini_entries().len(),
212 installer.registry_entries().len(),
213 installer.install_deletes().len(),
214 installer.uninstall_deletes().len(),
215 installer.run_entries().len(),
216 installer.uninstall_runs().len(),
217 installer.file_locations().len(),
218 );
219 for (i, f) in installer.files().iter().enumerate().take(4) {
220 println!(
221 " file[{i}]: src={:?} dst={:?} loc={} ext_size={} flags={}",
222 f.source,
223 f.destination,
224 f.location_index,
225 f.external_size,
226 f.flags.len(),
227 );
228 }
229 if installer.files().len() > 4 {
230 println!(" … {} more", installer.files().len().saturating_sub(4));
231 }
232 for (i, r) in installer.registry_entries().iter().enumerate() {
233 println!(
234 " reg[{i}]: hive={:?} subkey={:?} name={:?} type={:?}",
235 r.hive, r.subkey, r.value_name, r.value_type,
236 );
237 }
238 for (i, ic) in installer.icons().iter().enumerate().take(4) {
239 println!(
240 " icon[{i}]: name={:?} target={:?} args={:?}",
241 ic.name, ic.filename, ic.parameters,
242 );
243 }
244 for (i, r) in installer.run_entries().iter().enumerate().take(4) {
245 println!(
246 " run[{i}]: cmd={:?} args={:?} wait={:?} flags={:?}",
247 r.name, r.parameters, r.wait, r.flags,
248 );
249 }
250 for (i, d) in installer.file_locations().iter().enumerate().take(2) {
251 println!(
252 " file_loc[{i}]: orig_size={} compressed={} chunk_offset={} flags={:?}",
253 d.original_size, d.chunk_compressed_size, d.chunk_sub_offset, d.flags,
254 );
255 }
256
257 println!();
258 println!("== Extraction ==");
259 let mut extracted = 0usize;
260 let mut total_bytes: u64 = 0;
261 let mut errors = 0usize;
262 for f in installer.files() {
263 if f.location_index == u32::MAX {
264 continue;
265 }
266 match installer.extract_to_vec(f) {
267 Ok(bytes) => {
268 total_bytes = total_bytes.saturating_add(bytes.len() as u64);
269 extracted = extracted.saturating_add(1);
270 }
271 Err(e) => {
272 errors = errors.saturating_add(1);
273 if errors <= 3 {
274 println!(" ! {:?}: {e}", f.destination);
275 }
276 }
277 }
278 }
279 println!(" extracted: {extracted} files / {total_bytes} bytes / {errors} errors");
280}