今天在復習《加密與解密》時,在軟件保護這一章中有一個代碼與數據結合的案例,其原理是將代碼段中的代碼進行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工具依次輸入加密開始結束位置與密鑰,這里設置如下即可。

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

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

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


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