【问题标题】:sys_execve system call from Assembly来自 Assembly 的 sys_execve 系统调用
【发布时间】:2026-02-11 08:25:01
【问题描述】:

asm_execve.s:

.section .data 文件运行: .ascii "/bin/sh" .section .text .globl 主要 主要的: 推%ebp movl %esp, %ebp subl $0x8, %esp # 两个指针的数组。数组[0] = file_to_run 数组[1] = 0 movl file_to_run, %edi movl %edi, -0x4(%ebp) movl $0, -0x8(%ebp) movl $11, %eax # sys_execve movl file_to_run, %ebx # 要执行的文件 leal -4(%ebp), %ecx # 命令行参数 movl $0, %edx # 环境块 诠释 $0x80 离开 ret

制作文件:

NAME = asm_execve $(NAME) : $(NAME).s gcc -o $(NAME) $(NAME).s

程序已执行,但未调用 sys_execve:

alex@alex32:~/project$ 制作 gcc -o asm_execve asm_execve.s alex@alex32:~/project$ ./asm_execve alex@alex32:~/project$

预期输出是:

alex@alex32:~/project$ ./asm_execve $退出 alex@alex32:~/project$

这个汇编程序应该像下面的 C 代码一样工作:

字符*数据[2]; 数据[0] = "/bin/sh"; 数据[1] = NULL; 执行(数据[0],数据,空);

系统调用参数有问题?

【问题讨论】:

  • 使用strace -e execve 来跟踪您的程序实际执行的execve调用。

标签: linux assembly x86 system-calls


【解决方案1】:

您实际上不需要在其他参数中加载任何内容。如果您在 x86 中执行此操作,则以下更简单的代码也可以使用:

.global _main
.section .text

.data
file_to_run:
.asciz "/bin/sh"

.section .text
.globl main

_main:
pushl %ebp
movl %esp, %ebp

movl $11, %eax                      # sys_execve
movl $file_to_run, %ebx              # file to execute       
movl $0, %ecx                       # Null value will work too
movl $0, %edx                       # Null will works too
int  $0x80              

leave
ret

这实际上将在调用系统调用后打开一个 shell 终端。

【讨论】:

  • 您应该使用xor %ecx,%ecx 将寄存器归零。你确定传递非零垃圾有效吗?除了0(或有效指针)之外的任何东西都应该使系统调用返回-EFAULTargvenvp 的 NULL 指针被特别记录为 Linux 特定的行为 (man7.org/linux/man-pages/man2/execve.2.html),相当于将 指针传递给 NULL 指针。但这并不意味着其他坏指针会“起作用”。如果它执行某些操作而不是返回带有非 0 错误指针的-EFAULT,那实际上将是实现中的一个错误
  • 是的,你是对的。我对此感到抱歉。我的意思是,如果你想打开一个终端,你不需要提供任何特定的参数,如果你提供空值,它仍然可以工作。
  • 你确定你测试过这个吗?您需要$file_to_run,否则您只需加载字符串的前 4 个字节并将其作为指针传递。此外,您保存/恢复ebp,但您的代码破坏了ebx。如果您想在execve 返回错误而不是执行时安全返回而不是崩溃,您应该push/pop ebx 以及/而不是制作堆栈帧。 (如果你破坏 ebx,也许 CRT 代码是可以的。)可选的改进:你的字符串可以进入 .rodata,所以你不需要 .data 部分。您可以使用.asciiz 来获取以零结尾的字符串。
【解决方案2】:

execve 系统调用正在被调用,但您确实传递了错误的参数。

(您可以通过使用strace 运行您的可执行文件来看到这一点。)

存在三个问题:

  1. .ascii 不会以 0 结尾的字符串。 (您可能会很幸运,因为在此示例中您的 .data 部分中没有任何内容,但这不能保证...)添加一个 0,或使用 .asciz(或 .string)代替。

  2. movl file_to_run, %edifile_to_run符号指向的值移动到%edi,即字符串的前4个字节(0x6e69622f)。字符串的 address 只是符号本身的值,因此您需要使用 $ 前缀作为文字值:movl $file_to_run, %edi。同样,您需要在后面几行说movl $file_to_run, %ebx。 (这是 AT&T 语法和 Intel 语法之间混淆的常见来源!)

  3. 参数以错误的顺序放置在堆栈中:-0x8(%ebp) 的地址低于-0x4(%ebp)。所以命令字符串的地址应该写成-0x8(%ebp),0应该写成-0x4(%ebp)leal指令应该写成leal -8(%ebp), %ecx


固定代码:

.section .data
file_to_run:
.asciz       "/bin/sh"

.section .text
.globl main

main:
    pushl %ebp
    movl %esp, %ebp
    subl $0x8, %esp         # array of two pointers. array[0] = file_to_run  array[1] = 0

    movl $file_to_run, %edi
    movl %edi, -0x8(%ebp)   
    movl $0, -0x4(%ebp)

    movl $11, %eax                      # sys_execve
    movl $file_to_run, %ebx              # file to execute       
    leal -8(%ebp), %ecx                 # command line parameters
    movl $0, %edx                       # environment block
    int  $0x80              

    leave
    ret

【讨论】:

  • 作为一个不可移植的扩展,Linux 允许 argvenvp 实际上 be NULL(就像你为 envp 做的那样),所以 xor %ecx,%ecx ; xor %edx,%edx。在堆栈上创建argv[] 的更简单方法是mov $file_to_run, %ebxpush $0(或归零的注册); push %ebx; mov %esp, %ecx.
  • @PeterCordes 不可移植?我们处于组装级别,我们可以在这里拥有什么可移植性?
  • @Ruslan:没错;如果您使用 asm 编写,那么利用这一优势没有任何不利之处,因为没有理由期望这种非标准行为会在 Linux 上发生改变。记录这一点的execve(2) man page 是在谈论 C,它是不可移植的。
最近更新 更多