Windows Pwn 基础知识


环境搭建

winpwn

  • 安装
1
2
3
4
pip3 install winpwn
pip3 install pefile
pip3 install keystone-engine
pip3 install install capstone
  • 基本使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
1. process
+ process("./pwn")
+ process(["./pwn","argv[1]","argv[2]"])
+ p.readm(addr,n) # read process memory
+ p.writem(addr,con="") # write process memory
2. remote
+ remote("127.0.0.1", 65535)

3. context
+ context.timeout=512
+ context.debugger="gdb" # or "windbg" or "x64dbg"
+ context.endian="little"
+ context.log_level="" # or "debug"
+ context.terminal=[ ]
+ context.newline="\r\n"
+ context.arch="i386" # or "amd64"
+ content.pie=None
+ context.dbginit=None # used to set debugger init script
+ context.windbg=None # set debugger path, or use .winpwn to find debugger path
+ context.windbgx=None
+ content.gdb=None
+ context.x64dbg=None
+ context.nocolor=None # if set, will print non-colorful output to terminal
4. dbg: windbgx, windbg, gdb, x64dbg
+ windbgx.attach(p,script="bp 0x401000")
+ windbg.attach(p,script="bp 0x401000")
+ gdb.attach(p, script="b *0x401000")
+ x64dbg.attach(p) #can not parse script file yet

1. disable PIE:
+ PIE(exe_fpath="")
+ NOPIE(exe_fpath="")
2. asm/disasm:
+ asm("push ebp")
+ disasm("\x55")
3. winfile(fpath="./main.exe"):
+ winfile.symbols["CreateProcessA"] #通过符号解析导入导出表
4. wincs(ip,port)
5.
+ wincs(ip=None,port=512): run a server to asm/disasm in remote machine for client where does not install keystone/capstone
+ wincs(ip='123.123.123.123',512): create a client to connet to server
+ wincs.asm(asmcode='push ebp')
+ wincs.disasm(machinecode='\x55')```

- 库魔改:
- 添加`sendafter`和`sendlineafter`
- 添加自动获取文件架构功能


- winpwn联合调试配置

在 Windows 用户主目录下创建`.winpwn`文件,然后写入下面的文件内容并修改相应的调试器所在路径。

这样就可以通过 winpwn 的模块来进行联合调试了,就跟 pwntools 中调用 gdb 差不多。

```json
{
"debugger":{
"i386": {
"x64dbg": "D:\\MyTools\\Tools\\Debuggers\\x64dbg\\release\\x32\\x32dbg.exe",
"gdb": "D:\\MyTools\\Tools\\Debuggers\\mingw-w64-686\\mingw32\\bin\\gdb.exe",
"windbg": "D:\\MyTools\\Tools\\Debuggers\\WinDbg\\x86",
"windbgx": "C:\\Users\\nanhang\\AppData\\Local\\Microsoft\\WindowsApps\\Microsoft.WinDbg_8wekyb3d8bbwe\\WinDbgX.exe"
},
"amd64": {
"x64dbg": "D:\\MyTools\\Tools\\Debuggers\\x64dbg\\release\\x64\\x64dbg.exe",
"gdb": "D:\\MyTools\\Tools\\Debuggers\\mingw64\\bin\\gdb64.exe",
"windbg": "D:\\MyTools\\Tools\\Debuggers\\WinDbg\\x64",
"windbgx": "C:\\Users\\nanhang\\AppData\\Local\\Microsoft\\WindowsApps\\Microsoft.WinDbg_8wekyb3d8bbwe\\WinDbgX.exe"
}
},
"debugger_init": {
"i386": {
"x64dbg": "",
"gdb": "",
"windbg": ".load C:\\Users\\nanhang\\windbg_plugins\\x86\\pykd.dll;!py -g E:\\ShareDir\\building\\bywin\\byinit.py;",
"windbgx": ".load C:\\Users\\nanhang\\windbg_plugins\\x86\\pykd.dll;!py -g E:\\ShareDir\\building\\bywin\\byinit.py;"
},
"amd64": {
"x64dbg": "",
"gdb": "",
"windbg": ".load C:\\Users\\nanhang\\windbg_plugins\\x64\\pykd.dll;!py -g E:\\ShareDir\\building\\bywin\\byinit.py;",
"windbgx": ".load C:\\Users\\nanhang\\windbg_plugins\\x64\\pykd.dll;!py -g E:\\ShareDir\\building\\bywin\\byinit.py;"
}
}
}
  • winpwn exp 模板

