因此,我知道Linux对X86处理器(内核代码,内核数据,用户代码,用户数据)使用四个默认段,但是它们都具有相同的基础和限制(0x00000000和0xfffff),这意味着每个段映射到相同线性地址集。

鉴于此,为什么还具有用户/内核段?我了解为什么应该有代码和数据的单独段(仅仅是由于X86处理器如何处理CS和DS寄存器),但是为什么不具有单个代码段和一个数据段?内存保护是通过分页来完成的,无论如何,用户和内核段映射到相同的线性地址。

有帮助吗?

解决方案

X86体系结构将类型和特权级别与每个段描述符关联。描述符的类型允许仅读取,读/写,可执行等,但是具有相同基础和限制的不同段的主要原因是允许使用不同的描述符特权级别(DPL)。

DPL是两个位,允许编码0到3的值。当特权级别为0时,据说是 戒指0, ,这是最特权的。 Linux内核的段描述符为环0,而用户空间的段描述符为环3(最低特权)。对于大多数分段的操作系统都是如此。操作系统的核心是环0,其余为环3。

正如您提到的,Linux内核设置了四个部分:

  • __kernel_cs(内核代码段,base = 0,limit = 4GB,type = 10,dpl = 0)
  • __kernel_ds(内核数据段,base = 0,limit = 4GB,type = 2,dpl = 0)
  • __USER_CS(用户代码段,base = 0,limit = 4GB,type = 10,dpl = 3)
  • __USER_DS(用户数据段,base = 0,limit = 4GB,type = 2,dpl = 3)

所有四个的基础和限制都是相同的,但是内核段为DPL 0,用户段为DPL 3,代码段是可执行的和可读的(不写的),并且数据段是可读取的,并且可读取(不可执行)(不可执行) 。

也可以看看:

其他提示

X86内存管理体系结构同时使用分段和分页。非常粗略地说,一个细分市场是具有自己的保护政策的过程地址空间的分区。因此,在X86体系结构中,可以将过程看到的内存地址范围分为多个连续段,并为每个段分配不同的保护模式。分页是一种用于将过程地址空间的小(通常为4KB)区域映射到真实物理内存块的技术。因此,分页控制了段内部的区域映射到物理RAM上的方式。

所有流程都有两个部分:

  1. 一个段(地址为0x000000至0xbfffffff),用于用户级,特定于过程的数据,例如程序的代码,静态数据,堆和堆栈。每个过程都有自己的独立用户段。

  2. 一个段(地址为0xc00000至0xffffffff),其中包含特定于内核的数据,例如内核说明,数据,内核代码可以执行的一些堆栈,更有趣的是,该部分中的一个区域直接映射到物理内存中,以便该区域映射到物理内存中内核可以直接访问物理内存位置,而不必担心地址翻译。相同的内核段映射到每个过程中,但是只有在受保护的内核模式下执行时,进程才能访问它。

因此,在用户模式中,该过程只能访问地址小于0xC0000000;对地址的任何访问都高于此会导致故障。但是,当用户模式进程开始在内核中执行(例如,在进行系统调用后)时,CPU中的保护位更改为主管模式(并且某些分段寄存器更改),这意味着该过程是从而能够访问0xc0000000以上的地址。

参考: 这里

在X86中 - Linux段寄存器用于缓冲区溢出检查[请参阅以下代码段,该代码段定义了堆栈中的某些char数组]:

static void
printint(int xx, int base, int sgn)
{
    char digits[] = "0123456789ABCDEF";
    char buf[16];
    int i, neg;
    uint x;

    neg = 0;
    if(sgn && xx < 0){
        neg = 1;
        x = -xx;
    } else {
        x = xx;
    }

    i = 0;
    do{
        buf[i++] = digits[x % base];
    }while((x /= base) != 0);
    if(neg)
        buf[i++] = '-';

    while(--i >= 0)
        my_putc(buf[i]);
}

现在,如果我们看到代码GCC生成的代码的分组。

用于函数printint的汇编代码的转储:

 0x00000000004005a6 <+0>:   push   %rbp
   0x00000000004005a7 <+1>: mov    %rsp,%rbp
   0x00000000004005aa <+4>: sub    $0x50,%rsp
   0x00000000004005ae <+8>: mov    %edi,-0x44(%rbp)


  0x00000000004005b1 <+11>: mov    %esi,-0x48(%rbp)
   0x00000000004005b4 <+14>:    mov    %edx,-0x4c(%rbp)
   0x00000000004005b7 <+17>:    mov    %fs:0x28,%rax  ------> obtaining an 8 byte guard from based on a fixed offset from fs segment register [from the descriptor base in the corresponding gdt entry]
   0x00000000004005c0 <+26>:    mov    %rax,-0x8(%rbp) -----> pushing it as the first local variable on to stack
   0x00000000004005c4 <+30>:    xor    %eax,%eax
   0x00000000004005c6 <+32>:    movl   $0x33323130,-0x20(%rbp)
   0x00000000004005cd <+39>:    movl   $0x37363534,-0x1c(%rbp)
   0x00000000004005d4 <+46>:    movl   $0x42413938,-0x18(%rbp)
   0x00000000004005db <+53>:    movl   $0x46454443,-0x14(%rbp)

...
...
  // function end

   0x0000000000400686 <+224>:   jns    0x40066a <printint+196>
   0x0000000000400688 <+226>:   mov    -0x8(%rbp),%rax -------> verifying if the stack was smashed
   0x000000000040068c <+230>:   xor    %fs:0x28,%rax  --> checking the value on stack is matching the original one based on fs
   0x0000000000400695 <+239>:   je     0x40069c <printint+246>
   0x0000000000400697 <+241>:   callq  0x400460 <__stack_chk_fail@plt>
   0x000000000040069c <+246>:   leaveq 
   0x000000000040069d <+247>:   retq 

现在,如果我们从此功能中删除了基于堆栈的炭阵列,GCC将不会生成此防护检查。

即使是内核模块,我也看到了GCC生成的同样的。基本上,我看到了一些内核代码时看到的崩溃,这是虚拟地址0x28的错误。后来,我认为我已经正确地将堆栈指针初始化了并正确加载了程序,我在GDT中没有正确的条目,这会将基于FS的偏移转化为有效的虚拟地址。

但是,在内核代码的情况下,它只是忽略了,错误而不是跳到__stack_chk_fail@plt>之类的东西。

在GCC中添加此防护的相关编译器选项是-Fstack -Protector。我认为默认情况下启用了这是对用户应用程序进行编译的。

对于内核,我们可以通过config cc_stackprotector选项启用此GCC标志。

config CC_STACKPROTECTOR
 699        bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
 700        depends on SUPERH32
 701        help
 702          This option turns on the -fstack-protector GCC feature. This
 703          feature puts, at the beginning of functions, a canary value on
 704          the stack just before the return address, and validates
 705          the value just before actually returning.  Stack based buffer
 706          overflows (that need to overwrite this return address) now also
 707          overwrite the canary, which gets detected and the attack is then
 708          neutralized via a kernel panic.
 709
 710          This feature requires gcc version 4.2 or above.

此GS/FS为Linux/Arch/x86/includ/asm/stackProtector.h的相关内核文件

内核内存不应从用户空间中运行的程序中读取。

程序数据通常无法执行(DEP,处理器功能,有助于防止执行溢出的缓冲区和其他恶意攻击)。

这一切都是关于访问控制的 - 不同的部分具有不同的权利。这就是为什么访问错误的细分市场会给您“分割错误”的原因。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top