diff --git a/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs b/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs
index 723c800a..8b048258 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/PortableExecutable.cs
@@ -233,17 +233,38 @@ namespace BurnOutSharp.ExecutableType.Microsoft
///
/// Get the raw bytes from a section, if possible
///
- public byte[] ReadRawSection(Stream stream, string sectionName, bool first = true)
+ public byte[] ReadRawSection(Stream stream, string sectionName, bool first = true, int offset = 0)
{
+ // Special cases for non-offset data
+ if (offset == 0)
+ {
+ switch (sectionName)
+ {
+ case ".data":
+ return DataSectionRaw;
+ case ".edata":
+ return ExportDataSectionRaw;
+ case ".idata":
+ return ImportDataSectionRaw;
+ case ".rdata":
+ return ResourceDataSectionRaw;
+ case ".text":
+ return TextSectionRaw;
+ }
+ }
+
var section = first ? GetFirstSection(sectionName, true) : GetLastSection(sectionName, true);
if (section == null)
return null;
lock (stream)
{
+ int startingIndex = (int)Math.Max(section.PointerToRawData + offset, 0);
+ int readLength = (int)Math.Min(section.VirtualSize - offset, stream.Length);
+
long originalPosition = stream.Position;
- stream.Seek((int)section.PointerToRawData, SeekOrigin.Begin);
- byte[] sectionData = stream.ReadBytes((int)section.VirtualSize);
+ stream.Seek(startingIndex, SeekOrigin.Begin);
+ byte[] sectionData = stream.ReadBytes(readLength);
stream.Seek(originalPosition, SeekOrigin.Begin);
return sectionData;
}
@@ -253,14 +274,34 @@ namespace BurnOutSharp.ExecutableType.Microsoft
///
/// Get the raw bytes from a section, if possible
///
- public byte[] ReadRawSection(byte[] content, string sectionName, bool first = true)
+ public byte[] ReadRawSection(byte[] content, string sectionName, bool first = true, int offset = 0)
{
+ // Special cases for non-offset data
+ if (offset == 0)
+ {
+ switch (sectionName)
+ {
+ case ".data":
+ return DataSectionRaw;
+ case ".edata":
+ return ExportDataSectionRaw;
+ case ".idata":
+ return ImportDataSectionRaw;
+ case ".rdata":
+ return ResourceDataSectionRaw;
+ case ".text":
+ return TextSectionRaw;
+ }
+ }
+
var section = first ? GetFirstSection(sectionName, true) : GetLastSection(sectionName, true);
if (section == null)
return null;
- int offset = (int)section.PointerToRawData;
- return content.ReadBytes(ref offset, (int)section.VirtualSize);
+ int startingIndex = (int)Math.Max(section.PointerToRawData + offset, 0);
+ int readLength = (int)Math.Min(section.VirtualSize - offset, content.Length);
+
+ return content.ReadBytes(ref startingIndex, readLength);
}
///
@@ -358,7 +399,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft
pex.DataSectionRaw = pex.ReadRawSection(stream, ".data", false) ?? pex.ReadRawSection(stream, "DATA", false);
// Export Table
- pex.ImportDataSectionRaw = pex.ReadRawSection(stream, ".edata", false);
+ pex.ExportDataSectionRaw = pex.ReadRawSection(stream, ".edata", false);
// Import Table
pex.ImportDataSectionRaw = pex.ReadRawSection(stream, ".idata", false);
@@ -446,7 +487,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft
pex.DataSectionRaw = pex.ReadRawSection(content, ".data", false) ?? pex.ReadRawSection(content, "DATA", false);
// Export Table
- pex.ImportDataSectionRaw = pex.ReadRawSection(content, ".edata", false);
+ pex.ExportDataSectionRaw = pex.ReadRawSection(content, ".edata", false);
// Import Table
pex.ImportDataSectionRaw = pex.ReadRawSection(content, ".idata", false);
diff --git a/BurnOutSharp/ProtectionType/SafeCast.cs b/BurnOutSharp/ProtectionType/SafeCast.cs
deleted file mode 100644
index 32aadb89..00000000
--- a/BurnOutSharp/ProtectionType/SafeCast.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using BurnOutSharp.ExecutableType.Microsoft;
-using BurnOutSharp.Matching;
-using BurnOutSharp.Tools;
-
-namespace BurnOutSharp.ProtectionType
-{
- // TODO: Add the content checks from SafeDisc here
- // TODO: Investigate if this entire file should be wrapped into SafeDisc
- public class SafeCast : IContentCheck, IPathCheck
- {
- ///
- public string CheckContents(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, NewExecutable nex)
- {
- // TODO: Obtain a sample to find where this string is in a typical executable
- var contentMatchSets = new List
- {
- new ContentMatchSet(new List
- {
- // BoG_ *90.0&!! Yy>
- new byte?[]
- {
- 0x42, 0x6F, 0x47, 0x5F, 0x20, 0x2A, 0x39, 0x30,
- 0x2E, 0x30, 0x26, 0x21, 0x21, 0x20, 0x20, 0x59,
- 0x79, 0x3E
- },
-
- // product activation library
- new byte?[]
- {
- 0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74, 0x20,
- 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69,
- 0x6F, 0x6E, 0x20, 0x6C, 0x69, 0x62, 0x72, 0x61,
- 0x72, 0x79
- },
- }, GetVersion, "SafeCast"),
- };
-
- return MatchUtil.GetFirstMatch(file, fileContent, contentMatchSets, includeDebug);
- }
-
- ///
- public ConcurrentQueue CheckDirectoryPath(string path, IEnumerable files)
- {
- var matchers = new List
- {
- new PathMatchSet(new PathMatch("cdac11ba.exe", useEndsWith: true), "SafeCast"),
- };
-
- return MatchUtil.GetAllMatches(files, matchers, any: true);
- }
-
- ///
- public string CheckFilePath(string path)
- {
- var matchers = new List
- {
- new PathMatchSet(new PathMatch("cdac11ba.exe", useEndsWith: true), "SafeCast"),
- };
-
- return MatchUtil.GetFirstMatch(path, matchers, any: true);
- }
-
- public static string GetVersion(string file, byte[] fileContent, List positions)
- {
- int index = positions[0] + 20; // Begin reading after "BoG_ *90.0&!! Yy>" for old SafeDisc
- int version = fileContent.ReadInt32(ref index);
- int subVersion = fileContent.ReadInt32(ref index);
- int subsubVersion = fileContent.ReadInt32(ref index);
-
- if (version != 0)
- return $"{version}.{subVersion:00}.{subsubVersion:000}";
-
- index = positions[0] + 18 + 14; // Begin reading after "BoG_ *90.0&!! Yy>" for newer SafeDisc
- version = fileContent.ReadInt32(ref index);
- subVersion = fileContent.ReadInt32(ref index);
- subsubVersion = fileContent.ReadInt32(ref index);
-
- if (version == 0)
- return string.Empty;
-
- return $"{version}.{subVersion:00}.{subsubVersion:000}";
- }
- }
-}
diff --git a/BurnOutSharp/ProtectionType/SafeDisc.cs b/BurnOutSharp/ProtectionType/SafeDisc.cs
index a3955cfc..feae2230 100644
--- a/BurnOutSharp/ProtectionType/SafeDisc.cs
+++ b/BurnOutSharp/ProtectionType/SafeDisc.cs
@@ -1,4 +1,5 @@
-using System.Collections.Concurrent;
+using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using BurnOutSharp.ExecutableType.Microsoft;
@@ -7,6 +8,8 @@ using BurnOutSharp.Tools;
namespace BurnOutSharp.ProtectionType
{
+ // TODO: Figure out how to properly distinguish SafeDisc and SafeCast since both use
+ // the same generic BoG_ string. The current combination check doesn't seem consistent
public class SafeDisc : IContentCheck, IPathCheck
{
///
@@ -38,6 +41,9 @@ namespace BurnOutSharp.ProtectionType
new PathMatchSet(new PathMatch("00000001.LT1", useEndsWith: true), "SafeDisc Lite"),
new PathMatchSet(".SafeDiscDVD.bundle", "SafeDisc for Macintosh"),
+
+ new PathMatchSet(new PathMatch("cdac11ba.exe", useEndsWith: true), "SafeCast"),
+ new PathMatchSet(new PathMatch("cdac14ba.dll", useEndsWith: true), "SafeCast"),
};
///
@@ -48,117 +54,29 @@ namespace BurnOutSharp.ProtectionType
if (sections == null)
return null;
- // Get the .text section, if it exists -- TODO: Figure out how to capture this automatically
- var textSection = pex.GetFirstSection(".text", exact: true);
- if (textSection != null)
- {
- // This subtract is needed because BoG_ starts before the .text section
- int sectionAddr = (int)textSection.PointerToRawData - 64;
- int sectionEnd = sectionAddr + (int)textSection.VirtualSize;
- var matchers = new List
- {
- // BoG_ *90.0&!! Yy>
- new ContentMatchSet(
- new ContentMatch(new byte?[]
- {
- 0x42, 0x6F, 0x47, 0x5F, 0x20, 0x2A, 0x39, 0x30,
- 0x2E, 0x30, 0x26, 0x21, 0x21, 0x20, 0x20, 0x59,
- 0x79, 0x3E
- }, start: sectionAddr, end: sectionEnd),
- GetVersion, "SafeDisc"),
+ string name = Utilities.GetFileDescription(pex);
+ if (!string.IsNullOrWhiteSpace(name) && name.Equals("SafeCast2", StringComparison.OrdinalIgnoreCase))
+ return $"SafeCast";
- // (char)0x00 + (char)0x00 + BoG_
- new ContentMatchSet(
- new ContentMatch(new byte?[] { 0x00, 0x00, 0x42, 0x6F, 0x47, 0x5F }, start: sectionAddr, end: sectionEnd),
- Get320to4xVersion, "SafeDisc"),
- };
-
- string match = MatchUtil.GetFirstMatch(file, fileContent, matchers, includeDebug);
- if (!string.IsNullOrWhiteSpace(match))
- return match;
- }
+ // Get the .text section, if it exists
+ string match = CheckSectionForProtection(file, fileContent, includeDebug, pex, ".text");
+ if (!string.IsNullOrWhiteSpace(match))
+ return match;
// Get the .txt2 section, if it exists
- var txt2Section = pex.GetFirstSection(".txt2", exact: true);
- if (txt2Section != null)
- {
- // This subtract is needed because BoG_ starts before the .txt2 section
- int sectionAddr = (int)txt2Section.PointerToRawData - 64;
- int sectionEnd = sectionAddr + (int)txt2Section.VirtualSize;
- var matchers = new List
- {
- // BoG_ *90.0&!! Yy>
- new ContentMatchSet(
- new ContentMatch(new byte?[]
- {
- 0x42, 0x6F, 0x47, 0x5F, 0x20, 0x2A, 0x39, 0x30,
- 0x2E, 0x30, 0x26, 0x21, 0x21, 0x20, 0x20, 0x59,
- 0x79, 0x3E
- }, start: sectionAddr, end: sectionEnd),
- GetVersion, "SafeDisc"),
-
- // (char)0x00 + (char)0x00 + BoG_
- new ContentMatchSet(
- new ContentMatch(new byte?[] { 0x00, 0x00, 0x42, 0x6F, 0x47, 0x5F }, start: sectionAddr, end: sectionEnd),
- Get320to4xVersion, "SafeDisc"),
- };
-
- string match = MatchUtil.GetFirstMatch(file, fileContent, matchers, includeDebug);
- if (!string.IsNullOrWhiteSpace(match))
- return match;
- }
+ match = CheckSectionForProtection(file, fileContent, includeDebug, pex, ".txt2");
+ if (!string.IsNullOrWhiteSpace(match))
+ return match;
// Get the CODE section, if it exists
- var codeSection = pex.GetFirstSection("CODE", exact: true);
- if (codeSection != null)
- {
- // This subtract is needed because BoG_ starts before the CODE section
- int sectionAddr = (int)codeSection.PointerToRawData - 64;
- int sectionEnd = sectionAddr + (int)codeSection.VirtualSize;
- var matchers = new List
- {
- // BoG_ *90.0&!! Yy>
- new ContentMatchSet(
- new ContentMatch(new byte?[]
- {
- 0x42, 0x6F, 0x47, 0x5F, 0x20, 0x2A, 0x39, 0x30,
- 0x2E, 0x30, 0x26, 0x21, 0x21, 0x20, 0x20, 0x59,
- 0x79, 0x3E
- }, start: sectionAddr, end: sectionEnd),
- GetVersion, "SafeDisc"),
-
- // (char)0x00 + (char)0x00 + BoG_
- new ContentMatchSet(
- new ContentMatch(new byte?[] { 0x00, 0x00, 0x42, 0x6F, 0x47, 0x5F }, start: sectionAddr, end: sectionEnd),
- Get320to4xVersion, "SafeDisc"),
- };
-
- string match = MatchUtil.GetFirstMatch(file, fileContent, matchers, includeDebug);
- if (!string.IsNullOrWhiteSpace(match))
- return match;
- }
+ match = CheckSectionForProtection(file, fileContent, includeDebug, pex, "CODE");
+ if (!string.IsNullOrWhiteSpace(match))
+ return match;
// Get the .data section, if it exists
- if (pex.DataSectionRaw != null)
- {
- var matchers = new List
- {
- // BoG_ *90.0&!! Yy>
- new ContentMatchSet(new byte?[]
- {
- 0x42, 0x6F, 0x47, 0x5F, 0x20, 0x2A, 0x39, 0x30,
- 0x2E, 0x30, 0x26, 0x21, 0x21, 0x20, 0x20, 0x59,
- 0x79, 0x3E
- }, GetVersion, "SafeDisc"),
-
- // (char)0x00 + (char)0x00 + BoG_
- new ContentMatchSet(new byte?[] { 0x00, 0x00, 0x42, 0x6F, 0x47, 0x5F }, Get320to4xVersion, "SafeDisc"),
- };
-
- string match = MatchUtil.GetFirstMatch(file, pex.DataSectionRaw, matchers, includeDebug);
- if (!string.IsNullOrWhiteSpace(match))
- return match;
- }
+ match = CheckSectionForProtection(file, fileContent, includeDebug, pex, ".data");
+ if (!string.IsNullOrWhiteSpace(match))
+ return match;
// Get the stxt371 and stxt774 sections, if they exist -- TODO: Confirm if both are needed or either/or is fine
bool stxt371Section = pex.ContainsSection("stxt371", exact: true);
@@ -350,5 +268,51 @@ namespace BurnOutSharp.ProtectionType
else
return "1-4";
}
+
+ private string CheckSectionForProtection(string file, byte[] fileContent, bool includeDebug, PortableExecutable pex, string sectionName)
+ {
+ // This subtract is needed because BoG_ starts before the section
+ var sectionRaw = pex.ReadRawSection(fileContent, sectionName, first: true, offset: -64);
+ if (sectionRaw != null)
+ {
+ var matchers = new List
+ {
+ new ContentMatchSet(new List
+ {
+ // BoG_ *90.0&!! Yy>
+ new byte?[]
+ {
+ 0x42, 0x6F, 0x47, 0x5F, 0x20, 0x2A, 0x39, 0x30,
+ 0x2E, 0x30, 0x26, 0x21, 0x21, 0x20, 0x20, 0x59,
+ 0x79, 0x3E
+ },
+
+ // product activation library
+ new byte?[]
+ {
+ 0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74, 0x20,
+ 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x6C, 0x69, 0x62, 0x72, 0x61,
+ 0x72, 0x79
+ },
+ }, GetVersion, "SafeCast"),
+
+ // BoG_ *90.0&!! Yy>
+ new ContentMatchSet(new byte?[]
+ {
+ 0x42, 0x6F, 0x47, 0x5F, 0x20, 0x2A, 0x39, 0x30,
+ 0x2E, 0x30, 0x26, 0x21, 0x21, 0x20, 0x20, 0x59,
+ 0x79, 0x3E
+ }, GetVersion, "SafeDisc"),
+
+ // (char)0x00 + (char)0x00 + BoG_
+ new ContentMatchSet(new byte?[] { 0x00, 0x00, 0x42, 0x6F, 0x47, 0x5F }, Get320to4xVersion, "SafeDisc"),
+ };
+
+ return MatchUtil.GetFirstMatch(file, sectionRaw, matchers, includeDebug);
+ }
+
+ return null;
+ }
}
}