linux外壳程序,关于linux:Ubuntu 16.04外壳程序汇编代码

.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

I'm using Ubuntu 16.04 and x64 Architectures

一种替代方法是使用system函数来调用外部程序。 看一下这个查看汇编器示例

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

确实,system将调用execve,但是您认为它要复杂一些。 调用system更容易。 看看execve的帖子

您是否出于开发原因而尝试编写Shell代码? 我要求验证这种代码的原理。

是。 我只想研究如何制作shell代码。 互联网上的大多数参考都执行binsh,所以我尝试了。

由于使用怪异的方式使用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          // for __NR_execve

// #include     // 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          // for __NR_execve

// #include     // 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的指针。

我相信他在编写Shell代码,因此他不想像您的示例那样使用固定的部分和绝对地址。将他的字符串放置在代码中,以便他可以通过将返回地址弹出到RDI中来检索字符串的地址。他的方法是JMP / CALL / POP方法的变体。想法是从可执行堆栈运行此代码。

@MichaelPetch:我对此很纳闷,但是OP从来没有这么说过。将字符串放在main之前或之后(在.text部分中)将形成一个具有RIP相对LEA的PIC代码块:lea $shell(%rip), %rdi而不是mov $shell, %edi。原始的显然不是试图避免\0字节或其他任何东西,因此我的答案旨在使用strace进行基本调试。

实际上,当我首先看到问题的标题时,我认为这很可能是一个shell代码问题。当我看到代码中的字符串以及他弹出返回地址作为参数的方式时,它几乎在尖叫" shell代码"。至于RIP寻址,请确保您的原始代码没有执行此操作。

@MichaelPetch:是的,我认为这将是一个利用漏洞的问题,但这只是一个非常基本的main(),也许是从那些试图从利用漏洞的教程中学习asm而不是普通知识的人那里获得的。我的意思是,push %rsp使我认为OP可能不了解CALL内容的意义。不过,我认为您可能是对的,因为CALL / POP提示这是迈向shellcode的第一步,并且可能是OP想要的(但未提及)。

因此,我20分钟前在问题下的评论中要求OP进行澄清的原因。

看起来,尽管从OP的问题历史来看,他最近的大部分问题都与漏洞利用和Shell代码有关。我认为我们可以肯定地说他在做的事是故意的。

我知道我可以那样做。但是,当我将程序集转换为shell代码时,则无法使用shell: .string"binsh"这样的代码。所以我尝试了那样。

@ user4929293:您可以将标签与代码放在同一部分,并使用相对的LEA,就像我在与Michael的注释中谈到的那样。以一种与位置无关的平面二进制文件(例如shell代码)的方式更新了我的答案。如果您有这样奇怪的要求,则应在问题中提及它们。

这是我第一次学习asm。所以我没有通知这是奇怪的方法

@ user4929293:显然,使用CALL推送字符串地址并跳过它是漏洞利用编码中的常见技术。在32位代码中,没有RIP相对寻址,因此您不能以这种方式使用LEA。这使CALL成为显而易见的选择。此外,LEA中将包含一些零字节(因为相对位移始终为4字节),许多shellcode字符串需要避免零字节。但是,由于您没有在代码中指定任何这些要求,因此我对LEA使用了更容易理解的方法。

@Zibri:如果您在main中不满意,例如在shellcode中,则没有链接器支持的任何地方都不容易访问envp。但是可以肯定的是,即使OP没有这样做,我也会更改main示例以使其通过envp。

没关系,我做到了。非常简单。病后将其发布为答案。

C代码:

#define _GNU_SOURCE             /* See feature_test_macros(7) */

#include

#include

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

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

该问题被标记为[shellcode],因此OP希望提供一小段可作为漏洞利用有效负载注入的代码。这就是为什么他们不希望它依赖于main的args或_start堆栈布局的原因。您为其他问题(例如,没有Glibc的情况下,如何在C语言中使用内联汇编获取参数值?或其他有关编写自己的_start的信息(通常会调用main)。 OTOH,问题本身并没有真正说明这一点,所以我赞成您的回答,因为它对从搜索中到达此处的人可能有用。

我们的两个答案都不适合作为shellcode,因为它们对RIP相对LEA使用正rel32,在高字节中将包含零。 这可以通过在字符串上使用jmp来解决,因此rel32为负,或者使用一些技巧,例如对LEA使用任意偏移,然后使用sub或add进行修正。 避免使用CALL读取RIP的shellcode中的0xFF字节?

OP没有要求非零代码。 而且我什至没有包含setuid / setgid,因为它不在问题中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值