Linux x64ç°å¢ã«ããã¦ãELFå®è¡ãã¡ã¤ã«ãå ±æã©ã¤ãã©ãªãã¹ã¿ãã¯é åããã¼ãé åã®ã¢ãã¬ã¹ãã©ã®ããã«æ±ºã¾ãã®ãã«ã¤ãã¦ã®ã¡ã¢ã
ç°å¢
Ubuntu 12.04 LTS 64bitç
$ uname -a Linux vm-ubuntu64 3.11.0-15-generic #25~precise1-Ubuntu SMP Thu Jan 30 17:39:31 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 12.04.4 LTS Release: 12.04 Codename: precise $ gcc --version gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
å®è¡æã®ã¡ã¢ãªãããã確èªãã¦ã¿ã
ã¾ãã¯ããã¼ãé åã使ãé©å½ãªããã°ã©ã ãç¨æããã
/* hello.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char *buf; buf = malloc(20); strncpy(buf, "Hello, world!", 20); puts(buf); return 0; }
ã³ã³ãã¤ã«ããgdbã使ã£ã¦ã¡ã¢ãªãããã表示ããã¦ã¿ãã
$ gcc hello.c $ gdb -q a.out Reading symbols from /home/user/tmp/a.out...(no debugging symbols found)...done. (gdb) b puts Breakpoint 1 at 0x400470 (gdb) r Starting program: /home/user/tmp/a.out warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000 Breakpoint 1, 0x00007ffff7a8ace0 in puts () from /lib/x86_64-linux-gnu/libc.so.6 (gdb) i proc process 5927 cmdline = '/home/user/tmp/a.out' cwd = '/home/user/tmp' exe = '/home/user/tmp/a.out' (gdb) shell cat /proc/5927/maps 00400000-00401000 r-xp 00000000 08:01 1182040 /home/user/tmp/a.out 00600000-00601000 r--p 00000000 08:01 1182040 /home/user/tmp/a.out 00601000-00602000 rw-p 00001000 08:01 1182040 /home/user/tmp/a.out 00602000-00623000 rw-p 00000000 00:00 0 [heap] 7ffff7a1a000-7ffff7bcf000 r-xp 00000000 08:01 2097169 /lib/x86_64-linux-gnu/libc-2.15.so 7ffff7bcf000-7ffff7dcf000 ---p 001b5000 08:01 2097169 /lib/x86_64-linux-gnu/libc-2.15.so 7ffff7dcf000-7ffff7dd3000 r--p 001b5000 08:01 2097169 /lib/x86_64-linux-gnu/libc-2.15.so 7ffff7dd3000-7ffff7dd5000 rw-p 001b9000 08:01 2097169 /lib/x86_64-linux-gnu/libc-2.15.so 7ffff7dd5000-7ffff7dda000 rw-p 00000000 00:00 0 7ffff7dda000-7ffff7dfc000 r-xp 00000000 08:01 2097185 /lib/x86_64-linux-gnu/ld-2.15.so 7ffff7fee000-7ffff7ff1000 rw-p 00000000 00:00 0 7ffff7ff8000-7ffff7ffa000 rw-p 00000000 00:00 0 7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso] 7ffff7ffc000-7ffff7ffd000 r--p 00022000 08:01 2097185 /lib/x86_64-linux-gnu/ld-2.15.so 7ffff7ffd000-7ffff7fff000 rw-p 00023000 08:01 2097185 /lib/x86_64-linux-gnu/ld-2.15.so 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] (gdb) quit
ããã§ãgdbãASLRãç¡å¹ã«ãã¦ãããã¨ã«æ³¨æããã ä¸ã®çµæããã主ãªã¢ãã¬ã¹ãã¾ã¨ããã¨æ¬¡ã®ããã«ãªãã
- 0x400000: å®è¡ãã¡ã¤ã«ã®å®è¡å¯è½é å
- 0x600000: å®è¡ãã¡ã¤ã«ã®å®è¡ä¸å¯é å
- 0x602000: ãã¼ãé å
- 0x7ffff7a1a000: å ±æã©ã¤ãã©ãªï¼libcï¼
- 0x7ffffffde000: ã¹ã¿ãã¯é å
å®è¡ãã¡ã¤ã«ã®ã¡ã¢ãªé ç½®ã«ã¤ãã¦
å®è¡ãã¡ã¤ã«ã®ã¡ã¢ãªé ç½®ã¯ãPIEã§ãªãå ´åãªã³ã¯æã«æ±ºã¾ãã readelfã³ãã³ãã§ã»ã¯ã·ã§ã³æ å ±ã表示ãããã¨ãã¢ãã¬ã¹ã®é ç®ã«å®éã®ã¢ãã¬ã¹ãå ¥ã£ã¦ãããã¨ããããã
$ readelf -S a.out There are 30 section headers, starting at offset 0x1148: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000000400238 00000238 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.ABI-tag NOTE 0000000000400254 00000254 0000000000000020 0000000000000000 A 0 0 4 (snip) [18] .ctors PROGBITS 0000000000600e28 00000e28 0000000000000010 0000000000000000 WA 0 0 8 [19] .dtors PROGBITS 0000000000600e38 00000e38 0000000000000010 0000000000000000 WA 0 0 8 (snip) [25] .bss NOBITS 0000000000601030 00001030 0000000000000010 0000000000000000 WA 0 0 8 (snip)
ãã®ã¢ãã¬ã¹ã¯ããªã³ã«ã使ããªã³ã«ã¹ã¯ãªããã«ãã£ã¦æ±ºãããã¦ããã ã³ã³ãã¤ã«æã«ãªã³ã«ã«verboseãªãã·ã§ã³ãã¤ããã¨ã使ããã¦ãããªã³ã«ã¹ã¯ãªããã表示ããããã¨ãã§ããã
$ gcc -Wl,--verbose hello.c GNU ld (GNU Binutils for Ubuntu) 2.22 Supported emulations: elf_x86_64 elf32_x86_64 elf_i386 i386linux elf_l1om elf_k1om using internal linker script: ================================================== /* Script for -z combreloc: combine and sort reloc sections */ OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") OUTPUT_ARCH(i386:x86-64) ENTRY(_start) SEARCH_DIR("/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib64"); SEARCH_DIR(" =/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SECTIONS { /* Read-only sections, merged into text segment: */ PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS; .interp : { *(.interp) } .note.gnu.build-id : { *(.note.gnu.build-id) } .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } .dynsym : { *(.dynsym) } .dynstr : { *(.dynstr) } (snip) /* Adjust the address for the data segment. We want to adjust up to the same address within the page on the next page up. */ . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE)); (snip) } ================================================== attempt to open /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crt1.o succeeded /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crt1.o attempt to open /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crti.o succeeded /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crti.o (snip) attempt to open /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crtn.o succeeded /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../x86_64-linux-gnu/crtn.o ld-linux-x86-64.so.2 needed by /lib/x86_64-linux-gnu/libc.so.6 found ld-linux-x86-64.so.2 at /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
ãªã³ã«ã¹ã¯ãªãããè¦ãã¨ãread-onlyã»ã¯ã·ã§ã³ãtextã»ã¯ã·ã§ã³ã¨ãã¦0x400000ããé ç½®ããã¦ãããã¨ããããã ã¾ãããã¼ã¿ã»ã°ã¡ã³ãã¯æ¬¡ã®ã¡ã¢ãªãã¼ã¸ã¨ãªãããã«èª¿æ´ããã¦ãããã¨ããããã
対å¿ããã½ã¼ã¹ã³ã¼ãã¯ãGNU binutilsã®ldãã£ã¬ã¯ããªã«ããã ãªã³ã«ã¹ã¯ãªããã®ELFç¨ãã³ãã¬ã¼ãã¯æ¬¡ã®ãã¡ã¤ã«ã§ããã
ãã³ãã¬ã¼ãä¸ã§åç
§ããã¦ããTEXT_START_ADDR
ã¯æ¬¡ã®ãã¡ã¤ã«ã§å®ç¾©ããã¦ããã
TEXT_START_ADDR=0x400000 MAXPAGESIZE="CONSTANT (MAXPAGESIZE)" COMMONPAGESIZE="CONSTANT (COMMONPAGESIZE)"
ããã§ãCONSTANT (MAXPAGESIZE)
ãCONSTANT (COMMONPAGESIZE)
ã®å¤ã«ã¯ã次ã®ãã¡ã¤ã«ã®å®ç¾©ãåç
§ãããã
#define ELF_MAXPAGESIZE 0x200000 #define ELF_MINPAGESIZE 0x1000 #define ELF_COMMONPAGESIZE 0x1000
ä¸ã®å®ç¾©ã«ããã¦ELF_MAXPAGESIZE
ã0x200000ã§ããããããã¼ã¿ã»ã°ã¡ã³ãã¯0x400000+0x200000=0x600000ããå§ã¾ããã¨ã¨ãªãã
ãªãããããã®å¤ã¯ãªã³ã«ãªãã·ã§ã³ã«ããå¤æ´ãããã¨ãå¯è½ã§ããã
$ gcc -Wl,-Ttext-segment=0x8048000,-z,max-page-size=0x1000 hello.c $ readelf -S a.out There are 30 section headers, starting at offset 0x1158: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000008048238 00000238 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.ABI-tag NOTE 0000000008048254 00000254 0000000000000020 0000000000000000 A 0 0 4 ... [18] .ctors PROGBITS 0000000008049e28 00000e28 0000000000000010 0000000000000000 WA 0 0 8 [19] .dtors PROGBITS 0000000008049e38 00000e38 0000000000000010 0000000000000000 WA 0 0 8 ...
å ±æã©ã¤ãã©ãªã®ã¡ã¢ãªé ç½®ã«ã¤ãã¦
å ±æã©ã¤ãã©ãªã¯ãå®è¡ãã¡ã¤ã«ã§æå®ãããELFã¤ã³ã¿ããªã¿ï¼/lib/ld-linux.so.2ï¼ãmmapã«ããé ç½®ããã å ·ä½çã«ã¯ã次ã®ãããªé åºã§é¢æ°ãå¼ã°ãã¦ããã
[sysdeps/x86_64/dl-machine.h] _start [elf/rtld.c] _dl_start [elf/rtld.c] _dl_start_final [elf/dl-sysdep.c] _dl_sysdep_start [elf/rtld.c] dl_main [elf/dl-deps.c] _dl_map_object_deps [include/dlfcn.h] _dl_catch_error [elf/dl-deps.c] openaux [elf/dl-load.c] _dl_map_object [elf/dl-load.c] _dl_map_object_from_fd
setarchã³ãã³ãã§ASLRãç¡å¹åããä¸ã§ãstraceã³ãã³ãã使ãå®è¡æã®ã·ã¹ãã ã³ã¼ã«ããã¬ã¼ã¹ãã¦ã¿ãã
$ setarch x86_64 -R strace ./a.out >/dev/null execve("./a.out", ["./a.out"], [/* 18 vars */]) = 0 brk(0) = 0x1402000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff8000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=26780, ...}) = 0 mmap(NULL, 26780, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7ff1000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\30\2\0\0\0\0\0"..., 832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1815224, ...}) = 0 mmap(NULL, 3929304, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7a1a000 mprotect(0x7ffff7bcf000, 2097152, PROT_NONE) = 0 mmap(0x7ffff7dcf000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b5000) = 0x7ffff7dcf000 mmap(0x7ffff7dd5000, 17624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7dd5000 close(3) = 0 ... write(1, "Hello, world!\n", 14) = 14 exit_group(0) = ?
ä¸ã®çµæãããlibcã®ã¡ã¢ãªã確ä¿ããéã«ãããmmapã®ç¬¬ä¸å¼æ°ã¯NULLã¨ãªã£ã¦ãããå®éã«é ç½®ãããã¢ãã¬ã¹0x7ffff7a1a000ã¯mmapã決ãã¦ãããã¨ããããã
mmapã確ä¿ããã¡ã¢ãªã¢ãã¬ã¹ã®åæå¤ã¯ãLinuxã«ã¼ãã«ãELFå®è¡ãã¡ã¤ã«ãèªã¿è¾¼ãéã«æ±ºããããã å ·ä½çã«ã¯ã次ã®ãããªé åºã§é¢æ°ãå¼ã°ãã¦ããã
[fs/binfmt_elf.c] load_elf_binary [fs/exec.c] setup_new_exec [arch/x86/mm/mmap.c] arch_pick_mmap_layout [arch/x86/mm/mmap.c] mmap_base
ããã§ãmmap_baseé¢æ°ã¯æ¬¡ã®ããã«ãªã£ã¦ããã
54 #define MIN_GAP (128*1024*1024UL + stack_maxrandom_size()) 55 #define MAX_GAP (TASK_SIZE/6*5) ... 85 static unsigned long mmap_base(void) 86 { 87 unsigned long gap = rlimit(RLIMIT_STACK); 88 89 if (gap < MIN_GAP) 90 gap = MIN_GAP; 91 else if (gap > MAX_GAP) 92 gap = MAX_GAP; 93 94 return PAGE_ALIGN(TASK_SIZE - gap - mmap_rnd()); 95 }
ASLRãç¡å¹ã®å ´åãstack_maxrandom_size()
ãã¤mmap_rnd() == 0
ã¨ãªãã
ããã§ãrlimit(RLIMIT_STACK)
ã®å¤ãulimitã³ãã³ãã§èª¿ã¹ã¦ã¿ãã¨æ¬¡ã®ããã«ãªãã
$ ulimit -s 8192
ulimitã³ãã³ãã¯ãããã¤ãåä½ãrlimit(RLIMIT_STACK)
ã¯ãã¤ãåä½ã§ããããããã®å ´ågap == 8192*1024
ã¨ãªãã
ããã¯MIN_GAP
ãããå°ãããããgapã¯MIN_GAP
ã«è£æ£ãããã
ã¾ããTASK_SIZE
ã«é¢ããå®ç¾©ãã¾ã¨ããã¨æ¬¡ã®ããã«ãªãã
- Linux/arch/x86/include/asm/processor.h
- Linux/arch/x86/include/asm/page_types.h
- Linux/include/uapi/linux/const.h
833 #ifdef CONFIG_X86_32 ... 893 #else 894 /* 895 * User space process size. 47bits minus one guard page. 896 */ 897 #define TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE) 898 899 /* This decides where the kernel will search for a free chunk of vm 900 * space during mmap's. 901 */ 902 #define IA32_PAGE_OFFSET ((current->personality & ADDR_LIMIT_3GB) ? \ 903 0xc0000000 : 0xFFFFe000) 904 905 #define TASK_SIZE (test_thread_flag(TIF_ADDR32) ? \ 906 IA32_PAGE_OFFSET : TASK_SIZE_MAX) 907 #define TASK_SIZE_OF(child) ((test_tsk_thread_flag(child, TIF_ADDR32)) ? \ 908 IA32_PAGE_OFFSET : TASK_SIZE_MAX) 909 910 #define STACK_TOP TASK_SIZE 911 #define STACK_TOP_MAX TASK_SIZE_MAX ... 935 #endif /* CONFIG_X86_64 */
7 /* PAGE_SHIFT determines the page size */ 8 #define PAGE_SHIFT 12 9 #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT) 10 #define PAGE_MASK (~(PAGE_SIZE-1))
19 #define __AC(X,Y) (X##Y) 20 #define _AC(X,Y) __AC(X,Y)
以ä¸ããã¨ã«ãmmap_baseãè¿ãã¢ãã¬ã¹ãè¨ç®ãã¦ã¿ãã
$ python Python 2.7.3 (default, Feb 27 2014, 19:58:35) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> PAGE_SHIFT = 12 >>> PAGE_SIZE = 1L << PAGE_SHIFT >>> TASK_SIZE_MAX = (1L << 47) - PAGE_SIZE >>> TASK_SIZE = TASK_SIZE_MAX >>> MIN_GAP = 128*1024*1024L >>> hex(TASK_SIZE) '0x7ffffffff000L' >>> hex(TASK_SIZE - MIN_GAP) '0x7ffff7fff000L'
ããã¯ãä¸çªæåã«mmapã§é ç½®ãããld-linux.soã®ã¡ã¢ãªé åã®åºã¨ãªã£ã¦ããã
ã¹ã¿ãã¯é åã®ã¡ã¢ãªé ç½®ã«ã¤ãã¦
ã¹ã¿ãã¯é åã®ã¡ã¢ãªé ç½®ããLinuxã«ã¼ãã«ãELFå®è¡ãã¡ã¤ã«ãèªã¿è¾¼ãéã«æ±ºããããã å ·ä½çã«ã¯ãbinfmt_elf.cã®load_elf_binaryé¢æ°ã«ãã次ã®ã³ã¼ãã対å¿ããã
737 /* Do this so that we can load the interpreter, if need be. We will 738 change some of these later */ 739 retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), 740 executable_stack); 741 if (retval < 0) { 742 send_sig(SIGKILL, current, 0); 743 goto out_free_dentry; 744 } 745 746 current->mm->start_stack = bprm->p;
ASLRãç¡å¹ãªå ´åãrandomize_stack_top(STACK_TOP) == STACK_TOP
ã¨ãªãã
å
±æã©ã¤ãã©ãªã®ãã¼ãã§ç¤ºããå®ç¾©ãåç
§ããã¨ãSTACK_TOP == TASK_SIZE == 0x7ffffffff000
ã§ãããã¨ããããã
ããã¯ã¹ã¿ãã¯é åã®åºã¨ãªã£ã¦ããã
ãã®å¾ã¯ãsetup_arg_pagesé¢æ°ã«ãã次ã®ã³ã¼ãã«ããã¹ã¿ãã¯ãµã¤ãºã決ãããããã®åã®é åã確ä¿ãããã
723 stack_expand = 131072UL; /* randomly 32*4k (or 2*64k) pages */ 724 stack_size = vma->vm_end - vma->vm_start; 725 /* 726 * Align this down to a page boundary as expand_stack 727 * will align it up. 728 */ 729 rlim_stack = rlimit(RLIMIT_STACK) & PAGE_MASK; 730 #ifdef CONFIG_STACK_GROWSUP 731 if (stack_size + stack_expand > rlim_stack) 732 stack_base = vma->vm_start + rlim_stack; 733 else 734 stack_base = vma->vm_end + stack_expand; 735 #else 736 if (stack_size + stack_expand > rlim_stack) 737 stack_base = vma->vm_end - rlim_stack; 738 else 739 stack_base = vma->vm_start - stack_expand; 740 #endif 741 current->mm->start_stack = bprm->p; 742 ret = expand_stack(vma, stack_base);
ããã§ã¯stack_expand = 131072UL == 0x20000UL
ãã¤rlim_stack == 0x800000
ã§ãããããstack_base == vma->vm_start - stack_expand
ã¨ãªãã
ããã«guard pageã¨ãã¦ç¢ºä¿ãããé åã®ãµã¤ãº0x1000ãããã«å¼ãã¨0x7ffffffde000ã¨ãªãã確èªããã¹ã¿ãã¯é åã®ã¢ãã¬ã¹ã¨ä¸è´ããã
ãã¼ãé åã®ã¡ã¢ãªé ç½®ã«ã¤ãã¦
ãã¼ãé åã®ãã¼ã¹ã¢ãã¬ã¹ã¯ãåºæ¬çã«ã¯bssã»ã°ã¡ã³ãã®ãããã¼ã¸ã®æ¬¡ã®ãã¼ã¸ã¨ãªãã ããã¯ããã¼ãé åãbrkã·ã¹ãã ã³ã¼ã«ã使ã£ã¦ç¢ºä¿ãããããã§ããã brkã·ã¹ãã ã³ã¼ã«ã¯ãã¼ã¿ã»ã°ã¡ã³ãã®å¢çãå¤æ´ãããã®ã§ããããã¼ãé åã¯ãã®å¢çãæ¡å¼µããå½¢ã§ç¢ºä¿ãããã
ãã¼ãé åã®ç¢ºä¿ã¯ãåãã¦mallocé¢æ°ãå¼ã°ããã¿ã¤ãã³ã°ã§è¡ãããã å ·ä½çã«ã¯ãmallocã®ã¨ã¤ãªã¢ã¹ã§ããpublic_mALLOcããã_int_mallocãsYSMALLOcã®é ã®é²ã¿ã次ã®ã³ã¼ãã§ã¡ã¢ãªé åã確ä¿ããããã¨ã«ãªãã
/* Request enough space for nb + pad + overhead */ size = nb + mp_.top_pad + MINSIZE; /* If contiguous, we can subtract out existing space that we hope to combine with new space. We add it back later only if we don't actually get contiguous space. */ if (contiguous(av)) size -= old_size; /* Round to a multiple of page size. If MORECORE is not contiguous, this ensures that we only call it with whole-page arguments. And if MORECORE is contiguous and this is not first time through, this preserves page-alignment of previous calls. Otherwise, we correct to page-align below. */ size = (size + pagemask) & ~pagemask; /* Don't try to call MORECORE if argument is so big as to appear negative. Note that since mmap takes size_t arg, it may succeed below even if we cannot call MORECORE. */ if (size > 0) brk = (char*)(MORECORE(size));
ããã§ãnb + mp_.top_pad + MINSIZE
ã¯ãmallocã§è¦æ±ããããµã¤ãºï¼é åãåå¾ããéã®ãã¼ã¹ãµã¤ãºï¼malloc_chunkã®æå°ãµã¤ãºããæå³ããã
æåã«æ¸ããã³ã¼ãã§ã¯nb == 20
ã§ãããmalloc_chunkã®å®ç¾©ããMINSIZE == 32
ã§ããã
ããã¦ãmp_.top_pad
ã®ããã©ã«ãå¤ã¯DEFAULT_TOP_PAD
ã§ãããããã¯æ¬¡ã®ããã«å®ç¾©ããã¦ããã
#ifndef DEFAULT_TOP_PAD # define DEFAULT_TOP_PAD 131072 #endif
ãããã£ã¦ãsize == 20 + 131072 + 32 == 0x20034
ã¨ãªãããã¼ã¸ãµã¤ãºã®åæ°ã«æãããããã¨ã§ããã¯0x21000ã¨ãªãã
ã¡ã¢ãªã®ç¢ºä¿ã¯MORECORE(size)
ã«ããè¡ãããããããã«é¢ããå®ç¾©ãã¾ã¨ããã¨æ¬¡ã®ããã«ãªãã
#define MORECORE (*__morecore) void *(*__morecore)(ptrdiff_t) = __default_morecore;
/* Allocate INCREMENT more bytes of data space, and return the start of data space, or NULL on errors. If INCREMENT is negative, shrink data space. */ __malloc_ptr_t __default_morecore (increment) __malloc_ptrdiff_t increment; { __malloc_ptr_t result = (__malloc_ptr_t) __sbrk (increment); if (result == (__malloc_ptr_t) -1) return NULL; return result; } libc_hidden_def (__default_morecore)
/* Extend the process's data space by INCREMENT. If INCREMENT is negative, shrink data space by - INCREMENT. Return start of new space allocated, or -1 for errors. */ void * __sbrk (intptr_t increment) { void *oldbrk; /* If this is not part of the dynamic library or the library is used via dynamic loading in a statically linked program update __curbrk from the kernel's brk value. That way two separate instances of __brk and __sbrk can share the heap, returning interleaved pieces of it. */ if (__curbrk == NULL || __libc_multiple_libcs) if (__brk (0) < 0) /* Initialize the break. */ return (void *) -1; if (increment == 0) return __curbrk; oldbrk = __curbrk; if ((increment > 0 ? ((uintptr_t) oldbrk + (uintptr_t) increment < (uintptr_t) oldbrk) : ((uintptr_t) oldbrk < (uintptr_t) -increment)) || __brk (oldbrk + increment) < 0) return (void *) -1; return oldbrk; } libc_hidden_def (__sbrk) weak_alias (__sbrk, sbrk)
ã¤ã¾ããsbrké¢æ°ãçµç±ãã¦brkã·ã¹ãã ã³ã¼ã«ãå¼ã°ãããã¨ã«ãªãã brkã·ã¹ãã ã³ã¼ã«ã¯å¤æ´å¾ã®å¢çã®ã¢ãã¬ã¹ãå¼æ°ã«åãã ä¸æ¹ãsbrké¢æ°ã¯å¢åãå¼æ°ã«åããbrkã·ã¹ãã ã³ã¼ã«ã2度å¼ã¶ãã¨ã«ããå¢çã®ã¢ãã¬ã¹ãå¢æ¸ãããé¢æ°ã§ããã
setarchã³ãã³ãã§ASLRãç¡å¹ã«ããä¸ã§ãstraceã³ãã³ãã«ããå®è¡æã®ã·ã¹ãã ã³ã¼ã«ããã¬ã¼ã¹ãã¦ã¿ãã
$ setarch x86_64 -R strace ./a.out >/dev/null execve("./a.out", ["./a.out"], [/* 18 vars */]) = 0 ... brk(0) = 0x602000 brk(0x623000) = 0x623000 fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0 ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0x7fffffffe478) = -1 ENOTTY (Inappropriate ioctl for device) mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7ff7000 write(1, "Hello, world!\n", 14) = 14 exit_group(0) = ?
ä¸ã®çµæãããmallocãå¼ã°ããã¿ã¤ãã³ã°ã§brkã·ã¹ãã ã³ã¼ã«ã2度å¼ã°ãã0x21000ã ããã¼ãé åã確ä¿ããã¦ãããã¨ããããã å ·ä½çã«ã¯ãä¸åº¦ç®ã®brkã§ç¾å¨ã®å¢çã¢ãã¬ã¹ãåå¾ããäºåº¦ç®ã®brkã§å¢æ¸å¾ã®å¢çã¢ãã¬ã¹ãã»ããããã¦ããã
ASLRã«ããã¢ãã¬ã¹ã®ã©ã³ãã å
åºæ¬çã«ãbinfmt_elf.cã®load_elf_binaryé¢æ°ã§ELFãã¡ã¤ã«ãèªã¿è¾¼ã¾ããéã«è¡ãããã
PIEã®å ´åã®å®è¡ãã¡ã¤ã«
load_elf_binaryé¢æ°ã®æ¬¡ã®ç®æãé¢ä¿ããã
795 vaddr = elf_ppnt->p_vaddr; 796 if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) { 797 elf_flags |= MAP_FIXED; 798 } else if (loc->elf_ex.e_type == ET_DYN) { 799 /* Try and get dynamic programs out of the way of the 800 * default mmap base, as well as whatever program they 801 * might try to exec. This is because the brk will 802 * follow the loader, and is not movable. */ 803 #ifdef CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE 804 /* Memory randomization might have been switched off 805 * in runtime via sysctl or explicit setting of 806 * personality flags. 807 * If that is the case, retain the original non-zero 808 * load_bias value in order to establish proper 809 * non-randomized mappings. 810 */ 811 if (current->flags & PF_RANDOMIZE) 812 load_bias = 0; 813 else 814 load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr); 815 #else 816 load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr); 817 #endif 818 } 819 820 error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, 821 elf_prot, elf_flags, 0); 822 if (BAD_ADDR(error)) { 823 send_sig(SIGKILL, current, 0); 824 retval = IS_ERR((void *)error) ? 825 PTR_ERR((void*)error) : -EINVAL; 826 goto out_free_dentry; 827 }
ããã¯ELFã®ããã°ã©ã ãããã§ã¿ã¤ããPT_LOADã¨ãªã£ã¦ããã¨ã³ããªãèªã¿è¾¼ãç®æã§ããã
PIEã®å ´åELFèªèº«ã®ã¿ã¤ãã¯ET_DYNã¨ãªããããload_bias
ã¯0ã¨ãªãã
ããã¦ã次ã«ç¤ºãelf_mapé¢æ°ãããmmapã«ãã£ã¦ã¡ã¢ãªé åã確ä¿ãããã
335 static unsigned long elf_map(struct file *filep, unsigned long addr, 336 struct elf_phdr *eppnt, int prot, int type, 337 unsigned long total_size) 338 { 339 unsigned long map_addr; 340 unsigned long size = eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr); 341 unsigned long off = eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr); 342 addr = ELF_PAGESTART(addr); 343 size = ELF_PAGEALIGN(size); 344 345 /* mmap() will return -EINVAL if given a zero size, but a 346 * segment with zero filesize is perfectly valid */ 347 if (!size) 348 return addr; 349 350 /* 351 * total_size is the size of the ELF (interpreter) image. 352 * The _first_ mmap needs to know the full size, otherwise 353 * randomization might put this image into an overlapping 354 * position with the ELF binary image. (since size < total_size) 355 * So we first map the 'big' image - and unmap the remainder at 356 * the end. (which unmap is needed for ELF images with holes.) 357 */ 358 if (total_size) { 359 total_size = ELF_PAGEALIGN(total_size); 360 map_addr = vm_mmap(filep, addr, total_size, prot, type, off); 361 if (!BAD_ADDR(map_addr)) 362 vm_munmap(map_addr+size, total_size-size); 363 } else 364 map_addr = vm_mmap(filep, addr, size, prot, type, off); 365 366 return(map_addr); 367 }
ããã§ãPIEã§ã³ã³ãã¤ã«ããå®è¡ãã¡ã¤ã«ã®ããã°ã©ã ãããã調ã¹ã¦ã¿ãã
$ gcc -fPIE -pie hello.c $ readelf -l a.out Elf file type is DYN (Shared object file) Entry point 0x6c0 There are 9 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000001f8 0x00000000000001f8 R E 8 INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238 0x000000000000001c 0x000000000000001c R 1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x00000000000009cc 0x00000000000009cc R E 200000 LOAD 0x0000000000000e00 0x0000000000200e00 0x0000000000200e00 0x0000000000000238 0x0000000000000248 RW 200000 DYNAMIC 0x0000000000000e28 0x0000000000200e28 0x0000000000200e28 0x0000000000000190 0x0000000000000190 RW 8 NOTE 0x0000000000000254 0x0000000000000254 0x0000000000000254 0x0000000000000044 0x0000000000000044 R 4 GNU_EH_FRAME 0x00000000000008fc 0x00000000000008fc 0x00000000000008fc 0x000000000000002c 0x000000000000002c R 4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 8 GNU_RELRO 0x0000000000000e00 0x0000000000200e00 0x0000000000200e00 0x0000000000000200 0x0000000000000200 R 1 Section to Segment mapping: Segment Sections... 00 01 .interp 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 03 .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 04 .dynamic 05 .note.ABI-tag .note.gnu.build-id 06 .eh_frame_hdr 07 08 .ctors .dtors .jcr .dynamic .got
LOAD
ã¨ãªã£ã¦ããã¨ã³ããªã¯äºã¤ãããããããå®è¡å¯è½é åãå®è¡ä¸å¯é åã«å¯¾å¿ãã¦ããã
ã¾ããVirtAddr (vaddr)ãFileSiz (size) ã¯ãããã0ã0x9ccããã³0x200e00ã0x238ã¨ãªã£ã¦ããã
ãããã£ã¦ããã¼ã¸å¢çã¸ã®ã¢ã©ã¤ã³ã¡ã³ããèæ
®ããã¨ãã¾ãaddr = mmap(0, 0x1000, ...)
ã«ç¸å½ããå¦çãå¼ã°ãããã¨ã«ãªãã
ãã®å¾ãload_addr_set = 1
ããã³load_bias = addr
ãè¡ãããããã次ã®PT_LOADã¨ã³ããªã«ã¤ãã¦ã¯elf_flagsã«MAP_FIXEDãã»ãããããä¸ã§mmap(load_bias + 0x200000, 0x1000, ...)
ãå¼ã°ããã
ãã£ã¦ãã©ã³ãã åãè¡ãããbitæ°ã¯mmapã§ç¢ºä¿ãããå ´åã®bitæ°ã¨åãã§ããã
å
·ä½çãªbitæ°ã«ã¤ãã¦ã¯æ¬¡ã§èª¬æããã
å ±æã©ã¤ãã©ãªãªã©mmapã§ç¢ºä¿ãããé å
load_elf_binaryé¢æ°ãããsetup_new_execé¢æ°ãarch_pick_mmap_layouté¢æ°ãmmap_baseé¢æ°ã®é ã«é²ã¿ã©ã³ãã åãããã 32bitç°å¢ã®å ´å8bitã64bitç°å¢ã®å ´å28bitã
735 setup_new_exec(bprm);
1103 arch_pick_mmap_layout(current->mm);
68 static unsigned long mmap_rnd(void) 69 { 70 unsigned long rnd = 0; 71 72 /* 73 * 8 bits of randomness in 32bit mmaps, 20 address space bits 74 * 28 bits of randomness in 64bit mmaps, 40 address space bits 75 */ 76 if (current->flags & PF_RANDOMIZE) { 77 if (mmap_is_ia32()) 78 rnd = get_random_int() % (1<<8); 79 else 80 rnd = get_random_int() % (1<<28); 81 } 82 return rnd << PAGE_SHIFT; 83 } 84 85 static unsigned long mmap_base(void) 86 { 87 unsigned long gap = rlimit(RLIMIT_STACK); 88 89 if (gap < MIN_GAP) 90 gap = MIN_GAP; 91 else if (gap > MAX_GAP) 92 gap = MAX_GAP; 93 94 return PAGE_ALIGN(TASK_SIZE - gap - mmap_rnd()); 95 } ... 109 /* 110 * This function, called very early during the creation of a new 111 * process VM image, sets up which VM layout function to use: 112 */ 113 void arch_pick_mmap_layout(struct mm_struct *mm) 114 { 115 mm->mmap_legacy_base = mmap_legacy_base(); 116 mm->mmap_base = mmap_base(); 117 118 if (mmap_is_legacy()) { 119 mm->mmap_base = mm->mmap_legacy_base; 120 mm->get_unmapped_area = arch_get_unmapped_area; 121 } else { 122 mm->get_unmapped_area = arch_get_unmapped_area_topdown; 123 } 124 }
ã¹ã¿ãã¯é å
load_elf_binaryé¢æ°ã«ããã¦ãrandomize_stack_topé¢æ°ã«ãããã¼ã¸åä½ã§ã©ã³ãã åãè¡ãããã ãã ããã©ã³ãã å¤ãunsigned intããªãã¡32bit符å·ãªãæ´æ°ã¨ãã¦å®ç¾©ããã¦ãããããã©ã³ãã åã®ä¸éã¯20bitã¨ãªãã ããã«ãsetup_arg_pagesé¢æ°å ã§ãarch_align_stacké¢æ°ã«ããã©ã³ãã åãè¡ããããããã¼ã¸å¢çã¸ã®ã¢ã©ã¤ã³ã¡ã³ããè¡ãããããå ¨ä½ã«ã¯å½±é¿ããªãã 32bitç°å¢ã®å ´å11bitã64bitç°å¢ã®å ´å20bitã
- Linux/fs/binfmt_elf.c
- Linux/fs/exec.c
- Linux/arch/x86/kernel/process.c
- Linux/arch/x86/include/asm/elf.h
- Linux/arch/x86/include/asm/page_types.h
- Linux/include/linux/mm.h
- Linux/include/linux/kernel.h
- Linux/include/uapi/linux/kernel.h
555 static unsigned long randomize_stack_top(unsigned long stack_top) 556 { 557 unsigned int random_variable = 0; 558 559 if ((current->flags & PF_RANDOMIZE) && 560 !(current->personality & ADDR_NO_RANDOMIZE)) { 561 random_variable = get_random_int() & STACK_RND_MASK; 562 random_variable <<= PAGE_SHIFT; 563 } 564 #ifdef CONFIG_STACK_GROWSUP 565 return PAGE_ALIGN(stack_top) + random_variable; 566 #else 567 return PAGE_ALIGN(stack_top) - random_variable; 568 #endif 569 } 570 571 static int load_elf_binary(struct linux_binprm *bprm) 572 { ... 737 /* Do this so that we can load the interpreter, if need be. We will 738 change some of these later */ 739 retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), 740 executable_stack); 741 if (retval < 0) { 742 send_sig(SIGKILL, current, 0); 743 goto out_free_dentry; 744 } 745 746 current->mm->start_stack = bprm->p; ... 1007 }
675 stack_top = arch_align_stack(stack_top); 676 stack_top = PAGE_ALIGN(stack_top); 677 678 if (unlikely(stack_top < mmap_min_addr) || 679 unlikely(vma->vm_end - vma->vm_start >= stack_top - mmap_min_addr)) 680 return -ENOMEM; 681 682 stack_shift = vma->vm_end - stack_top; 683 684 bprm->p -= stack_shift; 685 mm->arg_start = bprm->p;
456 unsigned long arch_align_stack(unsigned long sp) 457 { 458 if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) 459 sp -= get_random_int() % 8192; 460 return sp & ~0xf; 461 }
290 /* 1GB for 64bit, 8MB for 32bit */ 291 #define STACK_RND_MASK (test_thread_flag(TIF_ADDR32) ? 0x7ff : 0x3fffff)
8 #define PAGE_SHIFT 12 9 #define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
73 #define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
49 #define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
9 #define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) 10 #define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
ãã¼ãé å
load_elf_binaryé¢æ°ã«ããã¦ãarch_randomize_brké¢æ°ã«ãã£ã¦ã©ã³ãã åãããã 32bitç°å¢ã64bitç°å¢ã©ã¡ãã®å ´åã13bitã
957 #ifdef arch_randomize_brk 958 if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) { 959 current->mm->brk = current->mm->start_brk = 960 arch_randomize_brk(current->mm); 961 #ifdef CONFIG_COMPAT_BRK 962 current->brk_randomized = 1; 963 #endif 964 } 965 #endif
463 unsigned long arch_randomize_brk(struct mm_struct *mm) 464 { 465 unsigned long range_end = mm->brk + 0x02000000; 466 return randomize_range(mm->brk, range_end, 0) ? : mm->brk; 467 }
1691 /* 1692 * randomize_range() returns a start address such that 1693 * 1694 * [...... <range> .....] 1695 * start end 1696 * 1697 * a <range> with size "len" starting at the return value is inside in the 1698 * area defined by [start, end], but is otherwise randomized. 1699 */ 1700 unsigned long 1701 randomize_range(unsigned long start, unsigned long end, unsigned long len) 1702 { 1703 unsigned long range = end - len - start; 1704 1705 if (end <= start + len) 1706 return 0; 1707 return PAGE_ALIGN(get_random_int() % range + start); 1708 }
ã©ã³ãã åbitæ°ã®ã¾ã¨ã
PIEå®è¡ãã¡ã¤ã« å ±æã©ã¤ãã©ãª | ã¹ã¿ãã¯é å | ãã¼ãé å | |
---|---|---|---|
32bit | 8bit | 11bit | 13bit |
64bit | 28bit | 20bit | 13bit |
é¢é£ãªã³ã¯
- H. J. Lu - PATCH: Getting MAXPAGESIZE from ELF_MAXPAGESIZE
- ããã°ã©ã ã¯ã©ãåãã®ã? ï½ ELFã®é»éè¡ãããã¾ã¿ã
- malloc(3)ã®ã¡ã¢ãªç®¡çæ§é | VA Linux Systems Japanæ ªå¼ä¼ç¤¾
- ASLR, setarch -RL, prelink, PIE and LD_USE_LOAD_BIAS - memologue
- Linux kernel ASLR Implementation | xorl %eax, %eax
- Stack Smashing as of Today (Black Hat Europe 2009)