Linux Execve内联程序集
我最近进入了gcc的内联汇编,有了基本的汇编知识,我非常了解如何进行系统调用,直到我尝试使用一个或多个参数执行一个简单的sys\u execve。 如果我没有向系统调用execve传递任何附加参数,那么系统调用execve可以正常工作,并且在尝试传递任何附加参数时只运行不带参数的可执行文件Linux Execve内联程序集,linux,gcc,x86-64,system-calls,inline-assembly,Linux,Gcc,X86 64,System Calls,Inline Assembly,我最近进入了gcc的内联汇编,有了基本的汇编知识,我非常了解如何进行系统调用,直到我尝试使用一个或多个参数执行一个简单的sys\u execve。 如果我没有向系统调用execve传递任何附加参数,那么系统调用execve可以正常工作,并且在尝试传递任何附加参数时只运行不带参数的可执行文件 #include <stdio.h> char *argv[]={"/bin/echo","parameter test", NULL}; int main(){ __asm__ vola
#include <stdio.h>
char *argv[]={"/bin/echo","parameter test", NULL};
int main(){
__asm__ volatile ("int $0x80"
:
:"a"(11), // syscall number (execve)
"b"(argv[0]), // filename
"c"(argv), // arguments
"d"(0)); // env
return 0;
}
这是32位代码,使用32位约定。使用gcc-m32编译,它会工作的。或者,切换到正确的64位版本,例如:
#include <stdio.h>
char *argv[]={"/bin/echo","parameter test", NULL};
int main(){
int ret;
__asm__ volatile ("syscall"
:"=a" (ret)
:"a"(59), // syscall number (execve)
"D"(argv[0]), // filename
"S"(argv), // arguments
"d"(0) // env
:"rcx","r11","cc");
return 0;
}
#包括
char*argv[]={/bin/echo”,“参数测试”,NULL};
int main(){
int ret;
__asm_uuuuu易失性(“系统调用”
:“=a”(ret)
:“a”(59),//系统调用号(execve)
“D”(argv[0]),//文件名
“S”(argv),//参数
“d”(0)//env
:“rcx”、“r11”、“cc”);
返回0;
}
实际问题是数组中有64位指针,但您使用的32位兼容性中断当然需要32位指针。这是32位代码,使用32位约定。使用gcc-m32编译,它会工作的。或者,切换到正确的64位版本,例如:
#include <stdio.h>
char *argv[]={"/bin/echo","parameter test", NULL};
int main(){
int ret;
__asm__ volatile ("syscall"
:"=a" (ret)
:"a"(59), // syscall number (execve)
"D"(argv[0]), // filename
"S"(argv), // arguments
"d"(0) // env
:"rcx","r11","cc");
return 0;
}
#包括
char*argv[]={/bin/echo”,“参数测试”,NULL};
int main(){
int ret;
__asm_uuuuu易失性(“系统调用”
:“=a”(ret)
:“a”(59),//系统调用号(execve)
“D”(argv[0]),//文件名
“S”(argv),//参数
“d”(0)//env
:“rcx”、“r11”、“cc”);
返回0;
}
实际问题是数组中有64位指针,但您使用的32位兼容性中断当然需要32位指针。查看api,我不确定您的参数是否正确。另外,您是否为x64编译?如果是这样的话,您就不应该使用int 0x80。我正在为x64编译,按照我目前的进度,文件名部分是正确的,因为我尝试执行的任何可执行文件都是有效的。我一直坚持的是给它任何参数。我需要用户确定我必须通过什么。查看api,我不确定您的参数是否正确。另外,您是否为x64编译?如果是这样的话,您就不应该使用int 0x80。我正在为x64编译,按照我目前的进度,文件名部分是正确的,因为我尝试执行的任何可执行文件都是有效的。我一直坚持的是给它任何参数。我需要用户确定我必须传递的内容。
=a
值得明确指出:系统调用在eax
(或rax
)寄存器中返回一个值,因此编译器需要知道这正在发生。否则,它将假定asm代码保持未修改的eax
,如果它以后需要代码中的值59,它可能会尝试从eax
加载它。这将导致问题,因为实际上,eax
已被系统调用的返回值覆盖。可能不太可能在这段代码中实际导致问题,但通常需要注意。顺便说一下,从我在amd64 abi中读到的内容来看,系统调用可能会对r11
以及rcx
@NateEldredge:这是正确的,syscall
/sysret
的设计保证了syscall
将始终对rcx和r11(带RIP和RFLAGS)进行缓冲。让内核看到这些regs中的内容需要用户空间的合作(例如,将它们放在堆栈上)。Linux系统调用不查看用户空间堆栈,因此ABI只是保持它的简单性,并让系统调用在amd64上的这两个reg上完成,而不像32位int0x80
中只修改eax
(返回值)。另外,我建议return1
frommain
,因为只有当execve
返回一个错误而不是正常工作时,才会达到此目的。并为全局argv
指定一个不同的名称,并使其const
,这样它就可以位于.rodata
部分,以防编译器自己无法做到这一点。@NateEldredge除了关闭rcx和r11之外,还关闭标志。您是否也应该添加“cc”?=a
值得明确指出:系统调用在eax
(或rax
)寄存器中返回一个值,因此编译器需要知道这正在发生。否则,它将假定asm代码保持未修改的eax
,如果它以后需要代码中的值59,它可能会尝试从eax
加载它。这将导致问题,因为实际上,eax
已被系统调用的返回值覆盖。可能不太可能在这段代码中实际导致问题,但通常需要注意。顺便说一下,从我在amd64 abi中读到的内容来看,系统调用可能会对r11
以及rcx
@NateEldredge:这是正确的,syscall
/sysret
的设计保证了syscall
将始终对rcx和r11(带RIP和RFLAGS)进行缓冲。让内核看到这些regs中的内容需要用户空间的合作(例如,将它们放在堆栈上)。Linux系统调用不查看用户空间堆栈,因此ABI只是保持它的简单性,并让系统调用在amd64上的这两个reg上完成,而不像32位int0x80
中只修改eax
(返回值)。另外,我建议return1
frommain
,因为只有当execve
返回一个错误而不是正常工作时,才会达到此目的。并为全局argv
指定一个不同的名称,并使其const
,这样它就可以位于.rodata
部分,以防编译器自己无法做到这一点。@NateEldredge除了关闭rcx和r11之外,还关闭标志。你不也应该加上“抄送”吗?