C 可能使某些应用程序崩溃的字符串的最小长度

C 可能使某些应用程序崩溃的字符串的最小长度,c,security,C,Security,我们每周对计算机系统漏洞进行一次测试,其中有以下问题: 下面的函数是在32位x86系统上运行的程序的一部分;编译器不会更改堆栈上变量的顺序 通过输入参数传递给函数的字符串的最小长度是多少,它会使应用程序崩溃 a10 b11 c12 d13 我写了一个主函数来调用void函数。。。并使用gcc-m32 test.c-o test编译程序,因为我在64位计算机上。主要功能如下: int main(int argc, char *argv[]) { function(argv[1]);

我们每周对计算机系统漏洞进行一次测试,其中有以下问题:

下面的函数是在32位x86系统上运行的程序的一部分;编译器不会更改堆栈上变量的顺序

通过输入参数传递给函数的字符串的最小长度是多少,它会使应用程序崩溃

a10 b11 c12 d13

我写了一个主函数来调用void函数。。。并使用gcc-m32 test.c-o test编译程序,因为我在64位计算机上。主要功能如下:

int main(int argc, char *argv[]) {
    function(argv[1]);
    return 1;
}
并通过输入进行测试:

~/Dir:./test 1234567
1 2 1234567
~/Dir:./test 12345678
1 2 12345678
~/Dir:./test 123456789
1 2 123456789
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)

我一输入123456789作为参数,就会检测到堆栈崩溃,所以这个问题的答案应该是9,但没有选择9的选项。以上问题的正确答案应该是什么?我如何知道可能使上述应用程序崩溃的字符串的最小长度?

这个问题是在以下假设下工作的:编译器没有对堆栈上的变量重新排序,也没有利用未定义的行为来执行某些优化,但您的测试程序正在这样做。它很可能将数组放在堆栈上的最高地址,从而减少导致崩溃所需的字符数


在问题的限制下,如果int为4字节,答案将是12。字符9-12将被写入其中一个int变量的字节,字符串的终止空字节将被写入超过该字节的一个字节,潜在地进入函数的返回地址。

这个问题是在假设编译器没有对堆栈上的变量重新排序,也没有利用未定义的行为来执行某些优化的情况下工作的,但是您的测试程序正在这样做。它很可能将数组放在堆栈上的最高地址,从而减少导致崩溃所需的字符数


在问题的限制下,如果int为4字节,答案将是12。字符9-12将被写入其中一个int变量的字节,字符串的终止空字节将被写入超过该字节的一个字节,可能会写入函数的返回地址。

您将得到带有9个字符的“Stack smashing detected”,因为您的编译器会对堆栈上的变量重新排序。GCC确实如此,即使在-O0。要防止这种情况,请将变量放入结构中

包括 包括 结构变量{ int i; 字符缓冲区[8]; int j; }; void functionchar*输入{ 结构变量s; s、 i=1; s、 j=2; strcpys.buffer,输入; printf%x%x%s\n,s.i,s.j,s.buffer; } int main argc,char*argv[]{ rgv[1]; 返回0; } 在关闭优化的情况下编译此文件,否则编译器很可能会自行优化

美元/年出1234567美元 1 2 1234567 美元/年,共计12345678美元 1 0 12345678 美元/年出123456789 1 39 123456789 美元/年出1234567890 1 3039 1234567890 美元/年出1234567890a 1 613039 1234567890a 美元/年出1234567890ab 162613039 1234567890ab 美元/年出1234567890abc 162613039 1234567890abc ***检测到堆栈崩溃***:已终止 [2] 6086中止堆芯转储。/a.out 1234567890abc 现在你可以看到发生了什么。该字符串最多有7个字符,加上空终止符,可放入8字节缓冲区。有8个字符时,字符串开始溢出到内存中的下一个对象,即j。在32位little endian机器上,组成j的字节的值为{0x02,0x00,0x00,0x00}。使用8到11个字符,字符串逐渐接管j

在12个字符处,空终止符覆盖s之后内存中的任何内容。在我的测试中,内存中的这个字节碰巧值为0,所以没有比重写j更糟糕的事情了。在13个字符时,字符串c的最后一个字符覆盖该字节,堆栈保护会检测到该字节,因为该字节实际上是堆栈金丝雀的一部分

在我的构建中,导致崩溃所需的字符数是13。然而,这是因为在j之后恰好有一个空字节。根据本练习的假设,可能导致应用程序崩溃的字符数为12。此时,strcpy调用写入函数的本地存储,这可能是一个未映射的地址

对于视觉参考,这是strcpy调用之前内存的内容:

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 01 | 00 | 00 | 00 | ?? | ?? | ?? | ?? | ?? | ?? | ?? | ?? | 02 | 00 | 00 | 00 | 00 | ?? | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ ^-i^-缓冲区^-j^-堆栈金丝雀 如果我使用gcc-O0-fno stack-protector进行编译,那么在我的平台上实际导致崩溃需要21个字节,这大概是因为覆盖r
eturn地址。练习我没有看过,也不知道有多难:使用调试器,在汇编代码和一些x86 ABI文档的帮助下,找出帧指针是什么?对齐间隙?

