zoukankan      html  css  js  c++  java
  • [转]linux进程内存布局

    内存管理是操作系统的核心之一,最近在研究内核的内存管理以及 C 运行时库对内存的分配和管理,涉及到进程在内存的布局,在此对进程的内存布局做一下总结:

    1. 32 位模式下的 linux 内存布局

    图上的各个部分描述得比较清楚,不需再做过多的描述。从上图可以看到,栈至顶向下扩展,并且栈是有界的。堆至底向上扩展, mmap 映射区域至顶向下扩展, mmap 映射区域和堆相对扩展,直至耗尽虚拟地址空间中的剩余区域,这种结构便于 C 运行时库使用 mmap 映射区域和堆进行内存分配。上图的布局形式是在内核 2.6.7 以后才引入的,这是 32 位模式下的默认内存布局形式。看看 cat 命令在 2.6.36 上内存布局:

    08048000-08051000 r-xp 00000000 08:01 786454     /bin/cat

    08051000-08052000 r--p 00008000 08:01 786454     /bin/cat

    08052000-08053000 rw-p 00009000 08:01 786454     /bin/cat

    08053000-08074000 rw-p 00000000 00:00 0          [heap]

    b73e3000-b75e3000 r--p 00000000 08:01 400578     /usr/lib/locale/locale-archive

    b75e3000-b75e4000 rw-p 00000000 00:00 0

    b75e4000-b773b000 r-xp 00000000 08:01 1053967    /lib/libc-2.12.1.so

    b773b000-b773c000 ---p 00157000 08:01 1053967    /lib/libc-2.12.1.so

    b773c000-b773e000 r--p 00157000 08:01 1053967    /lib/libc-2.12.1.so

    b773e000-b773f000 rw-p 00159000 08:01 1053967    /lib/libc-2.12.1.so

    b773f000-b7742000 rw-p 00000000 00:00 0

    b774f000-b7750000 r--p 002a1000 08:01 400578     /usr/lib/locale/locale-archive

    b7750000-b7752000 rw-p 00000000 00:00 0

    b7752000-b7753000 r-xp 00000000 00:00 0          [vdso]

    b7753000-b776f000 r-xp 00000000 08:01 1049013    /lib/ld-2.12.1.so

    b776f000-b7770000 r--p 0001b000 08:01 1049013    /lib/ld-2.12.1.so

    b7770000-b7771000 rw-p 0001c000 08:01 1049013    /lib/ld-2.12.1.so

    bfbed000-bfc0e000 rw-p 00000000 00:00 0          [stack]

    可以看到,栈和 mmap 映射区域并不是从一个固定地址开始,并且每次的值都不一样,这是程序在启动时随机改变这些值的设置,使得使用缓冲区溢出进行攻击更加困难。当然也可以让程序的栈和 mmap 映射区域从一个固定位置开始,只需要设置全局变量 randomize_v a_space 值为 0 ,这个变量默认值为 1 。用户可以通过设置 /proc/sys/kernel/randomize_va_space 来停用该特性,也可以用如下命令:

    sudo sysctl -w kernel.randomize_va_space=0

    设置 randomize_va_space 为 0 后,再看看 cat 的内存布局:

    08048000-08051000 r-xp 00000000 08:01 786454     /bin/cat

    08051000-08052000 r--p 00008000 08:01 786454     /bin/cat

    08052000-08053000 rw-p 00009000 08:01 786454     /bin/cat

    08053000-08074000 rw-p 00000000 00:00 0          [heap]

    b7c72000-b7e72000 r--p 00000000 08:01 400578     /usr/lib/locale/locale-archive

    b7e72000-b7e73000 rw-p 00000000 00:00 0

    b7e73000-b7fca000 r-xp 00000000 08:01 1053967    /lib/libc-2.12.1.so

    b7fca000-b7fcb000 ---p 00157000 08:01 1053967    /lib/libc-2.12.1.so

    b7fcb000-b7fcd000 r--p 00157000 08:01 1053967    /lib/libc-2.12.1.so

    b7fcd000-b7fce000 rw-p 00159000 08:01 1053967    /lib/libc-2.12.1.so

    b7fce000-b7fd1000 rw-p 00000000 00:00 0

    b7fde000-b7fdf000 r--p 002a1000 08:01 400578     /usr/lib/locale/locale-archive

    b7fdf000-b7fe1000 rw-p 00000000 00:00 0

    b7fe1000-b7fe2000 r-xp 00000000 00:00 0          [vdso]

    b7fe2000-b7ffe000 r-xp 00000000 08:01 1049013    /lib/ld-2.12.1.so

    b7ffe000-b7fff000 r--p 0001b000 08:01 1049013    /lib/ld-2.12.1.so

    b7fff000-b8000000 rw-p 0001c000 08:01 1049013    /lib/ld-2.12.1.so

    bffdf000-c0000000 rw-p 00000000 00:00 0          [stack]

    可以看出,栈和 mmap 区域都从固定位置开始了, stack 的起始位置为 0x c0000000 , mmap 区域的起始位置为 0x b8000000 ,可见系统为 stack 区域保留了 128M 内存地址空间。

    在某些情况下,设置 randomize_va_space 为 0 ,便于对系统做一些针对性的研究,例如:进程的内存映射有个叫 vdso 的区域,也就是用 ldd 命令看到的那个” linux-gate.so.1 “,这块区域可以看成是内核用于实现 vsyscall 而创建的 virtual shared object ,遵循 elf 的格式,并且可以被用户程序访问。在设置 randomize_va_space 为 0 的情况下,使用如下命令就可以把这个区域 dump 出来看过究竟。如果不设置 randomize_va_space ,每次 vdso 的地址都是随机的,下面的命令也无能为力。

    zhuang@ubuntu:~$ dd if=/proc/self/mem of=gate.so bs=4096 skip=$[0xb7fe1] count=1

    dd: `/proc/self/mem': cannot skip to specified offset

    1+0 records in

    1+0 records out

    4096 bytes (4.1 kB) copied, 0.00144225 s, 2.8 MB/s

    zhuang@ubuntu:~$ objdump -d gate.so

    gate.so:     file format elf32-i386

    Disassembly of section .text:

    ffffe400 <__kernel_sigreturn>:

    ffffe400:        58                           pop    %eax

    ffffe401:        b8 77 00 00 00               mov    $0x77,%eax

    ffffe406:        cd 80                        int    $0x80

    ffffe408:        90                           nop

    ffffe409:        8d 76 00                     lea    0x0(%esi),%esi

    ffffe40c <__kernel_rt_sigreturn>:

    ffffe40c:        b8 ad 00 00 00               mov    $0xad,%eax

    ffffe411:        cd 80                        int    $0x80

    ffffe413:        90                           nop

    ffffe414 <__kernel_vsyscall>:

    ffffe414:        51                           push   %ecx

    ffffe415:        52                           push   %edx

    ffffe416:        55                           push   %ebp

    ffffe417:        89 e5                        mov    %esp,%ebp

    ffffe419:        0f 34                        sysenter

    ffffe41b:        90                           nop

    ffffe41c:        90                           nop

    ffffe41d:        90                           nop

    ffffe41e:        90                           nop

    ffffe41f:        90                           nop

    ffffe420:        90                           nop

    ffffe421:        90                           nop

    ffffe422:        eb f3                        jmp    ffffe417 <__kernel_vsyscall+0x3>

    ffffe424:        5d                           pop    %ebp

    ffffe425:        5a                           pop    %edx

    ffffe426:        59                           pop    %ecx

    ffffe427:        c3                           ret

    2. 32 为模式下的经典布局:

    这种布局 mmap 区域与栈区域相对增长,这意味着堆只有 1GB 的虚拟地址空间可以使用,继续增长就会进入 mmap 映射区域,这显然不是我们想要的。这是由于 32 模式地址空间限制造成的,所以 内核引入了前一种虚拟地址空间的布局形式。但是对 64 位模式,提供了巨大的虚拟地址空间,这个布局就相当好。如果要在 2.6.7 以后的内核上使用 32 位模式内存经典布局,有两种办法可以设置:

    方法一: sudo sysctl -w vm.legacy_va_layout=1

    方法二: ulimit -s unlimited

    同时设置 randomize_va_space 为 0 后, cat 的内存布局已经回到经典形式了:

    08048000-08051000 r-xp 00000000 08:01 786454     /bin/cat

    08051000-08052000 r--p 00008000 08:01 786454     /bin/cat

    08052000-08053000 rw-p 00009000 08:01 786454     /bin/cat

    08053000-08074000 rw-p 00000000 00:00 0          [heap]

    40000000-4001c000 r-xp 00000000 08:01 1049013    /lib/ld-2.12.1.so

    4001c000-4001d000 r--p 0001b000 08:01 1049013    /lib/ld-2.12.1.so

    4001d000-4001e000 rw-p 0001c000 08:01 1049013    /lib/ld-2.12.1.so

    4001e000-4001f000 r-xp 00000000 00:00 0          [vdso]

    4001f000-40021000 rw-p 00000000 00:00 0

    40021000-40022000 r--p 002a1000 08:01 400578     /usr/lib/locale/locale-archive

    4002f000-40186000 r-xp 00000000 08:01 1053967    /lib/libc-2.12.1.so

    40186000-40187000 ---p 00157000 08:01 1053967    /lib/libc-2.12.1.so

    40187000-40189000 r--p 00157000 08:01 1053967    /lib/libc-2.12.1.so

    40189000-4018a000 rw-p 00159000 08:01 1053967    /lib/libc-2.12.1.so

    4018a000-4018e000 rw-p 00000000 00:00 0

    4018e000-4038e000 r--p 00000000 08:01 400578     /usr/lib/locale/locale-archive

    bffdf000-c0000000 rw-p 00000000 00:00 0          [stack]

    3. 64 位模式下的内存布局

    在 64 位模式下各个区域的起始位置是什么呢?对于 AMD64 , 内存布局采用的是经典模式, text 的起始地址为 0x0000000000400000 ,堆紧接着 BSS 段向上增长, mmap 映射区域开始位置一般设为 TASK_SIZE/3 ,

    #define TASK_SIZE_MAX   ((1UL << 47) - PAGE_SIZE)

    #define TASK_SIZE               (test_thread_flag(TIF_IA32) ? \
                                            IA32_PAGE_OFFSET : TASK_SIZE_MAX)
    #define STACK_TOP               TASK_SIZE

    #define TASK_UNMAPPED_BASE      (PAGE_ALIGN(TASK_SIZE / 3))

    计算一下可知, mmap 的开始区域地址为 0x0000 2AAAAAAAA000,栈顶地址为 0x0000 7FFFFFFFF000


  • 相关阅读:
    Codeforces467C George and Job
    Codeforces205E Little Elephant and Furik and RubikLittle Elephant and Furik and Rubik
    Codeforce205C Little Elephant and Interval
    51nod1829 函数
    51nod1574 排列转换
    nowcoder35B 小AA的数列
    Codeforce893E Counting Arrays
    gym101612 Consonant Fencity
    CodeForces559C Gerald and Giant Chess
    CodeForces456D A Lot of Games
  • 原文地址:https://www.cnblogs.com/Quincy/p/2489468.html
Copyright © 2011-2022 走看看