C/C++ 對代碼節的動態加解密


加殼的原理就是加密或者壓縮程序中的已有資源,然后當程序執行后外殼將模擬PE加載器對EXE中的區塊進行動態裝入,下面我們來自己實現一個簡單的區塊加解密程序,來讓大家學習了解一下殼的基本運作原理。

本次使用的工具,依舊是上次編寫的PETools: https://www.cnblogs.com/LyShark/p/12960816.html

搜索特定指令片段: 搜索指定模塊中是否存在jmp esp等特殊的指令集,並輸出其內存地址.

#include <stdio.h>
#include <Windows.h>

int main(int argc, char* argv[])
{
	HINSTANCE handle = LoadLibrary(L"user32.dll");
	BYTE* ptr = (BYTE*)handle;
	int position, address;
	BOOL Flag = FALSE;

	for (position = 0; !Flag; position++)
	{
		try
		{
			if (ptr[position] == 0xFF && ptr[position + 1] == 0xE4)
			{
				int address = (int)ptr + position;
				printf("找到JmpESP地址 = 0x%x\n", address);
			}
		}
		catch (...)
		{
			int address = (int)ptr + position;
			printf("找到JmpESP地址 = 0x%x\n", address);
			Flag = true;
		}
	}
	system("pause");
	return 0;
}

加密第一個節表:

#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")

void EncrySection(LPSTR szFileName)
{
	HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
	HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);

	PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
	PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
	PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);

	printf("節表數量: %d \n", FileHdr->NumberOfSections);
	printf("節虛擬地址: %x \n", pSection->Misc.VirtualSize);
	printf("讀入FOA基地址: %x \n", pSection->PointerToRawData);
	printf("讀入節表長度: %x \n", pSection->SizeOfRawData);

	DWORD dwRead = 0;
	PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);

	SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
	memset(pByte, 0, pSection->SizeOfRawData);
	ReadFile(hFile, pByte, pSection->SizeOfRawData, &dwRead, NULL);

	for (int x = 0; x < pSection->SizeOfRawData; x++)
		pByte[x] ^= 0x10;

	SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
	WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);

	UnmapViewOfFile(lpBase);
}

int main(int argc, char * argv[])
{
	EncrySection("c://win32.exe");
	system("pause");
	return 0;
}

當需要打印第二個節只需要遞增指針.

void EncrySection(LPSTR szFileName)
{
	HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
	HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);

	PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
	PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
	PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);

	printf("節表數量: %d \n", FileHdr->NumberOfSections);
	printf("節虛擬地址: %x \n", pSection->Misc.VirtualSize);
	printf("讀入FOA基地址: %x \n", pSection->PointerToRawData);
	printf("讀入節表長度: %x \n", pSection->SizeOfRawData);

	DWORD dwRead = 0;
	PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);

	SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
	memset(pByte, 0, pSection->SizeOfRawData);
	
	// 逐字節讀入
	for (int x = 0; x < pSection->PointerToRawData; x++)
	{
		ReadFile(hFile, &pByte[x], 1, &dwRead, NULL);
	}

	for (int x = 0; x < pSection->SizeOfRawData; x++)
		pByte[x] ^= 0x10;

	SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
	WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);

	pSection++;
	printf("節虛擬地址: %x \n", pSection->Misc.VirtualSize);
	printf("讀入FOA基地址: %x \n", pSection->PointerToRawData);
	printf("讀入節表長度: %x \n", pSection->SizeOfRawData);
	UnmapViewOfFile(lpBase);
}

循環加密所有的節,可能會出現問題

#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")

void EncrySection(LPSTR szFileName,DWORD Key)
{
	HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
	HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);

	PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
	PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
	PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);
	printf("[-] 加密節表數量: %d \n", FileHdr->NumberOfSections);

	for (int x = 0; x < FileHdr->NumberOfSections; x++)
	{
		printf("[-] 節虛擬地址: 0x%08X 虛擬大小: 0x%08X\n", pSection->VirtualAddress,pSection->Misc.VirtualSize);
		printf("[-] 讀入FOA基地址: 0x%08X 節表長度: 0x%08X \n\n", pSection->PointerToRawData,pSection->SizeOfRawData);

		DWORD dwRead = 0;
		PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);

		SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
		memset(pByte, 0, pSection->SizeOfRawData);
		ReadFile(hFile, pByte, pSection->SizeOfRawData, &dwRead, NULL);

		for (int x = 0; x < pSection->SizeOfRawData; x++)
		{
			pByte[x] ^= Key;
		}

		SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
		WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);

		free(pByte);
		pSection = pSection + 1;
	}
	UnmapViewOfFile(lpBase);
}

