我在线阅读以下代码:http://www.cse.scu.edu/~tschwarz/coen152_05/Lectures/BufferOverflow.html
我对此行中%p的使用感到困惑:
printf("Now the stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n\n");
取自这段代码:
/*
StackOverrun.c
This program shows an example of how a stack-based
buffer overrun can be used to execute arbitrary code. Its
objective is to find an input string that executes the function bar.
*/
#pragma check_stack(off)
#include <string.h>
#include <stdio.h>
void foo(const char* input)
{
char buf[10];
printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n% p\n\n");
strcpy(buf, input);
printf("%s\n", buf);
printf("Now the stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n\n");
}
void bar(void)
{
printf("Augh! I've been hacked!\n");
}
int main(int argc, char* argv[])
{
//Blatant cheating to make life easier on myself
printf("Address of foo = %p\n", foo);
printf("Address of bar = %p\n", bar);
if (argc != 2)
{
printf("Please supply a string as an argument!\n");
return -1;
}
foo(argv[1]);
return 0;
}
我知道%p是指针的格式化程序,但为什么格式化程序后面没有值?这里实际打印的是什么值?如果它打印提供给foo函数的参数的地址,那么为什么有5'%p'以及为什么不是所有的'%p'都格式化相同的值?
非常感谢。
答案 0 :(得分:8)
这是利用未定义的行为。
故意不向printf提供值,va_arg
将从堆栈中拉出任意值以进行打印。这不是正确的代码。实际上,看起来这段代码片段正在尝试解释黑客攻击技术,这些技术通常会利用未定义行为发生时出现的故障。
答案 1 :(得分:0)
这段代码很糟糕,并且用调试器查看堆栈的效果极差。除非/除非您了解它如何滥用调用约定以及它的确切打印内容,否则请不要浪费时间。 (还要查看编译器生成的代码,以了解调用时堆栈指针所指向的位置。)
取决于32位x86上典型的stack-args调用约定。 (因此,可变参数printf
函数会将堆栈上的数据视为额外的args)。函数“拥有”它们的args(并且可以对其进行修改),但是实际上大多数函数不会修改其堆栈args,而可变参数则更是如此。
这还取决于编译器是否不插入一堆额外的填充来使call printf
的ESP对齐16,这是i386 System V ABI的现代版本所要求的。如果发生这种情况,在您想用strcpy
缓冲区溢出覆盖的实际内容之前,将有一些额外的填充指针。
在x86-64代码中,x86-64系统V在整数寄存器中传递前6个整数/指针args,因此在格式字符串之后,前5个%p
转换将抢占RSI中的所有垃圾, RDX,RCX,R8和R9。 (在RDI中格式化字符串)。然后,您将从其余%p
转换中获得一些qword。
在Windows x64中,调用约定包括“影子空间”:被调用方拥有的RSP上方32个字节,在这里它们可以转储寄存器args以使args数组与堆栈args相邻。呼叫者必须保留此空间。 strcpy
也会发生这种情况,因此可能会有所平衡,但是您仍然要打印RDX,R8和R9中的所有垃圾。 (在RCX中格式化字符串)。
通常,大多数其他非x86 ISA的调用约定都有一些寄存器args(通常为4,例如ARM或MIPS),并且大多数不使用影子空间。