Ubuntu shell 的 16.04 汇编代码

Ubuntu 16.04 assembly code for shell

.global main
main:
    call func
    .string "/bin/sh"
func:
    push %rsp
    pop %rsi
    pop %rdi
    mov [=12=]x00, %edx
    mov [=12=]x3b, %eax
    syscall

我像上面那样为执行编写了程序集滞后 /bin/sh 我编译了它,但是当我尝试执行程序时,/bin/sh: 0: Can't open ???? 出现了这个错误。 它不执行 /bin/sh。我想知道为什么我不能执行 /bin/sh

I'm using Ubuntu 16.04 and x64 Architectures

你的代码因为以一种奇怪的方式使用 push/pop 而不必要地难以理解,但是 运行 你的程序在 strace -f ./a.out 下跟踪系统调用显示:

... dynamic linker and libc init stuff before main() is called ...
execve("/bin/sh", ["/bin/sh", "170\t2", "7", "7", 0x100000000, "0"], [/* 0 vars */]) = -1 EFAULT (Bad address)
exit_group(0)                           = ?
+++ exited with 0 +++

因此在我的系统上,execve return 出现错误,但程序成功退出。 IDK 如何获得 /bin/sh: 0: Can't open ????。您的问题没有包含足够的信息来重现您的结果。但也许当你尝试时,堆栈恰好包含不同的垃圾。我用 gcc -g foo.S.

构建了它

main 失败后 return,执行会进入 main 之后的任何 CRT 函数,它以 RET 指令结束。它还必须将 eax 归零,因为它将在 SYSCALL 之后 -EFAULT


无论如何,你的 asm 相当于这个无用的代码:

int main(void) {
    const char *p = "/bin/sh";
    execve(p, &p, NULL);
}

请注意 push %rsp; pop %rsi 等同于 mov %rsp, %rsi。因此 RSI 持有指向 CALL 写入 "return address".

的堆栈内存的指针

之后的另一个 POP 取消引用堆栈指针并将指向字符串的指针加载到 RDI。

用CALL 来push 一个字符串的地址实在是太恶心了。 IDK你为什么要那样做。像正常人一样使用MOV就可以了


如何正确执行此操作

# build with gcc -no-pie -g shell.S

#include <asm/unistd.h>         // for __NR_execve                                                                                                                
// #include <sys/syscall.h>     // or include this glibc header for SYS_execve

main:          # main(argc, argv, envp)
    mov   $shell, %edi     # filename = shell
                           # argv     = main's argv, already in rsi (not on the stack like in _start)

    # leave RDX = envp
    # xor   %edx, %edx       # envp     = NULL

    mov   $__NR_execve, %eax    # execve
    syscall                     # execve(shell, argv, envp)
    ret                    # in case the syscall fails

.section .rodata
shell:
.string "/bin/sh"

这行得通,而且没有做任何奇怪的事情。


或者,以一种与位置无关的平面二进制文件工作的方式,并且不使用 main() 的 argv,因为您添加了该请求:

#include <asm/unistd.h>         // for __NR_execve                                                                                                                
// #include <sys/syscall.h>     // or include this glibc header for SYS_execve

.globl main
main:
    lea   shell(%rip), %rdi     # filename = shell
    xor   %edx, %edx            # envp     = NULL

    push  %rdx                  # or push [=13=]
    push  %rdi
    mov   %rsp, %rsi            # argv     = { $shell, NULL } that we just pushed

    mov   $__NR_execve, %eax    # execve(
    syscall
    ret                    # in case the syscall fails
shell:                     # still part of the .text section, and we don't use the absolute address of the label, only for a RIP-relative LEA.
.string "/bin/sh"

你的 RSI=RSP 想法不错,但是你忘了在 argv[] 数组的末尾添加一个终止 NULL 指针。

在获取 envp[] 作为 arg 的 main 之外,我们没有一个已经构造好的 envp[] 可以在任何方便的地方访问,所以只需传递 NULL。在 Linux 上,这相当于将一个有效指针传递给一个空的以 NULL 结尾的数组,即指向内存中 8 字节 0 的指针。

C代码:

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv, char **env)
{
  argv[0] = "/bin/bash";
  return execve(argv[0], argv, env);
}

64位X86汇编代码:

// gcc shell.S -Wl,--build-id=none -static -nostdlib -fPIC -o shell
// strip -s shell

#include <asm/unistd.h>

        .global _start

        .text
_start:
        mov (%rsp), %rbx        # rbx = argc

        mov     $__NR_execve, %rax             # system call 3b is execve()
        lea     sh(%rip), %rdi     # command to execute
        lea 8(%rsp), %rsi       # argv
#        xor     %rsi, %rsi              # no args
        lea 16(%rsp, %rbx, 8), %rdx # envp !!!
#        xor     %rdx, %rdx              # no envp
        syscall                         # invoke system call

        # exit(0)
        mov     $__NR_exit, %rax               # system call 60 is exit
        xor     %rdi, %rdi              # we want return code 0
        syscall                         # invoke operating system to exit
sh:
        .string  "/bin/bash"

您可以调用 shell 作为:

./shell

./shell -c "echo hello;echo world"

使用此代码,命令 "shell" 的所有参数将原封不动地传递给 /bin/bash。

进程名称也将更改并变为 "bash"(在 ps -ef 中)

更重要的是(我在其他地方没有看到)

使用此代码可以保护环境。 (没有双关语意)

如果您不需要 args 并且不想保留 env,请取消注释并注释前几行。