int main(int argc, char * argv[])
{
	EncrySection("c://win32.exe",0x10);
	system("pause");
	return 0;
}

添加殼代碼

#include <stdio.h>
#include <Windows.h>
#include <ImageHlp.h>
#pragma comment(lib,"Imagehlp.lib")

void EncrySection(LPSTR szFileName, DWORD Key)
{
	HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
	HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);

	PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
	PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
	PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);
	printf("[-] 節虛擬地址: 0x%08X 虛擬大小: 0x%08X\n", pSection->VirtualAddress, pSection->Misc.VirtualSize);
	printf("[-] 讀入FOA基地址: 0x%08X 節表長度: 0x%08X \n", pSection->PointerToRawData, pSection->SizeOfRawData);
	printf("[*] 已對 %s 節 --> XOR加密/解密 --> XOR密鑰: %d \n\n", pSection->Name, Key);

	DWORD dwRead = 0;
	PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);

	SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
	memset(pByte, 0, pSection->SizeOfRawData);
	ReadFile(hFile, pByte, pSection->SizeOfRawData, &dwRead, NULL);

	for (int x = 0; x < pSection->SizeOfRawData; x++)
	{
		pByte[x] ^= Key;
	}

	SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
	WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);
	pSection->Characteristics = 0xE0000020;

	free(pByte);
	FlushViewOfFile(lpBase, 0);
	UnmapViewOfFile(lpBase);
}

void DecodeCode(LPSTR szFileName)
{
	// 第一步修正程序OEP位置,修正為最后一個節的地址
	HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
	HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);

	PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
	PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);

	DWORD ImageBase = NtHdr->OptionalHeader.ImageBase;
	DWORD BaseRVA = NtHdr->OptionalHeader.AddressOfEntryPoint;
	printf("Base RVA %x \n", BaseRVA);


	PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);


	// 首先得到最后一個節的指針,然后找到里面的虛擬偏移值,填入到程序OEP位置即可。
	DWORD SectionNum = FileHdr->NumberOfSections;
	char Code[] =
	{
		"\x60"
		"\xb8\x00\x00\x00\x00"
		"\x80\x30\x88"
		"\x40"
		"\x3d\xff\x4f\x40\x00"
		"\x75\xf5"
		"\x61"
		"\xb8\x00\x00\x00\x00"
		"\xff\xe0"
	};
	DWORD dwWrite = 0;
	printf("%x \n", ImageBase + pSection->VirtualAddress);
	*(DWORD *)&Code[2] = ImageBase + pSection->VirtualAddress;
	*(DWORD *)&Code[11] = ImageBase + pSection->VirtualAddress + pSection->Misc.VirtualSize;
	*(DWORD *)&Code[19] = ImageBase + BaseRVA;
	pSection = pSection + (SectionNum - 1);
	printf("得到最后一個節的實際地址: %x \n", pSection->PointerToRawData);

	SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
	WriteFile(hFile, (LPVOID)Code, sizeof(Code), &dwWrite, NULL);
	FlushViewOfFile(lpBase, 0);
	UnmapViewOfFile(lpBase);
}

加殼的首要目標是要創建一個具有可寫屬性的新節

我們使用PESection對win32.exe加一個.hack節,然后大小為2048

加入后再次使用PETools工具檢查,發現已經添加成功了。

下一步就是將.text節進行加密了,這里為了簡單我使用的是異或加密,如下是加密前的機器碼。

使用我們編寫的工具進行加密,傳入兩個參數,一個是文件,一個則是加密密鑰

加密有區段會變成如下樣子。

接着使用 addpack 傳入一個參數,寫入解密代碼。

電腦管家可能會攔截,請將其取出來。

我們X64dbg載入看看,程序默認停在了,我們的殼的位置,。

運行后對.text節進行動態解密,然后一個jmp跳轉到程序的OEP位置即可,這也就是殼的基本原理。


免責聲明!

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



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