diff --git a/BurnOutSharp/EVORE.cs b/BurnOutSharp/EVORE.cs
index d74420e9..e5eef670 100644
--- a/BurnOutSharp/EVORE.cs
+++ b/BurnOutSharp/EVORE.cs
@@ -17,8 +17,10 @@
//Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
using BurnOutSharp.ExecutableType.Microsoft;
@@ -27,35 +29,48 @@ namespace BurnOutSharp
internal static class EVORE
{
///
- /// Checks if the file contents that represent a PE is a DLL or an EXE
+ /// Checks if the file contents represent a PE executable
///
/// File contents to check
- /// True if the file is an EXE, false if it's a DLL
- internal static bool IsEXE(byte[] fileContent)
+ /// True if the file is an EXE, false otherwise
+ internal static bool IsPEExecutable(byte[] fileContent)
{
- int PEHeaderOffset = BitConverter.ToInt32(fileContent, 60);
- short Characteristics = BitConverter.ToInt16(fileContent, PEHeaderOffset + 22);
+ if (fileContent == null)
+ return false;
- // Check if file is dll
- return (Characteristics & 0x2000) != 0x2000;
+ try
+ {
+ IMAGE_DOS_HEADER idh = IMAGE_DOS_HEADER.Deserialize(fileContent, 0);
+ IMAGE_FILE_HEADER ifh = IMAGE_FILE_HEADER.Deserialize(fileContent, idh.NewExeHeaderAddr);
+
+ // Check if file is dll
+ return (ifh.Characteristics & 0x2000) != 0x2000;
+ }
+ catch
+ {
+ return false;
+ }
}
///
/// Writes the file contents to a temporary file, if possible
///
/// File contents to write
- /// Optional extension for the temproary file, defaults to ".exe"
+ /// Optional extension for the temporary file, defaults to ".exe"
/// Name of the new temporary file, null on error
- internal static string MakeTempFile(byte[] fileContent, string sExtension = ".exe")
+ internal static string MakeTempFile(byte[] fileContent, string extension = ".exe")
{
- string filei = Guid.NewGuid().ToString();
- string tempPath = Path.Combine(Path.GetTempPath(), "tmp", $"{filei}{sExtension}");
- try
- {
- File.Delete(tempPath);
- }
- catch { }
+ // Ensure the extension is set
+ if (string.IsNullOrWhiteSpace(extension))
+ extension = ".exe";
+ else if (!extension.StartsWith("."))
+ extension = $".{extension}";
+ // Get the temporary path to use
+ string tempFileName = Guid.NewGuid().ToString();
+ string tempPath = Path.Combine(Path.GetTempPath(), "tmp", $"{tempFileName}{extension}");
+
+ // Create and fill the file, if possible
try
{
Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
@@ -79,60 +94,73 @@ namespace BurnOutSharp
/// Paths for all of the copied DLLs, null on error
internal static string[] CopyDependentDlls(string file, byte[] fileContent)
{
- var sections = ReadSections(fileContent);
+ if (fileContent == null)
+ return null;
- long lastPosition;
- string[] saDependentDLLs = null;
- int index = 60;
- int PEHeaderOffset = BitConverter.ToInt32(fileContent, index);
- index = PEHeaderOffset + 120 + 8; //120 Bytes till IMAGE_DATA_DIRECTORY array,8 Bytes=size of IMAGE_DATA_DIRECTORY
- uint ImportTableRVA = BitConverter.ToUInt32(fileContent, index);
- index += 4;
- uint ImportTableSize = BitConverter.ToUInt32(fileContent, index);
- index = (int)RVA2Offset(ImportTableRVA, sections);
- index += 12;
- uint DllNameRVA = BitConverter.ToUInt32(fileContent, index);
- index += 4;
- while (DllNameRVA != 0)
+ // Process each of the DLLs that it finds
+ try
{
- string sDllName = "";
- byte bChar;
- lastPosition = index;
- uint DLLNameOffset = RVA2Offset(DllNameRVA, sections);
- if (DLLNameOffset > 0)
+ unsafe
{
- index = (int)DLLNameOffset;
- if ((char)fileContent[index] > -1)
+ // Read all of the executable header information
+ IMAGE_DOS_HEADER idh = IMAGE_DOS_HEADER.Deserialize(fileContent, 0);
+ IMAGE_FILE_HEADER ifh = IMAGE_FILE_HEADER.Deserialize(fileContent, idh.NewExeHeaderAddr);
+ IMAGE_OPTIONAL_HEADER ioh = IMAGE_OPTIONAL_HEADER.Deserialize(fileContent, idh.NewExeHeaderAddr + Marshal.SizeOf(ifh));
+
+ // Find the import directory entry
+ IMAGE_DATA_DIRECTORY idei = ioh.DataDirectory[Constants.IMAGE_DIRECTORY_ENTRY_IMPORT];
+
+ // Set the table index and size
+ int tableIndex = (int)idei.VirtualAddress;
+ int tableSize = (int)idei.Size;
+ if (tableIndex <= 0 || tableSize <= 0)
+ return null;
+
+ // Retrieve the table data
+ byte[] tableData = new byte[tableSize];
+ Array.Copy(fileContent, tableIndex, tableData, 0, tableSize);
+ int entryCount = tableSize / 4; // Each entry is a UInt32
+
+ // TODO: Validate what this table actually looks like.
+ // My concern about this is that it seems like each entry might be 16 bytes?
+ // The original code does a += 12, reads the address, and then moves on.
+ // That being said, the way that this works _does_ come up with a valid table,
+ // at least something that looks like a valid table, since it shows up with
+ // `ntoskrnl.exe` on the dot
+ //
+ // Unfortunately, for other programs, this comes up with nonsense data, so it's hard
+ // to tell if the table is accurate or not.
+
+ // Iterate through the table and get valid DLL names
+ List dependentDlls = new List();
+ for (int i = 0; i < entryCount; i++)
{
- do
- {
- bChar = fileContent[index];
- index++;
- sDllName += (char)bChar;
- } while (bChar != 0 && (char)fileContent[index] > -1);
+ // Get and validate the virtual offset
+ uint entryVirtualOffset = BitConverter.ToUInt32(tableData, i * 4);
+ if (entryVirtualOffset == 0)
+ continue;
- sDllName = sDllName.Remove(sDllName.Length - 1, 1);
- if (File.Exists(Path.Combine(Path.GetDirectoryName(file), sDllName)))
- {
- if (saDependentDLLs == null)
- saDependentDLLs = new string[0];
- else
- saDependentDLLs = new string[saDependentDLLs.Length];
+ // Get the DLL name from the table and add it to the list if possible
+ string entryDllName = new string(fileContent.Skip((int)entryVirtualOffset).TakeWhile(b => b > 0).Select(b => (char)b).ToArray());
- FileInfo fiDLL = new FileInfo(Path.Combine(Path.GetDirectoryName(file), sDllName));
- saDependentDLLs[saDependentDLLs.Length - 1] = fiDLL.CopyTo(Path.GetTempPath() + sDllName, true).FullName;
+ try
+ {
+ if (File.Exists(Path.Combine(Path.GetDirectoryName(file), entryDllName)))
+ {
+ FileInfo fiDLL = new FileInfo(Path.Combine(Path.GetDirectoryName(file), entryDllName));
+ dependentDlls.Add(fiDLL.CopyTo(Path.Combine(Path.GetTempPath(), entryDllName), true).FullName);
+ }
}
+ catch { }
}
- index = (int)lastPosition;
+ return dependentDlls.ToArray();
}
-
- index += 4 + 12;
- DllNameRVA = BitConverter.ToUInt32(fileContent, index);
- index += 4;
}
-
- return saDependentDLLs;
+ catch
+ {
+ return null;
+ }
}
///
@@ -140,45 +168,77 @@ namespace BurnOutSharp
///
/// Executable to attempt to run
/// Process representing the running executable, null on error
- internal static Process StartSafe(string file)
+ public static Process StartSafe(string file)
{
if (file == null || !File.Exists(file))
return null;
- Process startingprocess = new Process();
- startingprocess.StartInfo.FileName = file;
- startingprocess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
- startingprocess.StartInfo.CreateNoWindow = true;
- startingprocess.StartInfo.ErrorDialog = false;
+ // Create the process to start safely
+ Process safeProcess = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = file,
+ WindowStyle = ProcessWindowStyle.Hidden,
+ CreateNoWindow = true,
+ ErrorDialog = false,
+ }
+ };
+
+ // Try to start the process, returning the handle if we can
try
{
- startingprocess.Start();
+ safeProcess.Start();
+ return safeProcess;
}
catch
{
return null;
}
-
- return startingprocess;
}
+ ///
+ /// Read all section headers from a PE executable
+ ///
+ /// Byte array representing the executable
+ /// Pointer to the location in the array to read from
+ /// An array of section headers, null on error
private static IMAGE_SECTION_HEADER[] ReadSections(byte[] fileContent)
{
if (fileContent == null)
return null;
- uint PEHeaderOffset = BitConverter.ToUInt32(fileContent, 60);
- ushort NumberOfSections = BitConverter.ToUInt16(fileContent, (int)PEHeaderOffset + 6);
- var sections = new IMAGE_SECTION_HEADER[NumberOfSections];
- int index = (int)PEHeaderOffset + 120 + 16 * 8;
- for (int i = 0; i < NumberOfSections; i++)
+ try
{
- sections[i] = ReadSection(fileContent, index);
- }
+ unsafe
+ {
+ IMAGE_DOS_HEADER idh = IMAGE_DOS_HEADER.Deserialize(fileContent, 0);
+ IMAGE_FILE_HEADER ifh = IMAGE_FILE_HEADER.Deserialize(fileContent, idh.NewExeHeaderAddr);
- return sections;
+ var sections = new IMAGE_SECTION_HEADER[ifh.NumberOfSections];
+
+ int index = idh.NewExeHeaderAddr + Marshal.SizeOf(ifh) + ifh.SizeOfOptionalHeader;
+ for (int i = 0; i < ifh.NumberOfSections; i++)
+ {
+ sections[i] = ReadSection(fileContent, index); index += 40;
+ }
+
+ return sections;
+ }
+
+ }
+ catch
+ {
+ return null;
+ }
}
+ ///
+ /// Read a single section header from a PE executable
+ ///
+ /// Byte array representing the executable
+ /// Pointer to the location in the array to read from
+ /// Section header, null on error
private static IMAGE_SECTION_HEADER ReadSection(byte[] fileContent, int ptr)
{
try
@@ -226,20 +286,5 @@ namespace BurnOutSharp
return null;
}
}
-
- private static uint RVA2Offset(uint RVA, IMAGE_SECTION_HEADER[] sections)
- {
- for (int i = 0; i < sections.Length; i++)
- {
- if (sections[i] == null)
- continue;
-
- var section = sections[i];
- if (section.VirtualAddress <= RVA && section.VirtualAddress + section.PhysicalAddress > RVA)
- return RVA - section.VirtualAddress + section.PointerToRawData;
- }
-
- return 0;
- }
}
}
diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs
index feb0a14b..634931ce 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DATA_DIRECTORY.cs
@@ -10,6 +10,7 @@
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
+using System;
using System.IO;
using System.Runtime.InteropServices;
@@ -30,5 +31,15 @@ namespace BurnOutSharp.ExecutableType.Microsoft
return idd;
}
+
+ public static IMAGE_DATA_DIRECTORY Deserialize(byte[] content, int offset)
+ {
+ var idd = new IMAGE_DATA_DIRECTORY();
+
+ idd.VirtualAddress = BitConverter.ToUInt32(content, offset); offset += 4;
+ idd.Size = BitConverter.ToUInt32(content, offset); offset += 4;
+
+ return idd;
+ }
}
}
\ No newline at end of file
diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DOS_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DOS_HEADER.cs
index cfe4cde0..df7fdaa1 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DOS_HEADER.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_DOS_HEADER.cs
@@ -10,6 +10,7 @@
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
+using System;
using System.IO;
using System.Runtime.InteropServices;
@@ -77,5 +78,40 @@ namespace BurnOutSharp.ExecutableType.Microsoft
return idh;
}
+
+ public static IMAGE_DOS_HEADER Deserialize(byte[] content, int offset)
+ {
+ IMAGE_DOS_HEADER idh = new IMAGE_DOS_HEADER();
+
+ idh.Magic = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.LastPageBytes = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.Pages = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.Relocations = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.HeaderParagraphSize = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.MinimumExtraParagraphs = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.MaximumExtraParagraphs = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.InitialSSValue = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.InitialSPValue = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.Checksum = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.InitialIPValue = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.InitialCSValue = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.RelocationTableAddr = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.OverlayNumber = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.Reserved1 = new ushort[Constants.ERES1WDS];
+ for (int i = 0; i < Constants.ERES1WDS; i++)
+ {
+ idh.Reserved1[i] = BitConverter.ToUInt16(content, offset); offset += 2;
+ }
+ idh.OEMIdentifier = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.OEMInformation = BitConverter.ToUInt16(content, offset); offset += 2;
+ idh.Reserved2 = new ushort[Constants.ERES2WDS];
+ for (int i = 0; i < Constants.ERES2WDS; i++)
+ {
+ idh.Reserved2[i] = BitConverter.ToUInt16(content, offset); offset += 2;
+ }
+ idh.NewExeHeaderAddr = BitConverter.ToInt32(content, offset); offset += 4;
+
+ return idh;
+ }
}
}
diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs
index 291e82b1..aad994b2 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_FILE_HEADER.cs
@@ -10,6 +10,7 @@
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
+using System;
using System.IO;
using System.Runtime.InteropServices;
@@ -18,6 +19,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft
[StructLayout(LayoutKind.Sequential)]
internal class IMAGE_FILE_HEADER
{
+ public uint Signature;
public ushort Machine;
public ushort NumberOfSections;
public uint TimeDateStamp;
@@ -30,6 +32,7 @@ namespace BurnOutSharp.ExecutableType.Microsoft
{
var ifh = new IMAGE_FILE_HEADER();
+ ifh.Signature = stream.ReadUInt32();
ifh.Machine = stream.ReadUInt16();
ifh.NumberOfSections = stream.ReadUInt16();
ifh.TimeDateStamp = stream.ReadUInt32();
@@ -40,5 +43,21 @@ namespace BurnOutSharp.ExecutableType.Microsoft
return ifh;
}
+
+ public static IMAGE_FILE_HEADER Deserialize(byte[] content, int offset)
+ {
+ var ifh = new IMAGE_FILE_HEADER();
+
+ ifh.Signature = BitConverter.ToUInt32(content, offset); offset += 4;
+ ifh.Machine = BitConverter.ToUInt16(content, offset); offset += 2;
+ ifh.NumberOfSections = BitConverter.ToUInt16(content, offset); offset += 2;
+ ifh.TimeDateStamp = BitConverter.ToUInt32(content, offset); offset += 4;
+ ifh.PointerToSymbolTable = BitConverter.ToUInt32(content, offset); offset += 4;
+ ifh.NumberOfSymbols = BitConverter.ToUInt32(content, offset); offset += 4;
+ ifh.SizeOfOptionalHeader = BitConverter.ToUInt16(content, offset); offset += 2;
+ ifh.Characteristics = BitConverter.ToUInt16(content, offset); offset += 2;
+
+ return ifh;
+ }
}
}
\ No newline at end of file
diff --git a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs
index 1e5d4662..d74cfb35 100644
--- a/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs
+++ b/BurnOutSharp/ExecutableType/Microsoft/IMAGE_OPTIONAL_HEADER.cs
@@ -10,6 +10,7 @@
* http://csn.ul.ie/~caolan/pub/winresdump/winresdump/newexe.h
*/
+using System;
using System.IO;
using System.Runtime.InteropServices;
@@ -99,5 +100,49 @@ namespace BurnOutSharp.ExecutableType.Microsoft
return ioh;
}
+
+ public static IMAGE_OPTIONAL_HEADER Deserialize(byte[] content, int offset)
+ {
+ var ioh = new IMAGE_OPTIONAL_HEADER();
+
+ ioh.Magic = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.MajorLinkerVersion = content[offset]; offset++;
+ ioh.MinorLinkerVersion = content[offset]; offset++;
+ ioh.SizeOfCode = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.SizeOfInitializedData = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.SizeOfUninitializedData = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.AddressOfEntryPoint = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.BaseOfCode = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.BaseOfData = BitConverter.ToUInt32(content, offset); offset += 4;
+
+ ioh.ImageBase = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.SectionAlignment = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.FileAlignment = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.MajorOperatingSystemVersion = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.MinorOperatingSystemVersion = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.MajorImageVersion = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.MinorImageVersion = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.MajorSubsystemVersion = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.MinorSubsystemVersion = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.Reserved1 = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.SizeOfImage = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.SizeOfHeaders = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.CheckSum = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.Subsystem = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.DllCharacteristics = BitConverter.ToUInt16(content, offset); offset += 2;
+ ioh.SizeOfStackReserve = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.SizeOfStackCommit = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.SizeOfHeapReserve = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.SizeOfHeapCommit = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.LoaderFlags = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.NumberOfRvaAndSizes = BitConverter.ToUInt32(content, offset); offset += 4;
+ ioh.DataDirectory = new IMAGE_DATA_DIRECTORY[Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
+ for (int i = 0; i < Constants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
+ {
+ ioh.DataDirectory[i] = IMAGE_DATA_DIRECTORY.Deserialize(content, offset); offset += 8;
+ }
+
+ return ioh;
+ }
}
}
\ No newline at end of file
diff --git a/BurnOutSharp/ProtectionType/ProtectDisc.cs b/BurnOutSharp/ProtectionType/ProtectDisc.cs
index ab8ed2df..54565cfa 100644
--- a/BurnOutSharp/ProtectionType/ProtectDisc.cs
+++ b/BurnOutSharp/ProtectionType/ProtectDisc.cs
@@ -186,7 +186,7 @@ namespace BurnOutSharp.ProtectionType
private static string SearchProtectDiscVersion(string file, byte[] fileContent)
{
// If the file isn't executable, don't even bother
- if (!EVORE.IsEXE(fileContent))
+ if (!EVORE.IsPEExecutable(fileContent))
return string.Empty;
// Get some of the required paths
diff --git a/BurnOutSharp/ProtectionType/SafeDisc.cs b/BurnOutSharp/ProtectionType/SafeDisc.cs
index 79d4a9da..029f2920 100644
--- a/BurnOutSharp/ProtectionType/SafeDisc.cs
+++ b/BurnOutSharp/ProtectionType/SafeDisc.cs
@@ -224,7 +224,7 @@ namespace BurnOutSharp.ProtectionType
private static string SearchSafeDiscVersion(string file, byte[] fileContent)
{
// If the file isn't executable, don't even bother
- if (!EVORE.IsEXE(fileContent))
+ if (!EVORE.IsPEExecutable(fileContent))
return string.Empty;
// Get some of the required paths
diff --git a/BurnOutSharp/ProtectionType/VOBProtectCDDVD.cs b/BurnOutSharp/ProtectionType/VOBProtectCDDVD.cs
index dfc5d9a9..0fd81edd 100644
--- a/BurnOutSharp/ProtectionType/VOBProtectCDDVD.cs
+++ b/BurnOutSharp/ProtectionType/VOBProtectCDDVD.cs
@@ -110,7 +110,7 @@ namespace BurnOutSharp.ProtectionType
private static string SearchProtectDiscVersion(string file, byte[] fileContent)
{
// If the file isn't executable, don't even bother
- if (!EVORE.IsEXE(fileContent))
+ if (!EVORE.IsPEExecutable(fileContent))
return string.Empty;
// Get some of the required paths