重写一个小的 execve shellcode

use*_*694 5 c++ assembly nasm execve shellcode

浏览http://hackoftheday.securitytube.net/2013/04/demystifying-execve-shellcode-stack.html

execve我理解了调用并试图重写它的nasm 程序。

一些背景信息:

int execve(const char *filename, char *const argv[], char *const envp[]);
Run Code Online (Sandbox Code Playgroud)

因此,eax = 11( 的函数调用号execve),ebx应该指向char* filenameecx应该指向argv[](这将与第一个参数相同,ebx因为第一个参数是其*filename本身,例如在本例中为“/bin/sh”),并且edx将指向envp[]null在本例中)。

原始nasm代码:

global _start

section .text
_start:

xor eax, eax
push eax

; PUSH //bin/sh in reverse i.e. hs/nib//

push 0x68732f6e
push 0x69622f2f

mov ebx, esp

push eax
mov edx, esp

push ebx
mov ecx, esp

mov al, 11
int 0x80
Run Code Online (Sandbox Code Playgroud)

堆栈如下:

在此输入图像描述

现在我尝试通过减少一些指令来优化它。我同意直到mov ebx, esp代码保持不变。但是,由于ecxwill 需要指向ebx,我可以将代码重写如下:

global _start

section .text
_start:

xor eax, eax
push eax

; PUSH //bin/sh in reverse i.e. hs/nib//

push 0x68732f6e
push 0x69622f2f
mov ebx, esp

mov ecx,ebx

push eax
mov edx, esp

mov al, 11
int 0x80
Run Code Online (Sandbox Code Playgroud)

但是,当我运行重写的代码时,出现分段错误。

我的堆栈如下:在此输入图像描述

有什么想法为什么重写的代码不起作用?我也运行了 gdb,地址值是根据我的想法,但它就是无法运行。

Jam*_*ess 4

在这两种情况下,ebx 都指向字符串“//bin/sh”。相当于这样的 C 代码:

char *EBX = "//bin/sh";  
Run Code Online (Sandbox Code Playgroud)

但在第一个示例中, ecx 设置为指向该字符串的指针的地址。相当于这样的 C 代码:

char *temp = "//bin/sh"; // push ebx
char **ECX = &temp;      // mov ecx, esp
Run Code Online (Sandbox Code Playgroud)

在第二个示例中,ecx 只是设置为与 ebx 相同的值。

char *ECX = "//bin/sh";
Run Code Online (Sandbox Code Playgroud)

因此,这两个示例根本不同,ecx 有两种完全不同的类型和值。

更新:

我应该补充一点,从技术上讲 ecx 是一个 char 指针数组(argv参数),而不仅仅是指向 char 指针的指针。您实际上是在堆栈上构建一个包含两项的数组。

char *argv[2];
argv[1] = NULL;         // push eax, eax being zero
argv[0] = "//bin/sh";   // push ebx
ECX = argv;             // mov ecx,esp
Run Code Online (Sandbox Code Playgroud)

只是该数组的一半也作为envp参数加倍。由于envp是一个单项数组,并且该单项被设置为 NULL,因此您可以认为envp参数是使用 C 代码设置的,如下所示:

EDX = envp = &argv[1];           
Run Code Online (Sandbox Code Playgroud)

这是通过将 edx 设置为 esp 来实现的,而 argv 数组仅构建了一半。将两个作业的代码组合在一起,您将得到:

char *argv[2];
argv[1] = NULL;         // push eax, eax being zero
EDX = &argv[1];         // mov edx,esp
argv[0] = "//bin/sh";   // push ebx
ECX = argv;             // mov ecx,esp
Run Code Online (Sandbox Code Playgroud)

这有点复杂,但我希望这对您来说有意义。

更新2

所有参数都execve作为寄存器传递,但这些寄存器是指向需要在某处分配的内存的指针 - 在本例中是在堆栈上。由于堆栈在内存中向下构建,因此需要以相反的顺序构建内存块。

三个参数的内存如下所示:

char *filename:  2f 2f 62 69 | 6e 2f 73 68 | 00 00 00 00 
char *argv[]:    filename    | 00 00 00 00               
char *envp[]:    00 00 00 00   
Run Code Online (Sandbox Code Playgroud)

文件名的构造如下:

push eax        // '\0' terminator plus some extra
push 0x68732f6e // 'h','s','/','n'
push 0x69622f2f // 'i','b','/','/'
Run Code Online (Sandbox Code Playgroud)

argv参数如下:

push eax // NULL pointer
push ebx // filename
Run Code Online (Sandbox Code Playgroud)

envp参数如下:

push eax // NULL pointer
Run Code Online (Sandbox Code Playgroud)

但正如我所说,最初的示例决定在 argv 和 evp 之间共享内存,因此不需要最后一个push eax.

我还应该注意,构造字符串时使用的两个双字中的字符顺序相反是因为机器的字节顺序,而不是堆栈方向。