主頁 >  其他 > 加殼學習系列(二)-殼代碼

加殼學習系列(二)-殼代碼

2021-07-21 12:21:19 其他

殼代碼

  • 一.Shell.dll的原始碼和二進制檔案資訊
    • 1.1 殼代碼
      • shell.h
      • shell.cpp
      • start函式
    • 1.2 加殼器代碼
      • 入口函式
      • InitPE
      • 拷貝shell.dll中的殼代碼
      • MergeBuf
  • 二.DLL基礎
    • 2.1 DllMain簡介
    • 2.2 何時呼叫DllMain
    • 2.3 Dll匯出函式
  • 三.加殼后的示例代碼
    • 3.1 010Editor
    • 3.2 IDA

上一節寫到了,殼代碼是寫在shell.dll里的(看雪的大佬寫的),所以分析dll檔案就很有必要,

一.Shell.dll的原始碼和二進制檔案資訊

1.1 殼代碼

這里就直接粘貼了,涉及到的細節下面討論,覺得太長了可以直接跳過

shell.h

#ifdef SHELL_EXPORTS
#define SHELL_API __declspec(dllexport)
#else
#define SHELL_API __declspec(dllimport)
#endif

#include<Windows.h>

//匯出ShellData結構體
extern"C"  typedef struct _SHELL_DATA
{
	DWORD dwStartFun;							//啟動函式
	DWORD dwPEOEP;								//程式入口點
	DWORD dwXorKey;								//解密KEY
	DWORD dwCodeBase;							//代碼段起始地址
	DWORD dwXorSize;							//代碼段加密大小
	DWORD dwPEImageBase;						//PE檔案映像基址

	IMAGE_DATA_DIRECTORY	stcPERelocDir;		//重定位表資訊
	IMAGE_DATA_DIRECTORY	stcPEImportDir;		//匯入表資訊

	DWORD					dwIATSectionBase;	//IAT所在段基址
	DWORD					dwIATSectionSize;	//IAT所在段大小
	BOOL					bIsShowMesBox;		//是否顯示MessageBox

}SHELL_DATA, *PSHELL_DATA;

//匯出ShellData結構體變數
extern"C" SHELL_API SHELL_DATA g_stcShellData;



