1. 程式人生 > >殼的編寫(5)-- 進行加殼操作

殼的編寫(5)-- 進行加殼操作

由於我們在編寫殼的部分比較簡單,那麼我們在編寫加殼的過程中難免要複雜些。我們要完成加殼的操作必然會要讀取被加殼程式的各種資訊,並儲存到一個結構中,為了便於後面的操作。還有在操作上只能讀取原始檔,將加殼後的檔案要儲存到另外的檔案中去。

為此我們在專案Pack_Dll中,我們完成加殼的操作:

         1.讀取被加殼程式的PE資訊

         2.對相應的區段進行處理(加密)

         3.將殼(Stub)部分新增到加殼程式中

         4.加殼操作

5.1. 讀取被加殼程式的PE資訊

         在Pack_Dll.h檔案中定義一個結構如下:

// 用來儲存殼(Stub)中用到的PE資訊
typedef struct _GLOBAL_PARAM
{
	BOOL  bShowMessage; // 是否顯示解密資訊
	DWORD dwOEP;        // 程式入口點
	PBYTE lpStartVA;    // 起始虛擬地址(被異或加密區)
	PBYTE lpEndVA;      // 結束虛擬地址(被異或加密區)
}GLOBAL_PARAM, *PGLOBAL_PARAM; 

5.2. 對相應的區段進行處理(加密)

         給程式碼區段進行加密,併為其新增可讀屬性。
void Pretreatment(PBYTE lpCodeStart, PBYTE lpCodeEnd, PE_INFO stcPeInfo)
{
	// 1. 加密指定區域
	while (lpCodeStart < lpCodeEnd)
	{
		*lpCodeStart ^= 0x15;
		lpCodeStart++;
	}

	// 2. 給第一個區段附加上可寫屬性
	PDWORD pChara = &(stcPeInfo.pSectionHeader->Characteristics);
	*pChara = *pChara | IMAGE_SCN_MEM_WRITE;
}

5.3. 將殼(Stub)部分新增到加殼程式中

         我們要想將Stub部分新增到被加殼程式中去,就需要獲取到Stub的資訊。而我們已經在3.2的操作中將Stub工程產生的Stub.dll做為資源成為了專案Pack_Dll的一部分,那麼我們就需要讀取生成的Pack_Dll.dll並從中以資源的形式獲取到Stub.dll控制代碼。然後提取Stub部分的關鍵資訊,將Stub作為新的區段新增到被加殼程式中。為此我們還需要進行重定位和修復OEP。


需要新增標頭檔案

