在Linux上,熟悉的C语言
argc和
argv变量始终由内核传递到堆栈上,甚至可以用于完全独立的汇编程序,并且不与C库中的启动代码链接。这在i386
System V ABI中进行了记录,同时还介绍了流程启动环境的其他详细信息(寄存器值,堆栈对齐)。
在
_startx86 Linux可执行文件的ELF入口点(aka ):
- ESP 指向
argc
- ESP + 4 指向
argv[0]
,数组的开始。即,数值应该传递给主为char **argv
是lea eax, [esp+4]
,不是mov eax, [esp+4]
)
最小汇编程序如何获得argc和argv
我将展示如何阅读
argv和
argc[0]在GDB中。
cmdline-x86.S
#include <sys/syscall.h> .global _start_start: int $0x03 mov $SYS_exit_group, %eax mov $0, %ebx int $0x80
cmdline-x86.gdb
set confirm offfile cmdline-x86run# We'll regain control here after the breakpoint trapprintf "argc: %dn", *(int*)$espprintf "argv[0]: %sn", ((char**)($esp + 4))[0]quit
样品会议
$ cc -nostdlib -g3 -m32 cmdline-x86.S -o cmdline-x86$ gdb -q -x cmdline-x86.gdb cmdline-x86<...> Program received signal SIGTRAP, Trace/breakpoint trap._start () at cmdline-x86.S:88 mov $SYS_exit_group, %eaxargc: 1argv[0]: /home/scottt/Dropbox/stackoverflow/cmdline-x86
说明
- 我
int $0x03
在ELF入口点(_start
)之后放置了一个软件断点(),以使程序重新陷入调试器。 - 然后我
printf
在GDB脚本中使用了打印argc
与表达*(int*)$esp
argv
与表达((char**)($esp + 4))[0]
x86-64版本
差异很小:
- 用 RSP* 替换 ESP *
- 将地址大小从4更改为8
- 当我们调用
exit_group(0)
以正确终止进程时,请遵循不同的Linux syscall调用约定
命令行
#include <sys/syscall.h> .global _start_start: int $0x03 mov $SYS_exit_group, %rax mov $0, %rdi syscall
cmdline.gdb
set confirm offfile cmdlinerunprintf "argc: %dn", *(int*)$rspprintf "argv[0]: %sn", ((char**)($rsp + 8))[0]quit
常规C程序如何获取argc和argv
您可以
_start从常规C程序中反汇编以查看其如何从堆栈中获取
argc以及如何
argv在调用时传递它们
__libc_start_main。以
/bin/true我的x86-64机器上的程序为例:
$ gdb -q /bin/trueReading symbols from /usr/bin/true...Reading symbols from /usr/lib/debug/usr/bin/true.debug...done.done.(gdb) disassemble _startDump of assembler pre for function _start: 0x0000000000401580 <+0>: xor %ebp,%ebp 0x0000000000401582 <+2>: mov %rdx,%r9 0x0000000000401585 <+5>: pop %rsi 0x0000000000401586 <+6>: mov %rsp,%rdx 0x0000000000401589 <+9>: and $0xfffffffffffffff0,%rsp 0x000000000040158d <+13>: push %rax 0x000000000040158e <+14>: push %rsp 0x000000000040158f <+15>: mov $0x404040,%r8 0x0000000000401596 <+22>: mov $0x403fb0,%rcx 0x000000000040159d <+29>: mov $0x4014c0,%rdi 0x00000000004015a4 <+36>: callq 0x401310 <__libc_start_main@plt> 0x00000000004015a9 <+41>: hlt 0x00000000004015aa <+42>: xchg %ax,%ax 0x00000000004015ac <+44>: nopl 0x0(%rax)
前三个参数
__libc_start_main()是:
- RDI :指向的指针
main()
- RSI :
argc
,您可以看到它是如何从堆栈中弹出的第一件事 - RDX :
argv
,价值 RSP 权后,argc
被弹出。(ubp_av
在GLIBC来源中)
x86 _start 非常相似:
Dump of assembler pre for function _start: 0x0804842c <+0>: xor %ebp,%ebp 0x0804842e <+2>: pop %esi 0x0804842f <+3>: mov %esp,%ecx 0x08048431 <+5>: and $0xfffffff0,%esp 0x08048434 <+8>: push %eax 0x08048435 <+9>: push %esp 0x08048436 <+10>: push %edx 0x08048437 <+11>: push $0x80485e0 0x0804843c <+16>: push $0x8048570 0x08048441 <+21>: push %ecx 0x08048442 <+22>: push %esi 0x08048443 <+23>: push $0x80483d0 0x08048448 <+28>: call 0x80483b0 <__libc_start_main@plt> 0x0804844d <+33>: hlt 0x0804844e <+34>: xchg %ax,%axEnd of assembler dump.



