using System; using System.IO; using SharpHellsGate.Win32; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Linq; namespace SharpHellsGate.Module { /// /// Wrapper around the NTDLL module. /// Used to extract structures and find system calls. /// public class SystemModule : MemoryUtil { /// /// IMAGE_DOS_HEADER structure of the NTDLL module. /// public Structures.IMAGE_DOS_HEADER ModuleDOSHeader { get; private set; } /// /// IMAGE_NT_HEADERS64 structure of the NTDLL module. /// public Structures.IMAGE_NT_HEADERS64 ModuleNTHeaders { get; private set; } /// /// IMAGE_SECTION_HEADER structure from the NTDLL module. /// public List ModuleSectionHeaders { get; private set; } /// /// IMAGE_EXPORT_DIRECTORY structure from the NTDLL module. /// public Structures.IMAGE_EXPORT_DIRECTORY ModuleExportDirectory { get; private set; } /// /// Location in the memory stream of the IMAGE_EXPORT_DIRECTORY structure. /// public Int64 ModuleExportDirectoryOffset { get; private set; } /// /// Location in the memory stream of the exported functions' name. /// public Int64 ModuleExportDirectoryAddressNamesOffset { get; private set; } /// /// Location in the memory stream of the exported functions' address. /// public Int64 ModuleExportDirectoryAddressFunctionsOffset { get; private set; } /// /// Location in the memory stream of the exported functions' ordinal. /// public Int64 ModuleExportDirectoryAddressNameOrdinalesOffset { get; private set; } /// /// Name of the module. Will be NTDLL. /// public string ModuleName { get; private set; } /// /// Path of the module. Will be %WINDIR%\System32\ntdll.dll /// public string ModulePath { get; private set; } /// /// .ctor /// /// Name of the module public SystemModule(string name) : base() { this.ModuleName = name; this.ModulePath = $"{Environment.SystemDirectory}\\{name}"; this.ModuleSectionHeaders = new List() { }; this.LoadModule(); } /// /// Load the module into a memory stream. /// /// Whether the loading process was a success. 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 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; } /// /// Reload all structures. /// /// Whether all structures were successfully reloaded. 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; } /// /// Get the _IMAGE_DOS_HEADERstructure from the module. /// /// Whether the data has to re-processed if not already cached. /// The IMAGE_NT_HEADERS64 structure of the module. 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(0); if (this.ModuleDOSHeader.e_magic != Macros.IMAGE_DOS_SIGNATURE) { Util.LogError("Invalid DOS header signature"); return default; } return this.ModuleDOSHeader; } /// /// Get the IMAGE_NT_HEADERS64 structure from the module. /// /// Whether the data has to re-processed if not already cached. /// The IMAGE_NT_HEADERS64 structure of the module. 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(this.ModuleDOSHeader.e_lfanew); if (this.ModuleNTHeaders.Signature != Macros.IMAGE_NT_SIGNATURE) { Util.LogError("Invalid NT headers signature"); return default; } return this.ModuleNTHeaders; } /// /// Get list of _IMAGE_SECTION_HEADER structures from the module. /// /// Whether the data has to re-processed if not already cached. /// The list of _IMAGE_SECTION_HEADER structures. public List 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(iSectionOffset); if (!ImageSection.Equals(default(Structures.IMAGE_SECTION_HEADER))) this.ModuleSectionHeaders.Add(ImageSection); } return this.ModuleSectionHeaders; } /// /// Get a _IMAGE_SECTION_HEADER structure by name. /// /// The name of the section. /// The _IMAGE_SECTION_HEADER structure if exists. 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(); } /// /// Get the Export Address Table (aka EAT) from the module. /// /// Whether the data has to re-processed if not already cached. /// the _IMAGE_EXPORT_DIRECTORY structure 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(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; } /// /// Get the address, name, system call for a given function hash. /// /// DJB2 function hash. /// 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 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; } /// /// Get the offset of a _IMAGE_SECTION_HEADER structure. /// /// The section to get. /// The _IMAGE_SECTION_HEADER structure. private Int64 GetModuleSectionOffset(Int16 cx) => this.ModuleDOSHeader.e_lfanew + Marshal.SizeOf() + this.ModuleNTHeaders.FileHeader.SizeOfOptionalHeader + sizeof(Int32) // sizeof(DWORD) + (Marshal.SizeOf() * cx); /// /// Convert a relative virtual address (RVA) into an offset. /// /// The RVA to convert into an offset in the iamge. /// The section in which the relative virtual address (RVA) points to. /// The offset. private Int64 ConvertRvaToOffset(Int64 rva, Structures.IMAGE_SECTION_HEADER SectionHeader) => rva - SectionHeader.VirtualAddress + SectionHeader.PointerToRawData; /// /// Convert a relative virtual address (RVA) into an offset. /// /// The RVA to convert into an offset in the iamge. /// The offset. private Int64 ConvertRvaToOffset(Int64 rva) => this.ConvertRvaToOffset(rva, GetSectionByRVA(rva)); /// /// Get which image section is which a relative virtual address (RVA) points to. /// /// The RVA /// The _IMAGE_SECTION_HEADER structure private Structures.IMAGE_SECTION_HEADER GetSectionByRVA(Int64 rva) => this.ModuleSectionHeaders.Where(x => rva > x.VirtualAddress && rva <= x.VirtualAddress + x.SizeOfRawData).First(); } }