完成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 = 11
(execve
的函数号码),ebx
应指向char* filename
,ecx
应指向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
堆栈如下:
现在我尝试通过减少一些指令来优化它。我同意在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,地址值是根据我的想法,但它不会运行。
答案 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
。
我还应该注意,构造字符串时使用的两个双字中字符的相反顺序是由于机器的字节顺序而不是堆栈方向。