为了更便捷使用所以我在 vscode 的代码片段中设置了一个模板,在模板代码中添加了一些拉姆达表达式,并添加了命令行参数解析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
from winpwn import *
import argparse

parser = argparse.ArgumentParser(description='Pwn tool for exploiting .exe files')
parser.add_argument('exe', metavar='exe_file', type=str, help='Path to the executable file')
args = parser.parse_args()
exe = args.exe
io = process(exe)
winfile(fpath=exe)

def dbg(a=1, arg=None):
if a == 1:
windbgx.attach(io, script=arg)
elif a == 2:
x64dbg.attach(io, script=arg)
elif a == 3:
gdb.attach(io, script=arg)
else:
windbg.attach(io, script=arg)
s = lambda data: io.send(data)
sl = lambda data: io.sendline(data)
sa = lambda text, data: io.sendafter(text, data)
sla = lambda text, data: io.sendlineafter(text, data)
r = lambda num=4096: io.recv(num)
ru = lambda text: io.recvuntil(text)
pr = lambda num=4096: print(io.recv(num))
ia = lambda: io.interactive()
ic = lambda: io.close()
l32 = lambda: u32(io.recvuntil(b'\xf7')[-4:].ljust(4, '\x00'))
l64 = lambda: u64(io.recvuntil(b'\x7f')[-6:].ljust(8, '\x00'))
uu32 = lambda: u32(io.recv(4).ljust(4, '\x00'))
uu64 = lambda: u64(io.recv(6).ljust(8, '\x00'))
int16 = lambda data: int(data, 16)
lg = lambda s, num: print('%s -> 0x%x' % (s, num))

off=0
payload = 'a' * off
sl(payload)
sleep(1)
sl('cat flag')

ia()

checksec

github:https://github.com/Wenzel/checksec.py/releases/download/v0.6.2/checksec.exe

用来查看 exe 可执行文件程序保护,用法和 pwntools 的checksec 差不多。

ROPgadget

Windows下使用pip安装无法使用。

通过将 ROPgadget 项目源代码下载到本地,然后通过PyInstaller将 python 代码打包为 exe 可执行文件。

然后再将文件路径添加到环境变量中,之后就可以便捷使用了。

打包:

1
python -m PyInstaller --onefile .\scripts\ROPgadget

win_server

EX师傅写的,用来把程序映射到某个端口,类似 socat 一样。

github:https://github.com/Ex-Origin/win_server.git

windbg

Windows 下的调试工具有 windbg、x64dbg、ollydbg 和 gdb 等,推荐使用 windbg,毕竟它是微软的亲儿子。

推荐直接在微软商店中下载 windbg preview,它相比传统版的 windbg 来说对 win11 这些高版本的系统支持更好。

  • windbg 带符号调试

在 windbg 命令行中输入以下命令配置 windbg 符号。

1
2
3
4
5
6
7
#在windbg的符号文件路径中同时指定本地缓存路径和远程符号服务器
.sympath srv*c:\Symbols*https://msdl.microsoft.com/download/symbols

#为所有已加载模块载入符号信息
.reload
#强制立即载入
.reload /f /v

编译环境

我所使用的编译环境:

  • 系统:Windows XP SP3
  • 编译器:VC6

程序保护

ASLR

