为何sys_execve系统调用需两处传入命令相关参数?
问题描述
我无法理解为何在sys_execve中命令要被使用两次:一处是直接存入ebx寄存器,另一处是作为指针存入ecx指向的参数数组中。
以下是测试用的汇编代码:
%include 'commonlib.asm' section .data command db '/bin/echo', 0 arg1 db 'Hello world!', 0 callargs dd command ;命令在这里被使用 dd arg1 dd 0 environment dd 0 section .text global _start _start: mov edx, environment mov ecx, callargs mov ebx, command ;命令在这里被使用 mov eax, 11 int 80h call exit
exit仅执行sys_exit操作。我分别测试了去掉其中一处的情况,但两种情况下程序都无法正常运行(无法打印Hello world)。请问这背后有什么原因?为什么需要两处传入命令?
原因解释
这是由Linux系统调用sys_execve(对应eax=11)的参数定义和用户空间程序的运行逻辑共同决定的:
ebx寄存器的作用
ebx必须指向要执行的可执行文件的路径字符串(比如这里的/bin/echo),内核需要这个路径来定位磁盘上的可执行文件,加载它的代码和数据到内存中。如果去掉ebx的赋值,内核根本不知道要运行哪个程序,直接会调用失败。ecx指向的参数数组的作用
ecx指向的是参数数组的起始地址,这个数组有固定规则:
- 第一个元素必须是程序的路径(或别名),对应用户空间程序的
argv[0] - 后面依次是传给程序的参数(比如这里的
Hello world!对应argv[1]) - 最后以
NULL(0)结尾
用户空间的程序(比如echo)会依赖argv[0]:很多程序会用它来打印自身名称(比如报错时显示echo: invalid argument),有些甚至会根据argv[0]的不同表现出不同行为(比如bash和sh其实是同一个程序,靠argv[0]区分)。如果参数数组的第一个元素不是程序路径,echo运行时会因为argv[0]异常而无法正常工作,自然打不出内容。
简单总结:
- ebx是给内核看的:告诉内核要运行哪个程序
- 参数数组的第一个元素是给用户空间程序看的:告诉程序它自身的名称,保证程序能正常执行逻辑
补充验证
如果硬要调整,可以试试把参数数组的第一个元素换成任意非空字符串(比如db 'myecho',0),同时ebx保持/bin/echo,这时候程序大概率能正常打印Hello world!——因为内核能找到程序,而echo对argv[0]的要求只是非空,不会影响核心打印逻辑。但这不符合规范,且部分程序会因此报错。
内容的提问来源于stack exchange,提问作者Random

