没有参数的`printf("%p")`是什么意思?

时间:2015-08-04 09:59:07

标签: c printf format-specifiers

我当然知道它用于输出带参数的指针。

我读过迈克尔霍华德和大卫勒布朗的书编写安全代码

书中的一个程序演示了strcpy()

的堆栈溢出是如何工作的

注意printf()没有参数。

#include <stdio.h>
#include <string.h>

void foo(const char* input)
{
    char buf[10];

    //What? No extra arguments supplied to printf?
    //It's a cheap trick to view the stack 8-)
    //We'll see this trick again when we look at format strings.
    printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n% p\n\n");

    //Pass the user input straight to secure code public enemy #1.
    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;
}

结果是

C:\Secureco2\Chapter05>StackOverrun.exe Hello
Address of foo = 00401000
Address of bar = 00401045
My stack looks like:
00000000
00000000
7FFDF000
0012FF80 
0040108A <-- return address
00410EDE

Hello
Now the stack looks like:
6C6C6548 <-- 'l','l','e','h'
0000006F <-- 0, 0, 0, 'o'
7FFDF000
0012FF80
0040108A
00410EDE

代码中printf("%p")的含义是什么?为什么它可以打印堆栈的内容?

4 个答案:

答案 0 :(得分:8)

通常,%pformat specifier to print the pointer(地址值),预期的参数是指向void类型的指针。

那就是说,在你的代码中,

 printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n% p\n\n");

undefined behaviour。 根据标准中的printf()描述,如果提供的格式参数不足,则为UB。

引用标准C11,章节§7.21.6.1

  

[...]如果格式的参数不足,则行为为   未定义。 。[...]

代码段无法保证产生任何有效输出。

答案 1 :(得分:5)

由于您的参数列表与输入的字符串不匹配,printf("Now the stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n\n");的行为未定义

作者猜测,printf的这种特殊误用会从当前堆栈中注入变量,并输出它们的地址。有时它们可​​能是正确的。其他时候编译器可能会吃你的猫。

答案 2 :(得分:4)

这意味着它将以特定格式打印输出。在参数&#34;%p&#34;的情况下它将以您系统的内存地址格式打印(8位,十六进制)。

答案 3 :(得分:4)

正如其他人所说,这是未定义的行为。

但你所看到的似乎是一种天真的,但是很自然的&#34;事情。在大多数基于调用堆栈的C实现中,调用者将函数输入参数压入堆栈,并由被调用者从堆栈中弹出[1]。在这种特殊情况下,被调用者(printf)似乎根据格式化字符串中有多少%p格式化指令盲目地弹出连续的指针大小的元素,而不进行任何形式的检查。因此,它不是弹出(缺席)输入参数,而是遍历堆栈并且&#34;看到&#34;它不应该看到什么。但其他实现情况并非总是如此。

[1]通过&#34;弹出&#34;我并不意味着破坏性地将它们从堆叠中移除;不要从字面上理解;)很可能在幕后发生的事情是输入参数是由指针偏移引用的。但显然这里没有约束这种偏移可以达到多远。