ASLR:与 Linux 的 PIE 相同,值地址随机化,将在程序启动时将DLL随机的加载到内存中的未知,自Windows 10开始已经在系统中配置为默认启动。 High Entropy VA:高熵64位地址空间布局随机化,开启后标识此程序的随机化取值空间为64 bit,这会导致攻击者更难去推测随机化后的地址;

Force Integrity:强制签名保护,开启后标识程序加载时需要验证其中的前命,如果签名不正确,程序将会被阻止运行;

Isolation:隔离保护,开启后表示此程序加载时将会在一个相对独立的隔离环境中被加载,从而阻止攻击者过度提升权限; 与 Linux相同,在程序启动时将 DLL随机加载到内存中的位置,这将缓解恶意程序的加载。ASLR 在 Windows10后开始在系统中被配置为默认启用。

GS

类似 Canary保护,一旦开启会在返回地址和BP之前压入一个额外的 Security Cookie。系统会比较栈中的这个值和原先存放在 .data中的值做一个比较。如果不等则说明发生了栈溢出。

DEP

  • 定义:DEP 是一种内存保护机制,用于防止代码在标记为不可执行的内存区域中执行,从而防止缓冲区溢出攻击。
  • NX:指内存页不可执行。操作系统会将某些内存页标记为不可执行,从而阻止恶意代码的执行。
  • PAE (Physical Address Extension):这项技术允许 32 位处理器访问超过 4GB 的物理内存。在启用 PAE 后,操作系统可以使用多级地址转换来访问更多内存,同时启用 DEP 等内存保护机制。

SEH

即结构化异常处理保护,能够防止攻击者利用结构化异常处理来进行进一步的利用。

CFG

控制流防护,在间接跳转前插入校验代码,检查目标地址的有效性,进而可以组织执行流跳转到预期之外的地点,最终及时有效的进行异常处理。

RFG

返回地址防护,在每个函数头部将返回地址保存到 fs:[rsp](Thread Control Stack),并在函数返回前将其与栈上返回地址进行比较,从而有效阻止了这些攻击方式。

SafeSEH

安全结构化异常处理函数,即白名单安全沙箱,事先定义一些异常处理程序,并基于此构造安全结构化异常处理表,程序正式运行后,安全结构化异常处理表之外的异常处理程序将会被阻止运行。

Authenticode

签名保护。它是一种数字签名机制,旨在确保程序的完整性并验证其来源。

.NET

保护 .NET 应用程序中的 DLL,常用于提高应用程序的安全性。

函数调用约定

32位默认为为__cdecl调用约定,在函数调用的时候通过栈将函数参数从右至左压入栈中,最后返回的时候由调用函数进行平栈操作。

x64模式下只有一种调用方式 __fastcall,在发生函数调用的时候前4个参数通过寄存器 RCX,RDX,R8,R9,传递剩下的通过栈传递。函数的返回值保存在 RAX寄存器下。

  • 如果返回值为较大的值(结构体),那么由调用方在栈上分配空间,并将指针通过RCX传递给被调用函数,被调用函数通过RAX返回该指针
  • 栈需要十六字节对齐,但是call之后会push八字节的返回地址,但是这样的情况下栈就没办法对齐了,因此所有的非叶子节点调用函数都需要调整栈帧为16n+8
  • 对于 R8-R15 寄存器,我们可以使用 r8, r8d, r8w, r8b 分别代表r8寄存器的64位、低32位、低16位和低8
  • 一般情况下x64平台中RBP栈指针被废弃,只作为普通的寄存器使用,所有的栈操作都通过RSP指针完成。
  • 调用者负责清理栈帧,被调用者不用清理栈帧,但是有时候调用者不一定会清理栈帧。这是因为与通过 PUSH 和 POP 指令在堆栈中显式添加和移除参数的x86 编译器不同,x64 模式下,编译器会预留足够的堆栈空间,以调用最大目标函数(参数方法)所使用的任何内容。随后,在调用子函数时,它重复使用相同的堆栈区域来设置这些参数,从而实现不用调用者反复清栈的过程