[英]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.