diff --git a/BurnOutSharp/FileType/InstallShieldArchiveV3.cs b/BurnOutSharp/FileType/InstallShieldArchiveV3.cs new file mode 100644 index 00000000..0c262097 --- /dev/null +++ b/BurnOutSharp/FileType/InstallShieldArchiveV3.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using UnshieldSharp.Archive; + +namespace BurnOutSharp.FileType +{ + internal class InstallShieldArchiveV3 : IScannable + { + /// + public bool ShouldScan(byte[] magic) + { + if (magic.StartsWith(new byte?[] { 0x13, 0x5D, 0x65, 0x8C })) + return true; + + return false; + } + + /// + public ConcurrentDictionary> Scan(Scanner scanner, string file) + { + if (!File.Exists(file)) + return null; + + using (var fs = File.OpenRead(file)) + { + return Scan(scanner, fs, file); + } + } + + // TODO: Add stream opening support + /// + public ConcurrentDictionary> Scan(Scanner scanner, Stream stream, string file) + { + // Get the name of the first cabinet file or header + string directory = Path.GetDirectoryName(file); + string noExtension = Path.GetFileNameWithoutExtension(file); + string filenamePattern = Path.Combine(directory, noExtension); + filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty); + + bool cabinetHeaderExists = File.Exists(Path.Combine(directory, filenamePattern + "1.hdr")); + bool shouldScanCabinet = cabinetHeaderExists + ? file.Equals(Path.Combine(directory, filenamePattern + "1.hdr"), StringComparison.OrdinalIgnoreCase) + : file.Equals(Path.Combine(directory, filenamePattern + "1.cab"), StringComparison.OrdinalIgnoreCase); + + // If we have the first file + if (shouldScanCabinet) + { + // If the cab file itself fails + try + { + string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + UnshieldSharp.Archive.InstallShieldArchiveV3 archive = new UnshieldSharp.Archive.InstallShieldArchiveV3(file); + foreach (CompressedFile cfile in archive.Files.Select(kvp => kvp.Value)) + { + // If an individual entry fails + try + { + string tempFile = Path.Combine(tempPath, cfile.FullPath); + if (!Directory.Exists(Path.GetDirectoryName(tempFile))) + Directory.CreateDirectory(Path.GetDirectoryName(tempFile)); + + (byte[] fileContents, string error) = archive.Extract(cfile.FullPath); + if (!string.IsNullOrWhiteSpace(error)) + continue; + + using (FileStream fs = File.OpenWrite(tempFile)) + { + fs.Write(fileContents, 0, fileContents.Length); + } + } + catch { } + } + + // Collect and format all found protections + var protections = scanner.GetProtections(tempPath); + + // If temp directory cleanup fails + try + { + Directory.Delete(tempPath, true); + } + catch { } + + // Remove temporary path references + Utilities.StripFromKeys(protections, tempPath); + + return protections; + } + catch { } + } + + return null; + } + } +} diff --git a/BurnOutSharp/FileType/InstallShieldCAB.cs b/BurnOutSharp/FileType/InstallShieldCAB.cs index 8d7ee841..4e91a1d5 100644 --- a/BurnOutSharp/FileType/InstallShieldCAB.cs +++ b/BurnOutSharp/FileType/InstallShieldCAB.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; using UnshieldSharp.Cabinet; diff --git a/BurnOutSharp/Scanner.cs b/BurnOutSharp/Scanner.cs index d2681e70..3d59dec4 100644 --- a/BurnOutSharp/Scanner.cs +++ b/BurnOutSharp/Scanner.cs @@ -320,6 +320,14 @@ namespace BurnOutSharp Utilities.AppendToDictionary(protections, subProtections); } + // InstallShield Archive V3 (Z) + if (file != null && new InstallShieldArchiveV3().ShouldScan(magic)) + { + var subProtections = new InstallShieldArchiveV3().Scan(this, file); + Utilities.PrependToKeys(subProtections, file); + Utilities.AppendToDictionary(protections, subProtections); + } + // InstallShield Cabinet if (file != null && new InstallShieldCAB().ShouldScan(magic)) {