We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
当加载器(ELF Loader)将ELF文件加载到内存后,会初始化进程堆栈,包括argc, argv, 环境变量和辅助向量,初始化后的进程堆栈结构如下(x86_64)
argc
argv
环境变量
辅助向量
x86_64
position content size(bytes) + comment ------------------------------------------------------------------------------------------------- stack pointer -> [argc = number of args] 8 [argv[0](pointer)] 8 [argv[1](pointer)] 8 [argv[...](pointer)] 8 * x [argv[n-1](pointer)] 8 [argv[n](pointer)] 8 (=NULL) [envp[0](pointer)] 8 [envp[1](pointer)] 8 [envp[..](pointer)] 8 * x [envp[term](pointer)] 8 (=NULL) [auxv[0](Elf64_auxv_t)] 16 [auxv[1](Elf64_auxv_t)] 16 [auxv[..](Elf64_auxv_t)] 16 * x [auxv[term](Elf64_auxv_t)] 16 (=NULL) [padding] >= 0 [rand bytes] 16 [String identifying platform] >= 0 [padding for align] >= 0 (sp - (get_random_int() % 8192)) & (~0xf) [argument ASCIIZ strings] >= 0 [environment ASCIIZ str] >= 0
该图主要参考这里, 但进行了修改,因为在本地环境编写代码验证发现[auxv[term]]到[argument ASCIIZ strings]这部分区域的结构并不像参考文章里图示所展示的那样
[auxv[term]]
[argument ASCIIZ strings]
通过翻阅ELF Loader源码linux/fs/binfmt_elf.c,找到与初始化堆栈相关的函数create_elf_tables
create_elf_tables
我把create_elf_tables函数里面操作堆栈的代码圈出如下图所示
标号1处的arch_align_stack函数具体实现如下, 该函数定义在linux/arch/x86/kernel/process.c
unsigned long arch_align_stack(unsigned long sp) { if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space) sp -= get_random_int() % 8192; return sp & ~0xf; }
该函数将堆栈指针向下移动了x(0 <= x <=8192), 分配出[padding for align]这部分空间
标号2的代码为[String identifying platform]在栈上分配空间并进行赋值操作
标号3的代码生成16bytes大小的随机数,然后为[rand bytes]在栈上分配空间并进行赋值操作
标号4的代码
sp = STACK_ADD(p, ei_index)
在栈上分配辅助向量(auxiliary vector)所需存储空间
items = (argc + 1) + (envc + 1) + 1; bprm->p = STACK_ROUND(sp, items);
在栈上分配argc, argv, encironment vector所需存储空间, 值得注意的是这两步都只是移动堆栈指针分配空间,并没有进行赋值初始化数据操作
标号5, 6, 7, 8的代码做了如下操作 5. 初始化栈上argc的值 6. 初始化栈上argv的值 7. 初始化栈上envp的值 8. 初始化栈上auxv的值
通过阅读代码可以看出执行结果与上面的结构图是相匹配的, 可能会有疑惑的地方就是图示中[padding]这一区域, 这一块数据来源如下:
通过上面解释我们可以看到标号4的代码在栈上一次性分配了argc, argv, envp auxv所需要的空间,然后再通过堆栈指针按顺序向上初始化每一块数据, 重点在于分配的空间并不刚好等于所需空间, 因为标号4的分配空间时使用了宏STACK_ROUND, 该宏定义如下
#define STACK_ROUND(sp, items) \ (((unsigned long) (sp - items)) &~ 15UL)
会向下16字节对齐,因此分配的空间可能会比所需空间多,而标号5, 6 ,7 ,8的初始化操作又是从栈顶初始化的,所以最后在[auxvterm]和[rand bytes]这两块区域之间会多出[padding]这块数据
最后编写的测试代码如下
本机环境
$ uname -a Linux debian 4.16.0-2-amd64 #1 SMP Debian 4.16.16-2 (2018-06-22) x86_64 GNU/Linux
#include <stdio.h> #include <stdlib.h> #include <elf.h> #include <string.h> int main(int argc, char** argv) { int i = 0; Elf64_auxv_t *auxv = NULL; long *auxv_start_addr = NULL; long *auxv_end_addr = NULL; long *env_start_addr = NULL; long *env_end_addr = NULL; long *argv_end_addr = NULL; char *platform; long *p = (long *)argv; char *env_str = NULL; unsigned char *rand_bytes = NULL; int arg_num = *(p - 1); printf("arg_num:%d\n", arg_num); for (i = 0;i < arg_num;i++) { printf("arg %d:%s\n", i, *p); p++; } argv_end_addr = p; p++;//skip null printf("\nenvironment vector:\n"); env_str = (char *)*p; env_start_addr = p; while (*p) { printf("%s\n", *p); p++; } env_end_addr = p; p++;//skip null printf("\nauxiliary vectors:\n"); auxv = (Elf64_auxv_t *)p; auxv_start_addr = (long *)auxv; while (auxv->a_type != AT_NULL) { printf("auxv type:%ld, val:0x%x\n", auxv->a_type, auxv->a_un.a_val); if (auxv->a_type == AT_PLATFORM) { platform = (char *)(auxv->a_un.a_val); } auxv++; } auxv_end_addr = (long *)auxv; auxv++;//skip AT_NULL vector printf("\naddress:%lx, env0_ascii_string:%s\n", env_str, env_str); printf("address:%lx ~ %lx, argument0_ascii_string:%s\n", *argv, argv[0] + strlen(argv[0]),*argv); printf("address:%lx ~ %lx, here contain %ld bytes data\n", platform + strlen(platform) + 1, (char *)*argv - 1, (long)*argv - (long)platform - strlen(platform) - 1); printf("address:%lx ~ %lx, String identifying platform:%s\n", platform, platform + strlen(platform), platform); rand_bytes = platform - 16; printf("address:%lx ~ %lx, rand_bytes[16]:", rand_bytes, rand_bytes + 15); for (i = 0;i < 16;i++) { printf(" %d", *(rand_bytes + i)); } printf("\naddress:%lx ~ %lx, auxivilary vector\n", auxv_start_addr, auxv_end_addr); printf("address:%lx ~ %lx, environment vector\n", env_start_addr, env_end_addr); printf("address:%lx ~ %lx, argv\n", argv, argv_end_addr); printf("address:%lx, argc\n", argv - 1); return 0; }
本机运行结果如下:
$ ./a.out arg_num:1 arg 0:./a.out environment vector: LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36: XDG_MENU_PREFIX=gnome- LANG=en_US.UTF-8 GDM_LANG=en_US.UTF-8 DISPLAY=:0 COLORTERM=truecolor DESKTOP_AUTOSTART_ID=1067f02f661904b013153191243121980500000010550007 USERNAME=cike XDG_VTNR=2 SSH_AUTH_SOCK=/run/user/1000/keyring/ssh XDG_SESSION_ID=2 USER=cike DESKTOP_SESSION=gnome WAYLAND_DISPLAY=wayland-0 GNOME_TERMINAL_SCREEN=/org/gnome/Terminal/screen/1209a8c3_5cad_4323_b8e8_171909ba8809 PWD=/home/cike/workspace/linker_loader HOME=/home/cike XDG_SESSION_TYPE=wayland XDG_SESSION_DESKTOP=gnome TERM=xterm-256color SHELL=/bin/bash VTE_VERSION=5202 QT_IM_MODULE=fcitx XMODIFIERS=@im=fcitx XDG_CURRENT_DESKTOP=GNOME GNOME_TERMINAL_SERVICE=:1.107 XDG_SEAT=seat0 SHLVL=2 GDMSESSION=gnome GNOME_DESKTOP_SESSION_ID=this-is-deprecated LOGNAME=cike DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus XDG_RUNTIME_DIR=/run/user/1000 PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games SESSION_MANAGER=local/debian:@/tmp/.ICE-unix/1055,unix/debian:/tmp/.ICE-unix/1055 GTK_IM_MODULE=fcitx OLDPWD=/home/cike/workspace/test _=./a.out auxiliary vectors: auxv type:33, val:0x121d1000 auxv type:16, val:0xbfebfbff auxv type:6, val:0x1000 auxv type:17, val:0x64 auxv type:3, val:0xdb1e4040 auxv type:4, val:0x38 auxv type:5, val:0x9 auxv type:7, val:0x30912000 auxv type:8, val:0x0 auxv type:9, val:0xdb1e45d0 auxv type:11, val:0x3e8 auxv type:12, val:0x3e8 auxv type:13, val:0x3e8 auxv type:14, val:0x3e8 auxv type:23, val:0x0 auxv type:25, val:0x1200c7c9 auxv type:26, val:0x0 auxv type:31, val:0x1200eff0 auxv type:15, val:0x1200c7d9 address:7ffd1200e5fb, env0_ascii_string:LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36: address:7ffd1200e5f3 ~ 7ffd1200e5fa, argument0_ascii_string:./a.out address:7ffd1200c7e0 ~ 7ffd1200e5f2, here contain 7699 bytes data address:7ffd1200c7d9 ~ 7ffd1200c7df, String identifying platform:x86_64 address:7ffd1200c7c9 ~ 7ffd1200c7d8, rand_bytes[16]: 193 130 184 246 204 82 221 150 58 249 82 90 18 26 172 195 address:7ffd1200c680 ~ 7ffd1200c7b0, auxivilary vector address:7ffd1200c548 ~ 7ffd1200c678, environment vector address:7ffd1200c538 ~ 7ffd1200c540, argv address:7ffd1200c530, argc
参考: About ELF Auxiliary Vectors 《程序员自我修养》7.5章节
The text was updated successfully, but these errors were encountered:
---- 该图主要参考这里, 但进行了修改 请教一下, 这个图是根据后面的程序自己修改画出来的吗? 还是有工具或者命令直接查出来
Sorry, something went wrong.
根据后面实际分析自己修改画出来的
No branches or pull requests
当加载器(ELF Loader)将ELF文件加载到内存后,会初始化进程堆栈,包括
argc
,argv
,环境变量
和辅助向量
,初始化后的进程堆栈结构如下(x86_64
)该图主要参考这里, 但进行了修改,因为在本地环境编写代码验证发现
[auxv[term]]
到[argument ASCIIZ strings]
这部分区域的结构并不像参考文章里图示所展示的那样通过翻阅ELF Loader源码linux/fs/binfmt_elf.c,找到与初始化堆栈相关的函数
create_elf_tables
我把
create_elf_tables
函数里面操作堆栈的代码圈出如下图所示标号1处的arch_align_stack函数具体实现如下, 该函数定义在linux/arch/x86/kernel/process.c
该函数将堆栈指针向下移动了x(0 <= x <=8192), 分配出[padding for align]这部分空间
标号2的代码为[String identifying platform]在栈上分配空间并进行赋值操作
标号3的代码生成16bytes大小的随机数,然后为[rand bytes]在栈上分配空间并进行赋值操作
标号4的代码
在栈上分配辅助向量(auxiliary vector)所需存储空间
在栈上分配argc, argv, encironment vector所需存储空间, 值得注意的是这两步都只是移动堆栈指针分配空间,并没有进行赋值初始化数据操作
标号5, 6, 7, 8的代码做了如下操作
5. 初始化栈上argc的值
6. 初始化栈上argv的值
7. 初始化栈上envp的值
8. 初始化栈上auxv的值
通过阅读代码可以看出执行结果与上面的结构图是相匹配的, 可能会有疑惑的地方就是图示中[padding]这一区域, 这一块数据来源如下:
通过上面解释我们可以看到标号4的代码在栈上一次性分配了argc, argv, envp auxv所需要的空间,然后再通过堆栈指针按顺序向上初始化每一块数据, 重点在于分配的空间并不刚好等于所需空间, 因为标号4的分配空间时使用了宏STACK_ROUND, 该宏定义如下
会向下16字节对齐,因此分配的空间可能会比所需空间多,而标号5, 6 ,7 ,8的初始化操作又是从栈顶初始化的,所以最后在[auxvterm]和[rand bytes]这两块区域之间会多出[padding]这块数据
最后编写的测试代码如下
本机环境
本机运行结果如下:
参考:
About ELF Auxiliary Vectors
《程序员自我修养》7.5章节
The text was updated successfully, but these errors were encountered: