Kali rolling x86-64 环境,默认动态链接
参考 32 位: http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html
流程图如下:
64 位草图
最简单的 main
|
gcc -ggdb -o prog1 prog1.c
execve
运行程序时,shell 或者 gui 调用 execve() 函数触发系统调用:
- 系统会分配栈区并将 argc, argv, envp 压栈
- 按照 shell 设定 FD
- 装载器负责重定位和调用
preinitializers
(图中preinitarray1..n
) - 从程序代码段中 _start 位置开始执行程序
_start
_start
是程序执行的初始位置,通过objdump -d prog1
查看汇编
|
_start
的作用就是配置__libc_start_main
的参数并进行调用:
xor %ebp, %ebp
用于将%ebp
清零,同时作为最外层标记对
%rdi
,%rsi
,%rdx
,%rcx
,%r8
,%r9
的操作均为传参,因为共 7 个参数,还用到了栈来传递第一个参数main
地址, 由__libc_start_main
调用, 另外在程序终止后main
的返回值会由__libc_start_main
传递给exit
argc
argv
__libc_csu_init
__libc_csu_fini
- Destructor of dynamic linker. Registered by libc_start_main with cxat_exit()
to call the FINI for dynamic libraries that got loaded before us. - 栈 - 函数调用前的
%rsp
为了提升访存效率,编译器通常采用 16B 对齐。为了保证函数调用时的新栈帧是 16B 对齐的,使用
and
指令将 %esp 对齐。同时,为了将 %esp 保存在 16B 对齐的位置,使用
push %rax
提前填充 8B
_start
调用__libc_start_main
前的状态:
__libc_start_main
__libc_start_main
定义如下:
|
main
的完整参数调用应该是int main(int argc, char** argv, char** envp)
, 但是__libc_start_main
中却并不包含 envp
, 因为envp
可以由argc
和argv
计算得到
ELF auxiliary vector
使用 gdb 调试 prog1, 在_start
处下断点, 运行初始状态如下:
可以看到从栈顶向栈底依次放置的是: argc, argv, 0x0, 环境变量等, 即ELF 辅助向量
.
__libc_start_main 的主要功能
setuid, setgid
启动线程
寄存
fini
和rtld_fini
的参数, 等待at_exit
调用调用
__libc_csu_init
调用
main
调用
exit
__libc_csu_init
|
|
|
__libc_csu_init
返回__libc_start_main
前: