mirror of
https://github.com/vxunderground/VXUG-Papers.git
synced 2026-06-16 15:59:25 +00:00
Add files via upload
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user