简体   繁体   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

I wrote assembly lagunage like above for execute /bin/sh I compiled it but when I try to execute program, /bin/sh: 0: Can't open ???? 我为执行/ bin / sh编写了如上所述的汇编程序,但我尝试对其进行编译,但是当我尝试执行程序时, /bin/sh: 0: Can't open ???? this error is occur. 发生此错误。 It doesn't execute /bin/sh. 它不执行/ bin / sh。 I want to know why I can't execute /bin/sh 我想知道为什么我不能执行/ bin / sh

I'm using Ubuntu 16.04 and x64 Architectures 我正在使用Ubuntu 16.04和x64体系结构

Your code is needlessly hard to follow because of using push/pop in a weird way, but running your program under strace -f ./a.out to trace the system calls shows: 由于以怪异的方式使用了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 +++

So on my system, execve returns with an error, but the program exits successfully. 因此,在我的系统上, execve返回错误,但程序成功退出。 IDK how you're getting /bin/sh: 0: Can't open ???? IDK您如何获得/bin/sh: 0: Can't open ???? . Your question doesn't contain enough information to reproduce your results. 您的问题没有足够的信息来重现您的结果。 But maybe when you tried, the stack happened to contain different garbage. 但是也许当您尝试时,堆栈碰巧包含了不同的垃圾。 I built it with gcc -g foo.S . 我用gcc -g foo.S构建它。

After main fails to return, execution falls through into whatever CRT function follows main , which does end with a RET instruction. main无法返回之后,执行将落入main后面的任何CRT函数,该函数以RET指令结束。 It must also zero eax, since it will be -EFAULT after the SYSCALL. 它也必须为零eax,因为在SYSCALL之后它将是-EFAULT


Anyway, your asm is equivalent to this non-useful code: 无论如何,您的asm等效于以下无用的代码:

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

note that push %rsp; pop %rsi 注意push %rsp; pop %rsi push %rsp; pop %rsi is equivalent to mov %rsp, %rsi . push %rsp; pop %rsi等效于mov %rsp, %rsi So RSI holds a pointer to the stack memory where CALL wrote the "return address". 因此,RSI持有指向CALL写入“返回地址”的堆栈存储器的指针。

One more POP after that dereferences the stack pointer and loads the pointer to the string into RDI. 之后又有一个POP取消了对堆栈指针的引用,并将指向字符串的指针加载到RDI中。

Using CALL to push the address of a string is really nasty. 使用CALL推送字符串的地址确实很麻烦。 IDK why you'd do that. IDK为什么您要这么做。 Just use MOV like a normal person. 像普通人一样使用MOV。


How to do this correctly 如何正确做到这一点

# 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"

This works, and doesn't do anything weird. 这行得通,并且不会做任何奇怪的事情。


Or, in a way that works as a position-independent flat binary , and doesn't use main()'s argv, since you added that request: 或者,以一种与位置无关的平面二进制文件的方式工作 ,并且不使用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"

Your RSI=RSP idea is not bad, but you forgot to add a terminating NULL pointer to the end of the argv[] array. 您的RSI = RSP想法还不错,但是您忘记在argv[]数组的末尾添加一个终止NULL指针。

Outside of main which gets envp[] as an arg, we don't have an already-constructed envp[] accessible anywhere convenient, so just pass NULL. 除了将envp[]用作arg的main之外,我们还没有可在任何方便的地方访问的已经构造envp[] ,因此只需传递NULL。 On Linux, that's equivalent to passing a valid pointer to an empty NULL-terminated array, ie a pointer to an 8-byte 0 in memory. 在Linux上,这等效于将有效指针传递给以NULL结尾的空数组,即,指向内存中8字节0的指针。

C code: 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 bit X86 assembly code: 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"

you can invoke the shell as: 您可以通过以下方式调用shell:

./shell

or 要么

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

With this code all parameters of the command "shell" will be passed to /bin/bash unchanged. 使用此代码,命令“ shell”的所有参数将不变地传递到/ bin / bash。

Also the process name will change and become "bash" (in ps -ef) 进程名称也将更改并变为“ bash”(以ps -ef表示)

And what is more important (which I didn't see anywhere else) 还有更重要的是(我在其他任何地方都没有看到)

is that with this code the environment is preserved. 使用此代码可以保留环境。 (no pun intended) (无双关语)

If you don't need the args and you don't want the env preserved, uncomment the comments and comment the previous lines. 如果不需要args并且不想保留env,请取消注释注释并注释前面的行。

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

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