//Shell部分用到的函式的型別定義,均用函式指標型別表示
typedef DWORD(WINAPI *fnGetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef HMODULE(WINAPI *fnLoadLibraryA)(_In_ LPCSTR lpLibFileName);
typedef HMODULE(WINAPI *fnGetModuleHandleA)(_In_opt_ LPCSTR lpModuleName);
typedef BOOL(WINAPI *fnVirtualProtect)(_In_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flNewProtect, _Out_ PDWORD lpflOldProtect);
typedef LPVOID(WINAPI *fnVirtualAlloc)(_In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect);
typedef void(WINAPI *fnExitProcess)(_In_ UINT uExitCode);
typedef int(WINAPI *fnMessageBox)(HWND hWnd, LPSTR lpText, LPSTR lpCaption, UINT uType);

shell.h中匯出了變數g_stcShellDatafnGetProcAddressfnLoadLibraryA等相當于自定義的型別,

shell.cpp

這段有點長,是完整代碼,覺得又臭又長的話可以先跳到下面一個部分,

#include"Shell.h"

#pragma comment(linker, "/merge:.data=.text") 
#pragma comment(linker, "/merge:.rdata=.text")
#pragma comment(linker, "/section:.text,RWE")

//函式和變數的宣告
DWORD MyGetProcAddress();		//自定義GetProcAddress
HMODULE	GetKernel32Addr();		//獲取Kernel32加載基址
void Start();					//啟動函式(Shell部分的入口函式)
void InitFun();					//初始化函式指標和變數
void DeXorCode();				//解密操作
void RecReloc();				//修復重定位操作
void RecIAT();					//修復IAT操作
SHELL_DATA g_stcShellData = { (DWORD)Start }; // 注意這里,首先將g_stcShellData ->dwStartFun設為start()函式的地址
//Shell用到的全域變數結構體
DWORD dwImageBase = 0;		//整個程式的鏡像基址
DWORD dwPEOEP = 0;		//PE檔案的OEP

//Shell部分用到的函式指標定義
fnGetProcAddress	g_pfnGetProcAddress = NULL;
fnLoadLibraryA		g_pfnLoadLibraryA = NULL;
fnGetModuleHandleA	g_pfnGetModuleHandleA = NULL;
fnVirtualProtect	g_pfnVirtualProtect = NULL;
fnVirtualAlloc		g_pfnVirtualAlloc = NULL;
fnExitProcess		g_pfnExitProcess = NULL;
fnMessageBox		g_pfnMessageBoxA = NULL;

//************************************************************
// 函式名稱:	Start
// 函式說明:	啟動函式(Shell部分的入口函式)
// 作	者:	cyxvc
// 時	間:	2015/12/28
// 返 回	值:	void
//************************************************************

//__declspec(naked)是編譯器直接拿來用的匯編函式代碼,所以一定要記得在開始的// 時候保存背景關系標志位(壓堆疊),在結束的時候要記得恢復背景關系(出堆疊),
__declspec(naked) void Start()
{
	__asm pushad

	InitFun();

	DeXorCode();

	if (g_stcShellData.stcPERelocDir.VirtualAddress)
	{
		RecReloc();
	}
	RecIAT();

	if (g_stcShellData.bIsShowMesBox)
	{
		g_pfnMessageBoxA(0, "歡迎使用CyxvcProtect, by 15PB !", "Hello!", 0);
	}

	__asm popad

	//獲取OEP資訊
	dwPEOEP = g_stcShellData.dwPEOEP + dwImageBase;
	__asm jmp dwPEOEP

	g_pfnExitProcess(0);	//實際不會執行此條指令
}

//************************************************************
// 函式名稱:	RecIAT
// 函式說明:	修復IAT操作
// 作	者:	cyxvc
// 時	間:	2015/12/28
// 返 回	值:	void
//************************************************************
void RecIAT()
{
	//1.獲取匯入表結構體指標
	PIMAGE_IMPORT_DESCRIPTOR pPEImport =
		(PIMAGE_IMPORT_DESCRIPTOR)(dwImageBase + g_stcShellData.stcPEImportDir.VirtualAddress);

	//2.修改記憶體屬性為可寫
	DWORD dwOldProtect = 0;
	g_pfnVirtualProtect(
		(LPBYTE)(dwImageBase + g_stcShellData.dwIATSectionBase), g_stcShellData.dwIATSectionSize,
		PAGE_EXECUTE_READWRITE, &dwOldProtect);

	//3.開始修復IAT
	while (pPEImport->Name)
	{
		//獲取模塊名
		DWORD dwModNameRVA = pPEImport->Name;
		char* pModName = (char*)(dwImageBase + dwModNameRVA);
		HMODULE hMod = g_pfnLoadLibraryA(pModName);

		//獲取IAT資訊(有些PE檔案INT是空的,最好用IAT決議,也可兩個都決議作對比)
		PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)(dwImageBase + pPEImport->FirstThunk);

		//獲取INT資訊(同IAT一樣,可將INT看作是IAT的一個備份)
		//PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)(dwImageBase + pPEImport->OriginalFirstThunk);

		//通過IAT回圈獲取該模塊下的所有函式資訊(這里之獲取了函式名)
		while (pIAT->u1.AddressOfData)
		{
			//判斷是輸出函式名還是序號
			if (IMAGE_SNAP_BY_ORDINAL(pIAT->u1.Ordinal))
			{
				//輸出序號
				DWORD dwFunOrdinal = (pIAT->u1.Ordinal) & 0x7FFFFFFF;
				DWORD dwFunAddr = g_pfnGetProcAddress(hMod, (char*)dwFunOrdinal);
				*(DWORD*)pIAT = (DWORD)dwFunAddr;
			}
			else
			{
				//輸出函式名
				DWORD dwFunNameRVA = pIAT->u1.AddressOfData;
				PIMAGE_IMPORT_BY_NAME pstcFunName = (PIMAGE_IMPORT_BY_NAME)(dwImageBase + dwFunNameRVA);
				DWORD dwFunAddr = g_pfnGetProcAddress(hMod, pstcFunName->Name);
				*(DWORD*)pIAT = (DWORD)dwFunAddr;
			}
			pIAT++;
		}
		//遍歷下一個模塊
		pPEImport++;
	}

	//4.恢復記憶體屬性
	g_pfnVirtualProtect(
		(LPBYTE)(dwImageBase + g_stcShellData.dwIATSectionBase), g_stcShellData.dwIATSectionSize,
		dwOldProtect, &dwOldProtect);
}

