C中printf()中单引号和双引号的区别

C中printf()中单引号和双引号的区别,c,printf,string-formatting,C,Printf,String Formatting,我对double和single的用法有疑问 C的printf()中的引号。我有以下代码: #include<stdio.h> int main(int argc, char** argv) { char buffer[100]; strncpy(buffer, argv[1], 100); printf(buffer); return 0; } 注意,在第一种情况下,我输入了单引号,在后一种情况下输入了双引号 这里发生了什么 问题2。我正在关注这个链

我对double和single的用法有疑问 C的printf()中的引号。我有以下代码:

#include<stdio.h>
int main(int argc, char** argv) {
    char buffer[100];
    strncpy(buffer, argv[1], 100);
    printf(buffer);
    return 0;
}
注意,在第一种情况下,我输入了单引号,在后一种情况下输入了双引号

这里发生了什么

问题2。我正在关注这个链接。所以我的问题是,为什么输入argv[1]中的AAAA在运行时能够影响printf()的输出。首先,我认为这只是一个巧合,但我输入并得到以下信息

./o 'BBBB%8$p'

BBBB0x70243825**42424242**
编辑:所以我认为看到答案有一些误解。我知道直接“printf(buffer)”被滥用。因此,我将引用前面提到的链接:

由于printf的参数数量可变,因此它必须使用格式字符串来确定参数数量。在上述情况下,攻击者可以传递字符串“%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p”,并使printf误认为它有15个参数。它会天真地打印堆栈上接下来的15个地址,认为它们是它的参数:

在堆栈上大约10个参数处,我们可以看到0x252070的重复模式-这些是堆栈上的%ps!我们以AAAA开始字符串,以便更明确地看到这一点:

0x4141是AAAA的十六进制表示形式。我们现在有一种方法可以将任意值(在本例中,我们将0x4141)作为参数传递给printf。此时,我们将利用另一个格式字符串特性:在格式说明符中,我们还可以选择特定的参数。例如,printf(“%2$x”,1、2、3)将打印2。通常,我们可以使用printf(“%$x”)来选择printf的任意参数。在本例中,我们看到0x4141是printf的第10个参数,因此我们可以简化string1:

所以我不认为这是垃圾价值。相反,它实际上是一个地址的位置

我的平台:Ubuntu 14.04,64位机器


因此,我想知道输入中的“AAAA”或“bbbbbb”如何影响输出“AAAA0x7024382541414141或bbbbbb0x70243825424242

这与C中的引号无关。shell从命令行参数中删除单引号和双引号。但是shell也可能对美元符号应用特殊含义(
$p
如果使用双引号,而不是单引号,则在传递给应用程序之前,shell可能会对其进行扩展)

因此,这个:
/a.out'AAAA%8$p'
作为
argv[1]
传入
AAAA%8$p
,而这个:
/a.out'AAAA%8$p
可能传入
AAAA%8
(如果未定义
p
环境变量)

现在谈谈你看到的奇怪的输出

printf
,作为一个函数,无法知道传递给它的参数有多少。因此,它希望您传递的参数数量与格式字符串中的占位符数量相同

然后堆栈将如下所示:

但你没有提供足够的论据。因此,您的堆栈看起来更像:

然后-
printf
读取参数所在的内存,并点击
main()
的局部变量,即
buffer

然后它从那里读取8个字节并以十六进制打印它们(
%p
以十六进制格式打印指针,在您的情况下指针大小为8个字节)

十六进制中的
A
41
,这就是为什么你会看到4次41,这与
B
相同,后者是十六进制
42

这是未定义的行为。不应该依赖它(除非您正在编写漏洞)。它将在平台、编译器甚至优化模式之间发生变化

通常,您希望停留在已定义行为的领域中


p、 除了错误使用
printf
的危险(您显然知道),您可能还需要检查边缘情况,例如未提供参数或输入太长。

这与C中的引号无关。shell从命令行参数中删除单引号和双引号。但是shell也可能对美元符号应用特殊含义(
$p
如果使用双引号,而不是单引号,则在传递给应用程序之前,shell可能会对其进行扩展)

因此,这个:
/a.out'AAAA%8$p'
作为
argv[1]
传入
AAAA%8$p
,而这个:
/a.out'AAAA%8$p
可能传入
AAAA%8
(如果未定义
p
环境变量)

现在谈谈你看到的奇怪的输出

printf
,作为一个函数,无法知道传递给它的参数有多少。因此,它希望您传递的参数数量与格式字符串中的占位符数量相同

然后堆栈将如下所示:

但你没有提供足够的论据。因此,您的堆栈看起来更像:

然后-
printf
读取参数所在的内存,并点击
main()
的局部变量,即
buffer

然后它从那里读取8个字节并以十六进制打印它们(
%p
以十六进制格式打印指针,在您的情况下指针大小为8个字节)

十六进制中的
A
41
,这就是为什么你会看到4次41,这与
B
相同,后者是十六进制
42

这是未定义的行为。不应该依赖它(除非您正在编写漏洞)。它将在平台、编译器甚至优化模式之间发生变化

通常,您希望停留在已定义行为的领域中

p、 除了错误使用
printf
的危险外,您可能还需要检查边缘情况,例如没有提供参数或输入太长。

printf(缓冲区)非常非常危险

printf的第一个参数是格式字符串con
./o 'BBBB%8$p'

BBBB0x70243825**42424242**
$ ./a.out "%p %p %p %p %p %p %p %p %p %p %p %p %p %p %p"
0xffffdddd 0x64 0xf7ec1289 0xffffdbdf 0xffffdbde (nil) 0xffffdcc4 0xffffdc64 (nil) 0x25207025 0x70252070 0x20702520 0x25207025 0x70252070 0x20702520
$ ./a.out "AAAA%p %p %p %p %p %p %p %p %p %p"
AAAA0xffffdde8 0x64 0xf7ec1289 0xffffdbef 0xffffdbee (nil) 0xffffdcd4 0xffffdc74 (nil) 0x41414141
$ ./a.out 'AAAA%10$p'
AAAA0x41414141
char buffer [100];
strcpy (buffer, "%s");
printf (buffer);