欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[ShellCode] 动态解密 ShellCode,免杀

程序员文章站 2022-03-24 09:17:01
...

背景

今天在复习《加密与解密》时,在软件保护这一章中有一个代码与数据结合的案例,其原理是将代码段中的代码进行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找到开始于结束的位置。

[ShellCode] 动态解密 ShellCode,免杀

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

[ShellCode] 动态解密 ShellCode,免杀
接着打开Encrypter.exe工具依次输入加密开始结束位置与**,这里设置如下即可。

[ShellCode] 动态解密 ShellCode,免杀

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

[ShellCode] 动态解密 ShellCode,免杀

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

[ShellCode] 动态解密 ShellCode,免杀

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

[ShellCode] 动态解密 ShellCode,免杀

[ShellCode] 动态解密 ShellCode,免杀

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

相关标签: ShellCode