//************************************************************
// 函式名稱:	RecReloc
// 函式說明:	修復重定位操作
// 作	者:	cyxvc
// 時	間:	2015/12/28
// 返 回	值:	void
//************************************************************
void RecReloc()
{
	typedef struct _TYPEOFFSET
	{
		WORD offset : 12;		//偏移值
		WORD Type : 4;			//重定位屬性(方式)
	}TYPEOFFSET, *PTYPEOFFSET;

	//1.獲取重定位表結構體指標
	PIMAGE_BASE_RELOCATION	pPEReloc =
		(PIMAGE_BASE_RELOCATION)(dwImageBase + g_stcShellData.stcPERelocDir.VirtualAddress);

	//2.開始修復重定位
	while (pPEReloc->VirtualAddress)
	{
		//2.1修改記憶體屬性為可寫
		DWORD dwOldProtect = 0;
		g_pfnVirtualProtect((PBYTE)dwImageBase + pPEReloc->VirtualAddress,
			0x1000, PAGE_EXECUTE_READWRITE, &dwOldProtect);

		//2.2修復重定位
		PTYPEOFFSET pTypeOffset = (PTYPEOFFSET)(pPEReloc + 1);
		DWORD dwNumber = (pPEReloc->SizeOfBlock - 8) / 2;
		for (DWORD i = 0; i < dwNumber; i++)
		{
			if (*(PWORD)(&pTypeOffset[i]) == NULL)
				break;
			//RVA
			DWORD dwRVA = pTypeOffset[i].offset + pPEReloc->VirtualAddress;
			//FAR地址
			DWORD AddrOfNeedReloc = *(PDWORD)((DWORD)dwImageBase + dwRVA);
			*(PDWORD)((DWORD)dwImageBase + dwRVA) =
				AddrOfNeedReloc - g_stcShellData.dwPEImageBase + dwImageBase;
		}

		//2.3恢復記憶體屬性
		g_pfnVirtualProtect((PBYTE)dwImageBase + pPEReloc->VirtualAddress,
			0x1000, dwOldProtect, &dwOldProtect);

		//2.4修復下一個區段
		pPEReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pPEReloc + pPEReloc->SizeOfBlock);
	}
}


//************************************************************
// 函式名稱:	DeXorCode
// 函式說明:	解密操作
// 作	者:	cyxvc
// 時	間:	2015/12/28
// 返 回	值:	void
//************************************************************
void DeXorCode()
{
	PBYTE pCodeBase = (PBYTE)g_stcShellData.dwCodeBase + dwImageBase;

	DWORD dwOldProtect = 0;
	g_pfnVirtualProtect(pCodeBase, g_stcShellData.dwXorSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	for (DWORD i = 0; i < g_stcShellData.dwXorSize; i++)
	{
		pCodeBase[i] ^= i;
	}

	g_pfnVirtualProtect(pCodeBase, g_stcShellData.dwXorSize, dwOldProtect, &dwOldProtect);
}

//************************************************************
// 函式名稱:	InitFun
// 函式說明:	初始化函式指標和變數
// 作	者:	cyxvc
// 時	間:	2015/12/28
// 返 回	值:	void
//************************************************************
void InitFun()
{
	//從Kenel32中獲取函式
	HMODULE hKernel32 = GetKernel32Addr();
	g_pfnGetProcAddress = (fnGetProcAddress)MyGetProcAddress();
	g_pfnLoadLibraryA = (fnLoadLibraryA)g_pfnGetProcAddress(hKernel32, "LoadLibraryA");
	g_pfnGetModuleHandleA = (fnGetModuleHandleA)g_pfnGetProcAddress(hKernel32, "GetModuleHandleA");
	g_pfnVirtualProtect = (fnVirtualProtect)g_pfnGetProcAddress(hKernel32, "VirtualProtect");
	g_pfnExitProcess = (fnExitProcess)g_pfnGetProcAddress(hKernel32, "ExitProcess");
	g_pfnVirtualAlloc = (fnVirtualAlloc)g_pfnGetProcAddress(hKernel32, "VirtualAlloc");

	//從user32中獲取函式
	HMODULE hUser32 = g_pfnLoadLibraryA("user32.dll");
	g_pfnMessageBoxA = (fnMessageBox)g_pfnGetProcAddress(hUser32, "MessageBoxA");

	//初始化鏡像基址
	dwImageBase = (DWORD)g_pfnGetModuleHandleA(NULL);
}


//************************************************************
// 函式名稱:	GetKernel32Addr
// 函式說明:	獲取Kernel32加載基址
// 作	者:	cyxvc
// 時	間:	2015/12/28
// 返 回	值:	HMODULE
//************************************************************
HMODULE GetKernel32Addr()
{
	HMODULE dwKernel32Addr = 0;
	__asm
	{
		push eax
			mov eax, dword ptr fs : [0x30]   // eax = PEB的地址
			mov eax, [eax + 0x0C]            // eax = 指向PEB_LDR_DATA結構的指標
			mov eax, [eax + 0x1C]            // eax = 模塊初始化鏈表的頭指標InInitializationOrderModuleList
			mov eax, [eax]                   // eax = 串列中的第二個條目
			mov eax, [eax]                   // eax = 串列中的第三個條目
			mov eax, [eax + 0x08]            // eax = 獲取到的Kernel32.dll基址(Win7下第三個條目是Kernel32.dll)
			mov dwKernel32Addr, eax
			pop eax
	}
	return dwKernel32Addr;
}


//************************************************************
// 函式名稱:	MyGetProcAddress
// 函式說明:	自定義GetProcAddress
// 作	者:	cyxvc
// 時	間:	2015/12/28
// 返 回	值:	DWORD
//************************************************************
DWORD MyGetProcAddress()
{
	HMODULE hModule = GetKernel32Addr();

	//1.獲取DOS頭
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(PBYTE)hModule;
	//2.獲取NT頭
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)hModule + pDosHeader->e_lfanew);
	//3.獲取匯出表的結構體指標
	PIMAGE_DATA_DIRECTORY pExportDir =
		&(pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);

	PIMAGE_EXPORT_DIRECTORY pExport =
		(PIMAGE_EXPORT_DIRECTORY)((PBYTE)hModule + pExportDir->VirtualAddress);

	//EAT
	PDWORD pEAT = (PDWORD)((DWORD)hModule + pExport->AddressOfFunctions);
	//ENT
	PDWORD pENT = (PDWORD)((DWORD)hModule + pExport->AddressOfNames);
	//EIT
	PWORD pEIT = (PWORD)((DWORD)hModule + pExport->AddressOfNameOrdinals);

	//4.遍歷匯出表,獲取GetProcAddress()函式地址
	DWORD dwNumofFun = pExport->NumberOfFunctions;
	DWORD dwNumofName = pExport->NumberOfNames;
	for (DWORD i = 0; i < dwNumofFun; i++)
	{
		//如果為無效函式,跳過
		if (pEAT[i] == NULL)
			continue;
		//判斷是以函式名匯出還是以序號匯出
		DWORD j = 0;
		for (; j < dwNumofName; j++)
		{
			if (i == pEIT[j])
			{
				break;
			}
		}
		if (j != dwNumofName)
		{
			//如果是函式名方式匯出的
			//函式名
			char* ExpFunName = (CHAR*)((PBYTE)hModule + pENT[j]);
			//進行對比,如果正確回傳地址
			if (!strcmp(ExpFunName, "GetProcAddress"))
			{
				return pEAT[i] + pNtHeader->OptionalHeader.ImageBase;
			}
		}
		else
		{
			//序號
		}
	}
	return 0;
}

