重写一个小的execve shellcode

时间:2013-05-19 08:13:28

标签: c++ assembly nasm shellcode execve

完成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* 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

堆栈如下:

enter image description here

现在我尝试通过减少一些指令来优化它。我同意在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

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

我的堆栈如下:enter image description here

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

1 个答案:

答案 0 :(得分: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   

filename 的构造如下:

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

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