用于shell的Ubuntu 16.04汇编代码

问题描述 投票:0回答:2
.global main
main:
    call func
    .string "/bin/sh"
func:
    push %rsp
    pop %rsi
    pop %rdi
    mov $0x00, %edx
    mov $0x3b, %eax
    syscall

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

我正在使用Ubuntu 16.04和x64架构

linux assembly x86-64 system-calls shellcode
2个回答
2
投票

由于以奇怪的方式使用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相当于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指针。

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


1
投票

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,请取消注释注释并注释前面的行。

© www.soinside.com 2019 - 2024. All rights reserved.