start函式

關于shell.cpp,首先注意最上面幾行中SHELL_DATA g_stcShellData = { (DWORD)Start }; 這里將匯出變數g_stcShellDatadwStartFun成員變數設為start()函式的地址,

其次是start函式

//************************************************************
// 函式名稱:	Start
// 函式說明:	啟動函式(Shell部分的入口函式)
// 作	者:	cyxvc
// 時	間:	2015/12/28
// 返 回	值:	void
//************************************************************

//__declspec(naked)是編譯器直接拿來用的匯編函式代碼,所以一定要記得在開始的// 時候保存背景關系標志位(壓堆疊),在結束的時候要記得恢復背景關系(出堆疊),
__declspec(naked) void Start()
{
	__asm pushad

	InitFun();

	DeXorCode();

	if (g_stcShellData.stcPERelocDir.VirtualAddress)
	{
		RecReloc();
	}
	RecIAT();

	if (g_stcShellData.bIsShowMesBox)
	{
		g_pfnMessageBoxA(0, "歡迎使用CyxvcProtect, by 15PB !", "Hello!", 0);
	}

	__asm popad

	//獲取OEP資訊
	dwPEOEP = g_stcShellData.dwPEOEP + dwImageBase;
	__asm jmp dwPEOEP

	g_pfnExitProcess(0);	//實際不會執行此條指令
}

start函式是殼代碼的入口函式,那它什么時候執行呢?就是把目標程式(Test.exe)被加殼之后,雙擊Test.exe,首先執行start函式,再執行原Test.exe的入口程式,

InitFun();為初始化函式,初始化些變數,有的已經被加殼器設定好了,DoXorCode定義如下:

