- 一、实验步骤
- 二、实验分析
- 三、总结
先把menu删掉,再克隆一个新menu,用test_exec.c覆盖掉test.c,仍命名为test.c。执行结果hello world! 是新加载的一个可执行程序输出的。
-S -s单步调试,窗口被冻结。
设置三个断点:sys_execve,load_elf_binary,start_thread。
list列出来跟踪, 输入s可以进入do_execve的内部。按c继续执行,到load_elf_binary。list查看代码,输入n一句一句跟踪,追踪到start_thread。
观察hello这个可执行程序的入口,发现也是0x8048d0a,和new_ip的位置一样。
将new_ip和new_sp赋值,并设了一个新堆栈。
1、新的可执行程序是从哪里开始执行的?
当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到ELF程序的入口地址。
2、为什么 execve 系统调用返回后新的可执行程序能顺利执行?
execve和fork都是特殊一点的系统调用:一般的都是陷入到内核态再返回到用户态。
fork父进程和一般进程调度一样,子进程返回到一个特定的点ret_from_fork,子进程是从ret_from_fork开始执行然后返回到用户态。
execve特殊:执行到可执行程序–陷入内核–构造新的可执行文件–覆盖掉原可执行程序–返回到新的可执行程序,作为起点(也就是main函数) ,需要构造他的执行环境。
3、对于静态链接的可执行程序和动态链接的可执行程序 execve 系统调用返回时会有什么不同?
静态链接:elf_entry指向可执行文件的头部,一般是main函数,是新程序执行的起点,一般地址为0x8048XXX的位置。
动态链接:elf_entry指向ld即动态链接器的起点load_elf_interp。
4、分析 exec*函数对应的系统调用处理过程
- exec会调用sys_execve
- 调用do_execve,调用do_execve_common(会把函数参数和系统环境传进来进行相应的处理)
- —调用exec_binprm(执行相应的程序,其中,会调用search_binary_handler,这个函数会调用各种不同的格式来识别相应的文件)
1、可执行文件的创建
C代码(.c) - 经过编译器预处理,编译成汇编代码(.asm) - 汇编器,生成目标代码(.o) - 链接器,链接成可执行文件(.out) - OS将可执行文件加载到内存里执行。
1.预处理
gcc -E -o hello.cpp hello.c -m32 预处理(文本文件)
预处理负责把include的文件包含进来及宏替换等工作
2.编译
gcc -x cpp-output -S -o hello.s hello.cpp -m32 编译成汇编代码(文本文件)
3.汇编
gcc -x assembler -c hello.s -o hello.o -m32 汇编成目标代码(ELF格式,二进制文件,有一些机器指令,只是还不能运行)
4.链接
gcc -o hello hello.o -m32 链接成可执行文件(ELF格式,二进制文件)
在hello可执行文件里面使用了共享库,会调用printf,libc库里的函数
gcc -o hello.static hello.o -m32 -static 静态链接
把执行所需要依赖的东西都放在程序内部
2、目标文件的格式ELF
ELF格式分类:
- 可重定位文件.o,用来和其他object文件一起创建可执行文件和共享文件
- 可执行文件,指出应该从哪里开始执行
- 共享文件,主要是.so文件,用来被链接编辑器和动态链接器链接
对ELF头的描述告诉系统如何创建一个进程的内存映像,section头表包含了描述文件sections的信息。当创建或增加一个进程映像时,理论上它会把程序段拷贝到虚拟内存中某个段
ELF文件的头部规定了许多与二进制兼容性相关的信息。所以在加载ELF文件的时候,必须先加载头部,分析ELF的具体信息
entry代表刚加载过新的可执行文件之后的程序的入口地址,头部后是代码和数据,进程的地址空间是4G,上面的1G是内核用,下面的3G是程序使用。默认的ELF头加载地址是0x8048000
3、可执行程序的执行环境
一般我们执行一个程序的Shell环境,我们的实验直接使用execve系统调用。
Shell本身不限制命令行参数的个数,命令行参数的个数受限于命令自身。
- int main(int argc, char *argv[])
- int main(int argc, char *argv[], char *envp[])
其中, envp是shell的执行环境
Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数
- int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
- 库函数exec*都是execve的封装例程
4、可执行程序的装载
sys_execve内部会解析可执行文件格式
- do_execve -> do_execve_common -> exec_binprm
装载和启动一个可执行程序依次调用以下函数:
sys_execve() -> do_execve() -> do_execve_common() -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)