#include <stdlib.h>
#include "resource.h"                   //匯入資源
#include "ProcessingPE.h"                //PE操作
#include <winuser.h>                    //資源轉換
在Pack_Dll.cpp檔案中的Implantation方法中完成即可。
DWORD Implantation(LPVOID &lpFileData, DWORD dwSize, CProcessingPE* pobjPE, 
	PE_INFO stcPeInfo, GLOBAL_PARAM stcParam)
{
	// 1. 在資源中讀取檔案內容
	HRSRC   hREC = NULL; // 資源物件
	HGLOBAL hREC_Handle = NULL; // 資源控制代碼
	DWORD   dwStubSize = NULL; // 檔案大小
	LPVOID  lpResData = NULL; // 資源資料指標
	HMODULE hModule = GetModuleHandle(L"Pack_Dll.dll");
	if (!(hREC = FindResource(hModule, MAKEINTRESOURCE(IDR_STUB1), L"STUB")))  return false;
	if (!(hREC_Handle = LoadResource(hModule, hREC)))                          return false;
	if (!(lpResData = LockResource(hREC_Handle)))                              return false;
	if (!(dwStubSize = SizeofResource(hModule, hREC)))                         return false;

	// 2. 提取Stub部分的關鍵資訊
	CProcessingPE objProcPE;
	PE_INFO       stcStubPeInfo;
	PBYTE         lpData = new BYTE[dwStubSize];
	// 2.1 將Stub複製到臨時緩衝區,防止重複操作
	CopyMemory(lpData, lpResData, dwStubSize);
	// 2.2 獲取Stub的PE資訊
	objProcPE.GetPeInfo(lpData, dwStubSize, &stcStubPeInfo);
	// 2.3 算出程式碼段的相關資訊(預設第一個區段為程式碼段)
	PBYTE lpText = (PBYTE)(stcStubPeInfo.pSectionHeader->PointerToRawData + (DWORD)lpData);
	DWORD dwTextSize = stcStubPeInfo.pSectionHeader->SizeOfRawData;

	// 3. 新增區段
	DWORD                 dwNewSectionSize = 0;
	IMAGE_SECTION_HEADER  stcNewSection = { 0 };
	PVOID lpNewSectionData = pobjPE->AddSection(L".LiPass", dwTextSize, 
	IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE, &stcNewSection, &dwNewSectionSize);

	// 4. 對Stub部分進行的重定位操作
	//    新的載入地址 = (新區段的起始RVA - Stub的".Text"區段的起始RVA) + 映像基址
	DWORD dwLoadImageAddr = (stcNewSection.VirtualAddress - stcStubPeInfo.pSectionHeader->VirtualAddress) + stcPeInfo.dwImageBase;
	objProcPE.FixReloc(dwLoadImageAddr);

	// 5. 寫入配置引數
	// 5.1 獲取Stub的匯出變數地址
	PVOID lpPatam = objProcPE.GetExpVarAddr(L"g_stcParam");
	// 5.2 儲存配置資訊到Stub中
	CopyMemory(lpPatam, &stcParam, sizeof(GLOBAL_PARAM));

	// 6. 將Stub複製到新區段中
	CopyMemory(lpNewSectionData, lpText, dwTextSize);

	// 7. 計算並設定新OEP
	DWORD dwNewOEP = 0;
	// 7.1 計算新OEP
	DWORD dwStubOEP = stcStubPeInfo.dwOEP;
	DWORD dwStubTextRVA = stcStubPeInfo.pSectionHeader->VirtualAddress;
	DWORD dwNewSectionRVA = stcNewSection.VirtualAddress;
	dwNewOEP = (dwStubOEP - dwStubTextRVA) + dwNewSectionRVA;
	// 7.2 設定新OEP
	pobjPE->SetOEP(dwNewOEP);
	pobjPE->SetDLL();

	// 8. 釋放資源,函式返回
	delete[] lpData;
	FreeResource(hREC_Handle);
	return dwNewSectionSize;
}

5.4. 加殼操作

         先讀取PE資訊,並進行加密處理。然後將殼部分追加到程式中,並儲存為新的檔案。

         在Pack_Dll.cpp檔案中的Pack方法中完成即可。