void DeXorCode()
{
	PBYTE pCodeBase = (PBYTE)g_stcShellData.dwCodeBase + dwImageBase; //獲取被加殼代碼代碼段地址,示例代碼中的計算出來為 0x401000

	DWORD dwOldProtect = 0;
	// g_pfnVirtualProtect為kernel32.dll的VirtualProtect函式
	g_pfnVirtualProtect(pCodeBase, g_stcShellData.dwXorSize, PAGE_EXECUTE_READWRITE, &dwOldProtect);

	for (DWORD i = 0; i < g_stcShellData.dwXorSize; i++)
	{
		pCodeBase[i] ^= i;
	}

	g_pfnVirtualProtect(pCodeBase, g_stcShellData.dwXorSize, dwOldProtect, &dwOldProtect);
}

執行shell中的代碼時,原始代碼已經被加殼了,所以需要先脫殼,g_stcShellData.dwXorSize為加殼器設定好的變數,即代碼段長度,示例中的為0xA00,PAGE_EXECUTE_READWRITE(0x40)表示修改脫殼后的代碼段可讀可寫可執行,

RecReloc();表示修復重定位表,有個if說明不一定執行,這個不太明白,先放著,

RecIAT為修復IAT表,執行程序如下

void RecIAT()
{
	//1.獲取匯入表結構體指標
	PIMAGE_IMPORT_DESCRIPTOR pPEImport =
		(PIMAGE_IMPORT_DESCRIPTOR)(dwImageBase + g_stcShellData.stcPEImportDir.VirtualAddress); // 0x402244

	//2.修改記憶體屬性為可寫(IAT所在區段)
	DWORD dwOldProtect = 0;
	g_pfnVirtualProtect(
		(LPBYTE)(dwImageBase + g_stcShellData.dwIATSectionBase), g_stcShellData.dwIATSectionSize,
		PAGE_EXECUTE_READWRITE, &dwOldProtect);

	//3.開始修復IAT
	while (pPEImport->Name)
	{
		//獲取模塊名
		DWORD dwModNameRVA = pPEImport->Name;
		char* pModName = (char*)(dwImageBase + dwModNameRVA);
		HMODULE hMod = g_pfnLoadLibraryA(pModName);

		//獲取IAT資訊(有些PE檔案INT是空的,最好用IAT決議,也可兩個都決議作對比)
		PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)(dwImageBase + pPEImport->FirstThunk);

		//獲取INT資訊(同IAT一樣,可將INT看作是IAT的一個備份)
		//PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)(dwImageBase + pPEImport->OriginalFirstThunk);

		//通過IAT回圈獲取該模塊下的所有函式資訊(這里之獲取了函式名)
		while (pIAT->u1.AddressOfData)
		{
			//判斷是輸出函式名還是序號
			if (IMAGE_SNAP_BY_ORDINAL(pIAT->u1.Ordinal))
			{
				//輸出序號
				DWORD dwFunOrdinal = (pIAT->u1.Ordinal) & 0x7FFFFFFF;
				DWORD dwFunAddr = g_pfnGetProcAddress(hMod, (char*)dwFunOrdinal);
				*(DWORD*)pIAT = (DWORD)dwFunAddr;
			}
			else
			{
				//輸出函式名
				DWORD dwFunNameRVA = pIAT->u1.AddressOfData;
				PIMAGE_IMPORT_BY_NAME pstcFunName = (PIMAGE_IMPORT_BY_NAME)(dwImageBase + dwFunNameRVA);
				DWORD dwFunAddr = g_pfnGetProcAddress(hMod, pstcFunName->Name);
				*(DWORD*)pIAT = (DWORD)dwFunAddr;
			}
			pIAT++;
		}
		//遍歷下一個模塊
		pPEImport++;
	}

	//4.恢復記憶體屬性
	g_pfnVirtualProtect(
		(LPBYTE)(dwImageBase + g_stcShellData.dwIATSectionBase), g_stcShellData.dwIATSectionSize,
		dwOldProtect, &dwOldProtect);
}

示例代碼IAT表結構體資訊如下,其VirtualAddress為0x2244
在這里插入圖片描述
在這里插入圖片描述
其中,g_stcShellData.dwIATSectionBase為IAT所在區段的起始,示例代碼有.text, .rdata, .data等5個區段,IAT base為0x2244,.rdata為0x2000, .data為0x3000,所以IAT在.rdata段,經過一系列操作(中間還沒太弄懂),最后需要恢復記憶體屬性不可執行,

然后start函式輸出"歡迎使用CyxvcProtect, by 15PB !", "Hello!"表示執行了殼代碼,之后便用__asm jmp dwPEOEP跳到原先示例代碼的OEP,

1.2 加殼器代碼

加殼器代碼太多了,就選幾個重點的

