Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# innoextract - A tool to unpack installers created by Inno Setup

[Inno Setup](https://jrsoftware.org/isinfo.php) is a tool to create installers for Microsoft Windows applications. innoextract allows to extract such installers under non-Windows systems without running the actual installer using wine. innoextract currently supports installers created by Inno Setup 1.2.10 to 6.3.3.
[Inno Setup](https://jrsoftware.org/isinfo.php) is a tool to create installers for Microsoft Windows applications. innoextract allows to extract such installers under non-Windows systems without running the actual installer using wine. innoextract currently supports installers created by Inno Setup 1.2.10 to 6.5.0.

In addition to standard Inno Setup installers, innoextract also supports some modified Inno Setup variants including Martijn Laan's My Inno Setup Extensions 1.3.10 to 3.0.6.1 as well as GOG.com's Inno Setup-based game installers. innoextract is able to unpack Wadjet Eye Games installers (to play with AGS), Arx Fatalis patches (for use with Arx Libertatis) as well as various other Inno Setup executables.

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
innoextract 1.10-dev

Known working Inno Setup versions:
Inno Setup 1.2.10 to 6.3.3
Inno Setup 1.2.10 to 6.5.0

Bug tracker:
https://innoextract.constexpr.org/issues
2 changes: 1 addition & 1 deletion src/cli/extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ void process_file(const fs::path & installer, const extract_options & o) {
}
#ifdef DEBUG
if(logger::debug) {
entries = setup::info::entry_types::all();
entries = setup::info::entry_types::all() & ~setup::info::NoUnknownVersion;
}
#endif

Expand Down
45 changes: 42 additions & 3 deletions src/loader/offsets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,59 @@ bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) {
checksum.init();
checksum.update(magic, sizeof(magic));

boost::uint32_t revision = 0;
if(version >= INNO_VERSION(5, 1, 5)) {
boost::uint32_t revision = checksum.load<boost::uint32_t>(is);
revision = checksum.load<boost::uint32_t>(is);
if(is.fail()) {
is.clear();
debug("could not read loader header revision");
return false;
} else if(revision != 1) {
} else if(revision != 1 && revision != 2) {
log_warning << "Unexpected setup loader revision: " << revision;
}
}

if(revision == 2) {
boost::uint64_t total_size = checksum.load<boost::uint64_t>(is);
boost::uint64_t offset_exe = checksum.load<boost::uint64_t>(is);
exe_uncompressed_size = checksum.load<boost::uint32_t>(is);
exe_checksum.type = crypto::CRC32;
exe_checksum.crc32 = checksum.load<boost::uint32_t>(is);
boost::uint64_t offset0 = checksum.load<boost::uint64_t>(is);
boost::uint64_t offset1 = checksum.load<boost::uint64_t>(is);
(void)checksum.load<boost::uint32_t>(is); // ReservedPadding
if(is.fail()) {
is.clear();
debug("could not read revision 2 loader header");
return false;
}
if(total_size > std::numeric_limits<boost::uint32_t>::max()
|| offset_exe > std::numeric_limits<boost::uint32_t>::max()
|| offset0 > std::numeric_limits<boost::uint32_t>::max()
|| offset1 > std::numeric_limits<boost::uint32_t>::max()) {
log_warning << "Unsupported large revision 2 setup loader offsets!";
return false;
}
exe_offset = static_cast<boost::uint32_t>(offset_exe);
exe_compressed_size = 0;
message_offset = 0;
header_offset = static_cast<boost::uint32_t>(offset0);
data_offset = static_cast<boost::uint32_t>(offset1);
boost::uint32_t expected = util::load<boost::uint32_t>(is);
if(is.fail()) {
is.clear();
debug("could not read revision 2 loader header checksum");
return false;
}
if(checksum.finalize() != expected) {
log_warning << "Setup loader checksum mismatch!";
}
return true;
}

(void)checksum.load<boost::uint32_t>(is);
exe_offset = checksum.load<boost::uint32_t>(is);

if(version >= INNO_VERSION(4, 1, 6)) {
exe_compressed_size = 0;
} else {
Expand Down
93 changes: 52 additions & 41 deletions src/setup/data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,52 +133,63 @@ void data_entry::load(std::istream & is, const info & i) {

options = 0;

stored_flag_reader<flags> flagreader(is, i.version.bits());

flagreader.add(VersionInfoValid);
flagreader.add(VersionInfoNotValid);
if(i.version >= INNO_VERSION(2, 0, 17) && i.version < INNO_VERSION(4, 0, 1)) {
flagreader.add(BZipped);
}
if(i.version >= INNO_VERSION(4, 0, 10)) {
if(i.version >= INNO_VERSION(6, 5, 0)) {
stored_flag_reader<flags> flagreader(is, i.version.bits());
flagreader.add(VersionInfoValid);
flagreader.add(TimeStampInUTC);
}
if(i.version >= INNO_VERSION(4, 1, 0)) {
flagreader.add(IsUninstallerExe);
}
if(i.version >= INNO_VERSION(4, 1, 8)) {
flagreader.add(CallInstructionOptimized);
}
if(i.version >= INNO_VERSION(4, 2, 0)) {
flagreader.add(Touch);
}
if(i.version >= INNO_VERSION(4, 2, 2)) {
flagreader.add(ChunkEncrypted);
}
if(i.version >= INNO_VERSION(4, 2, 5)) {
flagreader.add(ChunkCompressed);
} else {
options |= ChunkCompressed;
}
if(i.version >= INNO_VERSION(5, 1, 13)) {
flagreader.add(SolidBreak);
}
if(i.version >= INNO_VERSION(5, 5, 7) && i.version < INNO_VERSION(6, 3, 0)) {
// Actually added in Inno Setup 5.5.9 but the data version was not bumped
flagreader.add(Sign);
flagreader.add(SignOnce);
}

options |= flagreader.finalize();

if(i.version >= INNO_VERSION(6, 3, 0)) {
sign = stored_enum<stored_sign_mode>(is).get();
} else if(options & SignOnce) {
sign = Once;
} else if(options & Sign) {
sign = Yes;
} else {
options |= flagreader.finalize();
sign = NoSetting;
} else {
stored_flag_reader<flags> flagreader(is, i.version.bits());

flagreader.add(VersionInfoValid);
flagreader.add(VersionInfoNotValid);
if(i.version >= INNO_VERSION(2, 0, 17) && i.version < INNO_VERSION(4, 0, 1)) {
flagreader.add(BZipped);
}
if(i.version >= INNO_VERSION(4, 0, 10)) {
flagreader.add(TimeStampInUTC);
}
if(i.version >= INNO_VERSION(4, 1, 0)) {
flagreader.add(IsUninstallerExe);
}
if(i.version >= INNO_VERSION(4, 1, 8)) {
flagreader.add(CallInstructionOptimized);
}
if(i.version >= INNO_VERSION(4, 2, 0)) {
flagreader.add(Touch);
}
if(i.version >= INNO_VERSION(4, 2, 2)) {
flagreader.add(ChunkEncrypted);
}
if(i.version >= INNO_VERSION(4, 2, 5)) {
flagreader.add(ChunkCompressed);
} else {
options |= ChunkCompressed;
}
if(i.version >= INNO_VERSION(5, 1, 13)) {
flagreader.add(SolidBreak);
}
if(i.version >= INNO_VERSION(5, 5, 7) && i.version < INNO_VERSION(6, 3, 0)) {
// Actually added in Inno Setup 5.5.9 but the data version was not bumped
flagreader.add(Sign);
flagreader.add(SignOnce);
}

options |= flagreader.finalize();

if(i.version >= INNO_VERSION(6, 3, 0)) {
sign = stored_enum<stored_sign_mode>(is).get();
} else if(options & SignOnce) {
sign = Once;
} else if(options & Sign) {
sign = Yes;
} else {
sign = NoSetting;
}
}

if(options & ChunkCompressed) {
Expand Down
36 changes: 34 additions & 2 deletions src/setup/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
#include "setup/info.hpp"
#include "setup/version.hpp"
#include "util/load.hpp"
#include "util/log.hpp"
#include "util/storedenum.hpp"

namespace setup {
Expand Down Expand Up @@ -91,6 +90,34 @@ void file_entry::load(std::istream & is, const info & i) {
}

load_condition_data(is, i);

excludes.clear();
download_issig_source.clear();
download_user_name.clear();
download_password.clear();
extract_archive_password.clear();
checksum.type = crypto::None;
if(i.version >= INNO_VERSION(6, 5, 0)) {
is >> util::encoded_string(excludes, i.codepage);
is >> util::encoded_string(download_issig_source, i.codepage);
is >> util::encoded_string(download_user_name, i.codepage);
is >> util::encoded_string(download_password, i.codepage);
is >> util::encoded_string(extract_archive_password, i.codepage);
std::string issig_allowed_keys;
is >> util::binary_string(issig_allowed_keys);
(void)issig_allowed_keys;
is.read(checksum.sha256, std::streamsize(sizeof(checksum.sha256)));
switch(util::load<boost::uint8_t>(is)) {
case 1:
checksum.type = crypto::SHA256;
break;
case 0:
case 2:
default:
checksum.type = crypto::None;
break;
}
}

load_version_data(is, i.version);

Expand Down Expand Up @@ -189,6 +216,10 @@ void file_entry::load(std::istream & is, const info & i) {
if(i.version >= INNO_VERSION(5, 2, 5)) {
flagreader.add(GacInstall);
}
if(i.version >= INNO_VERSION(6, 5, 0)) {
flagreader.add(Download);
flagreader.add(ExtractArchive);
}

options |= flagreader.finalize();

Expand All @@ -199,7 +230,6 @@ void file_entry::load(std::istream & is, const info & i) {
}

additional_locations.clear();
checksum.type = crypto::None;
size = 0;

}
Expand Down Expand Up @@ -239,6 +269,8 @@ NAMES(setup::file_entry::flags, "File Option",
"set ntfs compression",
"unset ntfs compression",
"gac install",
"download",
"extract archive",
"readme",
)

Expand Down
7 changes: 7 additions & 0 deletions src/setup/file.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ struct file_entry : public item {
SetNtfsCompression,
UnsetNtfsCompression,
GacInstall,
Download,
ExtractArchive,

// obsolete options:
IsReadmeFile
Expand All @@ -96,6 +98,11 @@ struct file_entry : public item {
std::string destination;
std::string install_font_name;
std::string strong_assembly_name;
std::string excludes;
std::string download_issig_source;
std::string download_user_name;
std::string download_password;
std::string extract_archive_password;

boost::uint32_t location; //!< index into the data entry list
boost::uint32_t attributes;
Expand Down
55 changes: 50 additions & 5 deletions src/setup/header.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -265,20 +265,28 @@ void header::load(std::istream & is, const version & version) {
// Valid architectures: 'Unknown', 'x86', 'x64', 'Arm32', 'Arm64'
is >> util::binary_string(architectures_allowed_expr);
is >> util::binary_string(architectures_installed_in_64bit_mode_expr);
} else {
architectures_allowed_expr.clear();
architectures_installed_in_64bit_mode_expr.clear();
}
if(version >= INNO_VERSION(6, 5, 0)) {
is >> util::binary_string(close_applications_filter_excludes);
is >> util::binary_string(sevenzip_library_name);
} else {
close_applications_filter_excludes.clear();
sevenzip_library_name.clear();
}
if(version >= INNO_VERSION(5, 2, 5)) {
is >> util::ansi_string(license_text);
is >> util::ansi_string(info_before);
is >> util::ansi_string(info_after);
is >> util::ansi_string(compiled_code);
}
if(version >= INNO_VERSION(5, 2, 1) && version < INNO_VERSION(5, 3, 10)) {
is >> util::binary_string(uninstaller_signature);
} else {
uninstaller_signature.clear();
}
if(version >= INNO_VERSION(5, 2, 5)) {
is >> util::binary_string(compiled_code);
}

if(version >= INNO_VERSION(2, 0, 6) && !version.is_unicode()) {
lead_bytes = stored_char_set(is);
Expand Down Expand Up @@ -319,16 +327,21 @@ void header::load(std::istream & is, const version & version) {
}

directory_count = util::load<boost::uint32_t>(is, version.bits());
if(version >= INNO_VERSION(6, 5, 0)) {
issig_key_count = util::load<boost::uint32_t>(is, version.bits());
} else {
issig_key_count = 0;
}
file_count = util::load<boost::uint32_t>(is, version.bits());
data_entry_count = util::load<boost::uint32_t>(is, version.bits());
data_entry_count = util::load<boost::uint32_t>(is, version.bits()); // NumFileLocationEntries
icon_count = util::load<boost::uint32_t>(is, version.bits());
ini_entry_count = util::load<boost::uint32_t>(is, version.bits());
registry_entry_count = util::load<boost::uint32_t>(is, version.bits());
delete_entry_count = util::load<boost::uint32_t>(is, version.bits());
uninstall_delete_entry_count = util::load<boost::uint32_t>(is, version.bits());
run_entry_count = util::load<boost::uint32_t>(is, version.bits());
uninstall_run_entry_count = util::load<boost::uint32_t>(is, version.bits());

boost::int32_t license_size = 0;
boost::int32_t info_before_size = 0;
boost::int32_t info_after_size = 0;
Expand All @@ -338,6 +351,36 @@ void header::load(std::istream & is, const version & version) {
info_after_size = util::load<boost::int32_t>(is, version.bits());
}

if(version >= INNO_VERSION(6, 5, 0)) {
winver.load(is, version);
wizard_style = stored_enum<stored_setup_style>(is).get();
wizard_resize_percent_x = util::load<boost::uint32_t>(is);
wizard_resize_percent_y = util::load<boost::uint32_t>(is);
image_alpha_format = stored_enum<stored_alpha_format>(is).get();
back_color = 0;
back_color2 = 0;
image_back_color = 0;
small_image_back_color = 0;
password.type = crypto::None;
password_salt.clear();
extra_disk_space_required = util::load<boost::int64_t>(is);
slices_per_disk = util::load<boost::uint32_t>(is);
install_mode = NormalInstallMode;
uninstall_log_mode = stored_enum<stored_log_mode>(is).get();
uninstall_style = wizard_style;
dir_exists_warning = stored_enum<stored_bool_auto_no_yes>(is).get();
privileges_required = stored_enum<stored_privileges_1>(is).get();
privileges_required_override_allowed = stored_flags<stored_privileges_required_overrides>(is).get();
show_language_dialog = stored_enum<stored_bool_yes_no_auto>(is).get();
language_detection = stored_enum<stored_language_detection_method>(is).get();
compression = stored_enum<stored_compression_method_3>(is).get();
disable_dir_page = stored_enum<stored_bool_auto_no_yes>(is).get();
disable_program_group_page = stored_enum<stored_bool_auto_no_yes>(is).get();
uninstall_display_size = util::load<boost::uint64_t>(is);
options |= load_flags(is, version);
return;
}

winver.load(is, version);

if(version < INNO_VERSION_EXT(6, 4, 0, 1)) {
Expand Down Expand Up @@ -765,6 +808,8 @@ void header::decode(util::codepage_id codepage) {
util::to_utf8(setup_mutex, codepage, &lead_bytes);
util::to_utf8(changes_environment, codepage);
util::to_utf8(changes_associations, codepage);
util::to_utf8(close_applications_filter_excludes, codepage);
util::to_utf8(sevenzip_library_name, codepage, &lead_bytes);

}

Expand Down
Loading