我想通过将“Hello World”写入控制台(使用 Windows XP 32 位)来测试缓冲区溢出。shellcode 必须是无空的,才能通过“scanf”传递到我想要溢出的程序中。我发现了很多适用于 Linux 的汇编教程,但没有适用于 Windows 的。有人可以使用 NASM 引导我完成这个吗?哇!
2 回答
汇编操作码是相同的,因此生成无空 shellcode 的常规技巧仍然适用,但进行系统调用的方式不同。
在 Linux 中,您使用“int 0x80”指令进行系统调用,而在 Windows 中,您必须使用 DLL 库并对其导出的函数进行正常的用户模式调用。
因此,在 Windows 上,您的 shellcode 必须:
- 硬编码 Win32 API 函数地址(很可能只在您的机器上工作)
- 使用 Win32 API 解析器 shellcode(适用于每个 Windows 版本)
如果您只是在学习,那么现在对您在调试器中看到的地址进行硬编码可能会更容易。要使调用位置独立,您可以将地址加载到寄存器中。例如,调用具有 4 个参数的函数:
PUSH 4 ; argument #4 to the function
PUSH 3 ; argument #3 to the function
PUSH 2 ; argument #2 to the function
PUSH 1 ; argument #1 to the function
MOV EAX, 0xDEADBEEF ; put the address of the function to call
CALL EAX
请注意,参数以相反的顺序推送。在 CALL 指令 EAX 包含返回值之后,堆栈将与之前一样(即函数弹出它自己的参数)。ECX 和 EDX 寄存器可能包含垃圾,所以不要依赖它们在调用后保持它们的值。
直接 CALL 指令不起作用,因为这些指令取决于位置。
为了避免地址本身出现零,请尝试使用 x86 shellcode 的任何无空技巧,有很多技巧,但我最喜欢(虽然很长)是使用 XOR 指令对值进行编码:
MOV EAX, 0xDEADBEEF ^ 0xFFFFFFFF ; your value xor'ed against an arbitrary mask
XOR EAX, 0xFFFFFFFF ; the arbitrary mask
您还可以尝试 NEG EAX 或 NOT EAX(符号反转和位翻转)以查看它们是否有效,它便宜得多(每个两个字节)。
您可以在此处获取有关可以调用的不同 API 函数的帮助:http: //msdn.microsoft.com
您需要的最重要的可能如下:
- WinExec(): http: //msdn.microsoft.com/en-us/library/ms687393 (VS.85).aspx
- LoadLibrary(): http: //msdn.microsoft.com/en-us/library/windows/desktop/ms684175 (v=vs.85).aspx
- GetProcAddress():http: //msdn.microsoft.com/en-us/library/ms683212%28v=VS.85%29.aspx
第一个启动命令,接下来的两个用于加载 DLL 文件并获取其函数的地址。
这是一个关于编写 Windows shellcode 的完整教程:http: //www.codeproject.com/Articles/325776/The-Art-of-Win32-Shellcoding
汇编语言由您的处理器定义,汇编语法由汇编程序定义(因此,at&t 和 intel 语法)主要区别(至少我认为它曾经是......)是 windows 是实模式(调用执行操作的实际中断,您可以使用计算机可访问的所有内存,而不仅仅是您的程序)并且 linux 处于保护模式(您只能访问程序的小内存中的内存,并且您必须调用int 0x80 并调用内核,而不是调用硬件和 bios)无论如何,只要它们是兼容的处理器,hello world 类型的东西在 linux 和 windows 之间或多或少是相同的。
要从您制作的程序中获取 shellcode,只需将其加载到目标系统的调试器中(gdb for linux,debug for windows)并在调试中输入 d(或者是 u?无论如何,它应该说如果你输入h(帮助))以及指令和内存之间的操作码。只需将它们全部复制到您的文本编辑器中成为一个字符串,然后编写一个程序将它们全部转换为它们的 ascii 值。不知道如何在 gdb 中执行此操作...
无论如何,要使其成为 bof 漏洞利用,请输入 aaaaa... 并继续添加 a's 直到它因缓冲区溢出错误而崩溃。但是要准确找出崩溃它需要多少个a。然后,它应该告诉你那是什么内存地址。通常它应该在错误消息中告诉你。如果上面写着“9797 [其余的原始退货地址]”,那么你明白了。现在你必须使用你的调试器来找出它在哪里。使用调试器反汇编程序并查找调用 scanf 的位置。在那里设置一个断点,运行并检查堆栈。查找所有那些 97(我忘了提到的是 'a' 的 ascii 数字。)并查看它们的结束位置。然后删除断点并输入你发现它所花费的数量(确切的数量。如果错误消息是“缓冲区溢出”
快乐的黑客...