无WriteProcessMemory CreateRemoteThread实现shellcode注入 GhostWriting x64实现
Arcueid 发表于 浙江 二进制安全 1851浏览 · 2025-06-10 10:28

Ghost Writing

07年的文章 除作者的这个POC外网上没有找到太多相关的文章

https://web.archive.org/web/20090722171318/http://blog.txipinet.com/2007/04/05/69-a-paradox-writing-to-another-process-without-openning-it-nor-actually-writing-to-it/

我们在这里尝试搞出一个64位的实现

项目地址: https://github.com/Arcueld/GhostWriting-X64

首先介绍一下这个技术大概是什么概念

向一个进程注入shellcode执行 无需使用openProcess WriteProcessMemory CreateRemoteProcess DebugActiveProcess 之类的API

核心思想是找到类如

的汇编指令

配合SetThreadContext API,MOV [REG1],REG2指令就能实现内存写入

同时需要找到EB FE指令(无限循环),使得RET指令跳转到此处形成执行滞留 防止崩溃

那么我们首先需要改造POC来寻找64位环境下的指令

之类的指令

实际可用的指令并不多 而且这里全是易失寄存器 没法用

图片加载失败


所以退而求其次 中间插点pop add rsp 都可以接收 手动平衡一下堆栈即可

寻找gadget的代码限于篇幅不在文章中展示 可自行在项目代码中查看

图片加载失败


图片加载失败


拿到gadget了之后 我们修改返回地址为EB FE的地址 死循环

图片加载失败


图片加载失败


到目前为止我们所作所为看起来没有意义 只是进了死循环

实际上并非如此 我们成功构造了一个返回地址指向EB FE的栈 之后在使用MOV [REG1],REG2 RET赋值的时候可以不再关心返回值

于是我们可以写出如下复制内存的demo

因为一次写8字节 需要对齐一下

图片加载失败


图片加载失败


这里没有选择恢复进程原始的RIP notepad的GUI会卡死 但是我们的内存确实是写进去了

模拟调用

将对应参数写到栈里 然后修改RIP到ntdll!NtProtectVirtualMemory即可 我们先常规调用一下NtProtectVirtualMemory看看栈

图片加载失败


对着构造 额外需要(4+3+1)*sizeof(ULONG64)的长度 1个返回地址 3个指针的内容 4个shadowstack

指针就直接存栈上 值得注意的是shadow stack

图片加载失败


如果要调用其他的函数 可以用类似的方法 完全可以封装一个函数来调 这块由于篇幅就不贴NtProtectVirtualMemory的调用代码了 代码放在后面扩展的地方

执行

原项目是通过NtProtectVirtualMemory修改栈的内存权限 直接将payload写到栈上 然后线程劫持改RIP 修改程序运行流程

我就直接先用VirtualAllocEx申请了

图片加载失败


修复

现在解决一下需要手动触发的问题 这个好解决 获取一下窗口 postmessage就行了 因为GUI线程是消息驱动的

扩展

任意方法的调用

任意方法的调用 想了一下还是写了吧 也不难

考虑c++的模板方法

首先我们需要构造栈 要明确参数的个数 及其中的指针的个数



其中HANDLE这类typede下是指针的我们忽略 因为实际传参还是值而不是指针

代码如下 具体见注释

从栈上读指针的值

现在能进行调用了 但是有些函数是通过参数写回数据的 比如NtAllocateVirtualMemory 分配的地址会写回到BaseAddress 我们需要从栈上读取

具体函数如下

第三个参数pointerIndex 用于指明取第几个指针的值

图片加载失败


项目地址

https://github.com/Arcueld/GhostWriting-X64

6 条评论
某人
表情
可输入 255
用户WmHX8FI8iF
2025-06-24 09:28 0 回复
想找你帮忙,有偿指导一下大学里的作业
用户WmHX8FI8iF
2025-06-24 09:24 0 回复
可以加个微信好友嘛,有些大学里作业想找你帮我做一些
半块西瓜皮
2025-06-12 08:02 0 回复
笔误吧,需要使用OpenProcess
Arcueid
2025-06-16 10:00 0 回复
OpenThread是要的OpenProcess可以不用, 用不到进程句柄 直接遍历快照对比th32OwnerProcessID就行
如果需要彻底规避这类打开句柄的api 我就不了解了
半块西瓜皮
2025-06-16 06:35 0 回复
如何不使用OpenProcess时没有办法获取线程句柄,也就无法使用SetThreadContext
Arcueid
2025-06-15 03:59 0 回复

@半块西瓜皮 老哥我傻逼了, 写到后面没注意这个, 其实是可以不用OpenProcess的 之前的poc里面用这个的目的是读NtAllocateVirtualMemory申请出来的地址, 直接用VirtualAlloc拿返回值就行了 然后原作者X86的实现是直接在栈上跑的 地址自己是知道的