入口函式

BOOL CPACK::Pack(CString strFilePath, BOOL bIsShowMesBox)
{
	//1.讀取PE檔案資訊并保存
	CPE objPE;
	if (objPE.InitPE(strFilePath) == FALSE)
		return FALSE;

	//2.加密代碼段操作
	DWORD dwXorSize = 0;
	dwXorSize=objPE.XorCode(0x15);

	//3.將必要的資訊保存到Shell
	HMODULE hShell = LoadLibrary(L"Shell.dll");
	if (hShell == NULL)
	{
		MessageBox(NULL, _T("加載Shell.dll模塊失敗,請確保程式的完整性!"), _T("提示"), MB_OK);
		//釋放資源
		delete[] objPE.m_pFileBuf;
		return FALSE;
	}

	PSHELL_DATA pstcShellData = (PSHELL_DATA)GetProcAddress(hShell, "g_stcShellData");

	pstcShellData->dwXorKey = 0x15;
	pstcShellData->dwCodeBase = objPE.m_dwCodeBase;
	pstcShellData->dwXorSize = dwXorSize;
	pstcShellData->dwPEOEP = objPE.m_dwPEOEP;
	pstcShellData->dwPEImageBase = objPE.m_dwImageBase;
	pstcShellData->stcPERelocDir = objPE.m_PERelocDir;
	pstcShellData->stcPEImportDir = objPE.m_PEImportDir;
	pstcShellData->dwIATSectionBase = objPE.m_IATSectionBase;
	pstcShellData->dwIATSectionSize = objPE.m_IATSectionSize;
	pstcShellData->bIsShowMesBox = bIsShowMesBox;

	//4.將Shell附加到PE檔案
	//4.1.讀取Shell代碼
	MODULEINFO modinfo = { 0 };
	GetModuleInformation(GetCurrentProcess(), hShell, &modinfo, sizeof(MODULEINFO));
	PBYTE  pShellBuf = new BYTE[modinfo.SizeOfImage];
	memcpy_s(pShellBuf, modinfo.SizeOfImage, hShell, modinfo.SizeOfImage);
	//4.2.設定Shell重定位資訊
	objPE.SetShellReloc(pShellBuf, (DWORD)hShell);	
	//4.3.修改被加殼程式的OEP,指向Shell
	DWORD dwShellOEP = pstcShellData->dwStartFun - (DWORD)hShell;
	objPE.SetNewOEP(dwShellOEP);
	//4.4.合并PE檔案和Shell的代碼到新的緩沖區
	LPBYTE pFinalBuf = NULL;
	DWORD dwFinalBufSize = 0;
	objPE.MergeBuf(objPE.m_pFileBuf, objPE.m_dwImageSize,
		pShellBuf, modinfo.SizeOfImage, 
		pFinalBuf, dwFinalBufSize);

	//5.保存檔案(處理完成的緩沖區)
	SaveFinalFile(pFinalBuf, dwFinalBufSize, strFilePath);
	
	//6.釋放資源
	delete[] objPE.m_pFileBuf;
	delete[] pShellBuf;
	delete[] pFinalBuf;
	objPE.InitValue();

	return TRUE;
}

InitPE負責讀取PE基礎資訊,XorCode從代碼段起始加密代碼,之后就是從shell.dll加載殼代碼,首先就是獲取shell.dll中匯出的結構體變數pstcShellData,并對其進行設定,之后就是一些設定和設定重定向資訊(這部分不是很懂),

InitPE

這里InitPE并不只是簡單讀取檔案,而是將PE檔案以記憶體映像的方式讀取,也就是會創建更大的緩沖區來模擬記憶體分布,所以加密代碼段時并不需要考慮磁盤檔案分布,

