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

KyL*_*KyL 2 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;
}
Run Code Online (Sandbox Code Playgroud)

结果是

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
Run Code Online (Sandbox Code Playgroud)

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

Sou*_*osh 8

通常,%p是一个打印指针格式说明符(地址值),期望的参数是指向void类型的指针.

那说,在你的代码中,

 printf("My stack looks like:\n%p\n%p\n%p\n%p\n%p\n% p\n\n");
Run Code Online (Sandbox Code Playgroud)

未定义的行为.根据printf()标准中的描述,如果提供的格式参数不足,则为UB.

引用标准C11,第7.21.6.1章

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

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


Bat*_*eba 6

的行为printf("Now the stack looks like:\n%p\n%p\n%p\n%p\n%p\n%p\n\n");不确定的,因为你的参数列表不输入的字符串相匹配.

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

  • 这里作者的目标是演示如何通过`strcpy()`而不是`printf()`来破坏系统.作者使用不带参数的`printf`来输出堆栈的内容,以显示如何滥用`strcpy`来篡改返回地址. (2认同)

Con*_* Ma 5

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

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

[1] 我所说的“弹出”并不是指从堆栈中破坏性地删除它们;而是指将它们从堆栈中删除。不要从字面上理解它;)最有可能发生的情况是输入参数是通过指针的偏移量引用的。但显然,这个偏移量可以达到多远并没有限制。