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工具依次輸入加密開始結束位置與密鑰,這里設置如下即可。

打開程序執行,會首先經過解密函數將加密后的代碼片段釋放到內存中,然后才會執行彈窗,非常的安全。

反匯編看一下,解密前,代碼是混亂的,根本不是代碼。

而執行解密后,內存中立刻恢復到了可以執行的代碼狀態,然后就可以開心的執行下去了。

此方法也可以規避部分逆向分析,由於不是匯編代碼,所以也就無法分析出到底是做什么的了,當然了,如果能找到加密算法的密鑰,同樣可以解密出來,此處我們並不是用來防范解密者的,而是用來切斷程序中的病毒特征的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM