Combine SafeCast into SafeDisc; improvements

This commit is contained in:
Matt Nadareski
2021-09-14 13:56:43 -07:00
parent c915f29c05
commit 0d75ee135c
3 changed files with 118 additions and 199 deletions

View File

@@ -233,17 +233,38 @@ namespace BurnOutSharp.ExecutableType.Microsoft
/// <summary>
/// Get the raw bytes from a section, if possible
/// </summary>
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
/// <summary>
/// Get the raw bytes from a section, if possible
/// </summary>
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);
}
/// <summary>
@@ -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);

View File

@@ -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
{
/// <inheritdoc/>
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<ContentMatchSet>
{
new ContentMatchSet(new List<byte?[]>
{
// 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);
}
/// <inheritdoc/>
public ConcurrentQueue<string> CheckDirectoryPath(string path, IEnumerable<string> files)
{
var matchers = new List<PathMatchSet>
{
new PathMatchSet(new PathMatch("cdac11ba.exe", useEndsWith: true), "SafeCast"),
};
return MatchUtil.GetAllMatches(files, matchers, any: true);
}
/// <inheritdoc/>
public string CheckFilePath(string path)
{
var matchers = new List<PathMatchSet>
{
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<int> 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}";
}
}
}

View File

@@ -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
{
/// <summary>
@@ -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"),
};
/// <inheritdoc/>
@@ -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<ContentMatchSet>
{
// 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<ContentMatchSet>
{
// 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<ContentMatchSet>
{
// 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<ContentMatchSet>
{
// 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<ContentMatchSet>
{
new ContentMatchSet(new List<byte?[]>
{
// 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;
}
}
}