为什么在C中发生格式字符串攻击?

时间:2012-10-05 11:19:51

标签: c printf buffer-overflow format-string

我知道格式化字符串攻击发生在格式化的I / O函数需要的参数多于提供的参数时。

在C中,

读取内存位置的一个示例:

printf("%x"); // this prints a memory address location in the stack

覆盖内存位置的另一个例子:

printf("Overwritten%n"); //this prints the number of chars in "Overwritten"

我的问题是:为什么在这两种情况下都会发生这种情况?为什么在没有提供相应值的情况下只在格式化字符串中使用%x会在内存中打印一个地址?那究竟是什么解决了?我知道它会发生,但真正发生了什么?

覆盖相同。

4 个答案:

答案 0 :(得分:4)

发生的是未定义的行为。 Printf不知道没有相应的值。它尝试访问您的呼叫的第二个参数(您没有提供),并访问一些随机内存值。

对于"overwritten%n",%n存储在内存中,在调用%n之前写入的字符数。如果你打电话,如果没有传递正确的地址,它会在随机的地方写一些东西,冒着破坏你的记忆的风险。

答案 1 :(得分:2)

鉴于C被命名为“高级汇编程序”,答案在于其编译器结构。 printf是一个函数,它接受可变数量的参数,而不检查是否所有参数都是实际提供的。因此,根据该函数如何在编译器级别接受参数,可能出现以下情况:

首先,解析传递给printf的字符串,导致后续调用内部函数,在第二种情况下输出字符串'Overwritten'(在第一种情况下,格式化字符串的第一个符号是a '%'表示参数)。然后,当请求打印参数时,调用相应的原始数据打印例程,其中下一个应该放在堆栈中的参数(在编译时计算偏移量)。在%x的情况下,没有参数,并且要打印的未更改字符串是空的,因此未分配,因此堆栈中的下一个32位值是当前返回地址,即由操作系统在EXE加载时,实际上通过call printf_hex_address汇编指令放入堆栈。这种攻击显然基于以下事实:如果处理程序实际上在内存中持久存在,并且不会被交换,则该地址是程序地址空间内的可写内存位置。为什么出现“覆盖”的长度,可以通过设计的内部字符串操作例程来解释,以便将字符串的实际长度传递给它。

答案 2 :(得分:1)

可能对您有所帮助:
我想你在windows工作。没有进行严格的检查 我在Linux上尝试过相同的警告。

  

int main(){
    的printf( “%X”);
    的printf( “被覆盖的%N”);
  }

发出以下警告:
  $ gcc test.c
test.c:在函数'main'中:
test.c:4:警告:格式的参数太少了 test.c:5:警告:格式

的参数太少

答案 3 :(得分:1)

这是因为printf不知道程序员的意图。当它在格式字符串中看到格式标识符时,它会在那里格式化相应的参数。

printf("%x");

需要一个额外的参数(通常存储在寄存器或堆栈中)。由于程序员没有告诉编译器在这个调用中提供一个额外的参数,而printf期望在那里存储的任何内容将被打印出来。

出于优化原因,编译器通常在调用下一个函数之前不清除寄存器。