From 7e3ef544f0a763a8cef5b3860411e809e9bdfde5 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 21 Mar 2021 15:24:23 -0700 Subject: [PATCH] Use content matching helper, part 2 --- BurnOutSharp/Matching/ContentMatch.cs | 30 +++++++++++ BurnOutSharp/Matching/Matcher.cs | 59 ++++++++++++++++++++ BurnOutSharp/PackerType/Armadillo.cs | 9 ++-- BurnOutSharp/PackerType/EXEStealth.cs | 14 +++-- BurnOutSharp/PackerType/InnoSetup.cs | 18 ++++--- BurnOutSharp/PackerType/NSIS.cs | 22 ++++---- BurnOutSharp/PackerType/PECompact.cs | 35 ++++++------ BurnOutSharp/PackerType/SetupFactory.cs | 44 ++++++++------- BurnOutSharp/PackerType/UPX.cs | 69 +++++++++++------------- BurnOutSharp/PackerType/WinRARSFX.cs | 12 +++-- BurnOutSharp/PackerType/WinZipSFX.cs | 25 +++++---- BurnOutSharp/PackerType/WiseInstaller.cs | 7 +-- BurnOutSharp/PackerType/dotFuscator.cs | 12 +++-- BurnOutSharp/Utilities.cs | 68 ++++++++++++----------- 14 files changed, 275 insertions(+), 149 deletions(-) create mode 100644 BurnOutSharp/Matching/ContentMatch.cs create mode 100644 BurnOutSharp/Matching/Matcher.cs diff --git a/BurnOutSharp/Matching/ContentMatch.cs b/BurnOutSharp/Matching/ContentMatch.cs new file mode 100644 index 00000000..9fef7cf9 --- /dev/null +++ b/BurnOutSharp/Matching/ContentMatch.cs @@ -0,0 +1,30 @@ +namespace BurnOutSharp.Matching +{ + /// + /// Single matching criteria + /// + internal class ContentMatch + { + /// + /// Content to match + /// + public byte?[] Needle { get; set; } + + /// + /// Starting index for matching + /// + public int Start { get; set; } + + /// + /// Ending index for matching + /// + public int End { get; set; } + + public ContentMatch(byte?[] needle, int start = -1, int end = -1) + { + Needle = needle; + Start = start; + End = end; + } + } +} \ No newline at end of file diff --git a/BurnOutSharp/Matching/Matcher.cs b/BurnOutSharp/Matching/Matcher.cs new file mode 100644 index 00000000..c4ff63da --- /dev/null +++ b/BurnOutSharp/Matching/Matcher.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BurnOutSharp.Matching +{ + /// + /// Wrapper for a single set of matching criteria + /// + internal class Matcher + { + /// + /// Set of all content matches + /// + public IEnumerable ContentMatches { get; set; } + + /// + /// Function to get a version for this Matcher + /// + public Func GetVersion { get; set; } + + /// + /// Name of the protection to show + /// + public string ProtectionName { get; set; } + + #region Constructors + + public Matcher(byte?[] needle, string protectionName) + : this(new List() { needle }, null, protectionName) { } + + public Matcher(List needles, string protectionName) + : this(needles, null, protectionName) { } + + public Matcher(byte?[] needle, Func getVersion, string protectionName) + : this(new List() { needle }, getVersion, protectionName) { } + + public Matcher(List needles, Func getVersion, string protectionName) + : this(needles.Select(n => new ContentMatch(n)).ToList(), getVersion, protectionName) { } + + public Matcher(ContentMatch needle, string protectionName) + : this(new List() { needle }, null, protectionName) { } + + public Matcher(List needles, string protectionName) + : this(needles, null, protectionName) { } + + public Matcher(ContentMatch needle, Func getVersion, string protectionName) + : this(new List() { needle }, getVersion, protectionName) { } + + public Matcher(List needles, Func getVersion, string protectionName) + { + ContentMatches = needles; + GetVersion = getVersion; + ProtectionName = protectionName; + } + + #endregion + } +} \ No newline at end of file diff --git a/BurnOutSharp/PackerType/Armadillo.cs b/BurnOutSharp/PackerType/Armadillo.cs index 26cfd701..cd3aafe3 100644 --- a/BurnOutSharp/PackerType/Armadillo.cs +++ b/BurnOutSharp/PackerType/Armadillo.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using BurnOutSharp.Matching; namespace BurnOutSharp.PackerType { @@ -7,16 +8,16 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - var mappings = new Dictionary + var matchers = new List { // .nicode + (char)0x00 - [new byte?[] { 0x2E, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65, 0x00 }] = "Armadillo", + new Matcher(new byte?[] { 0x2E, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65, 0x00 }, "Armadillo"), // ARMDEBUG - [new byte?[] { 0x41, 0x52, 0x4D, 0x44, 0x45, 0x42, 0x55, 0x47 }] = "Armadillo", + new Matcher(new byte?[] { 0x41, 0x52, 0x4D, 0x44, 0x45, 0x42, 0x55, 0x47 }, "Armadillo"), }; - return Utilities.GetContentMatches(fileContent, mappings, includePosition); + return Utilities.GetContentMatches(fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/PackerType/EXEStealth.cs b/BurnOutSharp/PackerType/EXEStealth.cs index b40c8c96..b4abfcb0 100644 --- a/BurnOutSharp/PackerType/EXEStealth.cs +++ b/BurnOutSharp/PackerType/EXEStealth.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using BurnOutSharp.Matching; namespace BurnOutSharp.PackerType { @@ -7,13 +8,20 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - var mappings = new Dictionary + var matchers = new List { // ??[[__[[_ + (char)0x00 + {{ + (char)0x0 + (char)0x00 + {{ + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x0 + (char)0x00 + (char)0x00 + (char)0x00 + (char)0x00 + ?;??;?? - [new byte?[] { 0x3F, 0x3F, 0x5B, 0x5B, 0x5F, 0x5F, 0x5B, 0x5B, 0x5F, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3F, 0x3B, 0x3F, 0x3F, 0x3B, 0x3F, 0x3F }] = "EXE Stealth", + new Matcher(new byte?[] + { + 0x3F, 0x3F, 0x5B, 0x5B, 0x5F, 0x5F, 0x5B, 0x5B, + 0x5F, 0x00, 0x7B, 0x7B, 0x00, 0x00, 0x7B, 0x7B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x3F, 0x3B, 0x3F, 0x3F, 0x3B, 0x3F, + 0x3F + }, "EXE Stealth"), }; - return Utilities.GetContentMatches(fileContent, mappings, includePosition); + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/PackerType/InnoSetup.cs b/BurnOutSharp/PackerType/InnoSetup.cs index c8a76976..e73226be 100644 --- a/BurnOutSharp/PackerType/InnoSetup.cs +++ b/BurnOutSharp/PackerType/InnoSetup.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using BurnOutSharp.Matching; namespace BurnOutSharp.PackerType { @@ -10,16 +11,19 @@ namespace BurnOutSharp.PackerType /// public bool ShouldScan(byte[] magic) => true; - /// /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - // Inno - byte?[] check = new byte?[] { 0x49, 0x6E, 0x6E, 0x6F }; - if (fileContent.FirstPosition(check, out int position) && position == 0x30) - return $"Inno Setup {GetVersion(fileContent)}" + (includePosition ? $" (Index {position})" : string.Empty); + var matchers = new List + { + // Inno + new Matcher( + new ContentMatch(new byte?[] { 0x49, 0x6E, 0x6E, 0x6F }, start: 0x30, end: 0x31), + GetVersion, + "Inno Setup"), + }; - return null; + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } /// @@ -42,7 +46,7 @@ namespace BurnOutSharp.PackerType return null; } - private static string GetVersion(byte[] fileContent) + public static string GetVersion(string file, byte[] fileContent, int position) { byte[] signature = new ArraySegment(fileContent, 0x30, 12).ToArray(); diff --git a/BurnOutSharp/PackerType/NSIS.cs b/BurnOutSharp/PackerType/NSIS.cs index aac78d72..fd7fd319 100644 --- a/BurnOutSharp/PackerType/NSIS.cs +++ b/BurnOutSharp/PackerType/NSIS.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text; +using BurnOutSharp.Matching; namespace BurnOutSharp.PackerType { @@ -9,23 +11,19 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - // Nullsoft Install System - byte?[] check = new byte?[] { 0x4e, 0x75, 0x6c, 0x6c, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d }; - if (fileContent.FirstPosition(check, out int position)) + var matchers = new List { - string version = GetVersion(fileContent, position); - return $"NSIS {version}" + (includePosition ? $" (Index {position})" : string.Empty); - } + // Nullsoft Install System + new Matcher(new byte?[] { 0x4e, 0x75, 0x6c, 0x6c, 0x73, 0x6f, 0x66, 0x74, 0x20, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d }, GetVersion, "NSIS"), - // NullsoftInst - check = new byte?[] { 0x4e, 0x75, 0x6c, 0x6c, 0x73, 0x6f, 0x66, 0x74, 0x49, 0x6e, 0x73, 0x74 }; - if (fileContent.FirstPosition(check, out position)) - return $"NSIS" + (includePosition ? $" (Index {position})" : string.Empty); + // NullsoftInst + new Matcher(new byte?[] { 0x4e, 0x75, 0x6c, 0x6c, 0x73, 0x6f, 0x66, 0x74, 0x49, 0x6e, 0x73, 0x74 }, "NSIS"), + }; - return null; + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } - private static string GetVersion(byte[] fileContent, int index) + public static string GetVersion(string file, byte[] fileContent, int index) { try { diff --git a/BurnOutSharp/PackerType/PECompact.cs b/BurnOutSharp/PackerType/PECompact.cs index ac44c02e..e1d11dd9 100644 --- a/BurnOutSharp/PackerType/PECompact.cs +++ b/BurnOutSharp/PackerType/PECompact.cs @@ -1,5 +1,6 @@ using System; -using System.IO; +using System.Collections.Generic; +using BurnOutSharp.Matching; namespace BurnOutSharp.PackerType { @@ -10,26 +11,24 @@ namespace BurnOutSharp.PackerType public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { // Another possible version string for version 1 is "PECO" (50 45 43 4F) - - // "pec1" - byte?[] check = new byte?[] { 0x70, 0x65, 0x63, 0x31 }; - if (fileContent.FirstPosition(check, out int position, end: 2048)) - return "PE Compact 1" + (includePosition ? $" (Index {position})" : string.Empty); - - // "PEC2" - check = new byte?[] { 0x50, 0x45, 0x43, 0x32 }; - if (fileContent.FirstPosition(check, out position, end: 2048)) + var matchers = new List { - int version = BitConverter.ToInt16(fileContent, position + 4); - return $"PE Compact 2 v{version}" + (includePosition ? $" (Index {position})" : string.Empty); - } + // pec1 + new Matcher(new byte?[] { 0x70, 0x65, 0x63, 0x31 }, "PE Compact 1"), - // "PECompact2" - check = new byte?[] { 0x50, 0x45, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x32 }; - if (fileContent.FirstPosition(check, out position)) - return "PE Compact 2" + (includePosition ? $" (Index {position})" : string.Empty); + // PEC2 + new Matcher(new byte?[] { 0x50, 0x45, 0x43, 0x32 }, GetVersion, "PE Compact 2 v"), - return null; + // PECompact2 + new Matcher(new byte?[] { 0x50, 0x45, 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x32 }, "PE Compact 2"), + }; + + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); + } + + public static string GetVersion(string file, byte[] fileContent, int position) + { + return BitConverter.ToInt16(fileContent, position + 4).ToString(); } } } diff --git a/BurnOutSharp/PackerType/SetupFactory.cs b/BurnOutSharp/PackerType/SetupFactory.cs index 8347ffa7..e4a6a0c5 100644 --- a/BurnOutSharp/PackerType/SetupFactory.cs +++ b/BurnOutSharp/PackerType/SetupFactory.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using BurnOutSharp.Matching; namespace BurnOutSharp.PackerType { @@ -11,27 +12,17 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - /* Longer version of the check that can be used if false positves become an issue: - "S.e.t.u.p. .F.a.c.t.o.r.y. .i.s. .a. .t.r.a.d.e.m.a.r.k. .o.f. .I.n.d.i.g.o. .R.o.s.e. .C.o.r.p.o.r.a.t.i.o.n" - byte?[] check = new byte?[] { 0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x20, 0x00, 0x46, 0x00, 0x61, 0x00, 0x63, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x79, 0x00, 0x20, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x20, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x72, 0x00, 0x6B, 0x00, 0x20, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x69, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x52, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x65, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E }; */ - // "S.e.t.u.p. .F.a.c.t.o.r.y." - byte?[] check = new byte?[] { 0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x20, 0x00, 0x46, 0x00, 0x61, 0x00, 0x63, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x79, 0x00 }; - if (fileContent.FirstPosition(check, out int position)) + // Longer version of the check that can be used if false positves become an issue: + // S.e.t.u.p. .F.a.c.t.o.r.y. .i.s. .a. .t.r.a.d.e.m.a.r.k. .o.f. .I.n.d.i.g.o. .R.o.s.e. .C.o.r.p.o.r.a.t.i.o.n + // byte?[] check = new byte?[] { 0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x20, 0x00, 0x46, 0x00, 0x61, 0x00, 0x63, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x79, 0x00, 0x20, 0x00, 0x69, 0x00, 0x73, 0x00, 0x20, 0x00, 0x61, 0x00, 0x20, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x72, 0x00, 0x6B, 0x00, 0x20, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6E, 0x00, 0x64, 0x00, 0x69, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x52, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x65, 0x00, 0x20, 0x00, 0x43, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x70, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6F, 0x00, 0x6E }; + + var matchers = new List { - // Check the manifest version first - string version = Utilities.GetManifestVersion(fileContent); - if (!string.IsNullOrEmpty(version)) - return $"Setup Factory {version}" + (includePosition ? $" (Index {position})" : string.Empty); - - // Then check the file version - version = Utilities.GetFileVersion(file); - if (!string.IsNullOrEmpty(version)) - return $"Setup Factory {version}" + (includePosition ? $" (Index {position})" : string.Empty); + // S.e.t.u.p. .F.a.c.t.o.r.y. + new Matcher(new byte?[] { 0x53, 0x00, 0x65, 0x00, 0x74, 0x00, 0x75, 0x00, 0x70, 0x00, 0x20, 0x00, 0x46, 0x00, 0x61, 0x00, 0x63, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x79, 0x00 }, GetVersion, "Setup Factory"), + }; - return $"Setup Factory (Unknown Version)" + (includePosition ? $" (Index {position})" : string.Empty); - } - - return null; + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } /// @@ -52,5 +43,20 @@ namespace BurnOutSharp.PackerType { return null; } + + public static string GetVersion(string file, byte[] fileContent, int position) + { + // Check the manifest version first + string version = Utilities.GetManifestVersion(fileContent); + if (!string.IsNullOrEmpty(version)) + return version; + + // Then check the file version + version = Utilities.GetFileVersion(file); + if (!string.IsNullOrEmpty(version)) + return version; + + return null; + } } } diff --git a/BurnOutSharp/PackerType/UPX.cs b/BurnOutSharp/PackerType/UPX.cs index 6a863f45..eebfc4b9 100644 --- a/BurnOutSharp/PackerType/UPX.cs +++ b/BurnOutSharp/PackerType/UPX.cs @@ -1,4 +1,6 @@ +using System.Collections.Generic; using System.Text; +using BurnOutSharp.Matching; namespace BurnOutSharp.PackerType { @@ -7,50 +9,43 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - // UPX! - byte?[] check = new byte?[] { 0x55, 0x50, 0x58, 0x21 }; - if (fileContent.FirstPosition(check, out int position)) + var matchers = new List { - string version = GetVersion(fileContent, position); - return $"UPX {version}" + (includePosition ? $" (Index {position})" : string.Empty); - } + // UPX! + new Matcher(new byte?[] { 0x55, 0x50, 0x58, 0x21 }, GetVersion, "Inno Setup"), - // NOS - check = new byte?[] { 0x55, 0x50, 0x58, 0x21 }; - if (fileContent.FirstPosition(check, out position)) - { - string version = GetVersion(fileContent, position); - return $"UPX (NOS Variant) {version}" + (includePosition ? $" (Index {position})" : string.Empty); - } + // NOS + new Matcher(new byte?[] { 0x4E, 0x4F, 0x53 }, GetVersion, "UPX (NOS Variant)"), - // UPX0 - check = new byte?[] { 0x55, 0x50, 0x58, 0x30 }; - if (fileContent.FirstPosition(check, out position, end: 2048)) - { - // UPX1 - byte?[] check2 = new byte?[] { 0x55, 0x50, 0x58, 0x31 }; - if (fileContent.FirstPosition(check2, out int position2, end: 2048)) - { - return $"UPX (Unknown Version)" + (includePosition ? $" (Index {position}, {position2})" : string.Empty); - } - } + new Matcher( + new List + { + // UPX0 + new byte?[] { 0x55, 0x50, 0x58, 0x30 }, - // NOS0 - check = new byte?[] { 0x4E, 0x4F, 0x53, 0x30 }; - if (fileContent.FirstPosition(check, out position, end: 2048)) - { - // NOS1 - byte?[] check2 = new byte?[] { 0x4E, 0x4F, 0x53, 0x31 }; - if (fileContent.FirstPosition(check2, out int position2, end: 2048)) - { - return $"UPX (NOS Variant) (Unknown Version)" + (includePosition ? $" (Index {position}, {position2})" : string.Empty); - } - } + // UPX1 + new byte?[] { 0x55, 0x50, 0x58, 0x31 }, + }, + "UPX (Unknown Version)" + ), - return null; + new Matcher( + new List + { + // NOS0 + new byte?[] { 0x4E, 0x4F, 0x53, 0x30 }, + + // NOS1 + new byte?[] { 0x4E, 0x4F, 0x53, 0x31 }, + }, + "UPX (NOS Variant) (Unknown Version)" + ), + }; + + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } - private static string GetVersion(byte[] fileContent, int index) + public static string GetVersion(string file, byte[] fileContent, int index) { try { diff --git a/BurnOutSharp/PackerType/WinRARSFX.cs b/BurnOutSharp/PackerType/WinRARSFX.cs index ef2dad3e..e082a2e3 100644 --- a/BurnOutSharp/PackerType/WinRARSFX.cs +++ b/BurnOutSharp/PackerType/WinRARSFX.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using BurnOutSharp.Matching; using SharpCompress.Archives; using SharpCompress.Archives.Rar; @@ -14,13 +15,18 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - var mappings = new Dictionary + var matchers = new List { // Software\WinRAR SFX - [new byte?[] { 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x5C, 0x57, 0x69, 0x6E, 0x52, 0x41, 0x52, 0x20, 0x53, 0x46, 0x58 }] = "WinRAR SFX", + new Matcher(new byte?[] + { + 0x53, 0x6F, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, + 0x5C, 0x57, 0x69, 0x6E, 0x52, 0x41, 0x52, 0x20, + 0x53, 0x46, 0x58 + }, "WinRAR SFX"), }; - return Utilities.GetContentMatches(fileContent, mappings, includePosition); + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } public Dictionary> Scan(Scanner scanner, string file) diff --git a/BurnOutSharp/PackerType/WinZipSFX.cs b/BurnOutSharp/PackerType/WinZipSFX.cs index 3b79b3e9..d9b074d1 100644 --- a/BurnOutSharp/PackerType/WinZipSFX.cs +++ b/BurnOutSharp/PackerType/WinZipSFX.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using BurnOutSharp.Matching; using SharpCompress.Archives; using SharpCompress.Archives.Zip; @@ -14,17 +15,21 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - // WinZip Self-Extractor - byte?[] check = new byte?[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70, 0x20, 0x53, 0x65, 0x6C, 0x66, 0x2D, 0x45, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x6F, 0x72 }; - if (fileContent.FirstPosition(check, out int position)) - return $"WinZip SFX {GetVersion(fileContent)}" + (includePosition ? $" (Index {position})" : string.Empty); + var matchers = new List + { + // WinZip Self-Extractor + new Matcher(new byte?[] + { + 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70, 0x20, 0x53, + 0x65, 0x6C, 0x66, 0x2D, 0x45, 0x78, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x6F, 0x72 + }, GetVersion, "WinZip SFX"), - // _winzip_ - check = new byte?[] { 0x5F, 0x77, 0x69, 0x6E, 0x7A, 0x69, 0x70, 0x5F }; - if (fileContent.FirstPosition(check, out position)) - return $"WinZip SFX {GetVersion(fileContent)}" + (includePosition ? $" (Index {position})" : string.Empty); + // _winzip_ + new Matcher(new byte?[] { 0x5F, 0x77, 0x69, 0x6E, 0x7A, 0x69, 0x70, 0x5F }, GetVersion, "WinZip SFX"), + }; - return null; + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } /// @@ -87,7 +92,7 @@ namespace BurnOutSharp.PackerType return null; } - private static string GetVersion(byte[] fileContent) + public static string GetVersion(string file, byte[] fileContent, int position) { // Check the manifest version first string version = Utilities.GetManifestVersion(fileContent); diff --git a/BurnOutSharp/PackerType/WiseInstaller.cs b/BurnOutSharp/PackerType/WiseInstaller.cs index 6d5d95ff..62595796 100644 --- a/BurnOutSharp/PackerType/WiseInstaller.cs +++ b/BurnOutSharp/PackerType/WiseInstaller.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using BurnOutSharp.Matching; using Wise = WiseUnpacker.WiseUnpacker; namespace BurnOutSharp.PackerType @@ -13,13 +14,13 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - var mappings = new Dictionary + var matchers = new List { // WiseMain - [new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }] = "Wise Installation Wizard Module", + new Matcher(new byte?[] { 0x57, 0x69, 0x73, 0x65, 0x4D, 0x61, 0x69, 0x6E }, "Wise Installation Wizard Module"), }; - return Utilities.GetContentMatches(fileContent, mappings, includePosition); + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } /// diff --git a/BurnOutSharp/PackerType/dotFuscator.cs b/BurnOutSharp/PackerType/dotFuscator.cs index 910ffd30..5e7a696b 100644 --- a/BurnOutSharp/PackerType/dotFuscator.cs +++ b/BurnOutSharp/PackerType/dotFuscator.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using BurnOutSharp.Matching; namespace BurnOutSharp.PackerType { @@ -7,13 +8,18 @@ namespace BurnOutSharp.PackerType /// public string CheckContents(string file, byte[] fileContent, bool includePosition = false) { - var mappings = new Dictionary + var matchers = new List { // DotfuscatorAttribute - [new byte?[] { 0x44, 0x6F, 0x74, 0x66, 0x75, 0x73, 0x63, 0x61, 0x74, 0x6F, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65 }] = "dotFuscator", + new Matcher(new byte?[] + { + 0x44, 0x6F, 0x74, 0x66, 0x75, 0x73, 0x63, 0x61, + 0x74, 0x6F, 0x72, 0x41, 0x74, 0x74, 0x72, 0x69, + 0x62, 0x75, 0x74, 0x65 + }, "dotFuscator"), }; - return Utilities.GetContentMatches(fileContent, mappings, includePosition); + return Utilities.GetContentMatches(file, fileContent, matchers, includePosition); } } } diff --git a/BurnOutSharp/Utilities.cs b/BurnOutSharp/Utilities.cs index bcecf433..1a3e30f7 100644 --- a/BurnOutSharp/Utilities.cs +++ b/BurnOutSharp/Utilities.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text; using System.Xml; +using BurnOutSharp.Matching; namespace BurnOutSharp { @@ -246,49 +247,56 @@ namespace BurnOutSharp #region Protection /// - /// Get simple content matches for a given protection + /// Get content matches for a given protection /// + /// File to check for matches /// Byte array representing the file contents - /// Mapping of byte array to string for matching + /// Enumerable of matchers to be run on the file /// True to include positional data, false otherwise /// String representing the matched protection, null otherwise - /// - /// This is useful for checks that don't do anything special with versions or other positions. /// TODO: Make variant of this that returns *all* content matches for later - /// TODO: Make variant of this that can take multiple content checks per check for later - /// - public static string GetContentMatches(byte[] fileContent, Dictionary mappings, bool includePosition = false) - { - return GetVersionedContentMatches(fileContent, mappings.Select(kvp => (kvp.Key, (Func)null, kvp.Value)).ToList(), includePosition); - } - - /// - /// Get versioned content matches for a given protection - /// - /// Byte array representing the file contents - /// Tuple of matching byte array, version method, and string to output - /// True to include positional data, false otherwise - /// String representing the matched protection, null otherwise - /// - /// This is useful for checks that don't do anything special with versions or other positions. - /// TODO: Make variant of this that returns *all* content matches for later - /// TODO: Make variant of this that can take multiple content checks per check for later - /// - public static string GetVersionedContentMatches(byte[] fileContent, List<(byte?[], Func, string)> mappings, bool includePosition = false) + public static string GetContentMatches(string file, byte[] fileContent, IEnumerable matchers, bool includePosition = false) { // If there's no mappings, we can't match - if (mappings == null || !mappings.Any()) + if (matchers == null || !matchers.Any()) return null; // Loop through and try everything otherwise - foreach (var mapping in mappings) + foreach (var matcher in matchers) { - if (fileContent.FirstPosition(mapping.Item1, out int position)) + // Setup for a single matcher + bool allMatches = true; + List positions = new List(); + + // Loop through all content matches and make sure all pass + foreach (var contentMatch in matcher.ContentMatches) { - string version = mapping.Item2 == null ? string.Empty : (mapping.Item2(fileContent, position) ?? "Unknown Version"); - string protection = string.IsNullOrWhiteSpace(version) ? mapping.Item3 : $"{mapping.Item3} {version}"; - return protection + (includePosition ? $" (Index {position})" : string.Empty); + if (!fileContent.FirstPosition(contentMatch.Needle, out int position, contentMatch.Start, contentMatch.End)) + { + allMatches = false; + break; + } + else + { + positions.Add(position); + } } + + // If not all matches pass, then we continue + if (!allMatches) + continue; + + // Format the list of all positions found + string positionsString = string.Join(", ", positions); + + // If we there is no version method, just return the protection name + if (matcher.GetVersion == null) + return (matcher.ProtectionName ?? "Unknown Protection") + (includePosition ? $" (Index {positionsString})" : string.Empty); + + // Otherwise, invoke the version method + // TODO: Pass all positions to the version finding method + string version = matcher.GetVersion(file, fileContent, positions[0]) ?? "Unknown Version"; + return $"{matcher.ProtectionName} {version}" + (includePosition ? $" (Index {positionsString})" : string.Empty); } return null;