Skip to content

Instantly share code, notes, and snippets.

@SoftPoison
Created May 23, 2024 23:50
Show Gist options
  • Save SoftPoison/5212a67ee23fe08d0005fe601541ecd1 to your computer and use it in GitHub Desktop.
Save SoftPoison/5212a67ee23fe08d0005fe601541ecd1 to your computer and use it in GitHub Desktop.
Runs an executable in-memory
$source = @"
using System;
using System.Runtime.InteropServices;
public class InMemoryExecutable : IDisposable
{
public class DllException : Exception
{
public DllException() : base() { }
public DllException(string message) : base(message) { }
public DllException(string message, Exception innerException) : base(message, innerException) { }
}
public bool Disposed { get; private set; }
public bool IsDll { get; private set; }
IntPtr pCode = IntPtr.Zero;
IntPtr pNTHeaders = IntPtr.Zero;
IntPtr[] ImportModules;
bool _initialized = false;
DllEntryDelegate _dllEntry = null;
ExeEntryDelegate _exeEntry = null;
bool _isRelocated = false;
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate bool DllEntryDelegate(IntPtr hinstDLL, DllReason fdwReason, IntPtr lpReserved);
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate int ExeEntryDelegate();
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate void ImageTlsDelegate(IntPtr dllHandle, DllReason reason, IntPtr reserved);
/// <summary>
/// Loads a unmanged (native) DLL in the memory.
/// </summary>
/// <param name="data">Dll as a byte array</param>
public InMemoryExecutable(byte[] data)
{
Disposed = false;
if (data == null) throw new ArgumentNullException("data");
MemoryLoadLibrary(data);
}
~InMemoryExecutable()
{
Dispose();
}
/// <summary>
/// Returns a delegate for a function inside the DLL.
/// </summary>
/// <typeparam name="TDelegate">The type of the delegate.</typeparam>
/// <param name="funcName">The name of the function to be searched.</param>
/// <returns>A delegate instance of type TDelegate</returns>
public TDelegate GetDelegateFromFuncName<TDelegate>(string funcName) where TDelegate : class
{
if (!typeof(Delegate).IsAssignableFrom(typeof(TDelegate))) throw new ArgumentException(typeof(TDelegate).Name + " is not a delegate");
TDelegate res = Marshal.GetDelegateForFunctionPointer((IntPtr)GetPtrFromFuncName(funcName), typeof(TDelegate)) as TDelegate;
if (res == null) throw new DllException("Unable to get managed delegate");
return res;
}
/// <summary>
/// Returns a delegate for a function inside the DLL.
/// </summary>
/// <param name="funcName">The Name of the function to be searched.</param>
/// <param name="delegateType">The type of the delegate to be returned.</param>
/// <returns>A delegate instance that can be cast to the appropriate delegate type.</returns>
public Delegate GetDelegateFromFuncName(string funcName, Type delegateType)
{
if (delegateType == null) throw new ArgumentNullException("delegateType");
if (!typeof(Delegate).IsAssignableFrom(delegateType)) throw new ArgumentException(delegateType.Name + " is not a delegate");
Delegate res = Marshal.GetDelegateForFunctionPointer(GetPtrFromFuncName(funcName), delegateType);
if (res == null) throw new DllException("Unable to get managed delegate");
return res;
}
IntPtr GetPtrFromFuncName(string funcName)
{
if (Disposed) throw new ObjectDisposedException("InMemoryExecutable");
if (string.IsNullOrEmpty(funcName)) throw new ArgumentException("funcName");
if (!IsDll) throw new InvalidOperationException("Loaded Module is not a DLL");
if (!_initialized) throw new InvalidOperationException("Dll is not initialized");
IntPtr pDirectory = PtrAdd(pNTHeaders, Of.IMAGE_NT_HEADERS_OptionalHeader + (Is64BitProcess ? Of64.IMAGE_OPTIONAL_HEADER_ExportTable: Of32.IMAGE_OPTIONAL_HEADER_ExportTable));
IMAGE_DATA_DIRECTORY Directory = PtrRead<IMAGE_DATA_DIRECTORY>(pDirectory);
if (Directory.Size == 0) throw new DllException("Dll has no export table");
IntPtr pExports = PtrAdd(pCode, Directory.VirtualAddress);
IMAGE_EXPORT_DIRECTORY Exports = PtrRead<IMAGE_EXPORT_DIRECTORY>(pExports);
if (Exports.NumberOfFunctions == 0 || Exports.NumberOfNames == 0) throw new DllException("Dll exports no functions");
IntPtr pNameRef = PtrAdd(pCode, Exports.AddressOfNames);
IntPtr pOrdinal = PtrAdd(pCode, Exports.AddressOfNameOrdinals);
for (int i = 0; i < Exports.NumberOfNames; i++, pNameRef = PtrAdd(pNameRef, sizeof(uint)), pOrdinal = PtrAdd(pOrdinal, sizeof(ushort)))
{
uint NameRef = PtrRead<uint>(pNameRef);
ushort Ordinal = PtrRead<ushort>(pOrdinal);
string curFuncName = Marshal.PtrToStringAnsi(PtrAdd(pCode, NameRef));
if (curFuncName == funcName)
{
if (Ordinal > Exports.NumberOfFunctions) throw new DllException("Invalid function ordinal");
IntPtr pAddressOfFunction = PtrAdd(pCode, (Exports.AddressOfFunctions + (uint)(Ordinal * 4)));
return PtrAdd(pCode, PtrRead<uint>(pAddressOfFunction));
}
}
throw new DllException("Dll exports no function named " + funcName);
}
/// <summary>
/// Call entry point of executable.
/// </summary>
/// <returns>Exitcode of executable</returns>
public int MemoryCallEntryPoint()
{
if (Disposed) throw new ObjectDisposedException("InMemoryExecutable");
if (IsDll || _exeEntry == null || !_isRelocated) throw new DllException("Unable to call entry point. Is loaded module a dll?");
return _exeEntry();
}
void MemoryLoadLibrary(byte[] data)
{
if (data.Length < Marshal.SizeOf(typeof(IMAGE_DOS_HEADER))) throw new DllException("Not a valid executable file");
IMAGE_DOS_HEADER DosHeader = BytesReadStructAt<IMAGE_DOS_HEADER>(data, 0);
if (DosHeader.e_magic != Win.IMAGE_DOS_SIGNATURE) throw new BadImageFormatException("Not a valid executable file");
if (data.Length < DosHeader.e_lfanew + Marshal.SizeOf(typeof(IMAGE_NT_HEADERS))) throw new DllException("Not a valid executable file");
IMAGE_NT_HEADERS OrgNTHeaders = BytesReadStructAt<IMAGE_NT_HEADERS>(data, DosHeader.e_lfanew);
if (OrgNTHeaders.Signature != Win.IMAGE_NT_SIGNATURE) throw new BadImageFormatException("Not a valid PE file");
if (OrgNTHeaders.FileHeader.Machine != GetMachineType()) throw new BadImageFormatException("Machine type doesn't fit (i386 vs. AMD64)");
if ((OrgNTHeaders.OptionalHeader.SectionAlignment & 1) > 0) throw new BadImageFormatException("Wrong section alignment"); //Only support multiple of 2
if (OrgNTHeaders.OptionalHeader.AddressOfEntryPoint == 0) throw new DllException("Module has no entry point");
SYSTEM_INFO systemInfo;
Win.GetNativeSystemInfo(out systemInfo);
uint lastSectionEnd = 0;
int ofSection = Win.IMAGE_FIRST_SECTION(DosHeader.e_lfanew, OrgNTHeaders.FileHeader.SizeOfOptionalHeader);
for (int i = 0; i != OrgNTHeaders.FileHeader.NumberOfSections; i++, ofSection += Sz.IMAGE_SECTION_HEADER)
{
IMAGE_SECTION_HEADER Section = BytesReadStructAt<IMAGE_SECTION_HEADER>(data, ofSection);
uint endOfSection = Section.VirtualAddress + (Section.SizeOfRawData > 0 ? Section.SizeOfRawData : OrgNTHeaders.OptionalHeader.SectionAlignment);
if (endOfSection > lastSectionEnd) lastSectionEnd = endOfSection;
}
uint alignedImageSize = AlignValueUp(OrgNTHeaders.OptionalHeader.SizeOfImage, systemInfo.dwPageSize);
uint alignedLastSection = AlignValueUp(lastSectionEnd, systemInfo.dwPageSize);
if (alignedImageSize != alignedLastSection) throw new BadImageFormatException("Wrong section alignment");
IntPtr oldHeader_OptionalHeader_ImageBase;
if (Is64BitProcess) oldHeader_OptionalHeader_ImageBase = (IntPtr)unchecked((long)(OrgNTHeaders.OptionalHeader.ImageBaseLong));
else oldHeader_OptionalHeader_ImageBase = (IntPtr)unchecked((int)(OrgNTHeaders.OptionalHeader.ImageBaseLong>>32));
// reserve memory for image of library
pCode = Win.VirtualAlloc(oldHeader_OptionalHeader_ImageBase, (UIntPtr)OrgNTHeaders.OptionalHeader.SizeOfImage, AllocationType.RESERVE | AllocationType.COMMIT, MemoryProtection.READWRITE);
//pCode = IntPtr.Zero; //test relocation with this
// try to allocate memory at arbitrary position
if (pCode == IntPtr.Zero) pCode = Win.VirtualAlloc(IntPtr.Zero, (UIntPtr)OrgNTHeaders.OptionalHeader.SizeOfImage, AllocationType.RESERVE | AllocationType.COMMIT, MemoryProtection.READWRITE);
if (pCode == IntPtr.Zero) throw new DllException("Out of Memory");
if (Is64BitProcess && PtrSpanBoundary(pCode, alignedImageSize, 32))
{
// Memory block may not span 4 GB (32 bit) boundaries.
System.Collections.Generic.List<IntPtr> BlockedMemory = new System.Collections.Generic.List<IntPtr>();
while (PtrSpanBoundary(pCode, alignedImageSize, 32))
{
BlockedMemory.Add(pCode);
pCode = Win.VirtualAlloc(IntPtr.Zero, (UIntPtr)alignedImageSize, AllocationType.RESERVE | AllocationType.COMMIT, MemoryProtection.READWRITE);
if (pCode == IntPtr.Zero) break;
}
foreach (IntPtr ptr in BlockedMemory) Win.VirtualFree(ptr, IntPtr.Zero, AllocationType.RELEASE);
if (pCode == IntPtr.Zero) throw new DllException("Out of Memory");
}
// commit memory for headers
IntPtr headers = Win.VirtualAlloc(pCode, (UIntPtr)OrgNTHeaders.OptionalHeader.SizeOfHeaders, AllocationType.COMMIT, MemoryProtection.READWRITE);
if (headers == IntPtr.Zero) throw new DllException("Out of Memory");
// copy PE header to code
Marshal.Copy(data, 0, headers, (int)(OrgNTHeaders.OptionalHeader.SizeOfHeaders));
pNTHeaders = PtrAdd(headers, DosHeader.e_lfanew);
IntPtr locationDelta = PtrSub(pCode, oldHeader_OptionalHeader_ImageBase);
if (locationDelta != IntPtr.Zero)
{
// update relocated position
Marshal.OffsetOf(typeof(IMAGE_NT_HEADERS), "OptionalHeader");
Marshal.OffsetOf(typeof(IMAGE_OPTIONAL_HEADER), "ImageBaseLong");
IntPtr pImageBase = PtrAdd(pNTHeaders, Of.IMAGE_NT_HEADERS_OptionalHeader + (Is64BitProcess ? Of64.IMAGE_OPTIONAL_HEADER_ImageBase : Of32.IMAGE_OPTIONAL_HEADER_ImageBase));
PtrWrite(pImageBase, pCode);
}
// copy sections from DLL file block to new memory location
CopySections(ref OrgNTHeaders, pCode, pNTHeaders, data);
// adjust base address of imported data
_isRelocated = (locationDelta != IntPtr.Zero ? PerformBaseRelocation(ref OrgNTHeaders, pCode, locationDelta) : true);
// load required dlls and adjust function table of imports
ImportModules = BuildImportTable(ref OrgNTHeaders, pCode);
// mark memory pages depending on section headers and release
// sections that are marked as "discardable"
FinalizeSections(ref OrgNTHeaders, pCode, pNTHeaders, systemInfo.dwPageSize);
// TLS callbacks are executed BEFORE the main loading
ExecuteTLS(ref OrgNTHeaders, pCode, pNTHeaders);
// get entry point of loaded library
IsDll = ((OrgNTHeaders.FileHeader.Characteristics & Win.IMAGE_FILE_DLL) != 0);
if (OrgNTHeaders.OptionalHeader.AddressOfEntryPoint != 0)
{
if (IsDll)
{
// notify library about attaching to process
IntPtr dllEntryPtr = PtrAdd(pCode, OrgNTHeaders.OptionalHeader.AddressOfEntryPoint);
_dllEntry = (DllEntryDelegate)Marshal.GetDelegateForFunctionPointer(dllEntryPtr, typeof(DllEntryDelegate));
_initialized = (_dllEntry != null && _dllEntry(pCode, DllReason.DLL_PROCESS_ATTACH, IntPtr.Zero));
if (!_initialized) throw new DllException("Can't attach DLL to process");
}
else
{
IntPtr exeEntryPtr = PtrAdd(pCode, OrgNTHeaders.OptionalHeader.AddressOfEntryPoint);
_exeEntry = (ExeEntryDelegate)Marshal.GetDelegateForFunctionPointer(exeEntryPtr, typeof(ExeEntryDelegate));
}
}
}
static void CopySections(ref IMAGE_NT_HEADERS OrgNTHeaders, IntPtr pCode, IntPtr pNTHeaders, byte[] data)
{
IntPtr pSection = Win.IMAGE_FIRST_SECTION(pNTHeaders, OrgNTHeaders.FileHeader.SizeOfOptionalHeader);
for (int i = 0; i < OrgNTHeaders.FileHeader.NumberOfSections; i++, pSection = PtrAdd(pSection, Sz.IMAGE_SECTION_HEADER))
{
IMAGE_SECTION_HEADER Section = PtrRead<IMAGE_SECTION_HEADER>(pSection);
if (Section.SizeOfRawData == 0)
{
// section doesn't contain data in the dll itself, but may define uninitialized data
uint size = OrgNTHeaders.OptionalHeader.SectionAlignment;
if (size > 0)
{
IntPtr dest = Win.VirtualAlloc(PtrAdd(pCode, Section.VirtualAddress), (UIntPtr)size, AllocationType.COMMIT, MemoryProtection.READWRITE);
if (dest == IntPtr.Zero) throw new DllException("Unable to allocate memory");
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
dest = PtrAdd(pCode, Section.VirtualAddress);
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
PtrWrite(PtrAdd(pSection, Of.IMAGE_SECTION_HEADER_PhysicalAddress), unchecked((uint)(ulong)(long)dest));
Win.MemSet(dest, 0, (UIntPtr)size);
}
// section is empty
continue;
}
else
{
// commit memory block and copy data from dll
IntPtr dest = Win.VirtualAlloc(PtrAdd(pCode, Section.VirtualAddress), (UIntPtr)Section.SizeOfRawData, AllocationType.COMMIT, MemoryProtection.READWRITE);
if (dest == IntPtr.Zero) throw new DllException("Out of memory");
// Always use position from file to support alignments smaller than page size (allocation above will align to page size).
dest = PtrAdd(pCode, Section.VirtualAddress);
Marshal.Copy(data, checked((int)Section.PointerToRawData), dest, checked((int)Section.SizeOfRawData));
// NOTE: On 64bit systems we truncate to 32bit here but expand again later when "PhysicalAddress" is used.
PtrWrite(PtrAdd(pSection, Of.IMAGE_SECTION_HEADER_PhysicalAddress), unchecked((uint)(ulong)(long)dest));
}
}
}
static bool PerformBaseRelocation(ref IMAGE_NT_HEADERS OrgNTHeaders, IntPtr pCode, IntPtr delta)
{
if (OrgNTHeaders.OptionalHeader.BaseRelocationTable.Size == 0) return (delta == IntPtr.Zero);
for (IntPtr pRelocation = PtrAdd(pCode, OrgNTHeaders.OptionalHeader.BaseRelocationTable.VirtualAddress);;)
{
IMAGE_BASE_RELOCATION Relocation = PtrRead<IMAGE_BASE_RELOCATION>(pRelocation);
if (Relocation.VirtualAdress == 0) break;
IntPtr pDest = PtrAdd(pCode, Relocation.VirtualAdress);
IntPtr pRelInfo = PtrAdd(pRelocation, Sz.IMAGE_BASE_RELOCATION);
uint RelCount = ((Relocation.SizeOfBlock - Sz.IMAGE_BASE_RELOCATION) / 2);
for (uint i = 0; i != RelCount ; i++, pRelInfo = PtrAdd(pRelInfo, sizeof(ushort)))
{
ushort relInfo = (ushort)Marshal.PtrToStructure(pRelInfo, typeof(ushort));
BasedRelocationType type = (BasedRelocationType)(relInfo >> 12); // the upper 4 bits define the type of relocation
int offset = (relInfo & 0xfff); // the lower 12 bits define the offset
IntPtr pPatchAddr = PtrAdd(pDest, offset);
switch (type)
{
case BasedRelocationType.IMAGE_REL_BASED_ABSOLUTE:
// skip relocation
break;
case BasedRelocationType.IMAGE_REL_BASED_HIGHLOW:
// change complete 32 bit address
int patchAddrHL = (int)Marshal.PtrToStructure(pPatchAddr, typeof(int));
patchAddrHL += (int)delta;
Marshal.StructureToPtr(patchAddrHL, pPatchAddr, false);
break;
case BasedRelocationType.IMAGE_REL_BASED_DIR64:
long patchAddr64 = (long)Marshal.PtrToStructure(pPatchAddr, typeof(long));
patchAddr64 += (long)delta;
Marshal.StructureToPtr(patchAddr64, pPatchAddr, false);
break;
}
}
// advance to next relocation block
pRelocation = PtrAdd(pRelocation, Relocation.SizeOfBlock);
}
return true;
}
static IntPtr[] BuildImportTable(ref IMAGE_NT_HEADERS OrgNTHeaders, IntPtr pCode)
{
System.Collections.Generic.List<IntPtr> ImportModules = new System.Collections.Generic.List<IntPtr>();
uint NumEntries = OrgNTHeaders.OptionalHeader.ImportTable.Size / Sz.IMAGE_IMPORT_DESCRIPTOR;
IntPtr pImportDesc = PtrAdd(pCode, OrgNTHeaders.OptionalHeader.ImportTable.VirtualAddress);
for (uint i = 0; i != NumEntries; i++, pImportDesc = PtrAdd(pImportDesc, Sz.IMAGE_IMPORT_DESCRIPTOR))
{
IMAGE_IMPORT_DESCRIPTOR ImportDesc = PtrRead<IMAGE_IMPORT_DESCRIPTOR>(pImportDesc);
if (ImportDesc.Name == 0) break;
IntPtr handle = Win.LoadLibrary(PtrAdd(pCode, ImportDesc.Name));
if (PtrIsInvalidHandle(handle))
{
foreach (IntPtr m in ImportModules) Win.FreeLibrary(m);
ImportModules.Clear();
throw new DllException("Can't load libary " + Marshal.PtrToStringAnsi(PtrAdd(pCode, ImportDesc.Name)));
}
ImportModules.Add(handle);
IntPtr pThunkRef, pFuncRef;
if (ImportDesc.OriginalFirstThunk > 0)
{
pThunkRef = PtrAdd(pCode, ImportDesc.OriginalFirstThunk);
pFuncRef = PtrAdd(pCode, ImportDesc.FirstThunk);
}
else
{
// no hint table
pThunkRef = PtrAdd(pCode, ImportDesc.FirstThunk);
pFuncRef = PtrAdd(pCode, ImportDesc.FirstThunk);
}
for (int SzRef = IntPtr.Size; ; pThunkRef = PtrAdd(pThunkRef, SzRef), pFuncRef = PtrAdd(pFuncRef, SzRef))
{
IntPtr ReadThunkRef = PtrRead<IntPtr>(pThunkRef), WriteFuncRef;
if (ReadThunkRef == IntPtr.Zero) break;
if (Win.IMAGE_SNAP_BY_ORDINAL(ReadThunkRef))
{
WriteFuncRef = Win.GetProcAddress(handle, Win.IMAGE_ORDINAL(ReadThunkRef));
}
else
{
WriteFuncRef = Win.GetProcAddress(handle, PtrAdd(PtrAdd(pCode, ReadThunkRef), Of.IMAGE_IMPORT_BY_NAME_Name));
}
if (WriteFuncRef == IntPtr.Zero) throw new DllException("Can't get adress for imported function");
PtrWrite(pFuncRef, WriteFuncRef);
}
}
return (ImportModules.Count > 0 ? ImportModules.ToArray() : null);
}
static void FinalizeSections(ref IMAGE_NT_HEADERS OrgNTHeaders, IntPtr pCode, IntPtr pNTHeaders, uint PageSize)
{
UIntPtr imageOffset = (Is64BitProcess ? (UIntPtr)(unchecked((ulong)pCode.ToInt64()) & 0xffffffff00000000) : UIntPtr.Zero);
IntPtr pSection = Win.IMAGE_FIRST_SECTION(pNTHeaders, OrgNTHeaders.FileHeader.SizeOfOptionalHeader);
IMAGE_SECTION_HEADER Section = PtrRead<IMAGE_SECTION_HEADER>(pSection);
SectionFinalizeData sectionData = new SectionFinalizeData();
sectionData.Address = PtrBitOr(PtrAdd((IntPtr)0, Section.PhysicalAddress), imageOffset);
sectionData.AlignedAddress = PtrAlignDown(sectionData.Address, (UIntPtr)PageSize);
sectionData.Size = GetRealSectionSize(ref Section, ref OrgNTHeaders);
sectionData.Characteristics = Section.Characteristics;
sectionData.Last = false;
pSection = PtrAdd(pSection, Sz.IMAGE_SECTION_HEADER);
// loop through all sections and change access flags
for (int i = 1; i < OrgNTHeaders.FileHeader.NumberOfSections; i++, pSection = PtrAdd(pSection, Sz.IMAGE_SECTION_HEADER))
{
Section = PtrRead<IMAGE_SECTION_HEADER>(pSection);
IntPtr sectionAddress = PtrBitOr(PtrAdd((IntPtr)0, Section.PhysicalAddress), imageOffset);
IntPtr alignedAddress = PtrAlignDown(sectionAddress, (UIntPtr)PageSize);
IntPtr sectionSize = GetRealSectionSize(ref Section, ref OrgNTHeaders);
// Combine access flags of all sections that share a page
// TODO(fancycode): We currently share flags of a trailing large section with the page of a first small section. This should be optimized.
IntPtr a = PtrAdd(sectionData.Address, sectionData.Size);
ulong b = unchecked((ulong)a.ToInt64()), c = unchecked((ulong)alignedAddress);
if (sectionData.AlignedAddress == alignedAddress || unchecked((ulong)PtrAdd(sectionData.Address, sectionData.Size).ToInt64()) > unchecked((ulong)alignedAddress))
{
// Section shares page with previous
if ((Section.Characteristics & Win.IMAGE_SCN_MEM_DISCARDABLE) == 0 || (sectionData.Characteristics & Win.IMAGE_SCN_MEM_DISCARDABLE) == 0)
{
sectionData.Characteristics = (sectionData.Characteristics | Section.Characteristics) & ~Win.IMAGE_SCN_MEM_DISCARDABLE;
}
else
{
sectionData.Characteristics |= Section.Characteristics;
}
sectionData.Size = PtrSub(PtrAdd(sectionAddress, sectionSize), sectionData.Address);
continue;
}
FinalizeSection(sectionData, PageSize, OrgNTHeaders.OptionalHeader.SectionAlignment);
sectionData.Address = sectionAddress;
sectionData.AlignedAddress = alignedAddress;
sectionData.Size = sectionSize;
sectionData.Characteristics = Section.Characteristics;
}
sectionData.Last = true;
FinalizeSection(sectionData, PageSize, OrgNTHeaders.OptionalHeader.SectionAlignment);
}
static void FinalizeSection(SectionFinalizeData SectionData, uint PageSize, uint SectionAlignment)
{
if (SectionData.Size == IntPtr.Zero)
return;
if ((SectionData.Characteristics & Win.IMAGE_SCN_MEM_DISCARDABLE) > 0)
{
// section is not needed any more and can safely be freed
if (SectionData.Address == SectionData.AlignedAddress &&
(SectionData.Last ||
SectionAlignment == PageSize ||
(unchecked((ulong)SectionData.Size.ToInt64()) % PageSize) == 0)
)
{
// Only allowed to decommit whole pages
Win.VirtualFree(SectionData.Address, SectionData.Size, AllocationType.DECOMMIT);
}
return;
}
// determine protection flags based on characteristics
int readable = (SectionData.Characteristics & (uint)ImageSectionFlags.IMAGE_SCN_MEM_READ) != 0 ? 1 : 0;
int writeable = (SectionData.Characteristics & (uint)ImageSectionFlags.IMAGE_SCN_MEM_WRITE) != 0 ? 1 : 0;
int executable = (SectionData.Characteristics & (uint)ImageSectionFlags.IMAGE_SCN_MEM_EXECUTE) != 0 ? 1 : 0;
uint protect = (uint)ProtectionFlags[executable, readable, writeable];
if ((SectionData.Characteristics & Win.IMAGE_SCN_MEM_NOT_CACHED) > 0) protect |= Win.PAGE_NOCACHE;
// change memory access flags
uint oldProtect;
if (!Win.VirtualProtect(SectionData.Address, SectionData.Size, protect, out oldProtect))
throw new DllException("Error protecting memory page");
}
static void ExecuteTLS(ref IMAGE_NT_HEADERS OrgNTHeaders, IntPtr pCode, IntPtr pNTHeaders)
{
if (OrgNTHeaders.OptionalHeader.TLSTable.VirtualAddress == 0) return;
IMAGE_TLS_DIRECTORY tlsDir = PtrRead<IMAGE_TLS_DIRECTORY>(PtrAdd(pCode, OrgNTHeaders.OptionalHeader.TLSTable.VirtualAddress));
IntPtr pCallBack = tlsDir.AddressOfCallBacks;
if (pCallBack != IntPtr.Zero)
{
for (IntPtr Callback; (Callback = PtrRead<IntPtr>(pCallBack)) != IntPtr.Zero; pCallBack = PtrAdd(pCallBack, IntPtr.Size))
{
ImageTlsDelegate tls = (ImageTlsDelegate)Marshal.GetDelegateForFunctionPointer(Callback, typeof(ImageTlsDelegate));
tls(pCode, DllReason.DLL_PROCESS_ATTACH, IntPtr.Zero);
}
}
}
/// <summary>
/// Check if the process runs in 64bit mode or in 32bit mode
/// </summary>
/// <returns>True if process is 64bit, false if it is 32bit</returns>
public static bool Is64BitProcess { get { return IntPtr.Size == 8; } }
static uint GetMachineType() { return (IntPtr.Size == 8 ? Win.IMAGE_FILE_MACHINE_AMD64 : Win.IMAGE_FILE_MACHINE_I386); }
static uint AlignValueUp(uint value, uint alignment) { return (value + alignment - 1) & ~(alignment - 1); }
static IntPtr GetRealSectionSize(ref IMAGE_SECTION_HEADER Section, ref IMAGE_NT_HEADERS NTHeaders)
{
uint size = Section.SizeOfRawData;
if (size == 0)
{
if ((Section.Characteristics & Win.IMAGE_SCN_CNT_INITIALIZED_DATA) > 0)
{
size = NTHeaders.OptionalHeader.SizeOfInitializedData;
}
else if ((Section.Characteristics & Win.IMAGE_SCN_CNT_UNINITIALIZED_DATA) > 0)
{
size = NTHeaders.OptionalHeader.SizeOfUninitializedData;
}
}
return (IntPtr.Size == 8 ? (IntPtr)unchecked((long)size) : (IntPtr)unchecked((int)size));
}
public void Close() { ((IDisposable)this).Dispose(); }
void IDisposable.Dispose()
{
Dispose();
GC.SuppressFinalize(this);
}
public void Dispose()
{
if (_initialized)
{
if (_dllEntry != null) _dllEntry.Invoke(pCode, DllReason.DLL_PROCESS_DETACH, IntPtr.Zero);
_initialized = false;
}
if (ImportModules != null)
{
foreach (IntPtr m in ImportModules) if (!PtrIsInvalidHandle(m)) Win.FreeLibrary(m);
ImportModules = null;
}
if (pCode != IntPtr.Zero)
{
Win.VirtualFree(pCode, IntPtr.Zero, AllocationType.RELEASE);
pCode = IntPtr.Zero;
pNTHeaders = IntPtr.Zero;
}
Disposed = true;
}
// Protection flags for memory pages (Executable, Readable, Writeable)
static readonly PageProtection[,,] ProtectionFlags = new PageProtection[2,2,2]
{
{
// not executable
{ PageProtection.NOACCESS, PageProtection.WRITECOPY },
{ PageProtection.READONLY, PageProtection.READWRITE }
},
{
// executable
{ PageProtection.EXECUTE, PageProtection.EXECUTE_WRITECOPY },
{ PageProtection.EXECUTE_READ, PageProtection.EXECUTE_READWRITE }
}
};
struct SectionFinalizeData
{
internal IntPtr Address;
internal IntPtr AlignedAddress;
internal IntPtr Size;
internal uint Characteristics;
internal bool Last;
}
class Of
{
internal const int IMAGE_NT_HEADERS_OptionalHeader = 24;
internal const int IMAGE_SECTION_HEADER_PhysicalAddress = 8;
internal const int IMAGE_IMPORT_BY_NAME_Name = 2;
}
class Of32
{
internal const int IMAGE_OPTIONAL_HEADER_ImageBase = 28;
internal const int IMAGE_OPTIONAL_HEADER_ExportTable = 96;
}
class Of64
{
internal const int IMAGE_OPTIONAL_HEADER_ImageBase = 24;
internal const int IMAGE_OPTIONAL_HEADER_ExportTable = 112;
}
class Sz
{
internal const int IMAGE_SECTION_HEADER = 40;
internal const int IMAGE_BASE_RELOCATION = 8;
internal const int IMAGE_IMPORT_DESCRIPTOR = 20;
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_DOS_HEADER
{
public ushort e_magic; // Magic number
public ushort e_cblp; // Bytes on last page of file
public ushort e_cp; // Pages in file
public ushort e_crlc; // Relocations
public ushort e_cparhdr; // Size of header in paragraphs
public ushort e_minalloc; // Minimum extra paragraphs needed
public ushort e_maxalloc; // Maximum extra paragraphs needed
public ushort e_ss; // Initial (relative) SS value
public ushort e_sp; // Initial SP value
public ushort e_csum; // Checksum
public ushort e_ip; // Initial IP value
public ushort e_cs; // Initial (relative) CS value
public ushort e_lfarlc; // File address of relocation table
public ushort e_ovno; // Overlay number
public ushort e_res1a,e_res1b,e_res1c,e_res1d; // Reserved words
public ushort e_oemid; // OEM identifier (for e_oeminfo)
public ushort e_oeminfo; // OEM information; e_oemid specific
public ushort e_res2a,e_res2b,e_res2c,e_res2d,e_res2e,e_res2f,e_res2g,e_res2h,e_res2i,e_res2j; // Reserved words
public int e_lfanew; // File address of new exe header
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_NT_HEADERS
{
public uint Signature;
public IMAGE_FILE_HEADER FileHeader;
public IMAGE_OPTIONAL_HEADER OptionalHeader;
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_FILE_HEADER
{
public ushort Machine;
public ushort NumberOfSections;
public uint TimeDateStamp;
public uint PointerToSymbolTable;
public uint NumberOfSymbols;
public ushort SizeOfOptionalHeader;
public ushort Characteristics;
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_OPTIONAL_HEADER
{
public MagicType Magic;
public byte MajorLinkerVersion;
public byte MinorLinkerVersion;
public uint SizeOfCode;
public uint SizeOfInitializedData;
public uint SizeOfUninitializedData;
public uint AddressOfEntryPoint;
public uint BaseOfCode;
public ulong ImageBaseLong;
public uint SectionAlignment;
public uint FileAlignment;
public ushort MajorOperatingSystemVersion;
public ushort MinorOperatingSystemVersion;
public ushort MajorImageVersion;
public ushort MinorImageVersion;
public ushort MajorSubsystemVersion;
public ushort MinorSubsystemVersion;
public uint Win32VersionValue;
public uint SizeOfImage;
public uint SizeOfHeaders;
public uint CheckSum;
public SubSystemType Subsystem;
public DllCharacteristicsType DllCharacteristics;
public IntPtr SizeOfStackReserve;
public IntPtr SizeOfStackCommit;
public IntPtr SizeOfHeapReserve;
public IntPtr SizeOfHeapCommit;
public uint LoaderFlags;
public uint NumberOfRvaAndSizes;
public IMAGE_DATA_DIRECTORY ExportTable;
public IMAGE_DATA_DIRECTORY ImportTable;
public IMAGE_DATA_DIRECTORY ResourceTable;
public IMAGE_DATA_DIRECTORY ExceptionTable;
public IMAGE_DATA_DIRECTORY CertificateTable;
public IMAGE_DATA_DIRECTORY BaseRelocationTable;
public IMAGE_DATA_DIRECTORY Debug;
public IMAGE_DATA_DIRECTORY Architecture;
public IMAGE_DATA_DIRECTORY GlobalPtr;
public IMAGE_DATA_DIRECTORY TLSTable;
public IMAGE_DATA_DIRECTORY LoadConfigTable;
public IMAGE_DATA_DIRECTORY BoundImport;
public IMAGE_DATA_DIRECTORY IAT;
public IMAGE_DATA_DIRECTORY DelayImportDescriptor;
public IMAGE_DATA_DIRECTORY CLRRuntimeHeader;
public IMAGE_DATA_DIRECTORY Reserved;
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_DATA_DIRECTORY
{
public uint VirtualAddress;
public uint Size;
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_SECTION_HEADER
{
public ulong Name; //8 byte string
public uint PhysicalAddress;
public uint VirtualAddress;
public uint SizeOfRawData;
public uint PointerToRawData;
public uint PointerToRelocations;
public uint PointerToLinenumbers;
public ushort NumberOfRelocations;
public ushort NumberOfLinenumbers;
public uint Characteristics;
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_BASE_RELOCATION
{
public uint VirtualAdress;
public uint SizeOfBlock;
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_IMPORT_DESCRIPTOR
{
public uint OriginalFirstThunk;
public uint TimeDateStamp;
public uint ForwarderChain;
public uint Name;
public uint FirstThunk;
}
[StructLayout(LayoutKind.Sequential)] struct IMAGE_EXPORT_DIRECTORY
{
public uint Characteristics;
public uint TimeDateStamp;
public ushort MajorVersion;
public ushort MinorVersion;
public uint Name;
public uint Base;
public uint NumberOfFunctions;
public uint NumberOfNames;
public uint AddressOfFunctions; // RVA from base of image
public uint AddressOfNames; // RVA from base of image
public uint AddressOfNameOrdinals; // RVA from base of image
}
[StructLayout(LayoutKind.Sequential)] struct SYSTEM_INFO
{
public ushort wProcessorArchitecture;
public ushort wReserved;
public uint dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public IntPtr lpMaximumApplicationAddress;
public IntPtr dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public ushort wProcessorLevel;
public ushort wProcessorRevision;
};
[StructLayout(LayoutKind.Sequential)] struct IMAGE_TLS_DIRECTORY
{
public IntPtr StartAddressOfRawData;
public IntPtr EndAddressOfRawData;
public IntPtr AddressOfIndex;
public IntPtr AddressOfCallBacks;
public IntPtr SizeOfZeroFill;
public uint Characteristics;
}
enum MagicType : ushort
{
IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b,
IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
}
enum SubSystemType : ushort
{
IMAGE_SUBSYSTEM_UNKNOWN = 0,
IMAGE_SUBSYSTEM_NATIVE = 1,
IMAGE_SUBSYSTEM_WINDOWS_GUI = 2,
IMAGE_SUBSYSTEM_WINDOWS_CUI = 3,
IMAGE_SUBSYSTEM_POSIX_CUI = 7,
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI = 9,
IMAGE_SUBSYSTEM_EFI_APPLICATION = 10,
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11,
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER = 12,
IMAGE_SUBSYSTEM_EFI_ROM = 13,
IMAGE_SUBSYSTEM_XBOX = 14
}
enum DllCharacteristicsType : ushort
{
RES_0 = 0x0001,
RES_1 = 0x0002,
RES_2 = 0x0004,
RES_3 = 0x0008,
IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040,
IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY = 0x0080,
IMAGE_DLL_CHARACTERISTICS_NX_COMPAT = 0x0100,
IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200,
IMAGE_DLLCHARACTERISTICS_NO_SEH = 0x0400,
IMAGE_DLLCHARACTERISTICS_NO_BIND = 0x0800,
RES_4 = 0x1000,
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER = 0x2000,
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000
}
enum BasedRelocationType
{
IMAGE_REL_BASED_ABSOLUTE = 0,
IMAGE_REL_BASED_HIGH = 1,
IMAGE_REL_BASED_LOW = 2,
IMAGE_REL_BASED_HIGHLOW = 3,
IMAGE_REL_BASED_HIGHADJ = 4,
IMAGE_REL_BASED_MIPS_JMPADDR = 5,
IMAGE_REL_BASED_MIPS_JMPADDR16 = 9,
IMAGE_REL_BASED_IA64_IMM64 = 9,
IMAGE_REL_BASED_DIR64 = 10
}
enum AllocationType : uint
{
COMMIT = 0x1000,
RESERVE = 0x2000,
RESET = 0x80000,
LARGE_PAGES = 0x20000000,
PHYSICAL = 0x400000,
TOP_DOWN = 0x100000,
WRITE_WATCH = 0x200000,
DECOMMIT = 0x4000,
RELEASE = 0x8000
}
enum MemoryProtection : uint
{
EXECUTE = 0x10,
EXECUTE_READ = 0x20,
EXECUTE_READWRITE = 0x40,
EXECUTE_WRITECOPY = 0x80,
NOACCESS = 0x01,
READONLY = 0x02,
READWRITE = 0x04,
WRITECOPY = 0x08,
GUARD_Modifierflag = 0x100,
NOCACHE_Modifierflag = 0x200,
WRITECOMBINE_Modifierflag = 0x400
}
enum PageProtection
{
NOACCESS = 0x01,
READONLY = 0x02,
READWRITE = 0x04,
WRITECOPY = 0x08,
EXECUTE = 0x10,
EXECUTE_READ = 0x20,
EXECUTE_READWRITE = 0x40,
EXECUTE_WRITECOPY = 0x80,
GUARD = 0x100,
NOCACHE = 0x200,
WRITECOMBINE = 0x400,
}
enum ImageSectionFlags : uint
{
IMAGE_SCN_LNK_NRELOC_OVFL = 0x01000000, // Section contains extended relocations.
IMAGE_SCN_MEM_DISCARDABLE = 0x02000000, // Section can be discarded.
IMAGE_SCN_MEM_NOT_CACHED = 0x04000000, // Section is not cachable.
IMAGE_SCN_MEM_NOT_PAGED = 0x08000000, // Section is not pageable.
IMAGE_SCN_MEM_SHARED = 0x10000000, // Section is shareable.
IMAGE_SCN_MEM_EXECUTE = 0x20000000, // Section is executable.
IMAGE_SCN_MEM_READ = 0x40000000, // Section is readable.
IMAGE_SCN_MEM_WRITE = 0x80000000 // Section is writeable.
}
enum DllReason : uint
{
DLL_PROCESS_ATTACH = 1,
DLL_THREAD_ATTACH = 2,
DLL_THREAD_DETACH = 3,
DLL_PROCESS_DETACH = 0
}
class Win
{
public const ushort IMAGE_DOS_SIGNATURE = 0x5A4D;
public const uint IMAGE_NT_SIGNATURE = 0x00004550;
public const uint IMAGE_FILE_MACHINE_I386 = 0x014c;
public const uint IMAGE_FILE_MACHINE_AMD64 = 0x8664;
public const uint PAGE_NOCACHE = 0x200;
public const uint IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040;
public const uint IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080;
public const uint IMAGE_SCN_MEM_DISCARDABLE = 0x02000000;
public const uint IMAGE_SCN_MEM_NOT_CACHED = 0x04000000;
public const uint IMAGE_FILE_DLL = 0x2000;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, UIntPtr count);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr LoadLibrary(IntPtr lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, IntPtr procName);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, AllocationType dwFreeType);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern void GetNativeSystemInfo(out SYSTEM_INFO lpSystemInfo);
// Equivalent to the IMAGE_FIRST_SECTION macro
public static IntPtr IMAGE_FIRST_SECTION(IntPtr pNTHeader, ushort ntheader_FileHeader_SizeOfOptionalHeader)
{
return PtrAdd(pNTHeader, Of.IMAGE_NT_HEADERS_OptionalHeader + (int)ntheader_FileHeader_SizeOfOptionalHeader);
}
// Equivalent to the IMAGE_FIRST_SECTION macro
public static int IMAGE_FIRST_SECTION(int lfanew, ushort ntheader_FileHeader_SizeOfOptionalHeader)
{
return lfanew + Of.IMAGE_NT_HEADERS_OptionalHeader + ntheader_FileHeader_SizeOfOptionalHeader;
}
// Equivalent to the IMAGE_ORDINAL32/64 macros
public static IntPtr IMAGE_ORDINAL(IntPtr ordinal)
{
return (IntPtr)(int)(unchecked((ulong)ordinal.ToInt64()) & 0xffff);
}
// Equivalent to the IMAGE_SNAP_BY_ORDINAL32/64 macro
public static bool IMAGE_SNAP_BY_ORDINAL(IntPtr ordinal)
{
return (IntPtr.Size == 8 ? (ordinal.ToInt64() < 0) : (ordinal.ToInt32() < 0));
}
}
static T PtrRead<T>(IntPtr ptr) { return (T)Marshal.PtrToStructure(ptr, typeof(T)); }
static void PtrWrite<T>(IntPtr ptr, T val) { Marshal.StructureToPtr(val, ptr, false); }
static IntPtr PtrAdd(IntPtr p, int v) { return (IntPtr)(p.ToInt64() + v); }
static IntPtr PtrAdd(IntPtr p, uint v) { return (IntPtr.Size == 8 ? (IntPtr)(p.ToInt64() + unchecked((long)v)) : (IntPtr)(p.ToInt32() + unchecked((int)v))); }
static IntPtr PtrAdd(IntPtr p, IntPtr v) { return (IntPtr.Size == 8 ? (IntPtr)(p.ToInt64() + v.ToInt64()) : (IntPtr)(p.ToInt32() + v.ToInt32())); }
static IntPtr PtrAdd(IntPtr p, UIntPtr v) { return (IntPtr.Size == 8 ? (IntPtr)(p.ToInt64() + unchecked((long)v.ToUInt64())) : (IntPtr)(p.ToInt32() + unchecked((int)v.ToUInt32()))); }
static IntPtr PtrSub(IntPtr p, IntPtr v) { return (IntPtr.Size == 8 ? (IntPtr)(p.ToInt64() - v.ToInt64()) : (IntPtr)(p.ToInt32() - v.ToInt32())); }
static IntPtr PtrBitOr(IntPtr p, UIntPtr v) { return (IntPtr.Size == 8 ? (IntPtr)unchecked((long)(unchecked((ulong)p.ToInt64()) | v.ToUInt64())) : (IntPtr)unchecked((int)(unchecked((uint)p.ToInt32()) | v.ToUInt32()))); }
static IntPtr PtrAlignDown(IntPtr p, UIntPtr align) { return (IntPtr)unchecked((long)(unchecked((ulong)p.ToInt64()) & ~(align.ToUInt64() - 1))); }
static bool PtrIsInvalidHandle(IntPtr h) { return (h == IntPtr.Zero || h == (IntPtr.Size == 8 ? (IntPtr)(long)-1 : (IntPtr)(int)-1)); }
static bool PtrSpanBoundary(IntPtr p, uint Size, int BoundaryBits) { return ((unchecked((ulong)p.ToInt64()) >> BoundaryBits) < ((unchecked((ulong)(p.ToInt64())) + Size) >> BoundaryBits)); }
static T BytesReadStructAt<T>(byte[] buf, int offset)
{
int size = Marshal.SizeOf(typeof(T));
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(buf, offset, ptr, size);
T res = (T)Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return res;
}
}
"@
Add-Type -TypeDefinition $source
$exe = New-Object InMemoryExecutable -ArgumentList (,$bytes)
$path = 'C:\path\to\executable.exe'
$bytes = [System.IO.File]::ReadAllBytes($path)
$exe = New-Object InMemoryExecutable -ArgumentList (,$bytes)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment