• C/C++ 动态解密释放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工具依次输入加密开始结束位置与密钥,这里设置如下即可。

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

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

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

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


    版权声明: 本博客,文章与代码均为学习时整理的笔记,博客中除去明确标注有参考文献的文章,其他文章【均为原创】作品,转载请务必【添加出处】,您添加出处是我创作的动力!

    警告:如果您恶意转载本人文章,则您的整站文章,将会变为我的原创作品,请相互尊重!
  • 相关阅读:
    coding
    我的大学
    《活出生命的意义》读后感
    《世界是数字的》读后感
    学习进度表
    阅读《我是一只IT小小鸟有感》
    《软件工程师职业规范》心得
    软件工程第二次结对作业
    软件工程第三次作业
    软件工程第二次作业
  • 原文地址:https://www.cnblogs.com/LyShark/p/13651803.html
Copyright © 2020-2023  润新知