[ShellCode] 动态解密 ShellCode,免杀

背景

今天在复习《加密与解密》时,在软件保护这一章中有一个代码与数据结合的案例,其原理是将代码段中的代码进行xor异或加密处理以后回写到原始位置,当程序运行后将此处的内容动态的进行解密,解密后回写替换回原始内存位置,这样就能实现内存加载。

由此案例我想到一个关于免杀的利用思路,首先杀软的运作方式多数为特征码查杀,当我们程序中使用了敏感的函数时,就会存在被杀的风险,而如果将代码段中的代码进行加密,需要时直接在内存中解密,那么杀软将无法捕捉硬盘文件的特征,从而可以规避杀软针对硬盘特征的查杀手法。

经过阅读该案例的源码,我首先提取出了案例中的核心代码,并加以改进后将其从软件保护改为了免杀手法,其注册码生成工具核心代码如下所示,这里我没有动使用原始的加密工具即可。

for ( i=0;i<strlen(szBuffer);i++) 
	{
    
    
		k = k*6 + szBuffer[i];
	}

	Size=address2-address1;
	Size=Size/0x4; 	//加密时,每次异或 DWORD数据,Size是为最终需要异或的次数
	offset=address1;
 	for (i=0;i<Size;i++)
	{
    
    
		SetFilePointer(hFile,offset,NULL,FILE_BEGIN); 
		ReadFile(hFile,szBuffer, 4, &szTemp, NULL);//读取DWORD字节的文件内容
		ptr=(DWORD*)szBuffer;
		*ptr=(*ptr)^k;
		SetFilePointer(hFile,offset,NULL,FILE_BEGIN); 

		if(!WriteFile(hFile,ptr,4,&nbWritten,NULL))// 写入文件
		{
    
    
			MessageBox(NULL,"Error while patching !","Patch aborted",MB_ICONEXCLAMATION);
			CloseHandle(hFile);
		
			return 1;
		}
		offset=offset+4;
	}
	CloseHandle(hFile);
	MessageBox(NULL,"Patch successfull !","Patch",MB_ICONINFORMATION);
	return 1;
}

下面则是客户端解密代码,该代码的原始部分是注册机加密,我把它抽取出来改成了这个样子,首先使用__asm mov AddressA, offset BeginOEP定义两个段标签,分别用于表示段的开始与结束,也就是我们需要加密与解密的代码段位置,在两个标签内部的就是我们的恶意代码,将其写入到标签中,标签中的__asm inc eax dec eax则是一串标志用于快速定位到需要加密的位置。

#include <stdio.h>
#include <Windows.h>
#include <tchar.h>

void Decrypt(DWORD*, DWORD, DWORD);

void Decrypt(DWORD* pData, DWORD Size, DWORD value)
{
    
    
	//首先要做的是改变这一块虚拟内存的内存保护状态,以便可以自由存取代码
	MEMORY_BASIC_INFORMATION mbi_thunk;
	//查询页信息
	VirtualQuery(pData, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));
	//改变页保护属性为读写。
	VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect);
	Size = Size / 0x4; //对数据共需要异或的次数
						//解密begindecrypt与enddecrypt标签处的数据
	while (Size--)
	{
    
    
		*pData = (*pData) ^ value;
		pData++;
	}

	//恢复页的原保护属性。
	DWORD dwOldProtect;
	VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect);
}

int main(int argc, char* argv[])
{
    
    
	DWORD AddressA, AddressB, Size, key;
	DWORD *ptr;
	TCHAR cCode[30] = {
    
     0 };

	__asm mov AddressA, offset BeginOEP
	__asm mov AddressB, offset EndOEP

	Size = AddressB - AddressA;
	ptr = (DWORD*)AddressA;

	_tcscpy(cCode, L"lyshark");    // 设置加密密钥

	key = 1;
	for (unsigned int i = 0; i< lstrlen(cCode); i++)
	{
    
    
		key = key * 6 + cCode[i];
	}

	Decrypt(ptr, Size, key); //执行解密函数

BeginOEP:
	__asm inc eax  // 在十六进制工具中对应0x40  
	__asm dec eax  // 在十六进制工具中对应0x48

	MessageBoxA(0, "hello lyshark", 0, 0);
	MessageBoxA(0, "hello lyshark", 0, 0);

EndOEP:
	__asm inc eax
	__asm dec eax
	return 0;
}

程序在运行时,首先会循环计算异或密钥,计算完成后执行Decrypt函数,对特定的段进行解密后,释放到源文件中(注意是内存中)然后在调用执行,打印出一句问候语hello lyshark程序结束。

注意:编译时,请关闭DEP,ASLR,地址随机化等保护,否则VA不固定,无法确定位置。

首先我们需要编译上方魔改版的代码片段,然后使用winhex然后按下【ctrl+alt+X】输入4048找到开始于结束的位置。

在这里插入图片描述

这里我们记下,需要加密的开始位置是【526】结束位置是【54b】中间代码部分就是我们需要加密的恶意代码。

在这里插入图片描述
接着打开Encrypter.exe工具依次输入加密开始结束位置与密钥,这里设置如下即可。

在这里插入图片描述

打开程序执行,会首先经过解密函数将加密后的代码片段释放到内存中,然后才会执行弹窗,非常的安全。

在这里插入图片描述

反汇编看一下,解密前,代码是混乱的,根本不是代码。

在这里插入图片描述

而执行解密后,内存中立刻恢复到了可以执行的代码状态,然后就可以开心的执行下去了。

在这里插入图片描述

在这里插入图片描述

此方法也可以规避部分逆向分析,由于不是汇编代码,所以也就无法分析出到底是做什么的了,当然了,如果能找到加密算法的密钥,同样可以解密出来,此处我们并不是用来防范解密者的,而是用来切断程序中的病毒特征的。

猜你喜欢

转载自blog.csdn.net/Simon798/article/details/110239533