加殼的原理就是加密或者壓縮程序中的已有資源,然后當程序執行后外殼將模擬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位置即可,這也就是殼的基本原理。

