实践堆栈缓冲区溢出(2)

上篇文章给出的程序代码如下:

//测试环境:Windows XP + SP2  Intel T2050 Centrino Duo

//编译环境:Visual C++ 8.0 (CLR)

#include "stdafx.h"

void  func(){

     printf("Never called implicitly./n");

}

 

void inject(){

     //用来测试溢出的函数

     int sth=10;

     int* p=&sth;

     p[3]=(int)func;

}

 

int _tmain(int argc, _TCHAR* argv[])

{

     inject();

     printf("Normal END./n");

}


结果却出现问题,归结为两个:

1,  为什么p[3]才是保存的EIP?!

2,  即使修改了EIP值,但是为什么程序会出现非法操作?


呵呵,经过一番波折and努力,终于都顺利解决了……

(偶毕竟是这方面新手,高手嘛……别笑话亚…… |-_-

 

先说第一个了……

既然,p[3]才是EIP,根据首次的出错信息,p[2]应该是EBP。那么……

p[0]应该是sth,呃……p[1]是什么?

测试下……应该就知道了。

 

inject中添加如下代码:

for(int i=0;i<=3;i++)

     printf("p[%d] value: %8x/n",i,p[i]);  //均以8字符宽的16进制输出

 

输出如下(不同机器肯定有所不同):

p[0] value:        a

p[1] value:  cccccccc

p[2] value:   12ff68

p[3] value:   411483

 

p[0]a,即十进制的10,是变量sth的值,这个没问题。

p[1]……暂时不明白。

p[2]p[3],显得无规则,这倒正常,因为都是内存地址嘛……

 

呃……

Visual Studio,让很多程序员开心的一点……就是它提供了很强大的调试功能(如反汇编)。

呵呵,这里貌似只有试试它了,兴许可以解决问题。

 

直接在调试时选择“反汇编”,进入inject函数的汇编码。

如下:

void inject()

{

004135A 0  push        ebp 

004135A 1  mov         ebp,esp

004135A 3  sub         esp,0E4h

004135A 9  push        ebx 

004135AA  push        esi 

004135AB  push        edi 

004135AC   lea         edi,[ebp-0E4h]

004135B2  mov         ecx,39h

004135B7  mov         eax,0CCCCCCCCh

004135BC  rep stos    dword ptr es:[edi]

     int sth=10;

004135BE  mov         dword ptr [sth],0Ah

     int* p=&sth;

004135C 5  lea         eax,[sth]

004135C 8  mov         dword ptr [p],eax

     p[2]=(int)func;

00413626  mov         eax,dword ptr [p]

00413629  mov         dword ptr [eax+8],offset func (41108Ch)

}

00413630  push        edx 

00413631  mov         ecx,ebp

00413633  push        eax 

00413634  lea         edx,[ (413658h)]

0041363A   call        @ILT+145(@_RTC_CheckStackVars@8) (411096h)

0041363F   pop         eax 

00413640  pop         edx 

00413641  pop         edi 

00413642  pop         esi 

00413643  pop         ebx 

00413644  add         esp,0E4h

0041364A   cmp         ebp,esp

0041364C   call        @ILT+315(__RTC_CheckEsp) (411140h)

00413651  mov         esp,ebp

00413653  pop         ebp 

00413654  ret             

 

你会发现……编译器为我们生成了太多……貌似没什么用的代码。

……

先看看,自己为inject,写的汇编码:

; 汇编代码设计:小石头

; 适应于 IA32 体系

; 格式仿上述代码,红色为C++指令

; 经测试,如下代码成功转跳到 func 函数体内(仍然是p[2]

void inject()

{

  push        ebp 

  mov         ebp,esp

  sub         esp,008h

     int sth=10;

  mov         dword ptr [ebp-4],0Ah

     int* p=&sth;

  lea         eax,[ebp-4]

  mov         dword ptr [ebp-8],eax

     p[2]=(int)func;

  mov         eax,dword ptr [ebp-8]

  mov         dword ptr [eax+8],offset func

}

  add         esp,008h

  mov         esp,ebp

  pop         ebp 

  ret       

 

呃……与之对比,编译器生成的代码显得十分庞大。

原因,是程序编译版本为Debug

 

如果改为Release版,就不会有这么庞大的代码了。

说明:事后,我也做了测试。

当选择编译为Release版的时候,p[2]确实是保存的EIP,这个没错了!

 

现在,我们简单解读下Debug的汇编代码吧。

sub  esp,0E4h

先为栈帧预留这么大空间(57×4228字节),然后又push了一组寄存器。

lea  edi,[ebp-0E4h]

mov  ecx,39h  ;39h是十进制57

mov  eax,0CCCCCCCCh

rep stos  dword ptr es:[edi]

其实是写入cccccccc到范围[EDIEDI+ECX],计算下…正好到达EBP的位置。

而编译器把第一个临时变量,也就是sth,安排到了EBP-4的位置。

(这个……我不是太明白,编译器为什么这么做)

至于编译器为什么要这么做……大概是因为0cccccccch其实是int 3中断指令,它可以把程序中断,将控制权交给调试器。

 


现在说第二个问题。

怎么对付这个出错的窗口。

我们先想下出错的原因……

很明显,程序的流程被打乱了!

 

这样导致进入函数,却没有使用call指令,因此EIP也没有被压入堆栈。

当离开这个函数(func),弹出EIP就会出问题。

……

总之,是溢出,导致了堆栈的不平衡

 

这里,我们是让func取代了应该返回的main中的位置。那么,它就应该表现的像main一样……

 

呵呵,因此,我们需要拒绝编译器为我们生成的那些乱七八糟的代码。

C++中,可以很容易实现这一点。

就是使用关键字__declspec(naked),并且使用__asm关键字来内嵌汇编代码

我们修改下func函数,使之如下:

void __declspec(naked) func(){

     printf("Never called implicitly./n");

     __asm{

         leave

         ret 

     }

}

 

再次运行下吧……

呵呵,问题解决了,程序输出Never called implicitly

安然无恙!

 

需要说明下的是,leave指令在这里可以看成一串pop指令和一句很关键的mov esp,ebp,这样通过自己书写的汇编代码,平衡了堆栈结构。


下篇将会给出自己针对strcpy,这个常用函数的溢出实践……

因为对于正常的程序,肯定不可能写出这么有“破坏性”的代码。

需要做的是……嘿嘿,自己找出一个注入点,然后通过传递一定的参数使之溢出。

等有空再发表,谢谢支持喔。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值