【问题标题】:Execve inline assembly执行内联汇编
【发布时间】:2016-03-27 02:27:54
【问题描述】:

我最近接触了 gcc 的内联汇编并掌握了基本的汇编知识,在我尝试使用一个或多个参数进行简单的 sys_execve 之前,我了解如何很好地进行系统调用。 如果我不向它传递任何其他参数,系统调用 execve 可以正常工作,并且在尝试传递任何参数时只运行不带参数的可执行文件。

#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;
}

我不知道会出什么问题,因为我已经对此进行了测试

execve(argv[0], argv, NULL);

它按预期工作。

【问题讨论】:

  • 查看sys_execve 的api,我不确定你的参数是否正确。另外,你是为 x64 编译的吗?如果是这样,您不应该使用 int 0x80。
  • 我正在为 x64 编译,根据我目前的进度,文件名部分是正确的,因为我尝试执行的任何可执行文件都有效。我坚持的部分是给它任何参数。我使用this 来确定我必须通过什么。

标签: linux gcc x86-64 system-calls inline-assembly


【解决方案1】:

这是 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;
}

实际问题是数组中有 64 位指针,但您使用的 32 位兼容性中断当然需要 32 位指针。

【讨论】:

  • =a 值得明确指出:系统调用在eax(或rax)寄存器中返回一个值,因此编译器需要知道这正在发生。否则,它会假定 asm 代码未修改 eax,如果稍后在代码中需要值 59,它可能会尝试从 eax 加载它。这将导致问题,因为实际上eax 已被系统调用的返回值覆盖。可能不太可能在此代码中真正引起问题,但总的来说需要注意。
  • 顺便说一下,根据我在 amd64 abi 中读到的内容,系统调用可能会破坏 r11rcx
  • @NateEldredge:没错,syscall / sysret 的设计保证 syscall 将始终破坏 rcx 和 r11(使用 RIP 和 RFLAGS)。让内核甚至可以看到这些 regs 中的内容需要用户空间合作(例如,将它们放入堆栈)。 Linux 系统调用不查看用户空间堆栈,因此 ABI 只是保持简单,并且系统调用会破坏 amd64 上的这两个 reg,这与 32 位 int 0x80 不同,其中仅修改了 eax(返回值)。跨度>
  • 另外,我建议 return 1 来自 main,因为只有在 execve 返回错误而不是工作时才能达到此目的。并给全局argv 一个不同的名称,并将其命名为const,这样它就可以在.rodata 部分中,以防编译器自己无法做到这一点。
  • @NateEldredge 除了破坏 rcx 和 r11 之外,它还破坏了标志。不应该加个“cc”吗?
猜你喜欢
  • 1970-01-01
  • 2012-10-20
  • 2013-07-23
  • 2014-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-11
相关资源
最近更新 更多