Windows Pwn
Windows Pwn
本文适合
已经理解 栈、返回地址与控制流、ROP基础 和基础逆向调试的 Pwn 学习者。学完你能:识别 PE/WinAPI/调用约定带来的利用差异,在 x64dbg 中确认崩溃点、偏移、模块基址,并设计 ret2api 或 VirtualProtect ROP 路线
一句话判断
题目给 .exe、.dll、Windows 服务、x64dbg 截图、WinAPI 调用,或者崩溃发生在 PE 程序里,就按 Windows Pwn 思路处理。
Windows Pwn 仍然是控制流劫持,但不能照搬 Linux 的 pop rdi; ret、ELF/GOT/libc 模板。
题目中常见信号
文件信号:
challenge.exe
vuln.dll
service.exe
PE32
PE32+工具信号:
x64dbg
WinDbg
PE-bear
CFF Explorer
Process MonitorAPI 信号:
WinExec
system
VirtualProtect
VirtualAlloc
CreateFileA
ReadFile
WriteFile
LoadLibraryA
GetProcAddress保护信号:
DEP
ASLR
CFG
SafeSEH
SEHOP
Stack Cookie核心概念
Windows Pwn 和 Linux Pwn 的主要差异:
Linux 常见思路:ELF
Windows 常见思路:PE
Linux 常见思路:libc / ld
Windows 常见思路:DLL
Linux 常见思路:RDI, RSI, RDX, RCX...
Windows 常见思路:RCX, RDX, R8, R9
Linux 常见思路:NX
Windows 常见思路:DEP
Linux 常见思路:system, execve, syscall
Windows 常见思路:WinExec, VirtualProtect, VirtualAlloc
Linux 常见思路:GDB / pwndbg
Windows 常见思路:x64dbg / WinDbg
Windows x64 调用 API 时还要注意 shadow space:调用者通常要在栈上预留 0x20 字节空间。
最小分析流程
1. 识别架构和保护
用 PE-bear、Detect It Easy 或 CFF Explorer 看:
- 32 位还是 64 位。
- 是否 ASLR。
- 是否 DEP。
- 是否 CFG。
- 是否有 Stack Cookie。
- 导入了哪些 WinAPI。
命令行可用:
file challenge.exe2. 在 x64dbg 里确认崩溃
最小步骤:
- 打开程序。
- 输入长字符串,例如 cyclic pattern。
- 观察 RIP/EIP 或异常地址。
- 查看栈窗口,确认返回地址是否可控。
- 记录偏移。
如果是 32 位,还要关注 SEH 链是否被覆盖。
3. 确认可用模块
在 x64dbg 的 Memory Map 里看模块:
- 主程序是否固定基址。
- 哪些 DLL 没开 ASLR。
- 哪些段可执行。
- 哪些模块里有 ROP gadget。
优先使用非 ASLR 模块中的 gadget。如果全 ASLR,就需要先泄露地址。
4. 选择利用路线
常见路线:
有 win 函数 -> ret2win
可调用 WinExec -> ret2api
DEP 开启且有 shellcode -> VirtualProtect / VirtualAlloc
有文件读取目标 -> CreateFile + ReadFile + WriteFile
32 位 SEH 可控 -> SEH overwrite最小验证示例
x64 ret2api 参数顺序
Windows x64 调用 WinExec("calc.exe", 1) 时,参数是:
RCX = "calc.exe"
RDX = 1ROP 链要找类似 gadget:
pop rcx ; ret
pop rdx ; ret
ret
WinExec并保证栈对齐和 shadow space。
如果目标是 CTF 服务,不要执着 calc.exe。本地弹计算器只是验证“能调用 WinAPI”;远程题通常要调用题目内置函数、读文件或返回数据。
DEP 绕过验证
如果 shellcode 在栈上但 DEP 开启,直接跳栈会崩。
路线:
VirtualProtect(shellcode_addr, size, PAGE_EXECUTE_READWRITE, &old)
-> jump shellcode_addr关键参数:
RCX = shellcode_addr
RDX = size
R8 = 0x40
R9 = oldProtect_ptr这比 Linux 的 mprotect 多了 Windows 调用约定和 shadow space 细节。
常见利用 / 解题路线
路线总览:
路线一:ret2win
适合教学和入门题。
步骤:
- 找
win、print_flag、success交叉引用。 - 确认函数地址是否固定。
- 栈溢出覆盖返回地址。
- 跳到目标函数。
路线二:ret2api
适合程序导入了高价值 API。
目标 API:
WinExec
system
CreateFileA
ReadFile
WriteFile步骤:
- 确认 API 地址。
- 准备参数字符串和缓冲区。
- ROP 设置 RCX/RDX/R8/R9。
- 调用 API。
路线三:VirtualProtect + shellcode
适合:
- 可以写 shellcode。
- DEP 开启。
- 有足够 gadget。
步骤:
- 把 shellcode 放到可写内存。
- ROP 调
VirtualProtect改权限。 - 跳到 shellcode。
路线四:32 位 SEH overwrite
适合 32 位老题。
步骤:
- 覆盖 SEH 链。
- 找
pop pop ret。 - 触发异常。
- 跳到短跳转和 shellcode。
现代题不一定允许这条路线,但看到 32 位 Windows 程序要检查 SEH。
常见失败原因
- 照搬 Linux 参数寄存器:Windows x64 前四个参数是 RCX、RDX、R8、R9。
- 忘记 shadow space:调用 API 前栈不满足约定会崩。
- 模块基址变化:ASLR 开启时本地地址不能直接用于远程。
- DEP 开启还跳栈:需要先改权限或走 ret2api。
- CFG 拦截间接调用:某些间接调用目标会被限制。
- DLL 版本不同:本地 gadget 偏移和远程不一致。
- 字符串编码错误:A/W 版本 API 区分 ANSI 和 Wide 字符串。
迷你案例
题目是 64 位 Windows 程序:
vuln.exe输入 300 个 A 后在 x64dbg 中崩溃,RIP 可控。
第一步,用 pattern 确定偏移:
offset = 136第二步,导入表里有:
WinExec第三步,在 .data 里找可写地址,写入:
cmd.exe /c type flag.txt如果程序输出不可见,可以改成调用题目已有 print_flag 或用 CreateFileA/ReadFile/WriteFile 组合。
第四步,ROP:
padding
pop rcx ; ret
ptr_to_command
pop rdx ; ret
1
ret ; 对齐
WinExecWP 里要说明:
- 这是 Windows x64,参数不是 RDI/RSI。
- DEP 开启时没有直接跳栈。
- 使用导入表中的 WinExec 做 ret2api。
- 如果远程环境不同,要重新确认模块基址和 API 地址。