我当然知道它用于输出带参数的指针。
我读过迈克尔霍华德和大卫勒布朗的书编写安全代码。
书中的一个程序演示了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")
的含义是什么?为什么它可以打印堆栈的内容?
答案 0 :(得分:8)
通常,%p
是format 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;我并不意味着破坏性地将它们从堆叠中移除;不要从字面上理解;)很可能在幕后发生的事情是输入参数是由指针偏移引用的。但显然这里没有约束这种偏移可以达到多远。