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: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,8 @@ set(INNOEXTRACT_SOURCES
src/setup/info.cpp
src/setup/ini.hpp
src/setup/ini.cpp
src/setup/issigkey.hpp
src/setup/issigkey.cpp
src/setup/item.hpp
src/setup/item.cpp
src/setup/language.hpp
Expand Down
2 changes: 1 addition & 1 deletion src/cli/debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ static void dump_headers(std::istream & is, const setup::version & version, cons
void dump_headers(std::istream & is, const loader::offsets & offsets, const extract_options & o) {

setup::version version;
is.seekg(offsets.header_offset);
is.seekg(std::streamoff(offsets.header_offset));
version.load(is);

dump_headers(is, version, o, 0);
Expand Down
6 changes: 3 additions & 3 deletions src/cli/extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ void process_file(const fs::path & installer, const extract_options & o) {

if(o.data_version) {
setup::version version;
ifs.seekg(offsets.header_offset);
ifs.seekg(std::streamoff(offsets.header_offset));
version.load(ifs);
if(o.silent) {
std::cout << version << '\n';
Expand Down Expand Up @@ -985,7 +985,7 @@ void process_file(const fs::path & installer, const extract_options & o) {
}
#endif

ifs.seekg(offsets.header_offset);
ifs.seekg(std::streamoff(offsets.header_offset));
setup::info info;
try {
info.load(ifs, entries, o.codepage);
Expand Down Expand Up @@ -1100,7 +1100,7 @@ void process_file(const fs::path & installer, const extract_options & o) {
if(o.test || o.extract) {
boost::uint64_t offset = info.data_entries[file.entry().location].uncompressed_size;
boost::uint32_t sort_slice = info.data_entries[file.entry().location].chunk.first_slice;
boost::uint32_t sort_offset = info.data_entries[file.entry().location].chunk.sort_offset;
boost::uint64_t sort_offset = info.data_entries[file.entry().location].chunk.sort_offset;
BOOST_FOREACH(boost::uint32_t location, file.entry().additional_locations) {
setup::data_entry & data = info.data_entries[location];
files_for_location[location].push_back(output_location(&file, offset));
Expand Down
49 changes: 47 additions & 2 deletions src/loader/offsets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ bool offsets::load_from_exe_resource(std::istream & is) {
return load_offsets_at(is, resource.offset);
}

bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) {
bool offsets::load_offsets_at(std::istream & is, boost::uint64_t pos) {

if(is.seekg(pos).fail()) {
if(is.seekg(std::streamoff(pos)).fail()) {
is.clear();
debug("could not seek to loader header");
return false;
Expand Down Expand Up @@ -148,6 +148,51 @@ bool offsets::load_offsets_at(std::istream & is, boost::uint32_t pos) {
is.clear();
debug("could not read loader header revision");
return false;
} else if(revision == 2) {
/*
* Inno Setup 6.5.0 introduced a wider TSetupLdrOffsetTable layout
* (revision 2). The fields previously stored as 32-bit LongWord
* are now Int64, allowing setup binaries larger than 4 GiB. A new
* ReservedPadding UInt32 sits between Offset1 and TableCRC. The
* record itself is no longer Delphi-`packed`, but with 32-bit
* Delphi default alignment the on-disk layout remains contiguous
* (Int64 has 4-byte alignment in 32-bit mode), so the total size
* goes from 48 to 64 bytes. See Projects/Src/Shared.Struct.pas in
* issrc.
*/
(void)checksum.load<boost::int64_t>(is); // TotalSize
boost::int64_t offset_exe = checksum.load<boost::int64_t>(is);
boost::uint32_t uncompressed_exe = checksum.load<boost::uint32_t>(is);
boost::uint32_t crc_exe = checksum.load<boost::uint32_t>(is);
boost::int64_t offset0 = checksum.load<boost::int64_t>(is);
boost::int64_t offset1 = checksum.load<boost::int64_t>(is);
(void)checksum.load<boost::uint32_t>(is); // ReservedPadding
if(is.fail()) {
is.clear();
debug("could not read loader header (revision 2)");
return false;
}
if(offset_exe < 0 || offset0 < 0 || offset1 < 0) {
log_warning << "Loader header has negative offset(s)";
}
exe_offset = boost::uint64_t(offset_exe);
exe_compressed_size = 0;
exe_uncompressed_size = uncompressed_exe;
exe_checksum.type = crypto::CRC32;
exe_checksum.crc32 = crc_exe;
message_offset = 0;
header_offset = boost::uint64_t(offset0);
data_offset = boost::uint64_t(offset1);
boost::uint32_t expected = util::load<boost::uint32_t>(is);
if(is.fail()) {
is.clear();
debug("could not read loader header checksum (revision 2)");
return false;
}
if(checksum.finalize() != expected) {
log_warning << "Setup loader checksum mismatch!";
}
return true;
} else if(revision != 1) {
log_warning << "Unexpected setup loader revision: " << revision;
}
Expand Down
14 changes: 7 additions & 7 deletions src/loader/offsets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@ struct offsets {
*
* A value of \c 0 means there is no setup.e32 embedded in this file
*/
boost::uint32_t exe_offset;
boost::uint64_t exe_offset;

/*!
* Size of `setup.e32` after compression, in bytes
*
* A value of \c 0 means the executable size is not known
*/
boost::uint32_t exe_compressed_size;
boost::uint64_t exe_compressed_size;

//! Size of `setup.e32` before compression, in bytes
boost::uint32_t exe_uncompressed_size;
boost::uint64_t exe_uncompressed_size;

/*!
* Checksum of `setup.e32` before compression
Expand All @@ -80,7 +80,7 @@ struct offsets {
crypto::checksum exe_checksum;

//! Offset of embedded setup messages
boost::uint32_t message_offset;
boost::uint64_t message_offset;

/*!
* Offset of embedded `setup-0.bin` data (the setup headers)
Expand All @@ -93,7 +93,7 @@ struct offsets {
*
* Loading the version and headers is done in \ref setup::info.
*/
boost::uint32_t header_offset;
boost::uint64_t header_offset;

/*!
* Offset of embedded `setup-1.bin` data
Expand All @@ -109,7 +109,7 @@ struct offsets {
* The layout of the chunks and files is stored in the \ref setup::data_entry headers
* while the \ref setup::file_entry headers provide the filenames and meta information.
*/
boost::uint32_t data_offset;
boost::uint64_t data_offset;

/*!
* \brief Find the setup loader offsets in a file
Expand All @@ -128,7 +128,7 @@ struct offsets {

bool load_from_exe_resource(std::istream & is);

bool load_offsets_at(std::istream & is, boost::uint32_t pos);
bool load_offsets_at(std::istream & is, boost::uint64_t pos);

};

Expand Down
8 changes: 7 additions & 1 deletion src/setup/component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,13 @@ void component_entry::load(std::istream & is, const info & i) {
} else {
extra_disk_pace_required = util::load<boost::uint32_t>(is);
}
if(i.version >= INNO_VERSION(4, 0, 0) || (i.version.is_isx() && i.version >= INNO_VERSION(3, 0, 3))) {
if(i.version >= INNO_VERSION(6, 7, 0)) {
// Inno Setup 6.7.0 narrowed TSetupComponentEntry.Level from
// Integer (4 bytes) to Byte (1 byte). See
// Projects/Src/Shared.Struct.pas at tag is-6_7_0 in
// https://github.com/jrsoftware/issrc.
level = util::load<boost::uint8_t>(is);
} else if(i.version >= INNO_VERSION(4, 0, 0) || (i.version.is_isx() && i.version >= INNO_VERSION(3, 0, 3))) {
level = util::load<boost::int32_t>(is);
} else {
level = 0;
Expand Down
38 changes: 32 additions & 6 deletions src/setup/data.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,16 @@ void data_entry::load(std::istream & is, const info & i) {
}
}

chunk.sort_offset = chunk.offset = util::load<boost::uint32_t>(is);
if(i.version >= INNO_VERSION(6, 5, 2)) {
// Inno Setup 6.5.2 widened TSetupFileLocationEntry.StartOffset
// from LongWord (4 bytes) to Int64 (8 bytes) so data offsets
// within a slice can exceed 4 GiB. See
// Projects/Src/Shared.Struct.pas at tag is-6_5_2 in
// https://github.com/jrsoftware/issrc.
chunk.sort_offset = chunk.offset = util::load<boost::uint64_t>(is);
} else {
chunk.sort_offset = chunk.offset = util::load<boost::uint32_t>(is);
}

if(i.version >= INNO_VERSION(4, 0, 1)) {
file.offset = util::load<boost::uint64_t>(is);
Expand Down Expand Up @@ -136,20 +145,28 @@ void data_entry::load(std::istream & is, const info & i) {
stored_flag_reader<flags> flagreader(is, i.version.bits());

flagreader.add(VersionInfoValid);
flagreader.add(VersionInfoNotValid);
if(i.version < INNO_VERSION(6, 4, 3)) {
// Inno Setup 6.4.3 dropped foVersionInfoNotValid, foIsUninstExe,
// foApplyTouchDateTime and foSolidBreak from
// TSetupFileLocationEntry.Flags. See
// Projects/Src/Shared.Struct.pas at tag is-6_4_3 in
// https://github.com/jrsoftware/issrc (issrc commit 6aec0a55,
// "Distinguish file options (fo) and file location options (flo).").
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)) {
if(i.version >= INNO_VERSION(4, 1, 0) && i.version < INNO_VERSION(6, 4, 3)) {
flagreader.add(IsUninstallerExe);
}
if(i.version >= INNO_VERSION(4, 1, 8)) {
flagreader.add(CallInstructionOptimized);
}
if(i.version >= INNO_VERSION(4, 2, 0)) {
if(i.version >= INNO_VERSION(4, 2, 0) && i.version < INNO_VERSION(6, 4, 3)) {
flagreader.add(Touch);
}
if(i.version >= INNO_VERSION(4, 2, 2)) {
Expand All @@ -160,7 +177,7 @@ void data_entry::load(std::istream & is, const info & i) {
} else {
options |= ChunkCompressed;
}
if(i.version >= INNO_VERSION(5, 1, 13)) {
if(i.version >= INNO_VERSION(5, 1, 13) && i.version < INNO_VERSION(6, 4, 3)) {
flagreader.add(SolidBreak);
}
if(i.version >= INNO_VERSION(5, 5, 7) && i.version < INNO_VERSION(6, 3, 0)) {
Expand All @@ -171,7 +188,16 @@ void data_entry::load(std::istream & is, const info & i) {

options |= flagreader.finalize();

if(i.version >= INNO_VERSION(6, 3, 0)) {
if(i.version >= INNO_VERSION(6, 4, 3)) {
// Inno Setup 6.4.3 dropped the standalone Sign field from
// TSetupFileLocationEntry. The compiler still records the source
// signing intent in the [Files] section, but it is no longer
// serialised per-file-location. See issrc commit 00d335b7
// ("Cleanup: TSetupFileLocationEntry contained a few things which
// Setup doesn't need and are only for the compiler.") first
// tagged in is-6_4_3.
sign = NoSetting;
} else if(i.version >= INNO_VERSION(6, 3, 0)) {
sign = stored_enum<stored_sign_mode>(is).get();
} else if(options & SignOnce) {
sign = Once;
Expand Down
67 changes: 66 additions & 1 deletion src/setup/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ STORED_ENUM_MAP(stored_file_type_1, file_entry::UserFile,
file_entry::RegSvrExe,
);

// Inno Setup 6.5.0 introduced the per-file verification kind alongside
// the SHA-256 hash and ISSig allowed-keys list appended to TSetupFileEntry.
STORED_ENUM_MAP(stored_file_verification_type, file_entry::FileVerificationNone,
file_entry::FileVerificationNone,
file_entry::FileVerificationHash,
file_entry::FileVerificationISSig,
);

} // anonymous namespace

} // namespace setup
Expand All @@ -69,6 +77,12 @@ NAMES(setup::file_copy_mode, "File Copy Mode",
"always skip if same or older",
)

NAMES(setup::file_entry::file_verification_type, "File Verification Type",
"none",
"hash",
"IS sig",
)

namespace setup {

void file_entry::load(std::istream & is, const info & i) {
Expand All @@ -92,6 +106,34 @@ void file_entry::load(std::istream & is, const info & i) {

load_condition_data(is, i);

if(i.version >= INNO_VERSION(6, 5, 0)) {
// Inno Setup 6.5.0 appended five new expression strings (Excludes,
// DownloadISSigSource, DownloadUserName, DownloadPassword,
// ExtractArchivePassword) and a per-file Verification packed
// record (ISSigAllowedKeys ansi string, 32-byte SHA-256 digest,
// TSetupFileVerificationType byte enum) to TSetupFileEntry, after
// the BeforeInstall string and before MinVersion. See
// Projects/Src/Shared.Struct.pas at tag is-6_5_0 in
// https://github.com/jrsoftware/issrc.
is >> util::encoded_string(excludes, i.codepage, i.header.lead_bytes);
is >> util::encoded_string(download_source, i.codepage, i.header.lead_bytes);
is >> util::encoded_string(download_user, i.codepage, i.header.lead_bytes);
is >> util::encoded_string(download_password, i.codepage, i.header.lead_bytes);
is >> util::encoded_string(archive_password, i.codepage, i.header.lead_bytes);
is >> util::ansi_string(issig_allowed_keys);
is.read(checksum.sha256, std::streamsize(sizeof(checksum.sha256)));
checksum.type = crypto::SHA256;
verification = stored_enum<stored_file_verification_type>(is).get();
} else {
excludes.clear();
download_source.clear();
download_user.clear();
download_password.clear();
archive_password.clear();
issig_allowed_keys.clear();
verification = FileVerificationNone;
}

load_version_data(is, i.version);

location = util::load<boost::uint32_t>(is, i.version.bits());
Expand Down Expand Up @@ -189,8 +231,25 @@ 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)) {
// Inno Setup 6.5.0 added two flags at the end of the bitset for
// the new [Files] flags `download` and `extractarchive`
// (TSetupFileEntry.Options in Projects/Src/Shared.Struct.pas at
// tag is-6_5_0 in https://github.com/jrsoftware/issrc).
flagreader.add(Download);
flagreader.add(ExtractArchive);
}

options |= flagreader.finalize();
if(i.version >= INNO_VERSION(6, 7, 0)) {
// Inno Setup 6.7.0 padded TSetupFileEntryOption to 57 elements
// (foUnusedPadding=56) so the set is always 8 bytes regardless
// of the actual flag count, matching the analogous change to
// TSetupHeaderOption (see is-6_7_0 in issrc). Skip past the
// extra padding bytes so the trailing FileType byte is read
// from the right offset.
flagreader.discard_padding_to(8);
}

if(i.version.bits() == 16 || i.version >= INNO_VERSION(5, 0, 0)) {
type = stored_enum<stored_file_type_0>(is).get();
Expand All @@ -199,7 +258,11 @@ void file_entry::load(std::istream & is, const info & i) {
}

additional_locations.clear();
checksum.type = crypto::None;
if(i.version < INNO_VERSION(6, 5, 0)) {
// For Inno Setup 6.5.0+, file_entry::checksum carries the SHA-256
// from the per-file ISSig Verification block, populated above.
checksum.type = crypto::None;
}
size = 0;

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

Expand Down
Loading