2

我知道格式化字符串攻击发生在格式化的 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

4 回答 4

4

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

对于"overwritten%n", %n 将写入的字符数存储在内存中,直到您调用 %n。如果你调用 if 时没有传递正确的地址,它会在一个随机的地方写一些东西,有可能破坏你的记忆。

于 2012-10-05T11:28:20.063 回答
2

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

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

于 2012-10-05T11:31:56.903 回答
1

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

printf("%x");

需要一个附加参数(通常存储在寄存器或堆栈中)。由于程序员在此调用中没有告诉编译器提供额外printf的参数,因此将打印存储在哪里期望在那时找到参数的任何内容。

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

于 2012-10-05T11:29:54.200 回答
1

可能对您有帮助:
我猜您正在 Windows 中工作。没有进行严格的检查。
我在Linux上尝试过同样的警告。

int main(){
printf("%x");
printf("覆盖的%n");
}

给出以下警告:
$ gcc test.c
test.c: In function 'main':
test.c:4: warning: too little arguments for format
test.c:5: warning: too little arguments for format

于 2012-10-05T11:28:50.693 回答