需要一些关于 BOF 的高级解释
Need some advanced explanations about BOF
所以我正在关注 tutorial 关于缓冲区溢出的代码:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
modified = 0;
gets(buffer);
if(modified != 0) {
printf("you have changed the 'modified' variable\n");
} else {
printf("Try again?\n");
}
}
然后我用 gcc 编译它,另外 运行 预先 sudo sysctl -w kernel.randomize_va_space=0
以防止随机内存并允许堆栈粉碎(缓冲区溢出)利用
gcc protostar.c -g -z execstack -fno-stack-protector -o protostar
-g 允许在 gdb 中调试 ('list main')
-z execstack -fno-stack-protector 就是去掉栈保护
然后执行:
python -c 'print "A"*76' | ./protostar
再试一次?
python -c 'print "A"*77' | ./protostar
您已更改 'modified' 变量
所以我不明白为什么77会发生缓冲区溢出,而它应该是65,所以它有12位的差异(3字节)。不知道是什么原因,有没有人解释清楚?
从77到87也是这样:
python -c 'print "A"*87' | ./protostar
you have changed the 'modified' variable
从 88 开始,它添加了一个段错误:
python -c 'print "A"*88' | ./protostar
you have changed the 'modified' variable
Segmentation fault (core dumped)
此致
要完全了解正在发生的事情,首先要注意您的程序是如何布局内存的。
根据您的评论,对于这个特定的 运行,buffer
的内存从 0x7fffffffdf10
开始,然后 modified
从 0x7fffffffdf5c
开始(尽管 randomize_va_space
可能会在 运行 秒内保持一致,但我不太确定)。
所以你有这样的东西:
0x7fffffffdf10 0x7fffffffdf50 0x7fffffffdf5c
↓ ↓ ↓
(64 byte buffer)..........(some 12 bytes).....(modified)....
本质上,你有 64 个字符的缓冲区,然后当它结束时,有 12 个字节用于其他一些堆栈变量(可能 4 个字节 argc
和 8 个字节用于 argv
),然后修改之后,正好在缓冲区开始后开始 64+12 = 76 字节。
因此,当您将 65 到 76 个字符写入 64 字节缓冲区时,它会过去并开始写入 in-between 缓冲区和 modified
的 12 个字节。当您开始写入第 77 个字符时,它会开始覆盖 modified
中的内容,这会导致您看到“you have changed the 'modified' variable
”消息。
您还问过“如果我上升到 87 然后在 88 出现段错误,为什么它会起作用?答案是因为它是未定义的行为,一旦您开始写入无效内存并且内核会识别它,它会立即终止您的进程,因为您正在尝试 read/write 您无权访问的内存。
请注意,您几乎不应该在实践中使用 gets
,这是一个重要原因,因为您不知道要读取多少字节,因此有机会覆盖。另请注意,当我 运行 它时,您看到的行为与我在我的机器上看到的行为不同。这是正常的,那是因为它是未定义的行为。当您 运行 它时,无法保证会发生什么。在我的机器上,modified
实际上在内存中出现在 buffer
之前,所以我从来没有看到 modified
变量被覆盖。我认为这是一个很好的学习示例,可以理解为什么像这样的未定义行为是如此不可预测。
所以我正在关注 tutorial 关于缓冲区溢出的代码:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
modified = 0;
gets(buffer);
if(modified != 0) {
printf("you have changed the 'modified' variable\n");
} else {
printf("Try again?\n");
}
}
然后我用 gcc 编译它,另外 运行 预先 sudo sysctl -w kernel.randomize_va_space=0
以防止随机内存并允许堆栈粉碎(缓冲区溢出)利用
gcc protostar.c -g -z execstack -fno-stack-protector -o protostar
-g 允许在 gdb 中调试 ('list main')
-z execstack -fno-stack-protector 就是去掉栈保护
然后执行:
python -c 'print "A"*76' | ./protostar
再试一次?
python -c 'print "A"*77' | ./protostar
您已更改 'modified' 变量
所以我不明白为什么77会发生缓冲区溢出,而它应该是65,所以它有12位的差异(3字节)。不知道是什么原因,有没有人解释清楚?
从77到87也是这样:
python -c 'print "A"*87' | ./protostar
you have changed the 'modified' variable
从 88 开始,它添加了一个段错误:
python -c 'print "A"*88' | ./protostar
you have changed the 'modified' variable
Segmentation fault (core dumped)
此致
要完全了解正在发生的事情,首先要注意您的程序是如何布局内存的。
根据您的评论,对于这个特定的 运行,buffer
的内存从 0x7fffffffdf10
开始,然后 modified
从 0x7fffffffdf5c
开始(尽管 randomize_va_space
可能会在 运行 秒内保持一致,但我不太确定)。
所以你有这样的东西:
0x7fffffffdf10 0x7fffffffdf50 0x7fffffffdf5c
↓ ↓ ↓
(64 byte buffer)..........(some 12 bytes).....(modified)....
本质上,你有 64 个字符的缓冲区,然后当它结束时,有 12 个字节用于其他一些堆栈变量(可能 4 个字节 argc
和 8 个字节用于 argv
),然后修改之后,正好在缓冲区开始后开始 64+12 = 76 字节。
因此,当您将 65 到 76 个字符写入 64 字节缓冲区时,它会过去并开始写入 in-between 缓冲区和 modified
的 12 个字节。当您开始写入第 77 个字符时,它会开始覆盖 modified
中的内容,这会导致您看到“you have changed the 'modified' variable
”消息。
您还问过“如果我上升到 87 然后在 88 出现段错误,为什么它会起作用?答案是因为它是未定义的行为,一旦您开始写入无效内存并且内核会识别它,它会立即终止您的进程,因为您正在尝试 read/write 您无权访问的内存。
请注意,您几乎不应该在实践中使用 gets
,这是一个重要原因,因为您不知道要读取多少字节,因此有机会覆盖。另请注意,当我 运行 它时,您看到的行为与我在我的机器上看到的行为不同。这是正常的,那是因为它是未定义的行为。当您 运行 它时,无法保证会发生什么。在我的机器上,modified
实际上在内存中出现在 buffer
之前,所以我从来没有看到 modified
变量被覆盖。我认为这是一个很好的学习示例,可以理解为什么像这样的未定义行为是如此不可预测。