编译环境
xp + vc6
加壳过程
把源文件(被保护的exe),加密后放入到壳子程序的最后一个节里
实现代码
宏
#define ENDPATH "C:\\LOADSHELL.exe"
壳子程序新增加节
参数说明:
path :要新增节文件的路径
n:要新增多少字节
LSADDRESS:得到新生成节的首地址
void addnewsec(IN LPSTR path,IN DWORD n,OUT LPVOID* LSADDRESS)
{
LPVOID FileBuffer = NULL;
LPVOID ImageBuffer = NULL;
LPVOID NewBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
DWORD Size = 0;
FileToBigFileBuffer(path, n, &FileBuffer);
if (!FileBuffer)
{
printf("File->FileBuffer失败");
return;
}
FileBufferToBigImageBuffer(FileBuffer,n,&ImageBuffer);
if (!ImageBuffer)
{
printf("FileBUffer->ImageBuffer失败");
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
PIMAGE_SECTION_HEADER ptempSectionHeader = pSectionHeader;
for (int i = 0; i < pPEHeader->NumberOfSections; i++, ptempSectionHeader++);
if ((pOptionHeader->SizeOfHeaders - ((DWORD)ptempSectionHeader - (DWORD)pDosHeader)) < 80)
{
MessageBox(0,"开辟新节表空间不够",0,0);
return;
}
memcpy(ptempSectionHeader, pSectionHeader,40);
pPEHeader->NumberOfSections = (WORD)pPEHeader->NumberOfSections + 1;
pOptionHeader->SizeOfImage = (DWORD)pOptionHeader->SizeOfImage + n;
ptempSectionHeader->Misc.VirtualSize = n;
PIMAGE_SECTION_HEADER ptempSectionHeader1 = ptempSectionHeader-1;
if (ptempSectionHeader1->Misc.VirtualSize > ptempSectionHeader1->SizeOfRawData)
{
ptempSectionHeader->VirtualAddress = ptempSectionHeader1->VirtualAddress + ImageAlignment(ptempSectionHeader1->Misc.VirtualSize);
}
else
{
ptempSectionHeader->VirtualAddress = ptempSectionHeader1->VirtualAddress + ImageAlignment(ptempSectionHeader1->SizeOfRawData);
}
ptempSectionHeader->SizeOfRawData = FileAlignment(n);
ptempSectionHeader->PointerToRawData = ptempSectionHeader1->PointerToRawData + ptempSectionHeader1->SizeOfRawData;
ptempSectionHeader->Characteristics = pSectionHeader->Characteristics | ptempSectionHeader->Characteristics;
Size = ImageBufferToNewBuffer(ImageBuffer, &NewBuffer);
*LSADDRESS = (LPVOID)(ptempSectionHeader->VirtualAddress+(DWORD)ImageBuffer);
if (Size == 0 || !NewBuffer)
{
MessageBox(0,"ERROR(ImageBuffer->FileBuffer)",0,0);
free(FileBuffer);
free(ImageBuffer);
return;
}
BOOL isok = MemeryTOFile(NewBuffer, Size, ENDPATH);
if (isok == 0)
{
MessageBox(0,"存盘失败",0,0);
}
pshellImageBuffer = ImageBuffer;
free(NewBuffer);
free(FileBuffer);
return;
}
加密程序
DWORD encrypto(IN LPSTR path,OUT LPVOID* FileBuffer)
{
FILE* pFile;
pFile = fopen(path, "rb");
if (pFile == NULL)
{
fclose(pFile);
return 0;
}
else
{
fseek(pFile, 0, SEEK_END);
DWORD fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
char* p = (char *)malloc(fileSize*sizeof(char));
fread(p, sizeof(char), fileSize, pFile);
for (DWORD i = 0; i < fileSize; i++)
{
p[i] = p[i] ^ 0xFF;
}
*FileBuffer = p;
fclose(pFile);
return fileSize;
}
}这里的加密算法可以更复杂,我只是简单的进行异或
真正将源文件加载到壳子中
DWORD addshellcodeTolastSection(LPSTR srcpath,LPSTR despath)
{
LPVOID NewBuffer = NULL;
LPVOID pFileBuffer = NULL;
LPVOID LSADDRESS = NULL;
int
FileSize = encrypto(srcpath,&pFileBuffer);
addnewsec(despath,FileSize,&LSADDRESS);
memcpy(LSADDRESS,pFileBuffer,FileSize);
DWORD Size = ImageBufferToNewBuffer(pshellImageBuffer, &NewBuffer);
BOOL isok = MemeryTOFile(NewBuffer, Size, ENDPATH);
if (isok == 0)
{
MessageBox(0,"存盘失败",0,0);
}
free(pshellImageBuffer);
free(pFileBuffer);
free(NewBuffer);
return 1;
}
用到的一些映像操作函数
DWORD ImageBufferToNewBuffer(IN LPVOID pImageBuffer, OUT LPVOID* pNewBuffer)
{
if (!pImageBuffer)
{
printf("PE文件读取失败");
return 0;
}
if (*(PWORD)pImageBuffer != IMAGE_DOS_SIGNATURE)
{
printf("不是有效的MZ头");
free(pImageBuffer);
return 0;
}
DWORD FileSize = 0;
LPVOID ptempNewBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
PIMAGE_SECTION_HEADER ptempSectionHeader = pSectionHeader;
if (*(PDWORD)((DWORD)pImageBuffer + pDosHeader->e_lfanew) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE签名");
free(pImageBuffer);
return 0;
}
for (int i = 0; i < pPEHeader->NumberOfSections; i++, ptempSectionHeader++);
ptempSectionHeader--;
FileSize = ptempSectionHeader->PointerToRawData + ptempSectionHeader->SizeOfRawData;
ptempNewBuffer = malloc(FileSize);
if (!ptempNewBuffer)
{
printf("申请pNewBuffer堆空间失败");
return 0;
}
memset(ptempNewBuffer, 0, FileSize);
memcpy(ptempNewBuffer, pImageBuffer, pOptionHeader->SizeOfHeaders);
PIMAGE_SECTION_HEADER ptemp1SectionHeader = pSectionHeader;
for (DWORD n = 0; n < pPEHeader->NumberOfSections; n++)
{
memcpy((void*)((DWORD)ptempNewBuffer+ptemp1SectionHeader->PointerToRawData), (void*)((DWORD)pImageBuffer + ptemp1SectionHeader->VirtualAddress), ptemp1SectionHeader->SizeOfRawData);
ptemp1SectionHeader++;
}
*pNewBuffer = ptempNewBuffer;
ptemp1SectionHeader = NULL;
return FileSize;
}
DWORD MemeryTOFile(IN LPVOID pMemBuffer, IN size_t size, OUT LPSTR lpszFile)
{
FILE* pFile = NULL;
if (!pMemBuffer)
{
printf("读取内存失败");
return 0;
}
pFile = fopen(lpszFile, "wb");
if (!pFile)
{
printf("创建exe失败");
return 0;
}
size_t n = fwrite(pMemBuffer, size, 1, pFile);
if (!n)
{
printf("存盘失败");
free(pMemBuffer);
fclose(pFile);
pFile = NULL;
return 0;
}
fclose(pFile);
pFile = NULL;
return 1;
}
解壳过程
解壳过程就是编写壳子的过程,因为解壳程序都在壳子程序中,也是最关键的一环
实现代码
一些要用到的函数:内存对齐,文件对齐,拉伸过程等
DWORD FileAlignment
(int n)
{
DWORD i;
if (n % 0x200 != 0)
{
i = n / 0x200 + 1;
i = i * 0x200;
return i;
}
else
{
i = n / 0x200;
i = i * 0x200;
return i;
}
}
DWORD ImageAlignment(int n)
{
DWORD i;
if (n % 0x1000 != 0)
{
i = n / 0x1000 + 1;
i = i * 0x1000;
return i;
}
else
{
i = n / 0x1000;
i = i * 0x1000;
return i;
}
}
DWORD RvaToFoa(IN LPSTR PATH,IN DWORD rva)
{
LPVOID FileBuffer = NULL;
LPVOID ImageBuffer = NULL;
DWORD Foa = 0;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
FileToFileBuffer(PATH, &FileBuffer);
if (!FileBuffer)
{
printf("File->FileBuffer失败");
return 0;
}
FileBufferToImageBuffer(FileBuffer, &ImageBuffer);
if (!ImageBuffer)
{
printf("FileBUffer->ImageBuffer失败");
return 0;
}
pDosHeader = (PIMAGE_DOS_HEADER)ImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
PIMAGE_SECTION_HEADER ptempSectionHeader = pSectionHeader;
PIMAGE_SECTION_HEADER ptempSectionHeader1 = pSectionHeader;
for (int i = 0; i < pPEHeader->NumberOfSections; i++, ptempSectionHeader++);
if (rva <= FileAlignment(((DWORD)ptempSectionHeader - (DWORD)ImageBuffer)))
{
Foa = rva;
free(FileBuffer);
free(ImageBuffer);
return Foa;
}
for (int k = 0; k < pPEHeader->NumberOfSections-1; k++)
{
if (ptempSectionHeader1->VirtualAddress < rva && rva < (ptempSectionHeader1 + 1)->VirtualAddress)
{
Foa = ptempSectionHeader1->PointerToRawData + (rva - ptempSectionHeader1->VirtualAddress);
free(FileBuffer);
free(ImageBuffer);
return Foa;
}
if (ptempSectionHeader1->VirtualAddress == rva)
{
Foa = ptempSectionHeader1->PointerToRawData;
free(FileBuffer);
free(ImageBuffer);
return Foa;
}
ptempSectionHeader1++;
}
if (ptempSectionHeader1->VirtualAddress <= rva)
{
Foa = ptempSectionHeader1->PointerToRawData + (rva - ptempSectionHeader1->VirtualAddress);
free(FileBuffer);
free(ImageBuffer);
return Foa;
}
free(FileBuffer);
free(ImageBuffer);
return 0;
}
DWORD FileToFileBuffer(IN LPSTR lpszFile, OUT LPVOID* FileBuffer)
{
FILE* pFile = NULL;
LPVOID ptempFileBuffer = NULL;
DWORD fileSize = 0;
pFile = fopen(lpszFile, "rb");
if (!pFile)
{
printf("无法打开文件");
return 0;
}
fseek(pFile, 0, SEEK_END);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
ptempFileBuffer = malloc(fileSize);
memset(ptempFileBuffer,0,fileSize);
if (!ptempFileBuffer)
{
printf("分配空间失败");
fclose(pFile);
return 0;
}
size_t n = fread(ptempFileBuffer, fileSize, 1, pFile);
if (!n)
{
printf("拷贝数据失败");
free(ptempFileBuffer);
fclose(pFile);
return 0;
}
*FileBuffer = ptempFileBuffer;
ptempFileBuffer = NULL;
fclose(pFile);
return fileSize;
}
DWORD FileBufferToImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* pImageBuffer)
{
LPVOID p1ImageBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
p1ImageBuffer = malloc(pOptionHeader->SizeOfImage);
if (!p1ImageBuffer)
{
printf("申请ImageBuffer堆空间失败");
return 0;
}
memset(p1ImageBuffer , 0 , pOptionHeader->SizeOfImage);
memcpy(p1ImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders);
PIMAGE_SECTION_HEADER ptempSectionHeader = pSectionHeader;
for (int k = 0; k < pPEHeader->NumberOfSections; k++)
{
memcpy((void*)((DWORD)p1ImageBuffer+ ptempSectionHeader->VirtualAddress), (void*)((DWORD)pFileBuffer + ptempSectionHeader->PointerToRawData), ptempSectionHeader->SizeOfRawData);
ptempSectionHeader++;
}
*pImageBuffer = p1ImageBuffer;
ptempSectionHeader = NULL;
return pOptionHeader->SizeOfImage;
}
关键代码,思路最重要
一:获取自身文件的路径:因为我们需要将自身文件展开到内存来进行操作.
CHAR path[MAX_PATH];
DWORD dwTempImageBaseSrc = NULL;
HMODULE hm=GetModuleHandle(NULL);
GetModuleFileName(hm,path,sizeof(path));
FileToFileBuffer(path, &pzzFileBuffer);
用到的API: GetModuleHandle GetModuleFileName
二:解密.
for (DWORD i = 1; i < pPEHeader->NumberOfSections; i++, pSectionHeader++);
CHAR* SrcMoule = (CHAR*)((DWORD)pzzFileBuffer + pSectionHeader->PointerToRawData);
for(k = 0;kSizeOfRawData;k++)
{
SrcMoule[k] = SrcMoule[k] ^ 0xFF;
}
三:将解密后的源文件,在内存中拉伸
FileBufferToImageBuffer(SrcMoule,&pImageBuffer);
四:以挂起方式创建一个进程
STARTUPINFO ie_si = {0};
PROCESS_INFORMATION ie_pi;
TCHAR SrcBuffer[256] = {0};
sprintf(SrcBuffer,"%s",path);
ie_si.cb = sizeof(ie_si);
CreateProcess( //以挂起的方式创建进程
NULL,
SrcBuffer,
NULL,
NULL,
FALSE,
CREATE_SUSPENDED,
NULL,
NULL,
&ie_si,
&ie_pi
);
主要目的是为了起后面的源文件,创建进程的路径就写自己本身
关键参数:CREATE_SUSPENDED (以挂起方式创建)
用到的API:CreateProcess
五:卸载外壳程序
API:ZwUnmapViewOfSection
NTSTATUS ZwUnmapViewOfSection(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress );
这个函数在 wdm.h 里声明,它的功能是卸载进程的内存镜像(Image Buffer),内存镜像是指进程4GB虚拟地址空间中从 ImageBase 开始,长度为 SizeOfImage 的内存。
wdm.h 这个头文件需要安装wdk 微软链接 https://docs.microsoft.com/zh-cn/windows-hardware/drivers/download-the-wdk
也可以通过ntdll这个dll,将ZwUnmapViewOfSection函数通过GetProcAddress找到函数地址,再把函数地址赋给函数名,并不直接调用
pfnZwUnmapViewOfSection ZwUnmapViewOfSection = NULL;
HMODULE hModule = LoadLibrary("ntdll.dll");
if (hModule)
{
ZwUnmapViewOfSection = (pfnZwUnmapViewOfSection)GetProcAddress(hModule, "ZwUnmapViewOfSection");
if (ZwUnmapViewOfSection)
{
if (ZwUnmapViewOfSection((unsigned long)ie_pi.hProcess, dwImageBaseShell))
{
printf("ZwUnmapViewOfSection success\n");
}
}
FreeLibrary(hModule);
}
LoadLibrary是加载一个dll到进程中,如果进程有这个dll,直接返回该dll的句柄;如果没有这个dll,就先把dll贴到进程空间,再返回该dll的句柄
参考:https://github.com/smallzhong/ycpack
FARPROC GetProcAddress(
HMODULE hModule, // DLL模块句柄
LPCSTR lpProcName // 函数名
);
六:分配空间
FirstAD = VirtualAllocEx(ie_pi.hProcess,(LPVOID)pSrcOptionHeader->ImageBase,pSrcOptionHeader->SizeOfImage,MEM_RESERVE | MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if(FirstAD == NULL)
{
if(pSrcOptionHeader->DataDirectory[5].VirtualAddress == 0 || pSrcOptionHeader->DataDirectory[5].Size ==0)
{
MessageBox(0,"shibai",0,0);
return;
}
else
{
DWORD RVA_Data;
WORD reloData;
PWORD Location = NULL;
DWORD NumberOfRelocation = 0;
dwTempImageBaseSrc = pSrcOptionHeader->ImageBase + 0x50000;
DWORD fOA = RvaToFoa(path,pSrcOptionHeader->DataDirectory[5].VirtualAddress);
PIMAGE_BASE_RELOCATION pRelocationDirectory = (PIMAGE_BASE_RELOCATION)((DWORD)SrcMoule + fOA);
VirtualAllocEx(ie_pi.hProcess, (LPVOID)dwTempImageBaseSrc,pSrcOptionHeader->SizeOfImage,MEM_RESERVE | MEM_COMMIT,PAGE_EXECUTE_READWRITE);
pSrcOptionHeader->ImageBase = (DWORD)dwTempImageBaseSrc;
WriteProcessMemory(ie_pi.hProcess, (LPVOID)dwTempImageBaseSrc, pImageBuffer, pSrcOptionHeader->SizeOfImage, NULL);
while(pRelocationDirectory->SizeOfBlock && pRelocationDirectory->VirtualAddress){
NumberOfRelocation = (pRelocationDirectory->SizeOfBlock - 8)/2;
Location = (PWORD)((DWORD)pRelocationDirectory + 8);
for(DWORD i=0;i