What I like about EVORE...

This commit is contained in:
Matt Nadareski
2021-08-25 14:23:11 -07:00
parent 958d306f42
commit 0b75c6f046
8 changed files with 250 additions and 94 deletions

View File

@@ -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
{
/// <summary>
/// Checks if the file contents that represent a PE is a DLL or an EXE
/// Checks if the file contents represent a PE executable
/// </summary>
/// <param name="fileContent">File contents to check</param>
/// <returns>True if the file is an EXE, false if it's a DLL</returns>
internal static bool IsEXE(byte[] fileContent)
/// <returns>True if the file is an EXE, false otherwise</returns>
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;
}
}
/// <summary>
/// Writes the file contents to a temporary file, if possible
/// </summary>
/// <param name="fileContent">File contents to write</param>
/// <param name="sExtension">Optional extension for the temproary file, defaults to ".exe"</param>
/// <param name="extension">Optional extension for the temporary file, defaults to ".exe"</param>
/// <returns>Name of the new temporary file, null on error</returns>
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
/// <returns>Paths for all of the copied DLLs, null on error</returns>
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<string> dependentDlls = new List<string>();
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;
}
}
/// <summary>
@@ -140,45 +168,77 @@ namespace BurnOutSharp
/// </summary>
/// <param name="file">Executable to attempt to run</param>
/// <returns>Process representing the running executable, null on error</returns>
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;
}
/// <summary>
/// Read all section headers from a PE executable
/// </summary>
/// <param name="fileContent">Byte array representing the executable</param>
/// <param name="ptr">Pointer to the location in the array to read from</param>
/// <returns>An array of section headers, null on error</returns>
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;
}
}
/// <summary>
/// Read a single section header from a PE executable
/// </summary>
/// <param name="fileContent">Byte array representing the executable</param>
/// <param name="ptr">Pointer to the location in the array to read from</param>
/// <returns>Section header, null on error</returns>
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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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