环境搭建 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) + p.writem(addr,con="" ) 2. remote + remote("127.0.0.1" , 65535 )3. context + context.timeout=512 + context.debugger="gdb" + context.endian="little" + context.log_level="" + context.terminal=[ ] + context.newline="\r\n" + context.arch="i386" + content.pie=None + context.dbginit=None + context.windbg=None + context.windbgx=None + content.gdb=None + context.x64dbg=None + context.nocolor=None 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) 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;" } } }
为了更便捷使用所以我在 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 符号。
1 2 3 4 5 6 7 .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
模式下,编译器会预留足够的堆栈空间,以调用最大目标函数(参数方法)所使用的任何内容。随后,在调用子函数时,它重复使用相同的堆栈区域来设置这些参数,从而实现不用调用者反复清栈的过程