Add files via upload

This commit is contained in:
vxunderground
2020-10-11 00:53:07 -05:00
committed by GitHub
parent ad3ed5a13f
commit a6dbe47b59
24 changed files with 3744 additions and 0 deletions
@@ -0,0 +1,142 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
namespace SharpHellsGate.Module {
/// <summary>
/// Used to manipulate and extract information from a memory stream.
/// In this case the memory stream is the NTDLL module.
/// </summary>
public class MemoryUtil : IDisposable {
/// <summary>
/// The memory stream representation of the NTDLL module.
/// </summary>
protected Stream ModuleStream { get; set; }
/// <summary>
/// Dispose the memory stream when no longer needed.
/// </summary>
~MemoryUtil() => Dispose();
/// <summary>
/// Dispose the memory stream when no longer needed.
/// </summary>
public void Dispose() {
this.ModuleStream.Dispose();
this.ModuleStream.Close();
GC.SuppressFinalize(this);
}
/// <summary>
/// Extract a structure from the memory stream.
/// </summary>
/// <typeparam name="T">The Type of the structure to extract.</typeparam>
/// <param name="offset">The offset in the memory stream where the structure is located.</param>
/// <returns>The structure populated or the default structure.</returns>
protected T GetStructureFromBlob<T>(Int64 offset) where T : struct {
Span<byte> bytes = this.GetStructureBytesFromOffset<T>(offset);
if (Marshal.SizeOf<T>() != bytes.Length)
return default;
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<T>());
Marshal.Copy(bytes.ToArray(), 0, ptr, bytes.Length);
T s = Marshal.PtrToStructure<T>(ptr);
Marshal.FreeHGlobal(ptr);
return s;
}
/// <summary>
/// Extract the code from a native Windows function.
/// </summary>
/// <param name="offset">The location of the function in the memory stream.</param>
/// <returns>The 24 bytes representing the code of the function.</returns>
protected Span<byte> GetFunctionOpCode(Int64 offset) {
Span<byte> s = stackalloc byte[24];
this.ModuleStream.Seek(offset, SeekOrigin.Begin);
this.ModuleStream.Read(s);
return s.ToArray();
}
/// <summary>
/// Extract a DWORD value from the memory stream.
/// </summary>
/// <param name="offset">The location of the DWORD in the memory stream.</param>
/// <returns>The value of the DWORD.</returns>
protected UInt32 ReadPtr32(Int64 offset) {
Span<byte> s = stackalloc byte[4];
this.ModuleStream.Seek(offset, SeekOrigin.Begin);
this.ModuleStream.Read(s);
return BitConverter.ToUInt32(s);
}
/// <summary>
/// Extract a QWORD value from the memory stream.
/// </summary>
/// <param name="offset">The location of the QWORD in the memory stream.</param>
/// <returns>The value of the QWORD.</returns>
protected UInt64 ReadPtr64(Int64 offset) {
Span<byte> s = stackalloc byte[8];
this.ModuleStream.Seek(offset, SeekOrigin.Begin);
this.ModuleStream.Read(s);
return BitConverter.ToUInt64(s);
}
/// <summary>
/// Extract a WORD value from the memory stream.
/// </summary>
/// <param name="offset">The location of the WORD in the memory stream.</param>
/// <returns>The value of the WORD.</returns>
protected UInt16 ReadUShort(Int64 offset) {
Span<byte> s = stackalloc byte[2];
this.ModuleStream.Seek(offset, SeekOrigin.Begin);
this.ModuleStream.Read(s);
return BitConverter.ToUInt16(s);
}
/// <summary>
/// Extract an ASCII string from the memory stream.
/// </summary>
/// <param name="offset">The location of the ASCII string in the memory stream.</param>
/// <returns>The ASCII string.</returns>
protected string ReadAscii(Int64 offset) {
int length = 0;
this.ModuleStream.Seek(offset, SeekOrigin.Begin);
while (this.ModuleStream.ReadByte() != 0x00)
length++;
Span<byte> s = length <= 1024 ? stackalloc byte[length] : new byte[length];
this.ModuleStream.Seek(offset, SeekOrigin.Begin);
this.ModuleStream.Read(s);
return Encoding.ASCII.GetString(s);
}
/// <summary>
/// Extract the byte representation of a structure from the memory stream.
/// </summary>
/// <typeparam name="T">The Type of the structure to extract from the memory stream.</typeparam>
/// <param name="offset">The location of the structure in the memory stream.</param>
/// <returns>The structure as byte span.</returns>
protected Span<byte> GetStructureBytesFromOffset<T>(Int64 offset) where T : struct {
Span<byte> s = stackalloc byte[Marshal.SizeOf<T>()];
this.ModuleStream.Seek(offset, SeekOrigin.Begin);
this.ModuleStream.Read(s);
return s.ToArray();
}
/// <summary>
/// Get a specific amount of bytes at a specific location in the memory stream.
/// </summary>
/// <param name="offset">The location of the bytes to extract from the memory stream.</param>
/// <param name="size">The number of bytes to extract from the memory stream at a give location.</param>
/// <returns>The desired bytes as a byte span.</returns>
protected Span<byte> GetBytesFromOffset(Int64 offset, int size) {
Span<byte> s = size >= 1024 ? new byte[size] : stackalloc byte[size];
this.ModuleStream.Seek(offset, SeekOrigin.Begin);
this.ModuleStream.Read(s);
return s.ToArray();
}
}
}
@@ -0,0 +1,328 @@
using System;
using System.IO;
using SharpHellsGate.Win32;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
namespace SharpHellsGate.Module {
/// <summary>
/// Wrapper around the NTDLL module.
/// Used to extract structures and find system calls.
/// </summary>
public class SystemModule : MemoryUtil {
/// <summary>
/// IMAGE_DOS_HEADER structure of the NTDLL module.
/// </summary>
public Structures.IMAGE_DOS_HEADER ModuleDOSHeader { get; private set; }
/// <summary>
/// IMAGE_NT_HEADERS64 structure of the NTDLL module.
/// </summary>
public Structures.IMAGE_NT_HEADERS64 ModuleNTHeaders { get; private set; }
/// <summary>
/// IMAGE_SECTION_HEADER structure from the NTDLL module.
/// </summary>
public List<Structures.IMAGE_SECTION_HEADER> ModuleSectionHeaders { get; private set; }
/// <summary>
/// IMAGE_EXPORT_DIRECTORY structure from the NTDLL module.
/// </summary>
public Structures.IMAGE_EXPORT_DIRECTORY ModuleExportDirectory { get; private set; }
/// <summary>
/// Location in the memory stream of the IMAGE_EXPORT_DIRECTORY structure.
/// </summary>
public Int64 ModuleExportDirectoryOffset { get; private set; }
/// <summary>
/// Location in the memory stream of the exported functions' name.
/// </summary>
public Int64 ModuleExportDirectoryAddressNamesOffset { get; private set; }
/// <summary>
/// Location in the memory stream of the exported functions' address.
/// </summary>
public Int64 ModuleExportDirectoryAddressFunctionsOffset { get; private set; }
/// <summary>
/// Location in the memory stream of the exported functions' ordinal.
/// </summary>
public Int64 ModuleExportDirectoryAddressNameOrdinalesOffset { get; private set; }
/// <summary>
/// Name of the module. Will be NTDLL.
/// </summary>
public string ModuleName { get; private set; }
/// <summary>
/// Path of the module. Will be %WINDIR%\System32\ntdll.dll
/// </summary>
public string ModulePath { get; private set; }
/// <summary>
/// .ctor
/// </summary>
/// <param name="name">Name of the module</param>
public SystemModule(string name) : base() {
this.ModuleName = name;
this.ModulePath = $"{Environment.SystemDirectory}\\{name}";
this.ModuleSectionHeaders = new List<Structures.IMAGE_SECTION_HEADER>() { };
this.LoadModule();
}
/// <summary>
/// Load the module into a memory stream.
/// </summary>
/// <returns>Whether the loading process was a success.</returns>
public bool LoadModule() {
if (string.IsNullOrEmpty(this.ModuleName)) {
Util.LogError("Module name not provided");
return false;
}
if (!File.Exists(this.ModulePath)) {
Util.LogError($"Unable to find module: {this.ModuleName}");
return false;
}
ReadOnlySpan<byte> ModuleBlob = File.ReadAllBytes(this.ModulePath);
if (ModuleBlob.Length == 0x00) {
Util.LogError($"Empty module content: {this.ModuleName}");
return false;
}
base.ModuleStream = new MemoryStream(ModuleBlob.ToArray());
return true;
}
/// <summary>
/// Reload all structures.
/// </summary>
/// <returns>Whether all structures were successfully reloaded.</returns>
public bool LoadAllStructures() {
if (this.GetModuleDOSHeader(true).Equals(default(Structures.IMAGE_DOS_HEADER)))
return false;
if (this.GetModuleNTHeaders(true).Equals(default(Structures.IMAGE_NT_HEADERS64)))
return false;
if (this.GetModuleSectionHeaders(true).Count != this.ModuleNTHeaders.FileHeader.NumberOfSections)
return false;
if (this.GetModuleExportDirectory(true).Equals(default(Structures.IMAGE_EXPORT_DIRECTORY)))
return false;
return true;
}
/// <summary>
/// Get the _IMAGE_DOS_HEADERstructure from the module.
/// </summary>
/// <param name="ReloadCache">Whether the data has to re-processed if not already cached.</param>
/// <returns>The IMAGE_NT_HEADERS64 structure of the module.</returns>
public Structures.IMAGE_DOS_HEADER GetModuleDOSHeader(bool ReloadCache = false) {
if (!this.ModuleDOSHeader.Equals(default(Structures.IMAGE_DOS_HEADER)) && !ReloadCache)
return this.ModuleDOSHeader;
if (!base.ModuleStream.CanRead || base.ModuleStream.Length == 0x00) {
Util.LogError("Module not loaded");
return default;
}
this.ModuleDOSHeader = base.GetStructureFromBlob<Structures.IMAGE_DOS_HEADER>(0);
if (this.ModuleDOSHeader.e_magic != Macros.IMAGE_DOS_SIGNATURE) {
Util.LogError("Invalid DOS header signature");
return default;
}
return this.ModuleDOSHeader;
}
/// <summary>
/// Get the IMAGE_NT_HEADERS64 structure from the module.
/// </summary>
/// <param name="ReloadCache">Whether the data has to re-processed if not already cached.</param>
/// <returns>The IMAGE_NT_HEADERS64 structure of the module.</returns>
public Structures.IMAGE_NT_HEADERS64 GetModuleNTHeaders(bool ReloadCache = false) {
if (!this.ModuleNTHeaders.Equals(default(Structures.IMAGE_NT_HEADERS64)) && !ReloadCache)
return this.ModuleNTHeaders;
if (!base.ModuleStream.CanRead || base.ModuleStream.Length == 0x00) {
Util.LogError("Module not loaded");
return default;
}
if (this.ModuleDOSHeader.Equals(default(Structures.IMAGE_DOS_HEADER)))
this.GetModuleDOSHeader();
this.ModuleNTHeaders = base.GetStructureFromBlob<Structures.IMAGE_NT_HEADERS64>(this.ModuleDOSHeader.e_lfanew);
if (this.ModuleNTHeaders.Signature != Macros.IMAGE_NT_SIGNATURE) {
Util.LogError("Invalid NT headers signature");
return default;
}
return this.ModuleNTHeaders;
}
/// <summary>
/// Get list of _IMAGE_SECTION_HEADER structures from the module.
/// </summary>
/// <param name="ReloadCache">Whether the data has to re-processed if not already cached.</param>
/// <returns>The list of _IMAGE_SECTION_HEADER structures.</returns>
public List<Structures.IMAGE_SECTION_HEADER> GetModuleSectionHeaders(bool ReloadCache = false) {
if (this.ModuleSectionHeaders.Count == this.ModuleNTHeaders.FileHeader.NumberOfSections && !ReloadCache)
return this.ModuleSectionHeaders;
if (!base.ModuleStream.CanRead || base.ModuleStream.Length == 0x00) {
Util.LogError("Module not loaded");
return default;
}
if (this.ModuleNTHeaders.Equals(default(Structures.IMAGE_NT_HEADERS64)) || this.ModuleNTHeaders.FileHeader.Equals(default(Structures.IMAGE_FILE_HEADER)))
this.GetModuleNTHeaders();
for (Int16 cx = 0; cx < this.ModuleNTHeaders.FileHeader.NumberOfSections; cx++) {
Int64 iSectionOffset = this.GetModuleSectionOffset(cx);
Structures.IMAGE_SECTION_HEADER ImageSection = base.GetStructureFromBlob<Structures.IMAGE_SECTION_HEADER>(iSectionOffset);
if (!ImageSection.Equals(default(Structures.IMAGE_SECTION_HEADER)))
this.ModuleSectionHeaders.Add(ImageSection);
}
return this.ModuleSectionHeaders;
}
/// <summary>
/// Get a _IMAGE_SECTION_HEADER structure by name.
/// </summary>
/// <param name="name">The name of the section.</param>
/// <returns>The _IMAGE_SECTION_HEADER structure if exists.</returns>
public Structures.IMAGE_SECTION_HEADER GetModuleSectionHeaderByName(string name) {
if (name.Length > 8) {
Util.LogError("Invalid section name");
return default;
}
if (!base.ModuleStream.CanRead || base.ModuleStream.Length == 0x00) {
Util.LogError("Module not loaded");
return default;
}
if (this.ModuleSectionHeaders.Count == 0x00)
this.GetModuleSectionHeaders();
return this.ModuleSectionHeaders.Where(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
}
/// <summary>
/// Get the Export Address Table (aka EAT) from the module.
/// </summary>
/// <param name="ReloadCache">Whether the data has to re-processed if not already cached.</param>
/// <returns>the _IMAGE_EXPORT_DIRECTORY structure</returns>
public Structures.IMAGE_EXPORT_DIRECTORY GetModuleExportDirectory(bool ReloadCache = false) {
if (!this.ModuleExportDirectory.Equals(default(Structures.IMAGE_EXPORT_DIRECTORY)) && !ReloadCache)
return this.ModuleExportDirectory;
if (!base.ModuleStream.CanRead || base.ModuleStream.Length == 0x00) {
Util.LogError("Module not loaded");
return default;
}
if (this.ModuleNTHeaders.Equals(default(Structures.IMAGE_NT_HEADERS64)))
this.GetModuleNTHeaders();
if (this.ModuleSectionHeaders.Count == 0x00)
this.GetModuleSectionHeaders();
this.ModuleExportDirectoryOffset = this.ConvertRvaToOffset(this.ModuleNTHeaders.OptionalHeader.DataDirectory[0].VirtualAddress);
this.ModuleExportDirectory = base.GetStructureFromBlob<Structures.IMAGE_EXPORT_DIRECTORY>(this.ModuleExportDirectoryOffset);
if (this.ModuleExportDirectory.Equals(default(Structures.IMAGE_EXPORT_DIRECTORY))) {
Util.LogError("Invalid export address table (EAT).");
return default;
}
// Parse all functions
this.ModuleExportDirectoryAddressNamesOffset = this.ConvertRvaToOffset(this.ModuleExportDirectory.AddressOfNames);
this.ModuleExportDirectoryAddressFunctionsOffset = this.ConvertRvaToOffset(this.ModuleExportDirectory.AddressOfFunctions);
this.ModuleExportDirectoryAddressNameOrdinalesOffset = this.ConvertRvaToOffset(this.ModuleExportDirectory.AddressOfNameOrdinals);
return this.ModuleExportDirectory;
}
/// <summary>
/// Get the address, name, system call for a given function hash.
/// </summary>
/// <param name="FunctionHash">DJB2 function hash.</param>
/// <returns></returns>
public Util.APITableEntry GetAPITableEntry(UInt64 FunctionHash) {
if (this.ModuleExportDirectoryAddressNamesOffset == 0x00 || this.ModuleExportDirectoryAddressFunctionsOffset == 0x00|| this.ModuleExportDirectoryAddressNameOrdinalesOffset == 0x00)
this.GetModuleExportDirectory();
if (!base.ModuleStream.CanRead || base.ModuleStream.Length == 0x00) {
Util.LogError("Module not loaded");
return default;
}
Util.APITableEntry Entry = new Util.APITableEntry {
Hash = FunctionHash
};
for (Int32 cx = 0; cx < this.ModuleExportDirectory.NumberOfNames; cx++) {
UInt32 PtrFunctionName = base.ReadPtr32(this.ModuleExportDirectoryAddressNamesOffset + (sizeof(uint) * cx));
string FunctionName = base.ReadAscii(this.ConvertRvaToOffset(PtrFunctionName));
if (FunctionHash == Util.GetFunctionDJB2Hash(FunctionName)) {
UInt32 PtrFunctionAdddress = base.ReadPtr32(this.ModuleExportDirectoryAddressFunctionsOffset + (sizeof(uint) * (cx + 1)));
Span<byte> opcode = base.GetFunctionOpCode(this.ConvertRvaToOffset(PtrFunctionAdddress));
if (opcode[3] == 0xb8 && opcode[18] == 0x0f && opcode[19] == 0x05) {
Entry.Name = FunctionName;
Entry.Address = PtrFunctionAdddress;
Entry.Syscall = (Int16)(((byte)opcode[5] << 4) | (byte)opcode[4]);
return Entry;
}
}
}
return default;
}
/// <summary>
/// Get the offset of a _IMAGE_SECTION_HEADER structure.
/// </summary>
/// <param name="cx">The section to get.</param>
/// <returns>The _IMAGE_SECTION_HEADER structure.</returns>
private Int64 GetModuleSectionOffset(Int16 cx)
=> this.ModuleDOSHeader.e_lfanew
+ Marshal.SizeOf<Structures.IMAGE_FILE_HEADER>()
+ this.ModuleNTHeaders.FileHeader.SizeOfOptionalHeader
+ sizeof(Int32) // sizeof(DWORD)
+ (Marshal.SizeOf<Structures.IMAGE_SECTION_HEADER>() * cx);
/// <summary>
/// Convert a relative virtual address (RVA) into an offset.
/// </summary>
/// <param name="rva">The RVA to convert into an offset in the iamge.</param>
/// <param name="SectionHeader">The section in which the relative virtual address (RVA) points to.</param>
/// <returns>The offset.</returns>
private Int64 ConvertRvaToOffset(Int64 rva, Structures.IMAGE_SECTION_HEADER SectionHeader) => rva - SectionHeader.VirtualAddress + SectionHeader.PointerToRawData;
/// <summary>
/// Convert a relative virtual address (RVA) into an offset.
/// </summary>
/// <param name="rva">The RVA to convert into an offset in the iamge.</param>
/// <returns>The offset.</returns>
private Int64 ConvertRvaToOffset(Int64 rva) => this.ConvertRvaToOffset(rva, GetSectionByRVA(rva));
/// <summary>
/// Get which image section is which a relative virtual address (RVA) points to.
/// </summary>
/// <param name="rva">The RVA</param>
/// <returns>The _IMAGE_SECTION_HEADER structure</returns>
private Structures.IMAGE_SECTION_HEADER GetSectionByRVA(Int64 rva) => this.ModuleSectionHeaders.Where(x => rva > x.VirtualAddress && rva <= x.VirtualAddress + x.SizeOfRawData).First();
}
}