BOOL CPE::InitPE(CString strFilePath)
{
	//打開檔案
	if (OpenPEFile(strFilePath) == FALSE)
		return FALSE;

	//將PE以檔案分布格式讀取到記憶體
	m_dwFileSize = GetFileSize(m_hFile, NULL);
	m_pFileBuf = new BYTE[m_dwFileSize];
	DWORD ReadSize = 0;
	ReadFile(m_hFile, m_pFileBuf, m_dwFileSize, &ReadSize, NULL);	
	CloseHandle(m_hFile);
	m_hFile = NULL;

	//判斷是否為PE檔案
	if (IsPE() == FALSE)
		return FALSE;

	//將PE以記憶體分布格式讀取到記憶體
	//修正沒鏡像大小沒有對齊的情況
	m_dwImageSize = m_pNtHeader->OptionalHeader.SizeOfImage;
	m_dwMemAlign = m_pNtHeader->OptionalHeader.SectionAlignment;
	m_dwSizeOfHeader = m_pNtHeader->OptionalHeader.SizeOfHeaders;
	m_dwSectionNum = m_pNtHeader->FileHeader.NumberOfSections;

	if (m_dwImageSize % m_dwMemAlign)
		m_dwImageSize = (m_dwImageSize / m_dwMemAlign + 1) * m_dwMemAlign;
	LPBYTE pFileBuf_New = new BYTE[m_dwImageSize];
	memset(pFileBuf_New, 0, m_dwImageSize);
	//拷貝檔案頭
	memcpy_s(pFileBuf_New, m_dwSizeOfHeader, m_pFileBuf, m_dwSizeOfHeader);
	//拷貝區段
	PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(m_pNtHeader);
	for (DWORD i = 0; i < m_dwSectionNum; i++, pSectionHeader++)
	{
		memcpy_s(pFileBuf_New + pSectionHeader->VirtualAddress,
			pSectionHeader->SizeOfRawData,
			m_pFileBuf+pSectionHeader->PointerToRawData,
			pSectionHeader->SizeOfRawData);
	}
	delete[] m_pFileBuf;
	m_pFileBuf = pFileBuf_New;
	pFileBuf_New = NULL;

	//獲取PE資訊
	GetPEInfo();
	
	return TRUE;
}

拷貝shell.dll中的殼代碼

下面這段代碼可以看出這是將整個shell.dll的代碼拷貝出來

MODULEINFO modinfo = { 0 };
GetModuleInformation(GetCurrentProcess(), hShell, &modinfo, sizeof(MODULEINFO));
PBYTE  pShellBuf = new BYTE[modinfo.SizeOfImage];
memcpy_s(pShellBuf, modinfo.SizeOfImage, hShell, modinfo.SizeOfImage);

之后就是將被加殼后程式的OEP設定成殼代碼start函式相對于shell.dll的OEP,這里注意,這個objPE.SetNewOEP展開如下:

void CPE::SetNewOEP(DWORD dwShellOEP)
{
	m_dwShellOEP = dwShellOEP + m_dwImageSize;
	m_pNtHeader->OptionalHeader.AddressOfEntryPoint = m_dwShellOEP;
}

也就是加上了原PE檔案的Image Size,

MergeBuf

然后就是MergeBufpShellBuf為shell.dll內容的首地址,pShellBufSizeshellBuf的大小,

void CPE::MergeBuf(LPBYTE pFileBuf, DWORD pFileBufSize,
	LPBYTE pShellBuf, DWORD pShellBufSize, 
	LPBYTE& pFinalBuf, DWORD& pFinalBufSize)
{
	//獲取最后一個區段的資訊
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pFileBuf + pDosHeader->e_lfanew);
	PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
	PIMAGE_SECTION_HEADER pLastSection =
		&pSectionHeader[pNtHeader->FileHeader.NumberOfSections - 1];

	//1.修改區段數量
	pNtHeader->FileHeader.NumberOfSections += 1;

	//2.編輯區段表頭結構體資訊
	PIMAGE_SECTION_HEADER AddSectionHeader =
		&pSectionHeader[pNtHeader->FileHeader.NumberOfSections - 1];
	memcpy_s(AddSectionHeader->Name, 8, ".cyxvc", 7);

	//VOffset(1000對齊)
	DWORD dwTemp = 0;
	dwTemp = (pLastSection->Misc.VirtualSize / m_dwMemAlign) * m_dwMemAlign;
	if (pLastSection->Misc.VirtualSize % m_dwMemAlign)
	{
		dwTemp += 0x1000;
	}
	AddSectionHeader->VirtualAddress = pLastSection->VirtualAddress + dwTemp;

	//Vsize(實際添加的大小)
	AddSectionHeader->Misc.VirtualSize = pShellBufSize;

	//ROffset(舊檔案的末尾)
	AddSectionHeader->PointerToRawData = pFileBufSize;

	//RSize(200對齊)
	dwTemp = (pShellBufSize / m_dwFileAlign) * m_dwFileAlign;
	if (pShellBufSize % m_dwFileAlign)
	{
		dwTemp += m_dwFileAlign;
	}
	AddSectionHeader->SizeOfRawData = dwTemp;

	//標志
	AddSectionHeader->Characteristics = 0XE0000040;

	//3.修改PE頭檔案大小屬性,增加檔案大小
	dwTemp = (pShellBufSize / m_dwMemAlign) * m_dwMemAlign;
	if (pShellBufSize % m_dwMemAlign)
	{
		dwTemp += m_dwMemAlign;
	}
	pNtHeader->OptionalHeader.SizeOfImage += dwTemp;


	//4.申請合并所需要的空間
	pFinalBuf = new BYTE[pFileBufSize + dwTemp];
	pFinalBufSize = pFileBufSize + dwTemp;
	memset(pFinalBuf, 0, pFileBufSize + dwTemp);
	memcpy_s(pFinalBuf, pFileBufSize, pFileBuf, pFileBufSize);
	memcpy_s(pFinalBuf + pFileBufSize, dwTemp, pShellBuf, dwTemp);
}

