没有参数的`printf(“%p”)`的含义是什么?

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

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

书中的一个程序演示了strcpy()如何处理堆栈溢出

注意没有参数的printf()

 #include  #include  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")的含义是什么? 为什么它可以打印堆栈的内容?

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

那说,在你的代码中,

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

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

引用标准, C11 ,章节§7.21.6.1

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

代码片段没有保证产生任何有效的输出。

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

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

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

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

[1]通过“弹出”我并不是指破坏性地将它们从堆叠中移除; 不要从字面上理解;)很可能在幕后发生的事情是输入参数是由指针的偏移量引用的。 但显然,这种偏移可以达到的程度没有限制。

这意味着它将以特定格式打印输出。 在参数“%p”的情况下,它将以您系统的存储器地址格式打印(8位,hex)。