在printf中使用%p?

时间:2014-03-20 00:28:23

标签: c memory assembly

我在线阅读以下代码: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'都格式化相同的值?

非常感谢。

2 个答案:

答案 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),并且大多数不使用影子空间。