這里面包括幾個步驟

  • 在原先PE中將節的數量+1 pNtHeader->FileHeader.NumberOfSections += 1;
  • 設定好新的節資料區AddSectionHeader,名字叫.cyxvc
  • 將原PE檔案頭中檔案大小加大,pNtHeader->OptionalHeader.SizeOfImage += dwTemp;
  • 將殼代碼插入到PE檔案尾部,

二.DLL基礎

2.1 DllMain簡介

跟exe有個main或者WinMain入口函式一樣,DLL也有一個入口函式,就是DllMain,以“DllMain”為關鍵字,在MSDN的介紹中,The DllMain function is an optional method of entry into a dynamic-link library (DLL) ,也就是說,DllMain是可有可無的,

BOOL APIENTRY DllMain(HMODULE hModule,  
                      DWORD  ul_reason_for_call,  
                      LPVOID lpReserved)  
{  
     return TRUE;  
}  

2.2 何時呼叫DllMain

系統是在什么時候呼叫DllMain函式的呢?靜態鏈接時,或動態鏈接時呼叫LoadLibraryFreeLibrary都會呼叫DllMain函式,DllMain的第二個引數ul_reason_for_call指明了系統呼叫Dll的原因,它可能有以下取值

  • DLL_PROCESS_ATTACH:1,當Dll被行程第一次呼叫時,導致DllMain函式被呼叫, 如果同一個行程后來再次呼叫此Dll時,作業系統只會增加Dll的使用次數,不會再用DLL_PROCESS_ATTACH呼叫DLL的DllMain函式,
  • DLL_PROCESS_DETACH: 0,當Dll被從行程的地址空間解除映射時,系統呼叫了它的DllMain,傳遞的ul_reason_for_call值是DLL_PROCESS_DETACH,如果行程的終結是因為呼叫了TerminateProcess,系統就不會用DLL_PROCESS_DETACH來呼叫DLLDllMain函式,這就意味著DLL在行程結束前沒有機會執行任何清理作業,
  • DLL_THREAD_ATTACH:2,當行程創建一執行緒時,系統查看當前映射到行程地址空間中的所有Dll檔案映像,并用值DLL_THREAD_ATTACH呼叫Dll的DllMain函式,
  • DLL_THREAD_DETACH:3,如果執行緒呼叫了ExitThread來結束執行緒(執行緒函式回傳時,系統也會自動呼叫ExitThread),系統查看當前映射到行程空間中的所有Dll檔案映像,并用DLL_THREAD_DETACH來呼叫DllMain函式,

2.3 Dll匯出函式

這里用到了__declspec(dllexport)修飾符,但是shell.cpp里并沒有匯出函式,而是匯出了一個結構體變數g_stcShellData

extern"C" SHELL_API SHELL_DATA g_stcShellData;

三.加殼后的示例代碼

3.1 010Editor

  • NT可選頭:
    在這里插入圖片描述可以看到AddressEntryPoint從0x1159變成了0x7230,SizeOfImage變成了0xC000,以前是0x6000,其它重要資訊并無變化

  • 節表
    在這里插入圖片描述可以看到多了一個cyxvc節,其它資料并無變化

在這里插入圖片描述cyxvc的磁盤偏移,RVA,Size均為0x6000,

  • 代碼段
    在這里插入圖片描述 可以看到代碼段資料全變0了,

  • 節區
    在這里插入圖片描述跟之前相比多了一個cyxvc節,并且長度為0x6000(一個Image的長度)

3.2 IDA

start函式
在這里插入圖片描述
這個start就是shell代碼的start,也就是先執行殼代碼
在這里插入圖片描述4441的16進制為0x1159,也就是賦值操作是重新設定OEP,jump即跳到OEP,

執行效果和加殼前一模一樣,下一節,我會來除錯以下加殼程式和加殼后的test程式運行程序,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/289169.html

標籤:其他

上一篇:opencv檢測動態物體

下一篇:資訊搜集(一)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more