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架构
你的代码是不必要的困难,因为在使用推/流行的追随奇怪的方式,但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指针。
我相信他正在编写shell代码,所以他不会像你的例子那样使用固定部分和绝对地址。他的字符串被放置在代码中,以便他可以通过将返回地址弹出到_RDI_中来检索字符串的地址。他的方法是JMP/CALL/POP方法的变体。这个想法是从一个可执行堆栈运行这个代码。 –
@MichaelPetch:我想知道这件事,但OP从来没有这样说过。将字符串置于main(在.text部分)之后或之前将使用RIP相对LEA:'lea $ shell(%rip),%rdi而不是'mov $ shell,% edi'。显然,原来并没有试图避免'\ 0'字节或任何东西,所以我的答案旨在用strace教授基本的调试。 –
实际上,当我看到问题的标题时,我首先假定它可能是一个shell代码问题。当我看到代码中的字符串和他弹出返回地址作为参数的方式时,它几乎尖叫着“shell code”。至于RIP寻址,当然,但你的原代码并没有这样做。 –
另一种方法是使用调用外部程序的'system'函数。看看[this](https://godbolt.org/g/jN057x)查看汇编程序示例 – Garf365
但是我听说'system'函数在内部函数调用中也使用'execve'。所以我想用'execve' – Damotorie
确切的说,'system'会调用'execve',但是你觉得它有点复杂。调用'system'比较容易。看看这个[post](http://stackoverflow.com/questions/9342410/sys-execve-system-call-from-assembly)'execve' – Garf365