今天在复习《加密与解密》时,在软件保护这一章中有一个代码与数据结合的案例,其原理是将代码段中的代码进行xor异或加密处理以后回写到原始位置,当程序运行后将此处的内容动态的进行解密,解密后回写替换回原始内存位置,这样就能实现内存加载。
由此案例我想到一个关于免杀的利用思路,首先杀软的运作方式多数为特征码查杀,当我们程序中使用了敏感的函数时,就会存在被杀的风险,而如果将代码段中的代码进行加密,需要时直接在内存中解密,那么杀软将无法捕捉硬盘文件的特征,从而可以规避杀软针对硬盘特征的查杀手法。
经过阅读该案例的源码,我首先提取出了案例中的核心代码,并加以改进后将其从软件保护改为了免杀手法,其注册码生成工具核心代码如下所示,这里我没有动使用原始的加密工具即可。
for ( i=0;i<strlen(szBuffer);i++) { k = k*6 + szBuffer[i]; }
Size=address2-address1; Size=Size/0x4; offset=address1; for (i=0;i<Size;i++) { SetFilePointer(hFile,offset,NULL,FILE_BEGIN); ReadFile(hFile,szBuffer, 4, &szTemp, NULL); 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; 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 __asm dec eax
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工具依次输入加密开始结束位置与密钥,这里设置如下即可。

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

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

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


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