16.21 linux-gate.so.1是什么东西 https://scz.617.cn/unix/201205101859.txt Q: $ ldd `which col` linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7e60000) /lib/ld-linux.so.2 (0x80000000) $ find /lib -name linux-gate.so.1 -type f -print $ find /usr/lib -name linux-gate.so.1 -type f -print $ 用ldd时注意到有一个linux-gate.so.1,但在文件系统中找不到,这是神马东西? A: linux-gate.so.1是一个"Virtual Dynamic Shared Object",即vsdo,由内核导出, 被映射到每个进程的地址空间中。vsyscall位于该页。参看: 《Linux系统调用》 在没有ASLR机制的年代,vsyscall page被映射到固定地址(0xffffe000-0xffffefff)。 从Linux 2.6.18开始,该页被映射到一个随机地址: $ cat /proc/self/maps | grep vdso b7eef000-b7ef0000 r-xp b7eef000 00:00 0 [vdso] 过去AT_SYSINFO_EHDR指向vsyscall page起始地址,引入ASLR之后,AT_SYSINFO_EHDR 的值不可信了。但AT_SYSINFO仍指向随机化后的__kernel_vsyscall()。 -------------------------------------------------------------------------- /* * gcc-3.3 -Wall -pipe -O3 -s -o get_vsyscall_page get_vsyscall_page.c */ #include #include #include #include #include #include #include #include unsigned int get_auxv ( Elf32_auxv_t *auxv, unsigned int type ) { unsigned int value = 0xffffffff; for ( ; AT_NULL != auxv->a_type; auxv++ ) { if ( type == auxv->a_type ) { value = auxv->a_un.a_val; break; } } return( value ); } /* end of get_auxv */ int main ( int argc, char * argv[], char * envp[] ) { Elf32_auxv_t *auxv; Elf32_Ehdr *so; Elf32_Shdr *sh; unsigned int size; unsigned char *buf = NULL; int f = -1; while ( NULL != *envp++ ); auxv = ( Elf32_auxv_t * )envp; /* * 从AT_SYSINFO的值推测vsyscall page的起始地址,这是一个经验搞法,不可 * 靠。 */ so = ( Elf32_Ehdr * )( get_auxv( auxv, AT_SYSINFO ) & ~0xFFF ); sh = ( Elf32_Shdr * )( ( unsigned char * )so + so->e_shoff ); /* * 这是山寨搞法。假设Section header table位于最未尾。 */ size = so->e_shoff + ( so->e_shentsize * so->e_shnum ); buf = ( unsigned char * )calloc( size, 1 ); if ( NULL == buf ) { perror( "calloc() failed" ); goto main_exit; } memcpy( buf, so, size ); /* * write( 1, buf, size ); * * $ ./get_vsyscall_page > ... */ f = open( "/tmp/vsyscall_page.so", O_CREAT | O_WRONLY, S_IRWXU ); if ( -1 == f ) { perror( "open() failed" ); goto main_exit; } write( f, buf, size ); main_exit: if ( -1 != f ) { close( f ); f = -1; } if ( NULL != buf ) { free( buf ); buf = NULL; } return( 0 ); } /* end of main */ -------------------------------------------------------------------------- vsyscall page的内容是ELF格式的,可以调用getpagesize()获取页大小,转储该页。 get_vsyscall_page.c取了ELF文件的精确大小,这是出于演示目的,不是必要的。 $ ./get_vsyscall_page $ ls -l /tmp/vsyscall_page.so -rwx------ 1 root root 2216 05-10 17:49 /tmp/vsyscall_page.so* $ file -b /tmp/vsyscall_page.so ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped $ readelf -e /tmp/vsyscall_page.so ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: Intel 80386 Version: 0x1 Entry point address: 0xffffe400 Start of program headers: 52 (bytes into file) Start of section headers: 1696 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 4 Size of section headers: 40 (bytes) Number of section headers: 13 Section header string table index: 12 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .hash HASH ffffe0b4 0000b4 000038 04 A 2 0 4 [ 2] .dynsym DYNSYM ffffe0ec 0000ec 000090 10 A 3 5 4 [ 3] .dynstr STRTAB ffffe17c 00017c 000056 00 A 0 0 1 [ 4] .gnu.version VERSYM ffffe1d2 0001d2 000012 02 A 2 0 2 [ 5] .gnu.version_d VERDEF ffffe1e4 0001e4 000038 00 A 3 2 4 [ 6] .text PROGBITS ffffe400 000400 000060 00 AX 0 0 32 [ 7] .note NOTE ffffe460 000460 000018 00 A 0 0 4 [ 8] .eh_frame_hdr PROGBITS ffffe478 000478 000024 00 A 0 0 4 [ 9] .eh_frame PROGBITS ffffe49c 00049c 00010c 00 A 0 0 4 [10] .dynamic DYNAMIC ffffe5a8 0005a8 000078 08 WA 3 0 4 [11] .useless PROGBITS ffffe620 000620 00000c 04 WA 0 0 4 [12] .shstrtab STRTAB 00000000 00062c 000073 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0xffffe000 0xffffe000 0x0062c 0x0062c R E 0x1000 DYNAMIC 0x0005a8 0xffffe5a8 0xffffe5a8 0x00078 0x00078 R 0x4 NOTE 0x000460 0xffffe460 0xffffe460 0x00018 0x00018 R 0x4 GNU_EH_FRAME 0x000478 0xffffe478 0xffffe478 0x00024 0x00024 R 0x4 Section to Segment mapping: Segment Sections... 00 .hash .dynsym .dynstr .gnu.version .gnu.version_d .text .note .eh_frame_hdr .eh_frame .dynamic .useless 01 .dynamic 02 .note 03 .eh_frame_hdr $ readelf -s /tmp/vsyscall_page.so Symbol table '.dynsym' contains 9 entries: Num: Value Size Type Bind Vis Ndx Name 0: 00000000 0 NOTYPE LOCAL DEFAULT UND 1: ffffe400 0 SECTION LOCAL DEFAULT 6 2: ffffe478 0 SECTION LOCAL DEFAULT 8 3: ffffe49c 0 SECTION LOCAL DEFAULT 9 4: ffffe620 0 SECTION LOCAL DEFAULT 11 5: ffffe400 20 FUNC GLOBAL DEFAULT 6 __kernel_vsyscall@@LINUX_2.5 6: 00000000 0 OBJECT GLOBAL DEFAULT ABS LINUX_2.5 7: ffffe440 7 FUNC GLOBAL DEFAULT 6 __kernel_rt_sigreturn@@LINUX_2.5 8: ffffe420 8 FUNC GLOBAL DEFAULT 6 __kernel_sigreturn@@LINUX_2.5 $ objdump -d -j .text /tmp/vsyscall_page.so /tmp/vsyscall_page.so: file format elf32-i386 Disassembly of section .text: ffffe400 <__kernel_vsyscall>: ffffe400: 51 push %ecx ffffe401: 52 push %edx ffffe402: 55 push %ebp ffffe403: 89 e5 mov %esp,%ebp ffffe405: 0f 34 sysenter ffffe407: 90 nop ffffe408: 90 nop ffffe409: 90 nop ffffe40a: 90 nop ffffe40b: 90 nop ffffe40c: 90 nop ffffe40d: 90 nop ffffe40e: eb f3 jmp ffffe403 <__kernel_vsyscall+0x3> ffffe410: 5d pop %ebp ffffe411: 5a pop %edx ffffe412: 59 pop %ecx ffffe413: c3 ret ffffe414: 90 nop ... ffffe41f: 90 nop ffffe420 <__kernel_sigreturn>: ffffe420: 58 pop %eax ffffe421: b8 77 00 00 00 mov $0x77,%eax ffffe426: cd 80 int $0x80 ffffe428: 90 nop ... ffffe43f: 90 nop ffffe440 <__kernel_rt_sigreturn>: ffffe440: b8 ad 00 00 00 mov $0xad,%eax ffffe445: cd 80 int $0x80 ffffe447: 90 nop ... ffffe45f: 90 nop 可以不用get_vsyscall_page.c转储vsyscall page: $ setarch `uname -m` -R cat /proc/self/maps | grep vdso b7fe4000-b7fe5000 r-xp b7fe4000 00:00 0 [vdso] $ echo 0xb7fe4000 | awk '{printf "%u\n", $1/4096;}' 753636 $ setarch `uname -m` -R dd if=/proc/self/mem of=/tmp/vsyscall_page.dd bs=4096 skip=753636 count=1 $ file -b /tmp/vsyscall_page.dd $ readelf -e /tmp/vsyscall_page.dd $ readelf -s /tmp/vsyscall_page.dd $ objdump -d -j .text /tmp/vsyscall_page.dd