Load an EXE file and run it from memory
Asked Answered
W

4

27

I'm trying to run an executable from memory such as outlined in this article. I can run any .net/managed exes quite easily. But I cannot run executables such as notepad.exe or calc.exe. How can I get it so I can also run unmanaged exes?

Weariless answered 24/8, 2010 at 6:1 Comment(1)
I loaded Calc.exe with fancycode/MemoryModule on Github. It did not work (Entry point hanging forever, no window showing up). Then I found the trick: You must adapt the ImageBaseAddress in the PEB. Afterwards I could start Calc.exe and Notepad.exe. Additionally there are several EXE which don't have a relocation table. In this case it becomes difficult. You must set the base address of you Loader Application to another address under Linker Options --> Advanced.Bs
B
33

In the case of running .NET executables from memory, the libraries and CLR itself are doing a lot of heavy lifting for you. For native executables like notepad.exe and calc.exe, you'll have to do a lot of manual work to get it to happen. Basically, you have to act like the Windows loader.

There's probably pages of caveats here, but this in-depth article has the steps you need to load a PE wiki, msdn into memory and do the correct rebasing and fixups. Then, you should be able to find the entry point (like in the article) and run it.

If you're really just wanting to run notepad.exe and calc.exe, the easiest way, of course, would be to use Process.Start and run them off disk. Otherwise, if you have an executable embedded as a resource in your process, then the next easiest way would be to just write the contents out to disk in a temporary location (see Path.GetTempFileName) and then execute it from there.

Biannual answered 24/8, 2010 at 6:9 Comment(0)
A
26

I am not sure but the following code from this thread might be helpful:

using System;
using System.Runtime.InteropServices;

/* 
 * Title: CMemoryExecute.cs
 * Description: Runs an EXE in memory using native WinAPI. Very optimized and tiny.
 * 
 * Developed by: affixiate 
 * Release date: December 10, 2010
 * Released on: http://opensc.ws
 * Credits:
 *          MSDN (http://msdn.microsoft.com)
 *          NtInternals (http://undocumented.ntinternals.net)
 *          Pinvoke (http://pinvoke.net)
 *          
 * Comments: If you use this code, I require you to give me credits. Don't be a ripper! ;]
 */

// ReSharper disable InconsistentNaming
public static unsafe class CMemoryExecute
{
    public struct STARTUPINFO
    {
        public uint cb;
        public string lpReserved;
        public string lpDesktop;
        public string lpTitle;
        public uint dwX;
        public uint dwY;
        public uint dwXSize;
        public uint dwYSize;
        public uint dwXCountChars;
        public uint dwYCountChars;
        public uint dwFillAttribute;
        public uint dwFlags;
        public short wShowWindow;
        public short cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

