%n
C中格式说明符的用途是什么?有人可以举例说明吗?
%n
会printf
意外地使图灵完成,并且您可以例如在其中实现Brainfuck,请参阅github.com/HexHive/printbf和oilshell.org/blog/2019/02/07.html#appendix-a-minor-sublanguages
%n
C中格式说明符的用途是什么?有人可以举例说明吗?
%n
会printf
意外地使图灵完成,并且您可以例如在其中实现Brainfuck,请参阅github.com/HexHive/printbf和oilshell.org/blog/2019/02/07.html#appendix-a-minor-sublanguages
Answers:
什么都没打印。该参数必须是一个指向有符号int的指针,该整数存储了到目前为止已写入的字符数。
#include <stdio.h>
int main()
{
int val;
printf("blah %n blah\n", &val);
printf("val = %d\n", val);
return 0;
}
先前的代码显示:
blah blah
val = 5
int
始终签名。
n format specifies disabled
。什么原因?
这些答案大多数都解释了%n
做什么(即不打印任何内容并将到目前为止已打印的字符数写入int
变量),但是到目前为止,还没有人真正给出其用途的示例。这是一个:
int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");
将打印:
hello: Foo
Bar
Foo和Bar对齐。(在不使用%n
此特定示例的情况下做到这一点很简单,通常总会中断第一个printf
调用:
int n = printf("%s: ", "hello");
printf("Foo\n");
printf("%*sBar\n", n, "");
稍微增加一些便利是否值得使用一些深奥的东西%n
(可能会引入错误)值得商debate。)
&n
是一个指针(&
是地址运算符);指针是必需的,因为C是传递值,并且没有指针printf
就无法修改的值n
。的%*s
在使用printf
格式字符串打印一个%s
说明符(在此情况下,空字符串""
使用)字段宽度的n
字符。基本printf
原理的解释基本上不在此问题(和答案)的范围内;我建议阅读printf
文档或在SO上问您自己的单独问题。
我还没有真正看到过该%n
说明符在现实世界中的许多实际用途,但是我记得它曾在老式的printf漏洞中使用过格式字符串攻击很久。
这样的事情
void authorizeUser( char * username, char * password){
...code here setting authorized to false...
printf(username);
if ( authorized ) {
giveControl(username);
}
}
恶意用户可以利用username参数作为格式字符串传递给printf并结合使用%d
,%c
或W / E去通过调用栈,然后修改授权的真实值的变量。
是的,这是一种深奥的用法,但是在编写守护程序以避免安全漏洞时总是有用的吗?:D
%n
避免使用未经检查的输入字符串作为printf
格式字符串外,还有更多原因。
到目前为止,所有答案都是关于这样%n
做的,但不是为什么有人首先想要它的原因。我发现使用sprintf
/ snprintf
时它很有用,当您以后可能需要拆分或修改结果字符串时,因为存储的值是结果字符串中的数组索引。但是,使用sscanf
,此应用程序会有用得多,特别是因为scanf
系列中的不返回已处理的字符数,而是返回字段数。
另一个真正骇人听闻的用途是,在打印数字作为另一项操作的一部分的同时,免费获得了伪log10。
%n
,尽管我希望对“所有答案...”有所不同。= P
%n
绝对不会对攻击者造成零风险。放错地方的名声%n
确实属于通过传递消息字符串而不是格式字符串作为format参数的愚蠢做法。当这种情况%n
实际上是故意使用的故意格式字符串的一部分时,这种情况当然不会发生。
*p = f();
。为什么%n
只是将结果写到指针所指向的对象的另一种方式,而不是认为指针本身是危险的,这才是“危险的”呢?
前几天,我发现自己处在%n
可以很好地解决我的问题的境地。不像我以前的答案,在这种情况下,我无法设计出一个好的选择。
我有一个显示某些指定文本的GUI控件。该控件可以用粗体(或斜体或带下划线等)显示该文本的一部分,我可以通过指定开始和结束字符索引来指定哪个部分。
就我而言,我正在使用生成文本到控件snprintf
,并且我希望将其中一种替换内容设为粗体。找到此替换的开始和结束索引并非易事,因为:
该字符串包含多个替换项,替换项之一是用户指定的任意文本。这意味着对我所关心的替换进行文本搜索可能很含糊。
格式字符串可能已本地化,并且可能将$
POSIX扩展名用于位置格式说明符。因此,在原始格式字符串中搜索格式说明符本身并非易事。
本地化方面也意味着我不能轻易将格式字符串分解为多个对的调用snprintf
。
因此,找到围绕特定替换的索引的最直接方法是:
char buf[256];
int start;
int end;
snprintf(buf, sizeof buf,
"blah blah %s %f yada yada %n%s%n yakety yak",
someUserSpecifiedString,
someFloat,
&start, boldString, &end);
control->set_text(buf);
control->set_bold(start, end);
snprintf
在检查返回值时三个可以正常工作,因为snprintf
返回的是写入的字符数。也许是这样的:int begin = snprintf(..., "blah blah %s %f yada yada", ...);
和int end = snprintf(..., "%s", ...);
,然后尾巴:snprintf(..., "blah blah");
。
snprintf
调用的问题是替换可能会在其他语言环境中重新排列,因此不能像这样拆分它们。
%n
;我声称使用%n
比替代方法更直接。
它不会打印任何内容。它用于确定%n
在格式字符串中出现之前要打印多少个字符,并将其输出到提供的int:
#include <stdio.h>
int main(int argc, char* argv[])
{
int resultOfNSpecifier = 0;
_set_printf_count_output(1); /* Required in visual studio */
printf("Some format string%n\n", &resultOfNSpecifier);
printf("Count of chars before the %%n: %d\n", resultOfNSpecifier);
return 0;
}
它将存储到目前为止已打印的字符数的值 printf()
功能中。
例:
int a;
printf("Hello World %n \n", &a);
printf("Characters printed so far = %d",a);
该程序的输出将是
Hello World
Characters printed so far = 12
%n是C99,不适用于VC ++。
%n
存在于C89中。它不适用于MSVC,因为出于安全考虑,Microsoft默认情况下将其禁用。您必须先致电_set_printf_count_output
以启用它。(请参阅Merlyn Morgan-Graham的答案。)
printf
K&R第二版附录B的表B-1(转换)中明确列出。(我的副本的第244页。)或参见ISO C90规范的第7.9.6.1节(第134页)。