为什么每次内存地址的最后一位数字都相同

问题描述

这更像是一个请帮助我理解而不是调试问题。所以,我最近开始研究 C,在使用指针的时候我遇到了这个案例。

#include <stdio.h>

int main()
{
        char *name="Bob";
        for(int i=0;i<3;i++)
        {
                printf("Address is %0x and value is %ld",name,*name);
                printf("\n");
                name++;
        }
        printf("\n");
        return 0;
}

我运行了 3 次程序并得到类似 -->

输出
Address is 9a756008 and value is 66
Address is 9a756009 and value is 111
Address is 9a75600a and value is 98

Address is ba49d008 and value is 66
Address is ba49d009 and value is 111
Address is ba49d00a and value is 98

Address is 2cb44008 and value is 66
Address is 2cb44009 and value is 111
Address is 2cb4400a and value is 98

我的问题是:内存每次都是随机分配的,那么为什么三种情况下的最后三位数字(每次运行程序都得到相同的结果)相同?内存分配有固定的规则吗?

感谢任何信息。谢谢! :)

解决方法

地址是由程序加载器故意随机化的。这种随机化只影响地址的较高位。

如果程序中存在某些缺陷,并且攻击者知道程序存储某些数据或代码的某些地址,则攻击者可能能够利用这些缺陷获得他们不应访问的特权或数据。例如,如果一个程序错误地接受了如此多的输入,它会在程序为数据保留的数组上运行,额外的输入可能会覆盖内存中的其他内容,例如堆栈上的返回地址。当攻击者知道代码在内存中的位置时,他们可以制作输入,以便用攻击者选择的地址覆盖返回地址,从而允许攻击者选择程序执行的代码。

为了减少这种情况发生的可能性,优秀的现代程序加载器使用 address space layout randomization。程序加载时,随机选择程序各部分在内存中的起始位置。这可以防止攻击者准确知道数据或代码的位置。

程序加载器可能会受到操作系统的各种规则和可执行文件格式的限制,它可以选择的起始位置。操作系统仅以称为的某种大小为单位分配内存。 4096 字节是典型的页面大小。程序可能期望其布局的某些部分(常量数据、程序代码、初始化的非常量数据等)从页面边界开始。在这种情况下,程序加载器只能选择页边界的随机地址。如果一个页面是 4096 字节,那么每个页面的起始地址是 100016 的倍数(十六进制 1000 = 4096)。

这意味着起始地址的格式为 xxx00016,其中 xxx 是程序加载器选择的随机值,000是一个页面的开始。页面中的任何内容都会受到xxx 中的更改的影响,但 000 中的更改不会影响它。

因此,如果某些数据位于相对于其程序段开头的偏移量 678 处,则其地址将始终具有某个值 xxx678,并且 xxx 将程序运行之间会有所不同,但 678 不会。

这些规则源于操作系统和程序加载器的行为,它们是 C 实现的一部分。它们不是 C 标准的一部分。