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

Python如何直接运行内存中加载的EXE文件而无需保存到磁盘

结论

可以实现,但Windows系统原生没有提供直接从内存加载EXE执行的公开API,你需要手动完成PE格式解析、内存布局映射、导入表修复、重定位修正等系统加载器的工作,或者使用封装好相关逻辑的第三方工具库。

常见实现方案

  • 方案1:手动调用Windows API完成PE加载(仅适用Windows平台)
    你可以通过ctypes调用Windows原生API,逐步骤完成PE文件的加载逻辑,核心步骤如下:

    1. 解析PE头获取镜像大小、节区信息、导入表、重定位表等元数据
    2. 调用VirtualAlloc在当前进程/目标进程申请合适权限的内存空间
    3. 按照节区属性将PE内容复制到对应内存地址
    4. 修复导入表,加载依赖的系统DLL并填充函数地址
    5. 处理重定位表,修正基地址不匹配的地址偏移
    6. 调用CreateThread或者跳转到入口点执行代码
      对应的简化实现示例代码如下:
    import ctypes
    from ctypes import wintypes
    
    kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
    
    def run_exe_from_memory(exe_data: bytes):
        # 解析PE头
        pe_offset = int.from_bytes(exe_data[0x3C:0x40], byteorder='little')
        image_base = int.from_bytes(exe_data[pe_offset+0x34:pe_offset+0x38], byteorder='little')
        image_size = int.from_bytes(exe_data[pe_offset+0x50:pe_offset+0x54], byteorder='little')
        entry_point = int.from_bytes(exe_data[pe_offset+0x28:pe_offset+0x2C], byteorder='little')
    
        # 申请内存
        mem = kernel32.VirtualAlloc(
            wintypes.LPVOID(image_base),
            wintypes.DWORD(image_size),
            wintypes.DWORD(0x3000),  # MEM_COMMIT | MEM_RESERVE
            wintypes.DWORD(0x40)     # PAGE_EXECUTE_READWRITE
        )
        if not mem:
            mem = kernel32.VirtualAlloc(
                None,
                wintypes.DWORD(image_size),
                wintypes.DWORD(0x3000),
                wintypes.DWORD(0x40)
            )
        delta = mem - image_base
    
        # 复制PE头
        ctypes.memmove(wintypes.LPVOID(mem), exe_data, int.from_bytes(exe_data[pe_offset+0x54:pe_offset+0x58], byteorder='little'))
    
        # 复制节区
        num_sections = int.from_bytes(exe_data[pe_offset+0x6:pe_offset+0x8], byteorder='little')
        section_header_offset = pe_offset + 0xF8
        for i in range(num_sections):
            sec_offset = section_header_offset + i * 0x28
            virt_addr = int.from_bytes(exe_data[sec_offset+0xC:sec_offset+0x10], byteorder='little')
            raw_size = int.from_bytes(exe_data[sec_offset+0x10:sec_offset+0x14], byteorder='little')
            raw_offset = int.from_bytes(exe_data[sec_offset+0x14:sec_offset+0x18], byteorder='little')
            ctypes.memmove(
                wintypes.LPVOID(mem + virt_addr),
                exe_data[raw_offset:raw_offset+raw_size],
                wintypes.DWORD(raw_size)
            )
    
        # 重定位修复(简化版,仅处理基础重定位)
        if delta != 0:
            reloc_dir_offset = pe_offset + 0xA0
            reloc_virt = int.from_bytes(exe_data[reloc_dir_offset:reloc_dir_offset+4], byteorder='little')
            reloc_size = int.from_bytes(exe_data[reloc_dir_offset+4:reloc_dir_offset+8], byteorder='little')
            reloc_ptr = mem + reloc_virt
            end_ptr = reloc_ptr + reloc_size
            while reloc_ptr < end_ptr:
                base_page = int.from_bytes(ctypes.string_at(reloc_ptr, 4), byteorder='little')
                block_size = int.from_bytes(ctypes.string_at(reloc_ptr+4, 4), byteorder='little')
                num_entries = (block_size - 8) // 2
                entries_ptr = reloc_ptr + 8
                for j in range(num_entries):
                    entry = int.from_bytes(ctypes.string_at(entries_ptr + j*2, 2), byteorder='little')
                    entry_type = entry >> 12
                    entry_offset = entry & 0xFFF
                    if entry_type == 3:  # IMAGE_REL_BASED_HIGHLOW
                        addr = mem + base_page + entry_offset
                        orig_val = int.from_bytes(ctypes.string_at(addr, 4), byteorder='little')
                        new_val = orig_val + delta
                        ctypes.memmove(addr, new_val.to_bytes(4, byteorder='little'), 4)
                reloc_ptr += block_size
    
        # 修复导入表(简化版,仅处理基础依赖)
        import_dir_offset = pe_offset + 0x80
        import_virt = int.from_bytes(exe_data[import_dir_offset:import_dir_offset+4], byteorder='little')
        import_size = int.from_bytes(exe_data[import_dir_offset+4:import_dir_offset+8], byteorder='little')
        import_ptr = mem + import_virt
        while True:
            name_rva = int.from_bytes(ctypes.string_at(import_ptr + 12, 4), byteorder='little')
            if name_rva == 0:
                break
            dll_name = ctypes.string_at(mem + name_rva).decode('utf-8')
            dll_handle = kernel32.LoadLibraryA(dll_name.encode('utf-8'))
            thunk_rva = int.from_bytes(ctypes.string_at(import_ptr + 16, 4), byteorder='little')
            thunk_ptr = mem + thunk_rva
            while True:
                thunk_val = int.from_bytes(ctypes.string_at(thunk_ptr, 4), byteorder='little')
                if thunk_val == 0:
                    break
                if thunk_val & 0x80000000:
                    func_addr = kernel32.GetProcAddress(dll_handle, wintypes.LPCSTR(thunk_val & 0x7FFFFFFF))
                else:
                    func_name = ctypes.string_at(mem + thunk_val + 2).decode('utf-8')
                    func_addr = kernel32.GetProcAddress(dll_handle, func_name.encode('utf-8'))
                ctypes.memmove(thunk_ptr, func_addr.to_bytes(4, byteorder='little'), 4)
                thunk_ptr += 4
            import_ptr += 20
    
        # 执行入口点
        entry_func = ctypes.CFUNCTYPE(wintypes.DWORD)(mem + entry_point)
        entry_func()
    
    # 调用示例
    if __name__ == "__main__":
        with open("example.exe", "rb") as f:
            exe_data = f.read()
        run_exe_from_memory(exe_data)
    

    注意:上述代码是简化实现,仅能运行无复杂依赖的32位PE文件,64位PE、有动态链接依赖、需要运行时参数的EXE需要额外扩展逻辑,同时部分杀毒软件会对内存加载PE的行为报毒。
    你可以将上述逻辑封装为独立的Python模块,即可实现你问题中提到的直接调用example_run_exe_file_from_variable.run(full_file)执行内存中EXE的效果。

  • 方案2:使用内存盘中转
    如果不想手动处理PE加载逻辑,可以创建一个内存虚拟盘,把EXE内容写入内存盘后再用subprocess.Popen运行,本质上还是走磁盘加载逻辑但实际读写都在内存中,速度快且不会留下持久化磁盘文件,实现难度更低。

注意事项

  • 上述所有方案都仅适用于Windows平台,Linux/macOS的ELF/Mach-O文件内存加载逻辑完全不同,需要单独实现
  • 内存加载EXE的行为属于敏感操作,大部分安全软件都会拦截此类行为,仅建议在本地测试、自己可控的环境下使用

内容的提问来源于stack exchange,提问作者mal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 05:51:02