Ubuntu的壳

问题描述:

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

16.04汇编代码我写的装配lagunage像上面的执行/ bin/sh的 我编译它,但是当我尝试执行程序,/bin/sh: 0: Can't open ????这个错误发生。 它不执行/ bin/sh。我想知道为什么我不能执行/ bin/sh的Ubuntu的壳

我使用Ubuntu 16.04和x64架构

+0

另一种方法是使用调用外部程序的'system'函数。看看[this](https://godbolt.org/g/jN057x)查看汇编程序示例 – Garf365

+0

但是我听说'system'函数在内部函数调用中也使用'execve'。所以我想用'execve' – Damotorie

+0

确切的说,'system'会调用'execve',但是你觉得它有点复杂。调用'system'比较容易。看看这个[post](http://stackoverflow.com/questions/9342410/sys-execve-system-call-from-assembly)'execve' – Garf365

你的代码是不必要的困难,因为在使用推/流行的追随奇怪的方式,但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未能返回之后,执行会落入CRT函数main之后,该函数以RET指令结束。它也必须为0,因为在SYSCALL之后它将为-EFAULT


反正你的汇编相当于该无用代码:

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 -g shell.S 

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

main: 
    mov $shell, %edi  # filename = shell 
          # argv  = main's argv, already in rsi (not on the stack like in _start) 
    xor %edx, %edx  # envp  = NULL 
    mov $__NR_execve, %eax # execve(
    syscall 
    ret     # in case the syscall fails 

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

这工作,并没有做任何事情奇怪。


或者,在),它作为一个位置无关平二进制,并且不使用主(的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指针。

+0

我相信他正在编写shell代码,所以他不会像你的例子那样使用固定部分和绝对地址。他的字符串被放置在代码中,以便他可以通过将返回地址弹出到_RDI_中来检索字符串的地址。他的方法是JMP/CALL/POP方法的变体。这个想法是从一个可执行堆栈运行这个代码。 –

+0

@MichaelPetch:我想知道这件事,但OP从来没有这样说过。将字符串置于main(在.text部分)之后或之前将使用RIP相对LEA:'lea $ shell(%rip),%rdi而不是'mov $ shell,% edi'。显然,原来并没有试图避免'\ 0'字节或任何东西,所以我的答案旨在用strace教授基本的调试。 –

+0

实际上,当我看到问题的标题时,我首先假定它可能是一个shell代码问题。当我看到代码中的字符串和他弹出返回地址作为参数的方式时,它几乎尖叫着“shell code”。至于RIP寻址,当然,但你的原代码并没有这样做。 –