Ubuntu shell 的 16.04 汇编代码
Ubuntu 16.04 assembly code for shell
.global main
main:
call func
.string "/bin/sh"
func:
push %rsp
pop %rsi
pop %rdi
mov [=12=]x00, %edx
mov [=12=]x3b, %eax
syscall
我像上面那样为执行编写了程序集滞后 /bin/sh
我编译了它,但是当我尝试执行程序时,/bin/sh: 0: Can't open ????
出现了这个错误。
它不执行 /bin/sh。我想知道为什么我不能执行 /bin/sh
I'm using Ubuntu 16.04 and x64 Architectures
你的代码因为以一种奇怪的方式使用 push/pop 而不必要地难以理解,但是 运行 你的程序在 strace -f ./a.out
下跟踪系统调用显示:
... dynamic linker and libc init stuff before main() is called ...
execve("/bin/sh", ["/bin/sh", "170\t2", "7", "7", 0x100000000, "0"], [/* 0 vars */]) = -1 EFAULT (Bad address)
exit_group(0) = ?
+++ exited with 0 +++
因此在我的系统上,execve
return 出现错误,但程序成功退出。 IDK 如何获得 /bin/sh: 0: Can't open ????
。您的问题没有包含足够的信息来重现您的结果。但也许当你尝试时,堆栈恰好包含不同的垃圾。我用 gcc -g foo.S
.
构建了它
在 main
失败后 return,执行会进入 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 写入 "return address".
的堆栈内存的指针
之后的另一个 POP 取消引用堆栈指针并将指向字符串的指针加载到 RDI。
用CALL 来push 一个字符串的地址实在是太恶心了。 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 [=13=]
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,请取消注释并注释前几行。
.global main
main:
call func
.string "/bin/sh"
func:
push %rsp
pop %rsi
pop %rdi
mov [=12=]x00, %edx
mov [=12=]x3b, %eax
syscall
我像上面那样为执行编写了程序集滞后 /bin/sh
我编译了它,但是当我尝试执行程序时,/bin/sh: 0: Can't open ????
出现了这个错误。
它不执行 /bin/sh。我想知道为什么我不能执行 /bin/sh
I'm using Ubuntu 16.04 and x64 Architectures
你的代码因为以一种奇怪的方式使用 push/pop 而不必要地难以理解,但是 运行 你的程序在 strace -f ./a.out
下跟踪系统调用显示:
... dynamic linker and libc init stuff before main() is called ...
execve("/bin/sh", ["/bin/sh", "170\t2", "7", "7", 0x100000000, "0"], [/* 0 vars */]) = -1 EFAULT (Bad address)
exit_group(0) = ?
+++ exited with 0 +++
因此在我的系统上,execve
return 出现错误,但程序成功退出。 IDK 如何获得 /bin/sh: 0: Can't open ????
。您的问题没有包含足够的信息来重现您的结果。但也许当你尝试时,堆栈恰好包含不同的垃圾。我用 gcc -g foo.S
.
在 main
失败后 return,执行会进入 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 写入 "return address".
之后的另一个 POP 取消引用堆栈指针并将指向字符串的指针加载到 RDI。
用CALL 来push 一个字符串的地址实在是太恶心了。 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 [=13=]
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,请取消注释并注释前几行。