由于编译器会对堆栈上的变量重新排序,因此您将获得9个字符的“检测到堆栈崩溃”。GCC确实如此,即使在-O0。要防止这种情况,请将变量放入结构中

包括 包括 结构变量{ int i; 字符缓冲区[8]; int j; }; void functionchar*输入{ 结构变量s; s、 i=1; s、 j=2; strcpys.buffer,输入; printf%x%x%s\n,s.i,s.j,s.buffer; } int main argc,char*argv[]{ rgv[1]; 返回0; } 在关闭优化的情况下编译此文件,否则编译器很可能会自行优化

美元/年出1234567美元 1 2 1234567 美元/年,共计12345678美元 1 0 12345678 美元/年出123456789 1 39 123456789 美元/年出1234567890 1 3039 1234567890 美元/年出1234567890a 1 613039 1234567890a 美元/年出1234567890ab 162613039 1234567890ab 美元/年出1234567890abc 162613039 1234567890abc ***检测到堆栈崩溃***:已终止 [2] 6086中止堆芯转储。/a.out 1234567890abc 现在你可以看到发生了什么。该字符串最多有7个字符,加上空终止符,可放入8字节缓冲区。有8个字符时,字符串开始溢出到内存中的下一个对象,即j。在32位little endian机器上,组成j的字节的值为{0x02,0x00,0x00,0x00}。使用8到11个字符,字符串逐渐接管j

在12个字符处,空终止符覆盖s之后内存中的任何内容。在我的测试中,内存中的这个字节碰巧值为0,所以没有比重写j更糟糕的事情了。在13个字符时,字符串c的最后一个字符覆盖该字节,堆栈保护会检测到该字节,因为该字节实际上是堆栈金丝雀的一部分

在我的构建中,导致崩溃所需的字符数是13。然而,这是因为在j之后恰好有一个空字节。根据本练习的假设,可能导致应用程序崩溃的字符数为12。此时,strcpy调用写入函数的本地存储,这可能是一个未映射的地址

对于视觉参考,这是strcpy调用之前内存的内容:

+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | 01 | 00 | 00 | 00 | ?? | ?? | ?? | ?? | ?? | ?? | ?? | ?? | 02 | 00 | 00 | 00 | 00 | ?? | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ ^-i^-缓冲区^-j^-堆栈金丝雀
如果我使用gcc-O0-fno-stack-protector编译,在我的平台上实际导致崩溃需要21个字节,大概是因为覆盖返回地址需要21个字节。练习我没有看过,也不知道有多难:使用调试器,在汇编代码和一些x86 ABI文档的帮助下,找出帧指针是什么?对齐间隙?

平心而论,我已经预料到8个字符会出现一些奇怪的行为。它的开发人员负责确保缓冲区有足够的内存空间来存储输入,否则会导致缓冲区溢出,进而导致未定义的行为。使用这样的模式,它只允许7个字符复制到输入中。我的观点是,在8个字符时,您已经调用了未定义的行为,因此问题和那些可能的答案一开始就没有多大意义。无论如何,您是否尝试过反编译可执行文件并检查它如何处理这些参数?另外,我会在编译器调用中添加-O0,以消除任何类型的优化。。。。可使上述应用程序崩溃的字符串的最小长度?>7.它是否会崩溃取决于月球。缓冲区是8字节。在C语言中,字符串需要额外1个字节来存储0终止符。所以存储在8字节中的最大字符串长度是8字节减去1字节=7字节。额外的字节将被写入缓冲区的边界之外。在C中写出数组的边界调用了臭名昭著的未定义行为,从那时起,任何事情都可能发生。我已经预料到在8个字符时会出现一些奇怪的行为,公平地说,它的开发人员有责任确保缓冲区有足够的内存空间来存储输入,否则会导致缓冲区溢出,进而导致未定义的行为。使用这样的模式,它只允许7个字符复制到输入中。我的观点是,在8个字符时,您已经调用了未定义的行为,因此问题和那些可能的答案一开始就没有多大意义。无论如何,您是否尝试过反编译可执行文件并检查它如何处理这些参数?另外,我会在编译器调用中添加-O0,以消除任何类型的优化。。。。可使上述应用程序崩溃的字符串的最小长度?>7.它是否会崩溃取决于
月球。缓冲区是8字节。在C语言中,字符串需要额外1个字节来存储0终止符。所以存储在8字节中的最大字符串长度是8字节减去1字节=7字节。额外的字节将被写入缓冲区的边界之外。在C中写出数组的边界会调用臭名昭著的未定义行为,从那时起任何事情都可能发生。
~/Dir:./test 1234567
1 2 1234567
~/Dir:./test 12345678
1 2 12345678
~/Dir:./test 123456789
1 2 123456789
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)