声明here:
终结符canary包含NULL(0x00)、CR(0x0d)、LF(0x0a)和EOF(0xff)--
终止大多数字符串操作的四个字符,呈现
溢出尝试无害。
我知道空值(0x00)可以帮助防止strcpy、strncpy、stpcpy和strcat。
同样,LF(0x0A)也可以用于get和fgets。
0x0D和0xFF对停止有什么好处?

最佳答案

“金丝雀”的目的是检测缓冲区何时被溢出。它位于堆栈的返回地址之前,在当前堆栈帧中分配的任何缓冲区之后。如果其值更改,则堆栈检查代码知道缓冲区已溢出,并在程序造成任何损坏之前中止程序。
问题在于,如果攻击者使用与之前相同的值覆盖金丝雀,则不会检测到缓冲区溢出。为了使这更困难,要么使用随机数作为金丝雀,因此攻击者无法预测它,要么使用您询问的特殊“终结者金丝雀”值。选择构成终结符金丝雀的字节值是因为它们将终止程序使用的各种复制操作。如果这些值在字符串(“shellcode”)中,攻击者将尝试用大多数复制字符串的代码覆盖返回值,则在覆盖返回值之前,大多数复制字符串的代码将停止。
下面是如果源输入中出现terminate canary,则在重写返回值之前终止的内存复制操作的示例:
核终结

char buf[10];

strcpy(buf, src);

上面的例子是最常见的情况。复制C字符串的任何操作,在源字符串的第一个NUL(0)字节中停止。
下线终止
char buf[10];

gets(buf);

使用gets是初学者常犯的错误,通常不会出现在生产代码中,但编写更复杂的代码并不难,这些代码可以读取一行代码,但不必担心缓冲区溢出。什么标志着终点取决于惯例。通常的Unix约定是使用一个换行符(LF,0x0A),但是Windows使用回车和换行符(crlf,0x0d0x0a)序列。由于终结符canary包含CR和LF字节值,因此两个EOL终结符都存在于canary中。任何复制单行的操作都将在覆盖返回值之前停止。
EOF终止中断
char buf[10];
char *dest = buf;

while(1) {
    char c = getchar();
    if (c == EOF) {
        break;
    }
    *dest++ = c;
}

与其他两个例子相比,这里的EOF终结者是如何工作的更难理解和解释。除了缓冲区溢出错误外,此代码还包含另一个导致0xFF被解释为EOF的错误。与使用gets一样,这也是一个新手犯的错误,但在生产代码中更常见。错误是由使用char c而不是int c引起的。
getchar返回的值实际上是一个int而不是char。这使得区分有效字节值和特殊的EOF返回值成为可能,在大多数系统上是-1。从文件中读取的字节值将作为unsigned char值转换为int返回。因此文件中的字节值'\xFF'将作为int值255返回。当它被分配到char变量c时,它从32位有符号整数值截断为8位有符号整数值。这会将32位有符号整数值255转换为8位有符号整数值-1。此转换还将32位有符号整数值-1(EOF)转换为8位有符号整数值-1。
因为'\xFF'和EOF最终都转换为-1,所以它们最终都比较为等于EOF。这意味着在上面的示例代码中,当getchar返回其中一个值时,循环将终止。任何犯这种错误的代码都将停止在源输入的第一个'\xFF'字节处复制。但是,正确使用getchargetcfgetc或类似函数并将返回值赋给int的代码将继续复制任何'\xFF'字节。
摘要
如果溢出代码使用NUL(0)、EOL(0x0D和/或0x0A)或中断的EOF(0xFF)比较来终止副本,则终结符canary会使攻击者无法利用代码中的给定缓冲区溢出进行攻击。我的猜测是,大多数缓冲区溢出都是由于NUL位于终结者金丝雀中而无法利用的。其余的许多字符将受到EOL字符的保护,而中断的EOF字节可能根本没有太大的适用性。

10-02 06:36
查看更多