繁体   English   中英

Ubuntu 16.04 Shell汇编代码

[英]Ubuntu 16.04 assembly code for shell

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

我为执行/ bin / sh编写了如上所述的汇编程序,但我尝试对其进行编译,但是当我尝试执行程序时, /bin/sh: 0: Can't open ???? 发生此错误。 它不执行/ bin / sh。 我想知道为什么我不能执行/ bin / sh

我正在使用Ubuntu 16.04和x64体系结构

由于以怪异的方式使用了push / pop,因此不必要地难于遵循代码,但是在strace -f ./a.out下运行程序以跟踪系统调用显示:

... dynamic linker and libc init stuff before main() is called ...
execve("/bin/sh", ["/bin/sh", "\211\307\350\t\222\1", "\367", "\367", 0x100000000, "\350\10"], [/* 0 vars */]) = -1 EFAULT (Bad address)
exit_group(0)                           = ?
+++ exited with 0 +++

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

main无法返回之后,执行将落入main后面的任何CRT函数,该函数以RET指令结束。 它也必须为零eax,因为在SYSCALL之后它将是-EFAULT


无论如何,您的asm等效于以下无用的代码:

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

注意push %rsp; pop %rsi push %rsp; pop %rsi等效于mov %rsp, %rsi 因此,RSI持有指向CALL写入“返回地址”的堆栈存储器的指针。

之后又有一个POP取消了对堆栈指针的引用,并将指向字符串的指针加载到RDI中。

使用CALL推送字符串的地址确实很麻烦。 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 $0
    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,请取消注释注释并注释前面的行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM