4

通过http://hackoftheday.securitytube.net/2013/04/demystifying-execve-shellcode-stack.html

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

一些背景资料:

int execve(const char *filename, char *const argv[], char *const envp[]);

因此,(函数调用eax = 11execve)应该ebx指向_ _ _ )。char* filenameecxargv[]ebx*filenameedxenvp[]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

堆栈如下:

在此处输入图像描述

现在我试图通过减少一些指令来优化它。我同意直到mov ebx, esp代码保持不变。但是,由于ecx需要指向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

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

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

任何想法为什么重新编写的代码不起作用?我也运行过 gdb 并且地址值是根据我的想法,但它不会运行。

4

1 回答 1

4

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

char *EBX = "//bin/sh";  

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

char *temp = "//bin/sh"; // push ebx
char **ECX = &temp;      // mov ecx, esp

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

char *ECX = "//bin/sh";

因此,这两个示例根本不同,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

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

EDX = envp = &argv[1];           

这是通过将 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

这有点令人费解,但我希望这对你有意义。

更新 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   

文件名的构造如下:

push eax        // '\0' terminator plus some extra
push 0x68732f6e // 'h','s','/','n'
push 0x69622f2f // 'i','b','/','/'

argv参数如下:

push eax // NULL pointer
push ebx // filename

envp参数是这样的:

push eax // NULL pointer

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

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

于 2013-05-19T10:43:47.673 回答