BOOL Pack(CString strPath)
{
	CProcessingPE objProcPE; // PE處理物件
	PE_INFO       stcPeInfo; // PE資訊

	HANDLE  hFile_In;
	HANDLE  hFile_Out;
	DWORD   dwFileSize;
	LPVOID  lpFileImage;
	WCHAR   szOutPath[MAX_PATH] = { 0 };

	// 1. 生成輸出檔案路徑
	LPWSTR strSuffix = PathFindExtension(strPath);         // 獲取檔案的字尾名
	wcsncpy_s(szOutPath, MAX_PATH, strPath, wcslen(strPath)); // 備份目標檔案路徑到szOutPath
	PathRemoveExtension(szOutPath);                        // 將szOutPath中儲存路徑的字尾名去掉
	wcscat_s(szOutPath, MAX_PATH, L"_Pack");                 // 在路徑最後附加“_Pack”
	wcscat_s(szOutPath, MAX_PATH, strSuffix);                // 在路徑最後附加剛剛儲存的字尾名

	// 2. 獲取檔案資訊,並對映進記憶體中
	if (INVALID_HANDLE_VALUE == (hFile_In = CreateFile(strPath, GENERIC_READ, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, 0, NULL)))
	{
		return false;
	}
	if (INVALID_FILE_SIZE == (dwFileSize = GetFileSize(hFile_In, NULL)))
	{
		CloseHandle(hFile_In);
		return false;
	}
	if (!(lpFileImage = VirtualAlloc(NULL, dwFileSize * 2, MEM_COMMIT, PAGE_READWRITE)))
	{
		CloseHandle(hFile_In);
		return false;
	}
	DWORD dwRet;
	if (!ReadFile(hFile_In, lpFileImage, dwFileSize, &dwRet, NULL))
	{
		CloseHandle(hFile_In);
		VirtualFree(lpFileImage, 0, MEM_RELEASE);
		return false;
	}

	// 3. 獲取PE檔案資訊
	objProcPE.GetPeInfo(lpFileImage, dwFileSize, &stcPeInfo);

	// 4. 獲取目標檔案程式碼段的起始結束資訊
	//    讀取第一個區段的相關資訊,並將其加密(預設第一個區段為程式碼段)
	PBYTE lpStart = (PBYTE)(stcPeInfo.pSectionHeader->PointerToRawData + (DWORD)lpFileImage);
	PBYTE lpEnd = (PBYTE)((DWORD)lpStart + stcPeInfo.pSectionHeader->SizeOfRawData);
	PBYTE lpStartVA = (PBYTE)(stcPeInfo.pSectionHeader->VirtualAddress + stcPeInfo.dwImageBase);
	PBYTE lpEndVA = (PBYTE)((DWORD)lpStartVA + stcPeInfo.pSectionHeader->SizeOfRawData);

	// 5. 對檔案進行預處理
	Pretreatment(lpStart, lpEnd, stcPeInfo);

	// 6. 植入Stub
	DWORD        dwStubSize = 0;
	GLOBAL_PARAM stcParam = { 0 };
	stcParam.dwOEP = stcPeInfo.dwOEP + stcPeInfo.dwImageBase;
	stcParam.lpStartVA = lpStartVA;
	stcParam.lpEndVA = lpEndVA;
	dwStubSize = Implantation(lpFileImage, dwFileSize, &objProcPE, stcPeInfo, stcParam);

	// 7. 將處理完成後的結果寫入到新檔案中
	if (INVALID_HANDLE_VALUE != (hFile_Out = CreateFile(szOutPath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL)))
	{
		DWORD dwRet = 0;
		WriteFile(hFile_Out, lpFileImage, dwStubSize + dwFileSize, &dwRet, NULL);
	}

	// 8. 釋放相關資源並返回
	CloseHandle(hFile_In);
	CloseHandle(hFile_Out);
	VirtualFree(lpFileImage, 0, MEM_RELEASE);
	return true;
}

六 編寫介面

         在Pack BaseDlg.cpp檔案中進行完善即可。
#include "../Pack_Dll/Pack_Dll.h"
#ifdef _DEBUG
#pragma comment(lib, "../Debug/Pack_Dll.lib")
#else
#pragma comment(lib, "../Release/Pack_ Dll.lib")
#endif

void CPackBaseDlg::OnBnClickedButton2()
{
	UpdateData(true);
	//MessageBox(m_pathStr, m_pathStr, 0);

	if (!Pack(m_pathStr))
	{
		MessageBox(L"加密失敗-_-!");
	}
	else
	{
		MessageBox(L"加密成功!");
	}
}

七 執行結果如下

在使用靜態編譯的時候,要是以Release版本進行輸出的時候,一定要將Stub專案的輸出目錄進行更改回去。


由於之前一直使用Debug方式進行除錯,使用Release版後,需要將Pack_Dll專案中Pack_Dll.rc的資源路徑進行修改,將Debug修改成Release即可,如下:

//IDR_STUB1  STUB   "D:\\Users\\殼編寫3\\PackBase\\Debug\\Stub.dll"
IDR_STUB1   STUB  "D:\\Users\\殼編寫3\\Pack Base\\Release\\Stub.dll"

執行結果如下:

原始碼:http://download.csdn.net/detail/obuyiseng/9406707