二进制/进程内加密跳转执行:如何无文件落地运行加密嵌入的应用?
实现核心思路
你之前的方案触发段错误有两个核心原因:
- 堆内存默认开启
NX/DEP保护,无执行权限,直接跳转执行必然触发访问异常 - 你嵌入的是完整应用二进制而非位置无关的shellcode,缺少重定位、导入表解析、段权限配置等PE/ELF/Mach-O加载的必需步骤,直接跳转无法正常运行
如果你用Tauri的话,bootstrapper是桌面端程序,分Windows、Linux、macOS三个平台处理,不需要落地文件的核心是实现内存加载器,把解密后的二进制直接在用户态完成加载流程后执行。
C/C++ 实现方案
Windows平台(加载PE文件)
核心步骤:
- 对解密后的PE字节先校验
IMAGE_DOS_SIGNATURE、IMAGE_NT_SIGNATURE魔数合法性 - 调用
VirtualAlloc申请内存,参数指定MEM_COMMIT | MEM_RESERVE,权限先设为PAGE_READWRITE - 把PE头、各个节按
VirtualAddress偏移拷贝到申请的内存空间 - 处理重定位表:如果实际加载基址和PE可选头的
ImageBase不一致,遍历重定位表修正所有地址偏移 - 解析导入表:遍历导入的dll,调用
LoadLibrary加载,再调用GetProcAddress获取函数地址填充到IAT表 - 调用
VirtualProtect修改代码段内存权限为PAGE_EXECUTE_READ - 跳转到PE的入口点执行,示例代码如下:
typedef int (*PEEntryPoint)(); PEEntryPoint entry = (PEEntryPoint)((char*)alloc_base + nt_header->OptionalHeader.AddressOfEntryPoint); int ret = entry();
Linux/macOS平台(加载ELF/Mach-O)
逻辑和PE加载类似,Linux下需要解析ELF的程序头,处理动态段、重定位、GOT表,调用mmap分配内存,设置对应段的PROT_READ/PROT_WRITE/PROT_EXEC权限,最后跳转到ELF的e_entry地址执行。
Rust 实现方案
Rust下可以直接调用系统API完成上述流程,也可以用成熟的第三方库减少重复开发:
- Windows下可以用PE解析库处理PE结构,调用
winapi的VirtualAlloc、VirtualProtect等API完成加载 - Linux下可以用二进制解析库处理ELF结构,调用
libc的mmap、mprotect等API
Windows PE加载最小示例
use std::ptr; use winapi::um::memoryapi::{VirtualAlloc, VirtualProtect}; use winapi::um::winnt::{MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_READWRITE}; use pelite::PeFile; unsafe fn run_pe_in_memory(pe_data: &[u8]) -> i32 { let pe = PeFile::from_bytes(pe_data).unwrap(); let image_base = pe.optional_header().ImageBase; // 申请内存空间 let alloc_base = VirtualAlloc( ptr::null_mut(), pe.optional_header().SizeOfImage as usize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE, ); // 拷贝PE头和各节数据 ptr::copy_nonoverlapping(pe_data.as_ptr(), alloc_base as *mut u8, pe.optional_header().SizeOfHeaders as usize); for section in pe.section_headers() { let va = section.VirtualAddress as usize; let size = section.SizeOfRawData as usize; ptr::copy_nonoverlapping( pe_data.as_ptr().add(section.PointerToRawData as usize), (alloc_base as *mut u8).add(va), size, ); } // 此处省略重定位、导入表处理逻辑 // 修改内存为可执行权限 let mut old_protect = 0; VirtualProtect(alloc_base, pe.optional_header().SizeOfImage as usize, PAGE_EXECUTE_READ, &mut old_protect); // 跳转至入口点执行 let entry: extern "system" fn() -> i32 = std::mem::transmute((alloc_base as u64 + pe.optional_header().AddressOfEntryPoint as u64) as *mut ()); entry() }
Tauri适配注意事项
- 因为Tauri的bootstrapper本身运行在用户态,你加载的目标应用如果也是GUI程序,不需要额外创建进程,直接在当前进程执行即可
- 如果你需要隔离bootstrapper和目标应用的运行环境,也可以用进程空心化技术:创建挂起的进程,把解密后的二进制注入到挂起进程的地址空间,修改上下文后恢复进程执行,完全不需要落地文件
逆向强化补充建议
适配远程考试应用的防破解需求,你可以做额外加固:
- 解密逻辑可以做代码虚拟化、多分支混淆,避免被直接dump内存拿到解密后的二进制
- 可以分段解密,执行到对应代码段的时候才解密对应部分,减少完整二进制出现在内存中的时间
- 加入内存校验,检测是否被调试器dump内存,发现异常直接退出
内容的提问来源于stack exchange,提问作者t348575
相关产品推荐
相关产品推荐

