5.2.3 解码——decode程序

“编码之后我们得到了enShellCode;还需要一段解码程序decode,让编码后的enShellCode还原成原来的ShellCode,然后跳过去执行。有了decode和enShellCode,我们把decode放在前面,enShellCode跟在后面。如图5-4。”

 “当然,decode和enShellCode里面不能有非法字符,否则变换就失去了意义。”老师说道,“根据上面的分析,我们只需将enShellCode里面的字符再异或编码用的Key就可以了,这里还是0x97。”

“decode实现的汇编代码如下:”

jmp decode_end //为了获得enShellCode的地址
decode_start:
pop edx // 得到enShellCode的开始位置 esp -> edx
dec edx
xor ecx,ecx
mov cx,0x200 //要解码的 enShellCode长度,0x200应该足够 
decode_loop:
xor byte ptr [edx+ecx], 0x97 //因为编码时用的Key是0x97,所以解码要一样
loop decode_loop //循环解码
jmp decode_ok //解码完毕后,跳到解码后的地方执行!
decode_end:
call decode_start
decode_ok: //后面接编码后的enShellCode

“得到decode的机器码后,我们把enShellCode跟在后面就可以了。”老师指着程序说。

“理解起来还是有点困难啊!”同学们面带难色。

“嗯,我们一起来过一遍吧!”

“首先,以下代码段的作用是定位enShellCode的位置。”

jmp decode_end
decode_start:
pop edx
……
……
decode_end:
call decode_start

“如何定位的呢?”大家有点奇怪。

“Call的功能大家还记得吧? call decode_start 会完成两个功能: push EIP,JMP decode_start ,即先保存下一句指令的地址,然后跳到decode_start处执行。”

“而 call decode_start 后面紧跟enShellCode,所以 push EIP 就会把紧跟后面的enShellCode的地址保存在堆栈中,然后跳到decode_start处执行。”

大家点点头。

“而在decode_start处,马上 pop edx ,就会把保存的EIP(其实就是enShellCode的地址)赋给edx,这样edx就是enShellCode的地址了。示意图如图5-5。”

“哦!原来这样获得enShellCode的地址啊!”大家恍然大悟。

“这是种动态定位地址方法,很多地方都有使用。”老师说道,“我们继续往下看。”

decode_loop:
xor byte ptr [edx+ecx], 0x97 //因为编码时用的Key是0x97,所以解码要一样
loop decode_loop //循环解码

“ xor byte ptr [edx+ecx], 0x97 就是对enShellCode解码——异或0x97;而 loop decode_loop 是一个循环,功能是ecx减一,如果ecx不为0,就跳到decode_loop继续执行。这样,我们就可解码ecx这么多个字节,这里ecx赋成的是0x200(500多个字节),一般的ShellCode都应该够了。”

“看来解码的关键就是这里啊!”

“是的,最后 jmp decode_ok 是跳转到复原的ShellCode中执行,这样就完成了decode的功能。”

“哦,明白了!”

“好了,大家清楚了流程和思路,我们就提取出decode的机器码吧!”

“这种活就不要和我争了,我来!”古风争先恐后的说,“在VC里面加上‘__asm’关键字,然后按F10进入调试状态,选择‘disassemble’和‘Code Bytes’,就会出现汇编对应的机器码,如图5-6。我们把它们抄下来就可以了。”

“最后得到decode的代码为如下:”古风抄完后得意的说道。

decode[] = 
    “\xEB\x10\x5A\x4A\x33\xC9\x66\xB9\x00\x02”
    “\x80\x34\x0A\x97\xE2\xFA\xEB\x05\xE8\xEB\xFF\xFF\xFF”

“不错!”老师说,“我们用一个实际例子检验看看!”