    /// <summary>
    /// Runs an EXE (which is loaded in a byte array) in memory.
    /// </summary>
    /// <param name="exeBuffer">The EXE buffer.</param>
    /// <param name="hostProcess">Full path of the host process to run the buffer in.</param>
    /// <param name="optionalArguments">Optional command line arguments.</param>
    /// <returns></returns>
    public static bool Run(byte[] exeBuffer, string hostProcess, string optionalArguments = "")
    {
        // STARTUPINFO
        STARTUPINFO StartupInfo = new STARTUPINFO();
        StartupInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
        StartupInfo.wShowWindow = SW_HIDE;

        var IMAGE_SECTION_HEADER = new byte[0x28]; // pish
        var IMAGE_NT_HEADERS = new byte[0xf8]; // pinh
        var IMAGE_DOS_HEADER = new byte[0x40]; // pidh
        var PROCESS_INFO = new int[0x4]; // pi
        var CONTEXT = new byte[0x2cc]; // ctx

        byte* pish;
        fixed (byte* p = &IMAGE_SECTION_HEADER[0])
            pish = p;

        byte* pinh;
        fixed (byte* p = &IMAGE_NT_HEADERS[0])
            pinh = p;

        byte* pidh;
        fixed (byte* p = &IMAGE_DOS_HEADER[0])
            pidh = p;

        byte* ctx;
        fixed (byte* p = &CONTEXT[0])
            ctx = p;

        // Set the flag.
        *(uint*)(ctx + 0x0 /* ContextFlags */) = CONTEXT_FULL;

        // Get the DOS header of the EXE.
        Buffer.BlockCopy(exeBuffer, 0, IMAGE_DOS_HEADER, 0, IMAGE_DOS_HEADER.Length);

        /* Sanity check:  See if we have MZ header. */
        if (*(ushort*)(pidh + 0x0 /* e_magic */) != IMAGE_DOS_SIGNATURE)
            return false;

        var e_lfanew = *(int*)(pidh + 0x3c);

        // Get the NT header of the EXE.
        Buffer.BlockCopy(exeBuffer, e_lfanew, IMAGE_NT_HEADERS, 0, IMAGE_NT_HEADERS.Length);

        /* Sanity check: See if we have PE00 header. */
        if (*(uint*)(pinh + 0x0 /* Signature */) != IMAGE_NT_SIGNATURE)
            return false;

        // Run with parameters if necessary.
        if (!string.IsNullOrEmpty(optionalArguments))
            hostProcess += " " + optionalArguments;

        if (!CreateProcess(null, hostProcess, IntPtr.Zero, IntPtr.Zero, false, CREATE_SUSPENDED, IntPtr.Zero, null, ref StartupInfo, PROCESS_INFO))
            return false;

        var ImageBase = new IntPtr(*(int*)(pinh + 0x34));
        NtUnmapViewOfSection((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, ImageBase);
        if (VirtualAllocEx((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, ImageBase, *(uint*)(pinh + 0x50 /* SizeOfImage */), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE) == IntPtr.Zero)
            Run(exeBuffer, hostProcess, optionalArguments); // Memory allocation failed; try again (this can happen in low memory situations)

        fixed (byte* p = &exeBuffer[0])
            NtWriteVirtualMemory((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, ImageBase, (IntPtr)p, *(uint*)(pinh + 84 /* SizeOfHeaders */), IntPtr.Zero);

        for (ushort i = 0; i < *(ushort*)(pinh + 0x6 /* NumberOfSections */); i++)
        {
            Buffer.BlockCopy(exeBuffer, e_lfanew + IMAGE_NT_HEADERS.Length + (IMAGE_SECTION_HEADER.Length * i), IMAGE_SECTION_HEADER, 0, IMAGE_SECTION_HEADER.Length);
            fixed (byte* p = &exeBuffer[*(uint*)(pish + 0x14 /* PointerToRawData */)])
                NtWriteVirtualMemory((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, (IntPtr)((int)ImageBase + *(uint*)(pish + 0xc /* VirtualAddress */)), (IntPtr)p, *(uint*)(pish + 0x10 /* SizeOfRawData */), IntPtr.Zero);
        }

        NtGetContextThread((IntPtr)PROCESS_INFO[1] /* pi.hThread */, (IntPtr)ctx);
        NtWriteVirtualMemory((IntPtr)PROCESS_INFO[0] /* pi.hProcess */, (IntPtr)(*(uint*)(ctx + 0xAC /* ecx */)), ImageBase, 0x4, IntPtr.Zero);
        *(uint*)(ctx + 0xB0 /* eax */) = (uint)ImageBase + *(uint*)(pinh + 0x28 /* AddressOfEntryPoint */);
        NtSetContextThread((IntPtr)PROCESS_INFO[1] /* pi.hThread */, (IntPtr)ctx);
        NtResumeThread((IntPtr)PROCESS_INFO[1] /* pi.hThread */, IntPtr.Zero);


        return true;
    }

    #region WinNT Definitions

    private const uint CONTEXT_FULL = 0x10007;
    private const int CREATE_SUSPENDED = 0x4;
    private const int MEM_COMMIT = 0x1000;
    private const int MEM_RESERVE = 0x2000;
    private const int PAGE_EXECUTE_READWRITE = 0x40;
    private const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ
    private const uint IMAGE_NT_SIGNATURE = 0x00004550; // PE00

    private static short SW_SHOW = 5;
    private static short SW_HIDE = 0;
    private const uint STARTF_USESTDHANDLES = 0x00000100;
    private const uint STARTF_USESHOWWINDOW = 0x00000001;


    #region WinAPI
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, int[] lpProcessInfo);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);

    [DllImport("ntdll.dll", SetLastError = true)]
    private static extern uint NtUnmapViewOfSection(IntPtr hProcess, IntPtr lpBaseAddress);

    [DllImport("ntdll.dll", SetLastError = true)]
    private static extern int NtWriteVirtualMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, uint nSize, IntPtr lpNumberOfBytesWritten);

    [DllImport("ntdll.dll", SetLastError = true)]
    private static extern int NtGetContextThread(IntPtr hThread, IntPtr lpContext);

    [DllImport("ntdll.dll", SetLastError = true)]
    private static extern int NtSetContextThread(IntPtr hThread, IntPtr lpContext);

    [DllImport("ntdll.dll", SetLastError = true)]
    private static extern uint NtResumeThread(IntPtr hThread, IntPtr SuspendCount);
    #endregion

    #endregion
}
Abbeyabbi answered 10/2, 2012 at 19:24 Comment(7)
var dir = System.IO.Directory.GetCurrentDirectory(); byte[] file = Resource1.someExecutable; CMemoryExecute.Run(file, dir);Wiry
where Resorurce1.someExecutable is the executable that I will like to execute and that did not work...Wiry
@TonoNam, is your exe a 32 bit or 64?Abbeyabbi
@TonoNam, have you noticed these instructions after the code in the original link that I have posted: "Note that you have to inject into %WINDIR%\Microsoft.NET\Framework\v2.0.50727\vbc.ex e. It seems to be the only process that works with this code or "RunPE" variants. It must be a 32 bit exe you are injecting, and you must compile the .NET app that uses this as x86, not x64 or AnyCPU."?Abbeyabbi
the code in here may be useful for reference but totally useless when it comes to practice. It's buggy and cannot work in almost cases.Alceste
When i run the code attached to ConsoleApp1 it keeps executing the program in the array (ea quickly it runs 3000 times) but i only want to run it once any ideas of how that could be ?Zelmazelten
Code doesn't work! After some searching I came up to idea that running executable without extracting is nearly impossible or requires significant effort. Even Microsoft went with extraction: learn.microsoft.com/en-us/dotnet/core/whats-new/…Fanti
R
-8

if you are looking for running Exe files using C#, then this link provides a good explanation with a simple but easy to follow example on how Process and Process.Start should be used.

In a nutshell, you can do a

Process.Start("notepad.exe")

to run unmanaged exe/applications.

if that does not work, provide the fullpath of the application, like

Process.Start(@"c:\windows\system32\notepad.exe")

(I am just assuming notepad.exe exists on that folder, but you get the idea.

Rewire answered 15/2, 2012 at 1:3 Comment(3)
He should run from memory and not HDD.Amenable
I do not see how it should be a problem using Process.Start().Rewire
You simply described how to run a program. Executing it without file drops is both a requirement in specific cases end especially not trivial, thus the question.Cobelligerent
L
-12

Use [Process.Start()][1] (or another overload), and let the O/S do the loading into memory.

Lavona answered 24/8, 2010 at 6:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.