把前面一些程序中用到的PE相關函數集合在一個DLL中方便使用
#ifndef PE32_K_H
#define PE32_K_H
#ifdef __cplusplus
#define EXPORT extern "C" __declspec (dllexport)
#else
#define EXPORT __declspec (dllexport)
#endif
//指定
#define ADD_LAST_SECTION 1 //添加代碼到最後一個區段
#define ADD_NEW_SECTION 2 //添加代碼到一個新建的區段
#define ADD_PE_HEADER 3 //添加代碼到PE頭部
//重置,重置後可繼續用文件初始化此DLL
EXPORT BOOL _stdcall Reset(TCHAR szFileName[]);
//使用函數前必須調用此函數初始化
//參數---szFileName:文件名
EXPORT BOOL _stdcall InitPE32(TCHAR szFileName[]);
//取得原文件數據指針,只讀屬性
EXPORT CONST PBYTE _stdcall GetFileBuffer();
//取得文件名
EXPORT CONST PTCHAR _stdcall GetFileName();
//添加代碼到目標文件
//參數---szNewFile:添加代碼後新文件的名字 lpCOdeStart:指向代碼起始處
// dwCodeSize:添加代碼的大小 dwTypeOfAdd:添加方法類型
EXPORT BOOL _stdcall AddCode(TCHAR szNewFile[], PBYTE lpCodeStart, DWORD dwCodeSize, DWORD dwTypeOfAdd);
//獲得目標文件的CRC32校驗值
EXPORT DWORD _stdcall GetCRC32(TCHAR szFileName[] );
//檢測是否爲PE文件
//參數---szFileName:文件名
EXPORT BOOL _stdcall IsPeFile(TCHAR szFileName[]);
//將RVA偏移轉換成文件偏移,失敗返回-1
EXPORT DWORD _stdcall RvaToOffset (IMAGE_DOS_HEADER *lpFileHead, DWORD dwRva);
//將RVA偏移轉成文件指針偏移,失敗返回NULL
EXPORT PBYTE _stdcall RvaToPointer(IMAGE_DOS_HEADER *lpFileHead,DWORD dwRva);
//將虛擬地址轉成文件指針偏移,失敗返回NULL
EXPORT PBYTE _stdcall VirtualAddressToPointer(IMAGE_DOS_HEADER *lpFileHead,DWORD dwVirtualAddress);
//獲得RVA偏移處的節區名稱,失敗返回NULL
EXPORT CONST PBYTE _stdcall GetRvaSection (IMAGE_DOS_HEADER *lpFileHead, DWORD dwRva);
//獲得指定RVA所處節區的節表頭,失敗返回NULL
EXPORT PIMAGE_SECTION_HEADER _stdcall GetSectionOfRva (IMAGE_DOS_HEADER *lpFileHead, char* secName);
//文件偏移轉換成RVA,失敗返回-1
EXPORT DWORD _stdcall OffsetToRVA(IMAGE_DOS_HEADER *lpFileHead, DWORD dwOffset);
//文件偏移轉換成內存指針,失敗返回NULL
EXPORT PBYTE _stdcall OffsetToPointer(IMAGE_DOS_HEADER *lpFileHead, DWORD dwOffset);
//提取圖標數據,參數指定文件名
EXPORT BOOL _stdcall GetIcon (TCHAR szIconFileName[]);
#endif
#include <windows.h>
#include "PE32_K.H"
TCHAR g_szFileName[MAX_PATH] = { 0 };
HINSTANCE g_hInstDll = NULL; //DLL模塊句柄
PBYTE g_lpBuffer = NULL; //存儲文件數據
DWORD g_dwFileSize= 0; //文件數據大小
PBYTE g_lpNewFile = NULL; //存儲添加代碼後的數據
BOOL PE = FALSE; //指示是否是PE文件
void _stdcall DllClean();
///////////////////////////////////////////////////////////////////////////////
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
// DLL is attaching to the address space of the current process.
g_hInstDll = hInstDll;
break;
case DLL_THREAD_ATTACH:
// A new thread is being created in the current process.
break;
case DLL_THREAD_DETACH:
// A thread is exiting cleanly.
break;
case DLL_PROCESS_DETACH:
// The calling process is detaching the DLL from its address space.
DllClean();
break;
}
return(TRUE);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////將RVA偏移轉換成文件偏移,失敗返回-1////////////////////////////////////////////////
DWORD _stdcall RvaToOffset (IMAGE_DOS_HEADER *lpFileHead, DWORD dwRva)
{
::IMAGE_NT_HEADERS *lpPEHead;
::IMAGE_SECTION_HEADER *lpSectionHead;
DWORD i;
lpPEHead = (IMAGE_NT_HEADERS*)( (BYTE*)lpFileHead + lpFileHead->e_lfanew);
i = lpPEHead->FileHeader.NumberOfSections;
lpSectionHead = (IMAGE_SECTION_HEADER*)(++lpPEHead);
for ( ; i > 0 ; i--, lpSectionHead++)
{
if ( (dwRva >= lpSectionHead->VirtualAddress) && (dwRva < (lpSectionHead->VirtualAddress + lpSectionHead->SizeOfRawData) ) )
{
dwRva = dwRva - lpSectionHead->VirtualAddress + lpSectionHead->PointerToRawData;
return dwRva;
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////將RVA偏移轉成文件指針偏移,失敗返回NULL////////////////////////////////////////////////
PBYTE _stdcall RvaToPointer(IMAGE_DOS_HEADER *lpFileHead,DWORD dwRva)
{
DWORD Offset = RvaToOffset(lpFileHead, dwRva);
if(Offset == -1)
return NULL;
return (PBYTE)(lpFileHead) + Offset;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////將虛擬地址轉成文件指針偏移,失敗返回NULL////////////////////////////////////////////////
PBYTE _stdcall VirtualAddressToPointer(IMAGE_DOS_HEADER *lpFileHead,DWORD dwVirtualAddress)
{
::IMAGE_NT_HEADERS *lpPEHead;
lpPEHead = (IMAGE_NT_HEADERS*)( (BYTE*)lpFileHead + lpFileHead->e_lfanew);
return (PBYTE)RvaToPointer(lpFileHead, dwVirtualAddress - lpPEHead->OptionalHeader.ImageBase);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////獲得RVA偏移處的節區名稱////////////////////////////////////////////////
CONST PBYTE _stdcall GetRvaSection (IMAGE_DOS_HEADER *lpFileHead, DWORD dwRva)
{
IMAGE_NT_HEADERS *lpPEHead;
IMAGE_SECTION_HEADER *lpSectionHead;
DWORD i;
lpPEHead = (IMAGE_NT_HEADERS*)( (BYTE*)lpFileHead + lpFileHead->e_lfanew);
i = lpPEHead->FileHeader.NumberOfSections;
lpSectionHead = (IMAGE_SECTION_HEADER*)(++lpPEHead);
for ( ; i > 0 ; i--, lpSectionHead++)
{
if ( (dwRva >= lpSectionHead->VirtualAddress) && (dwRva < (lpSectionHead->VirtualAddress + lpSectionHead->SizeOfRawData) ) )
{
return (PBYTE)lpSectionHead;
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////獲得指定RVA所處節區的節表頭,失敗返回NULL/////////////////////////////////////////
PIMAGE_SECTION_HEADER _stdcall GetSectionOfRva (IMAGE_DOS_HEADER *lpFileHead, char* secName)
{
::PIMAGE_NT_HEADERS lpNtHead = (PIMAGE_NT_HEADERS)( (BYTE*)lpFileHead + lpFileHead->e_lfanew);
DWORD dwSec = lpNtHead->FileHeader.NumberOfSections;
IMAGE_SECTION_HEADER* lpSection = (PIMAGE_SECTION_HEADER) (lpNtHead + 1);
for (DWORD i=0; i < dwSec; i++)
{
if(!strncmp((char*)lpSection->Name, secName, IMAGE_SIZEOF_SHORT_NAME) )
return lpSection;
lpSection++;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////文件偏移轉換成RVA///////////////////////////////////////////////////////////
DWORD _stdcall OffsetToRVA(IMAGE_DOS_HEADER *lpFileHead, DWORD dwOffset)
{
::IMAGE_NT_HEADERS *lpPEHead;
::IMAGE_SECTION_HEADER *lpSectionHead;
DWORD i;
lpPEHead = (IMAGE_NT_HEADERS*)( (BYTE*)lpFileHead + lpFileHead->e_lfanew);
i = lpPEHead->FileHeader.NumberOfSections;
lpSectionHead = (IMAGE_SECTION_HEADER*)(++lpPEHead);
for ( ; i > 0; i--, lpSectionHead++)
{
if ( (dwOffset >= lpSectionHead->PointerToRawData) && (dwOffset < (lpSectionHead->PointerToRawData + lpSectionHead->SizeOfRawData) ) )
{
dwOffset = dwOffset - lpSectionHead->PointerToRawData + lpSectionHead->VirtualAddress;
return dwOffset;
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////文件偏移轉換成內存指針///////////////////////////////////////////////////////////
PBYTE _stdcall OffsetToPointer(IMAGE_DOS_HEADER *lpFileHead, DWORD dwOffset)
{
DWORD RVA = OffsetToRVA(lpFileHead, dwOffset);
if( RVA == -1)
return NULL;
return (PBYTE)(lpFileHead) + RVA;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////按指定大小對齊//////////////////////////////////////////////////////////
DWORD _stdcall Align(DWORD dwSize, DWORD dwAlignment)
{
return (dwSize + dwAlignment - 1) /dwAlignment * dwAlignment;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////獲得區塊有效數據部分大小/////////////////////////////////////////////////
DWORD _stdcall GetValidSize(PBYTE lpMemory, PIMAGE_SECTION_HEADER lpSection)
{
PBYTE lpData;
DWORD dwSize=0;
lpData = (PBYTE)( lpMemory + lpSection->PointerToRawData + lpSection->SizeOfRawData - 1);
while (*lpData == 0)
{
lpData--;
dwSize++;
}
dwSize -= 8; //減去8個字節防止是字符串或某結構的結尾
if (dwSize > 0)
return lpSection->SizeOfRawData - dwSize;
else
return lpSection->SizeOfRawData;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//檢測是否是PE文件
BOOL _stdcall IsPeFile(TCHAR szFileName[])
{
HANDLE hFile;
WORD wMagic;
DWORD dwRead,dw;
if (INVALID_HANDLE_VALUE != ( hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, \
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
{
ReadFile(hFile , &wMagic, 2, &dwRead, NULL);
if ( wMagic == 0x5A4D)
{
SetFilePointer(hFile, 0x3C, 0, FILE_BEGIN);
ReadFile(hFile , &dw, 4, &dwRead, NULL);
SetFilePointer(hFile, dw, 0, FILE_BEGIN);
ReadFile(hFile , &wMagic, 2, &dwRead, NULL);
if (wMagic == 0x4550)
return TRUE;
}
}
return FALSE;
}
//獲得文件數據指針
CONST PBYTE _stdcall GetFileBuffer()
{
return g_lpBuffer;
}
//獲得文件名
EXPORT CONST PTCHAR _stdcall GetFileName()
{
return g_szFileName;
}
//DLL卸載時清理
void _stdcall DllClean()
{
if (g_lpBuffer)
VirtualFree(g_lpBuffer, g_dwFileSize, MEM_RELEASE);
}
//重置DLL,然後重新初始化
BOOL _stdcall Reset(TCHAR szFileName[])
{
if (g_lpBuffer)
VirtualFree(g_lpBuffer, g_dwFileSize, MEM_RELEASE);
memset(g_szFileName, 0, sizeof(g_szFileName) ) ;
g_lpBuffer = NULL;
g_dwFileSize = 0;
g_lpNewFile = NULL;
PE = FALSE;
return InitPE32(szFileName);
}
//初始化,將文件數據copy到分配的緩衝區中
BOOL _stdcall InitPE32(TCHAR szFileName[])
{
DWORD dwFileSize; //文件大小
PBYTE lpMemory; //內存映射指針
HANDLE hFile, hMap;
if (!IsPeFile(szFileName) )
return FALSE;
if (INVALID_HANDLE_VALUE != ( hFile = CreateFile (szFileName, GENERIC_READ , FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
{
dwFileSize = GetFileSize (hFile, NULL);
if (dwFileSize)
{
hMap = CreateFileMapping (hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hMap)
{
lpMemory = (BYTE *)MapViewOfFile (hMap, FILE_MAP_READ, 0, 0, 0);
if (lpMemory)
{
lstrcpy(g_szFileName, szFileName);
g_lpBuffer = (PBYTE)VirtualAlloc(NULL, dwFileSize, MEM_COMMIT, PAGE_READWRITE);
if (g_lpBuffer)
{
PE = TRUE;
g_dwFileSize = dwFileSize;
memcpy(g_lpBuffer, lpMemory, dwFileSize);
UnmapViewOfFile(lpMemory);
CloseHandle(hMap);
CloseHandle(hFile);
return TRUE;
}
}
}
}
}
UnmapViewOfFile(lpMemory);
CloseHandle(hMap);
CloseHandle(hFile);
return FALSE;
}
//計算CRC32值,DLL內部函數
//參數---ptr:數據指針 dwSize:數據大小
static DWORD _stdcall CalCRC32(BYTE* ptr,DWORD dwSize)
{
DWORD crcTable[256],crcTmp1;
//動態生成CRC-32表
for (int i=0; i<256; i++)
{
crcTmp1 = i;
for (int j=8; j>0; j--)
{
if (crcTmp1&1) crcTmp1 = (crcTmp1 >> 1) ^ 0xEDB88320L;
else crcTmp1 >>= 1;
}
crcTable[i] = crcTmp1;
}
//計算CRC32值
DWORD crcTmp2= 0xFFFFFFFF;
while(dwSize--)
{
crcTmp2 = ((crcTmp2>>8) & 0x00FFFFFF) ^ crcTable[ (crcTmp2^(*ptr)) & 0xFF ];
ptr++;
}
return (crcTmp2^0xFFFFFFFF);
}
//獲得指定文件的CRC32值,錯誤返回0
DWORD _stdcall GetCRC32(TCHAR szFileName[] )
{
if (!g_lpBuffer)
return FALSE;
//如果需要的是本文件的CRC32則直接調用
if (!lstrcmp(szFileName, g_szFileName) )
return CalCRC32(g_lpBuffer + ((PIMAGE_DOS_HEADER)g_lpBuffer)->e_lfanew, g_dwFileSize);
else
{
PIMAGE_DOS_HEADER pDosHeader=NULL;
PIMAGE_NT_HEADERS pNtHeader=NULL;
PIMAGE_SECTION_HEADER pSecHeader=NULL;
DWORD fileSize, CRC32, NumberOfBytesRW;
PBYTE pBuffer ;
//打開文件
HANDLE hFile = CreateFile( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
if ( hFile == INVALID_HANDLE_VALUE )
return FALSE;
//獲得文件長度 :
fileSize = GetFileSize(hFile,NULL);
if (fileSize == 0xFFFFFFFF)
return FALSE;
pBuffer = new BYTE[fileSize]; // 申請內存
if (!pBuffer) return FALSE;
ReadFile(hFile,pBuffer, fileSize, &NumberOfBytesRW, NULL);//讀取文件內容
CloseHandle(hFile); //關閉文件
pDosHeader = (PIMAGE_DOS_HEADER)pBuffer;
fileSize = fileSize - pDosHeader->e_lfanew;
CRC32 = CalCRC32(pBuffer + pDosHeader->e_lfanew, fileSize);
delete pBuffer;
return CRC32;
}
}
//添加代碼到最後一個區塊,DLL內部函數
//參數---lpCOdeStart:指向代碼起始處 dwCodeSize:添加代碼的大小
static BOOL _stdcall AddToLastSection(TCHAR szNewFile[], PBYTE lpCodeStart, DWORD dwCodeSize)
{
PIMAGE_NT_HEADERS lpNtHeaders;
PIMAGE_SECTION_HEADER lpSectionHeader;
PIMAGE_SECTION_HEADER lpLastSectionHeader;
DWORD dwNewFileSize; //最終文件大小
DWORD dwFileAlignSize; //原文件對齊後大小
DWORD dwLastSectionAlignSize; //最後區段內存對齊後大小
DWORD dwFileAlign; //文件對齊粒度
DWORD dwSectionAlign; //內存對齊粒度
//PBYTE g_lpNewFile; //最終文件緩存
DWORD dwSectionNum;
lpNtHeaders = (PIMAGE_NT_HEADERS)( g_lpBuffer + ((PIMAGE_DOS_HEADER)g_lpBuffer)->e_lfanew );
lpSectionHeader = (PIMAGE_SECTION_HEADER)(lpNtHeaders + 1);
dwSectionNum = lpNtHeaders->FileHeader.NumberOfSections ;
lpLastSectionHeader = lpSectionHeader + dwSectionNum - 1;
dwFileAlign = lpNtHeaders->OptionalHeader.FileAlignment;
dwSectionAlign = lpNtHeaders->OptionalHeader.SectionAlignment;
dwFileAlignSize = Align(g_dwFileSize, dwFileAlign); //求原文件對齊大小
dwNewFileSize = Align(dwFileAlignSize + dwCodeSize, dwFileAlign); //獲得最終文件對齊後大小
dwLastSectionAlignSize = Align(lpLastSectionHeader->Misc.VirtualSize + dwCodeSize, dwSectionAlign); //獲得內存中最後區段大小
g_lpNewFile = (PBYTE)VirtualAlloc (NULL, dwNewFileSize, MEM_COMMIT, PAGE_READWRITE);
if ( !g_lpNewFile ) //分配內存失敗
return FALSE;
//複製原文件數據
memset(g_lpNewFile, 0, dwNewFileSize);
memcpy(g_lpNewFile, g_lpBuffer, g_dwFileSize);
//複製指定代碼
try {
memcpy(g_lpNewFile + dwFileAlignSize, lpCodeStart, dwCodeSize);
}
catch(...)
{
return FALSE;
}
//修正PE頭數據
PIMAGE_NT_HEADERS lpNewNtHeaders;
PIMAGE_SECTION_HEADER lpNewSectionHeader;
PIMAGE_SECTION_HEADER lpNewLastSection;
DWORD* lpNewEntry; //指向新入口處
DWORD OldEntry;
lpNewNtHeaders = (PIMAGE_NT_HEADERS)( g_lpNewFile + ((PIMAGE_DOS_HEADER)g_lpNewFile)->e_lfanew );
lpNewSectionHeader = (PIMAGE_SECTION_HEADER)(lpNewNtHeaders + 1);
lpNewLastSection = lpNewSectionHeader + dwSectionNum - 1;
//給最後區段添加讀寫執行屬性
lpNewLastSection->Characteristics |= 0xC0000020;
//修正最後一個區段的偏移量
lpNewLastSection->SizeOfRawData = dwNewFileSize - lpNewLastSection->PointerToRawData;
lpNewLastSection->Misc.VirtualSize = Align( GetValidSize(g_lpNewFile, lpNewLastSection), dwSectionAlign);//Align(lpNewLastSection->Misc.VirtualSize + dwCodeSize, dwSectionAlign) ;
//修正鏡像大小
lpNewNtHeaders->OptionalHeader.SizeOfImage = Align(lpNewLastSection->VirtualAddress + lpNewLastSection->Misc.VirtualSize, dwSectionAlign);
//修正入口地址
OldEntry = lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint;
lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint = OffsetToRVA( (IMAGE_DOS_HEADER *)g_lpNewFile, dwFileAlignSize) ;
//修正補丁代碼跳回OEP的參數
lpNewEntry = (DWORD*)(g_lpNewFile + dwFileAlignSize + dwCodeSize - 5);
*lpNewEntry = OldEntry - (lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint + dwCodeSize - 1);
//補丁完畢,寫回文件
HANDLE hNewFile;
DWORD dwRead;
if (INVALID_HANDLE_VALUE == ( hNewFile = CreateFile (szNewFile, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL, CREATE_NEW, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
{
VirtualFree(g_lpNewFile, dwNewFileSize, MEM_RELEASE);
g_lpNewFile = NULL;
return FALSE;
}
WriteFile(hNewFile, g_lpNewFile, dwNewFileSize, &dwRead, NULL);
CloseHandle(hNewFile);
//釋放內存
VirtualFree(g_lpNewFile, dwNewFileSize, MEM_RELEASE);
g_lpNewFile = NULL;
return TRUE;
}
//添加代碼到一個新區塊,DLL內部函數
//參數---lpCOdeStart:指向代碼起始處 dwCodeSize:添加代碼的大小
static BOOL _stdcall AddToNewSection(TCHAR szNewFile[], PBYTE lpCodeStart, DWORD dwCodeSize)
{
PIMAGE_NT_HEADERS lpNtHeaders;
PIMAGE_SECTION_HEADER lpSectionHeader;
PIMAGE_SECTION_HEADER lpLastSectionHeader;
DWORD dwNewFileSize; //最終文件大小
DWORD dwPatchSectionSize; //內存中補丁區塊的大小
DWORD dwPatchFileSize; //補丁區段文件中大小
DWORD dwFileAlign; //文件對齊粒度
DWORD dwSectionAlign; //內存對齊粒度
//PBYTE g_lpNewFile; //最終文件緩存
DWORD dwSectionNum;
DWORD dwNewHeaderSize; //新頭部大小
DWORD dwOldHeaderSize; //老頭部大小
DWORD dwSectionSize; //內存中所有區塊總大小
BOOL bChange = FALSE; //指示新節表的加入是否影響到文件頭大小,如有影響,則需修正各區段偏移
lpNtHeaders = (PIMAGE_NT_HEADERS)(g_lpBuffer + ((PIMAGE_DOS_HEADER)g_lpBuffer)->e_lfanew);
lpSectionHeader = (PIMAGE_SECTION_HEADER)(lpNtHeaders + 1);
dwSectionNum = lpNtHeaders->FileHeader.NumberOfSections;
lpLastSectionHeader = lpSectionHeader + dwSectionNum - 1;
dwFileAlign = lpNtHeaders->OptionalHeader.FileAlignment;
dwSectionAlign = lpNtHeaders->OptionalHeader.SectionAlignment;
dwOldHeaderSize = lpSectionHeader->PointerToRawData;
dwSectionSize = Align(lpLastSectionHeader->VirtualAddress + lpLastSectionHeader->Misc.VirtualSize - Align(dwOldHeaderSize, dwSectionAlign), dwSectionAlign); //內存中區段總大小
//獲得補丁相關數據
dwPatchSectionSize = Align(dwCodeSize, dwSectionAlign); //內存中新區段對齊大小
dwPatchFileSize = Align(dwCodeSize, dwFileAlign); //文件中新區段對齊大小
//頭部是否能增加一個節表
DWORD dwValidSize; //頭部當前有效大小
dwValidSize = sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER)*(dwSectionNum + 1) + ((PIMAGE_DOS_HEADER)g_lpBuffer)->e_lfanew ;
if ( Align(dwValidSize + sizeof(IMAGE_SECTION_HEADER), dwFileAlign) > 0x1000 )
return FALSE;
//是否要增加頭部大小
dwNewHeaderSize = Align(dwValidSize + sizeof(IMAGE_SECTION_HEADER), dwFileAlign); //新頭部大小
if (dwNewHeaderSize > Align(dwValidSize, dwFileAlign) )
bChange = TRUE; //記錄頭部增加,後面需要修改所有節表偏移
dwNewFileSize = Align (Align(g_dwFileSize, dwFileAlign) + dwPatchFileSize, dwFileAlign);
g_lpNewFile = (PBYTE)VirtualAlloc (NULL, dwNewFileSize, MEM_COMMIT, PAGE_READWRITE);
if ( !g_lpNewFile ) //分配內存失敗
return FALSE;
//複製原文件數據
memset(g_lpNewFile, 0, dwNewFileSize);
memcpy(g_lpNewFile, g_lpBuffer, dwValidSize); //頭部數據複製
//區段數據複製
DWORD dwSize = lpLastSectionHeader->PointerToRawData + lpLastSectionHeader->SizeOfRawData - dwOldHeaderSize; //文件中所有區段總大小
memcpy(g_lpNewFile + dwNewHeaderSize, g_lpBuffer + lpSectionHeader->PointerToRawData, dwSize);
//補丁數據複製
try {
memcpy(g_lpNewFile + dwNewHeaderSize + dwSize, lpCodeStart, dwCodeSize);
}
catch(...)
{
return FALSE;
}
//開始修正PE頭
PIMAGE_NT_HEADERS lpNewNtHeaders;
PIMAGE_SECTION_HEADER lpNewSectionHeader;
PIMAGE_SECTION_HEADER lpNewLastSection;
DWORD* lpNewEntry; //指向新入口處
DWORD OldEntry;
lpNewNtHeaders = (PIMAGE_NT_HEADERS)( g_lpNewFile + ((PIMAGE_DOS_HEADER)g_lpNewFile)->e_lfanew);
lpNewSectionHeader = (PIMAGE_SECTION_HEADER) (lpNewNtHeaders + 1);
lpNewLastSection = lpNewSectionHeader + dwSectionNum; //此處即指向要創建的新區段
lpNewNtHeaders->FileHeader.NumberOfSections += 1; //添加一個區段
lpNewNtHeaders->OptionalHeader.SizeOfHeaders = dwNewHeaderSize;
lpNewNtHeaders->OptionalHeader.SizeOfImage = dwNewHeaderSize + dwSectionSize + dwPatchSectionSize; //鏡像總大小
//根據需要修正節表偏移
if (bChange)
{
PIMAGE_SECTION_HEADER lpTempSection = lpNewSectionHeader;
DWORD dwOffset = dwNewHeaderSize - dwOldHeaderSize;
for (DWORD i=0; i < dwSectionNum; i++, lpTempSection++ )
{
lpTempSection->PointerToRawData += dwOffset;
}
}
//建立一個新節表
lpNewLastSection->Characteristics = 0xC0000020; //讀寫執行屬性添加
lpNewLastSection->VirtualAddress = Align(dwNewHeaderSize, dwSectionAlign) + dwSectionSize;
lpNewLastSection->Misc.VirtualSize = dwPatchSectionSize;
lpNewLastSection->PointerToRawData = dwNewHeaderSize + dwSize;
lpNewLastSection->SizeOfRawData = dwPatchFileSize;
lstrcpyA( (LPSTR)(lpNewLastSection->Name), ".crk");
//修正入口地址
OldEntry = lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint;
lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint = Align(dwNewHeaderSize, dwSectionAlign) + dwSectionSize ;
//修正補丁代碼跳回OEP的參數
lpNewEntry = (DWORD*)(g_lpNewFile + dwNewHeaderSize + dwSize + dwCodeSize - 5);
*lpNewEntry = OldEntry - (lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint + dwCodeSize - 1);
//補丁完畢,寫回文件
HANDLE hNewFile;
DWORD dwRead;
if (INVALID_HANDLE_VALUE == ( hNewFile = CreateFile (szNewFile, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL, CREATE_NEW, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
{
VirtualFree(g_lpNewFile, dwNewFileSize, MEM_RELEASE);
g_lpNewFile = NULL;
return FALSE;
}
WriteFile(hNewFile, g_lpNewFile, dwNewFileSize, &dwRead, NULL);
CloseHandle(hNewFile);
//釋放內存
VirtualFree(g_lpNewFile, dwNewFileSize, MEM_RELEASE);
g_lpNewFile = NULL;
return TRUE;
}
//添加代碼到PE頭部,DLL內部函數
//參數---lpCOdeStart:指向代碼起始處 dwCodeSize:添加代碼的大小
static BOOL _stdcall AddToHeaderSection(TCHAR szNewFile[], PBYTE lpCodeStart, DWORD dwCodeSize)
{
PIMAGE_NT_HEADERS lpNtHeaders;
PIMAGE_SECTION_HEADER lpSectionHeader;
PIMAGE_SECTION_HEADER lpLastSectionHeader;
DWORD dwNewFileSize; //最終文件大小
DWORD dwFileAlign; //文件對齊粒度
DWORD dwSectionAlign; //內存對齊粒度
//PBYTE g_lpNewFile; //最終文件緩存
DWORD dwSectionNum;
DWORD dwPatchOffset; //補丁代碼複製到文件的偏移,也即老頭部有效數據的大小
DWORD dwNewHeaderSize; //新頭部大小
DWORD dwOldHeaderSize; //老頭部大小
DWORD dwSectionSize; //所有區塊總大小
lpNtHeaders = (PIMAGE_NT_HEADERS)( g_lpBuffer + ((PIMAGE_DOS_HEADER)g_lpBuffer)->e_lfanew );
lpSectionHeader = (PIMAGE_SECTION_HEADER)(lpNtHeaders + 1);
dwSectionNum = lpNtHeaders->FileHeader.NumberOfSections;
lpLastSectionHeader = lpSectionHeader + dwSectionNum - 1; //獲得最後一個區塊的節表
dwFileAlign = lpNtHeaders->OptionalHeader.FileAlignment;
dwSectionAlign = lpNtHeaders->OptionalHeader.SectionAlignment;
dwOldHeaderSize = lpSectionHeader->PointerToRawData;
//dwPatchOffset = sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER)*(dwSectionNum + 1) + ((PIMAGE_DOS_HEADER)g_lpBuffer)->e_lfanew; //老PE頭有效數據大小,附加代碼從此放置
//原本是使用上面這種方式求有效大小,但考慮到如果PE已有補丁情況下的兼容,所有直接從頭最後面往前搜索以取得大小
PBYTE lpTemp = g_lpBuffer + lpSectionHeader->PointerToRawData - 1;
DWORD dwSize = 0;
while(*lpTemp == 0) { lpTemp--; dwSize++; } //獲得可填充數據的大小
dwSize -= sizeof(IMAGE_SECTION_HEADER); //因爲可能有個全零的節表,所以減去
dwPatchOffset = lpSectionHeader->PointerToRawData - dwSize ; //老PE頭有效數據大小,附加代碼從此放置
dwNewHeaderSize = Align(dwPatchOffset + dwCodeSize, dwFileAlign); //獲得新頭部對齊後大小
if (dwNewHeaderSize > 0x1000)
{
MessageBox (GetActiveWindow() , TEXT("PE頭大小不夠,請選擇其他補丁方式"), NULL, MB_OK);
return FALSE;
}
//最後區段偏移加上大小再減去老PE頭大小就等於原文件所有區段的文件大小,再加上新頭部大小並對齊就是我們需要的新文件大小
dwSectionSize = Align(lpLastSectionHeader->PointerToRawData + lpLastSectionHeader->SizeOfRawData - dwOldHeaderSize, dwFileAlign);
dwNewFileSize = Align(dwNewHeaderSize + dwSectionSize, dwFileAlign);
g_lpNewFile = (PBYTE)VirtualAlloc(NULL, dwNewFileSize, MEM_COMMIT, PAGE_READWRITE);
if (!g_lpNewFile) //分配內存失敗
return FALSE;
//複製原文件數據
memset(g_lpNewFile, 0, dwNewFileSize);
memcpy(g_lpNewFile, g_lpBuffer, dwPatchOffset); //複製原始PE頭
memcpy(g_lpNewFile + dwNewHeaderSize, g_lpBuffer + dwOldHeaderSize, dwSectionSize); //複製區塊數據
//複製補丁代碼
memcpy(g_lpNewFile + dwPatchOffset, lpCodeStart, dwCodeSize);
//開始對頭部數據進行修復
PIMAGE_NT_HEADERS lpNewNtHeaders;
PIMAGE_SECTION_HEADER lpNewSectionHeader;
lpNewNtHeaders = (PIMAGE_NT_HEADERS)(g_lpNewFile + ((PIMAGE_DOS_HEADER)g_lpNewFile)->e_lfanew);
lpNewSectionHeader = (PIMAGE_SECTION_HEADER)(lpNewNtHeaders + 1);
//由於文件頭部大小增加,節表所有的文件偏移都需要修復,RVA則不變
DWORD dwOffset;
PIMAGE_SECTION_HEADER lpTempSectionHeader = lpNewSectionHeader;
dwOffset = dwNewHeaderSize - dwOldHeaderSize; //計算出需要加上的文件偏移
for (DWORD i=0; i < dwSectionNum; i++, lpTempSectionHeader++)
{
lpTempSectionHeader->PointerToRawData += dwOffset;
}
//修正頭部大小
lpNewNtHeaders->OptionalHeader.SizeOfHeaders = dwNewHeaderSize;
//修正入口
DWORD dwOldEntry;
dwOldEntry = lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint;
lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint = dwPatchOffset;
//修正補丁代碼的最後一個跳轉參數
DWORD *lpNewEntry;
lpNewEntry = (DWORD*)(g_lpNewFile + dwPatchOffset + dwCodeSize - 5); //指向要修正的參數
*lpNewEntry = dwOldEntry - (dwPatchOffset + dwCodeSize - 1);
//補丁完畢,寫回文件
HANDLE hNewFile;
DWORD dwRead;
if (INVALID_HANDLE_VALUE == ( hNewFile = CreateFile (szNewFile, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL, CREATE_NEW, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
{
VirtualFree(g_lpNewFile, dwNewFileSize, MEM_RELEASE);
g_lpNewFile = NULL;
return FALSE;
}
WriteFile(hNewFile, g_lpNewFile, dwNewFileSize, &dwRead, NULL);
CloseHandle(hNewFile);
//釋放內存
VirtualFree(g_lpNewFile, dwNewFileSize, MEM_RELEASE);
g_lpNewFile = NULL;
return TRUE;
}
BOOL _stdcall AddCode(TCHAR szNewFileName[], PBYTE lpCodeStart, DWORD dwCodeSize, DWORD dwTypeOfAdd)
{
if ( (!g_lpBuffer) && !PE )
return FALSE;
try
{
//使用指定方法添加代碼
switch(dwTypeOfAdd)
{
case ADD_LAST_SECTION:
if (!AddToLastSection(szNewFileName, lpCodeStart, dwCodeSize) )
return FALSE;
break;
case ADD_NEW_SECTION:
if (!AddToNewSection(szNewFileName, lpCodeStart, dwCodeSize) )
return FALSE;
break;
case ADD_PE_HEADER:
if (!AddToHeaderSection(szNewFileName, lpCodeStart, dwCodeSize) )
return FALSE;
break;
default:
return FALSE;
}
}
catch(...)
{
return FALSE;
}
return TRUE;
}
//////////////////////////////////////////////////提取圖標數據///////////////////////////////////////////////////////
HANDLE hIconFile;
typedef struct _ICON
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
DWORD dwImageOffset;
}ICON_DIR_ENTRY;
typedef struct _ION
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes;
WORD wBitCount;
DWORD dwBytesInRes;
WORD Order;
}PE_ICON_DIR_ENTRY;
typedef struct ICON
{
WORD idReserved;
WORD idType;
WORD idCount;
}ICON_DIR;
void _stdcall WriteIconData(TCHAR szIconFileName[],PIMAGE_RESOURCE_DIRECTORY lpResource,DWORD dwOrder)
{
//存儲三層目錄的參數
PIMAGE_RESOURCE_DIRECTORY lpResDir1;
PIMAGE_RESOURCE_DIRECTORY_ENTRY lpResEntry1;
DWORD dwResNum1;
PIMAGE_RESOURCE_DIRECTORY lpResDir2;
PIMAGE_RESOURCE_DIRECTORY_ENTRY lpResEntry2;
DWORD dwResNum2;
PIMAGE_RESOURCE_DIRECTORY lpResDir3;
PIMAGE_RESOURCE_DIRECTORY_ENTRY lpResEntry3;
PIMAGE_RESOURCE_DATA_ENTRY lpDataEntry;
BYTE *lpResourceData;//指向真正的資源數據
DWORD dwResSize, temp;
DWORD dwNum=0;
lpResDir1 = lpResource;
dwResNum1 = lpResDir1->NumberOfIdEntries + lpResDir1->NumberOfNamedEntries;
lpResEntry1 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(++lpResDir1);
//在目錄中尋找圖標組數據
for (DWORD i=0; i < dwResNum1; i++)
{
//如果是圖標資源
if ( lpResEntry1->Id == 0x3)
{
//第二層,下面每次循環都是一個圖標組併產生一個圖標文件
lpResDir2 = (PIMAGE_RESOURCE_DIRECTORY)( (DWORD)lpResource + lpResEntry1->OffsetToDirectory);
dwResNum2 = lpResDir2->NumberOfIdEntries + lpResDir2->NumberOfNamedEntries;
lpResEntry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(++lpResDir2);
for (DWORD j=0; j < dwResNum2; j++)
{
dwNum++; //圖標ID
if (dwNum == dwOrder) // 是要尋找的那個圖標ID
{
//第三層,假設圖標組只有一個項目
lpResDir3 = (PIMAGE_RESOURCE_DIRECTORY)( (DWORD)lpResource + lpResEntry2->OffsetToDirectory);
lpResEntry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(++lpResDir3);
lpDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)( (DWORD)lpResource + lpResEntry3->OffsetToData);
dwResSize = lpDataEntry->Size;
lpResourceData = g_lpBuffer + RvaToOffset( (IMAGE_DOS_HEADER*)g_lpBuffer, lpDataEntry->OffsetToData);
//開始寫圖標數據
WriteFile(hIconFile, lpResourceData, dwResSize, &temp, NULL);
}
lpResEntry2++;
}
}
lpResEntry1++;
}
}
void _stdcall GetIconHeader(TCHAR szIconFileName[],BYTE *lpResourceData,DWORD dwSize, DWORD dwNum)
{
BYTE *lpData;
DWORD temp;
DWORD dwIconNum; //圖標數
ICON_DIR_ENTRY *lpIconEntry;
DWORD dwHeadSize;
if ( INVALID_HANDLE_VALUE == (hIconFile = CreateFile( szIconFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,0)) )
{
return ;
}
//寫入圖標文件頭
DWORD prev=0;
ICON_DIR_ENTRY icon;
lpData = lpResourceData;
dwIconNum = ((ICON_DIR*)lpData)->idCount;
WriteFile(hIconFile, lpData, 6, &temp, NULL);
lpData += 6;
lpIconEntry = (ICON_DIR_ENTRY*)lpData;
for (DWORD i=0; i < dwIconNum; i++)
{
memcpy(&icon, lpIconEntry, sizeof(ICON_DIR_ENTRY) );
icon.dwImageOffset = prev + dwIconNum * sizeof(ICON_DIR_ENTRY) + 6;
WriteFile(hIconFile, &icon, sizeof(ICON_DIR_ENTRY), &temp, NULL);
prev += icon.dwBytesInRes;
lpIconEntry = (ICON_DIR_ENTRY*)((DWORD)lpIconEntry + sizeof(PE_ICON_DIR_ENTRY) );
}
PE_ICON_DIR_ENTRY *lpPeIcon;
dwHeadSize = (BYTE*)lpIconEntry - lpResourceData;
lpPeIcon = (PE_ICON_DIR_ENTRY*)(lpResourceData + 6);
PIMAGE_DATA_DIRECTORY lpDataDir = (PIMAGE_DATA_DIRECTORY)( &((PIMAGE_NT_HEADERS)(g_lpBuffer + ((PIMAGE_DOS_HEADER)g_lpBuffer)->e_lfanew))->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
PIMAGE_RESOURCE_DIRECTORY lpResource= (PIMAGE_RESOURCE_DIRECTORY)(g_lpBuffer + RvaToOffset( (IMAGE_DOS_HEADER*)g_lpBuffer, lpDataDir->VirtualAddress) );
//寫入圖標數據
for (DWORD i=0; i < dwIconNum; i++)
{
WriteIconData(szIconFileName, lpResource, lpPeIcon->Order);
lpPeIcon++;
}
CloseHandle(hIconFile);
}
//提取圖標數據,參數指定圖標名
BOOL _stdcall GetIcon (TCHAR szIconFileName[])
{
if ( (!g_lpBuffer) && !PE )
return FALSE;
PIMAGE_DATA_DIRECTORY lpDataDir;
PIMAGE_NT_HEADERS lpPeHead = (PIMAGE_NT_HEADERS)(g_lpBuffer + ((PIMAGE_DOS_HEADER)g_lpBuffer)->e_lfanew);
BYTE *lpResource;
//存儲三層目錄的參數
PIMAGE_RESOURCE_DIRECTORY lpResDir1;
PIMAGE_RESOURCE_DIRECTORY_ENTRY lpResEntry1;
DWORD dwResNum1;
PIMAGE_RESOURCE_DIRECTORY lpResDir2;
PIMAGE_RESOURCE_DIRECTORY_ENTRY lpResEntry2;
DWORD dwResNum2;
PIMAGE_RESOURCE_DIRECTORY lpResDir3;
PIMAGE_RESOURCE_DIRECTORY_ENTRY lpResEntry3;
PIMAGE_RESOURCE_DATA_ENTRY lpDataEntry;
BYTE *lpResourceData;//指向真正的資源數據
DWORD dwResSize;
DWORD dwNum=0;//圖標組編號
lpDataDir = (PIMAGE_DATA_DIRECTORY)( &lpPeHead->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);
lpResource = ( g_lpBuffer + RvaToOffset( (IMAGE_DOS_HEADER*)g_lpBuffer, lpDataDir->VirtualAddress) );
lpResDir1 = (PIMAGE_RESOURCE_DIRECTORY)lpResource;
dwResNum1 = lpResDir1->NumberOfIdEntries + lpResDir1->NumberOfNamedEntries;
lpResEntry1 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(++lpResDir1);
//在目錄中尋找圖標組數據
for (DWORD i=0; i < dwResNum1; i++)
{
//如果是ID資源
if ( !lpResEntry1->NameIsString )
{
//如果是圖標組資源
if ( lpResEntry1->Id == 0xe)
{
//第二層,下面每次循環都是一個圖標組併產生一個圖標文件
lpResDir2 = (PIMAGE_RESOURCE_DIRECTORY)(lpResource + lpResEntry1->OffsetToDirectory);
dwResNum2 = lpResDir2->NumberOfIdEntries + lpResDir2->NumberOfNamedEntries;
lpResEntry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(++lpResDir2);
for (DWORD j=0; j < dwResNum2; j++)
{
//第三層,假設圖標組只有一個項目
lpResDir3 = (PIMAGE_RESOURCE_DIRECTORY)(lpResource + lpResEntry2->OffsetToDirectory);
lpResEntry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(++lpResDir3);
lpDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)(lpResource + lpResEntry3->OffsetToData);
dwResSize = lpDataEntry->Size;
lpResourceData = g_lpBuffer + RvaToOffset( (IMAGE_DOS_HEADER*)g_lpBuffer, lpDataEntry->OffsetToData);
//開始處理圖標數據
GetIconHeader(szIconFileName,lpResourceData, dwResSize,++dwNum);
lpResEntry2++;
}
}
}
lpResEntry1++;
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////