加载一个 EXE 文件并从内存中运行它
Posted
技术标签:
【中文标题】加载一个 EXE 文件并从内存中运行它【英文标题】:Load an EXE file and run it from memory 【发布时间】:2010-08-24 06:01:00 【问题描述】:我正在尝试从内存中运行一个可执行文件,如this 文章中所述。我可以很容易地运行任何 .net/managed exes。但我无法运行诸如notepad.exe
或calc.exe
之类的可执行文件。我怎样才能得到它,以便我也可以运行非托管 exe?
【问题讨论】:
我在 Github 上用 fancycode/MemoryModule 加载了 Calc.exe。它不起作用(入口点永远挂起,没有窗口出现)。然后我发现了诀窍:您必须在 PEB 中调整 ImageBaseAddress。之后我可以启动 Calc.exe 和 Notepad.exe。此外,还有几个 EXE 没有重定位表。在这种情况下,它变得很困难。您必须在 Linker Options --> Advanced 下将 Loader Application 的基地址设置为另一个地址。 【参考方案1】:在从内存运行 .NET 可执行文件的情况下,库和 CLR 本身为您做了很多繁重的工作。对于像 notepad.exe 和 calc.exe 这样的本机可执行文件,您必须进行大量手动工作才能实现。基本上,您必须像 Windows 加载程序一样行事。
这里可能有几页警告,但this in-depth article 提供了将 PE wiki、msdn 加载到内存中并执行正确操作所需的步骤变基和修复。然后,您应该能够找到入口点(如文章中所示)并运行它。
如果您真的只想运行 notepad.exe 和 calc.exe,最简单的方法当然是使用 Process.Start
并从磁盘运行它们。否则,如果您在进程中嵌入了作为资源的可执行文件,那么下一个最简单的方法是将内容写入磁盘的临时位置(请参阅Path.GetTempFileName
),然后从那里执行它。
【讨论】:
【参考方案2】:我不确定,但来自 thread 的以下代码可能会有所帮助:
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
【讨论】:
var dir = System.IO.Directory.GetCurrentDirectory();字节 [] 文件 = Resource1.someExecutable; CMemoryExecute.Run(file, dir); 其中 Resorurce1.someExecutable 是我想要执行但不起作用的可执行文件... @TonoNam,您是否注意到我发布的原始链接中的代码之后的这些说明:“请注意,您必须注入 %WINDIR%\Microsoft.NET\Framework\v2 .0.50727\vbc.ex e. 它似乎是唯一可以使用此代码或“RunPE”变体的进程。它必须是您要注入的 32 位 exe,并且您必须编译将其用作 x86 的 .NET 应用程序,而不是 x64 或 AnyCPU。”? 这里的代码可能仅供参考,但在实践中完全没用。它有问题,几乎无法正常工作。 代码不起作用!经过一番搜索后,我想到在不提取的情况下运行可执行文件几乎是不可能的,或者需要付出很大的努力。甚至微软也进行了提取:docs.microsoft.com/en-us/dotnet/core/whats-new/…【参考方案3】:如果您正在寻找使用 C# 运行的 Exe 文件,那么 this link 提供了一个很好的解释,其中包含一个简单但易于遵循的示例,说明应该如何使用 Process
和 Process.Start
。
简而言之,你可以做一个
Process.Start("notepad.exe")
运行非托管的 exe/应用程序。
如果这不起作用,请提供应用程序的完整路径,例如
Process.Start(@"c:\windows\system32\notepad.exe")
(我只是假设 notepad.exe
存在于该文件夹中,但你明白了。
【讨论】:
他应该从内存而不是硬盘中运行。 我不明白使用Process.Start()
应该是个问题。
您只是描述了如何运行程序。在不删除文件的情况下执行它是特定情况下的一项要求,尤其是不是微不足道的,因此是个问题。【参考方案4】:
使用[Process.Start()][1]
(或其他重载),让操作系统加载到内存中。
【讨论】:
以上是关于加载一个 EXE 文件并从内存中运行它的主要内容,如果未能解决你的问题,请参考以下文章