字节补丁生成工具

本文介绍了一个用于生成程序补丁的工具,通过加载器补丁和字节补丁等方式实现对程序的修改与升级。提供了详细的代码实现,涵盖补丁创建、自定义补丁生成等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可以生成加载器补丁exe和字节补丁exe,用附加可执行代码等方法可用于一些程序的破解

#ifndef APIMACRO_H
#define APIMACRO_H

////////////////通用控件头文件和链接库////////////
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")
//////////////////////////////////////////////////


///////////Sets the dialog box icons//////////////
inline void chSETDLGICONS(HWND hWnd, int idi) {
   SendMessage(hWnd, WM_SETICON, ICON_BIG,  (LPARAM) 
      LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
         MAKEINTRESOURCE(idi)));
   SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) 
      LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
      MAKEINTRESOURCE(idi)));
}

inline void chMB(PCSTR szMsg) {
   char szTitle[MAX_PATH];
   GetModuleFileNameA(NULL, szTitle, _countof(szTitle));
   MessageBoxA(GetActiveWindow(), szMsg, szTitle, MB_OK);
}

#define chBEGINTHREADEX(psa, cbStackSize, pfnStartAddr, \
   pvParam, dwCreateFlags, pdwThreadId)                 \
      ((HANDLE)_beginthreadex(                          \
         (void *)        (psa),                         \
         (unsigned)      (cbStackSize),                 \
         (PTHREAD_START) (pfnStartAddr),                \
         (void *)        (pvParam),                     \
         (unsigned)      (dwCreateFlags),               \
         (unsigned *)    (pdwThreadId)))


//通用控件使用前务必InitCommonControls初始化
//List_View 控件宏       行数列数索引均从0开始
static LV_ITEM		_stLVI;
static LV_COLUMN	_stLVC;

//////////////////////////////////在ListView中新增一行///////////////////////////////////
inline int ListView_AddLine(HWND hwndCtl)
{
	RtlZeroMemory (&_stLVI,sizeof(LV_ITEM) );
	_stLVI.mask			= LVIF_TEXT;
	_stLVI.pszText		= TEXT("无数据可显示");
	_stLVI.iSubItem		= 0;
	return ListView_InsertItem(hwndCtl, &_stLVI);
}



////////////////////////////////在ListView中增加一个标题列///////////////////////////////////
inline void ListView_InsertCaption(HWND hwndCtl, int iColumn, int iWidth, LPTSTR lpszCaption)
{
	RtlZeroMemory (&_stLVC,sizeof(LV_COLUMN) );
	_stLVC.mask			= LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
	_stLVC.fmt			= LVCFMT_LEFT;
	_stLVC.pszText		= lpszCaption;
	_stLVC.cx			= iWidth;
	_stLVC.iSubItem		= iColumn;
	ListView_InsertColumn(hwndCtl, iColumn, &_stLVC);
}


////////////////////////////////在ListView中更改一个标题列///////////////////////////////////
inline void ListView_SetCaption(HWND hwndCtl, int iColumn, LPTSTR lpszCaption)
{
	RtlZeroMemory (&_stLVC,sizeof(LV_COLUMN) );
	_stLVC.mask			= LVCF_TEXT | LVCF_FMT;
	_stLVC.fmt			= LVCFMT_LEFT;
	_stLVC.pszText		= lpszCaption;
	_stLVC.iSubItem		= iColumn;
	ListView_SetColumn(hwndCtl, iColumn, &_stLVC);
}


#endif

 

 

#ifndef RVATOOFFSET_H
#define RVATOOFFSET_H

#include <windows.h>

//////////////////////////////////////////各种PE中偏移的转换实现//////////////////////////////////////////////

char szNotFound[] = "无法查找";
///////////////////////////////////////将RVA偏移转换成文件偏移,失败返回-1////////////////////////////////////////////////

DWORD 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偏移转成文件指针偏移,失败返回-1////////////////////////////////////////////////

DWORD RvaToPointer(IMAGE_DOS_HEADER *lpFileHead,DWORD dwRva)
{
	DWORD	Offset	= RvaToOffset(lpFileHead, dwRva);
	if(Offset == -1)
		return -1;
	return	(DWORD)(lpFileHead) + Offset;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////将虚拟地址转成文件指针偏移,失败返回-1////////////////////////////////////////////////

DWORD VirtualAddressToPointer(IMAGE_DOS_HEADER *lpFileHead,DWORD dwVirtualAddress)
{
	::IMAGE_NT_HEADERS		*lpPEHead;
	lpPEHead		= (IMAGE_NT_HEADERS*)( (BYTE*)lpFileHead + lpFileHead->e_lfanew);
	
	return RvaToPointer(lpFileHead, dwVirtualAddress - lpPEHead->OptionalHeader.ImageBase);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////////////////获得RVA偏移处的节区名称////////////////////////////////////////////////

PBYTE 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 (PBYTE)szNotFound;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////获得指定RVA所处节区的节表头,失败返回NULL/////////////////////////////////////////

PIMAGE_SECTION_HEADER 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 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;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



////////////////////////////////////////////文件偏移转换成内存指针///////////////////////////////////////////////////////////

DWORD OffsetToPointer(IMAGE_DOS_HEADER *lpFileHead, DWORD dwOffset)
{
	DWORD	RVA	= OffsetToRva(lpFileHead, dwOffset);
	if( RVA == -1)
		return -1;
	return	(DWORD)(lpFileHead) + RVA;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////按指定大小对齐//////////////////////////////////////////////////////////

DWORD Align(DWORD dwSize, DWORD dwAlignment)
{
	return (dwSize + dwAlignment - 1) /dwAlignment * dwAlignment;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////获得区块有效数据部分大小/////////////////////////////////////////////////

DWORD 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;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif


 


 

#ifndef KERNEL_H
#define KERNEL_H
#include "RvaToOffset.h"

#define ITEM_NUM 16
//静态补丁类型
#define ADD_LAST_SECTION 1	//添加代码到最后一个区段	
#define ADD_NEW_SECTION  2  //添加代码到一个新建的区段
#define ADD_TO_HEADER	 3  //添加代码到PE头部
#define	BYTE_PATCH		 4  //这里再加一种字节补丁,针对一些简单的程序

//动态补丁类型
#define SLEEP_PATCH 1	
#define DEBUG_PATCH 2

extern HINSTANCE	hInst;   //此变量在主文件中定义

//主要功能函数实现
BOOL 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;
}


////////////////////////////////////////////////////////////////
// 计算字符串的CRC32值
// 参数:欲计算CRC32值字符串的首地址和大小
// 返回值: 返回CRC32值

DWORD CalCRC32(BYTE* ptr,DWORD Size)
{

	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(Size--)
	{
		crcTmp2 = ((crcTmp2>>8) & 0x00FFFFFF) ^ crcTable[ (crcTmp2^(*ptr)) & 0xFF ];
		ptr++;
	}
		
	return (crcTmp2^0xFFFFFFFF);
}

DWORD GetCRC32(TCHAR szFileName[])
{
	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];     // 申请内存
	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;
}


/*需要工具修正的节为.sdata节
#pragma data_seg(".sdata")
DWORD	dwTypeOfPatch = 0;			/指示补丁类型
DWORD	dwPatchNum = 2;				//补丁数量
//偏移8
TCHAR	szFileName[MAX_PATH] = { 0 };
//偏移528
DWORD	dwPatchAddress[16] = { 0}  //////////////////////利用调试寄存器打丁///////////////////////////////////////////////////////
/////////////打此类补丁应在补丁地址第一个地址填上希望中断的地址以确保所有地址数据已解码,以保证补丁正确性///////////
//偏移592
BYTE	byOldData[16] = { 0};		//补丁处旧数据和新数据
//偏移608
BYTE	byNewData[16] = { 0};
#pragma data_seg()
根据需要加入了CRC32验证,需要补丁工具在PE头前4个字节写上目标文件的CRC32*/



//创建补丁文件,补丁模版以资源的形式存储在程序中
//参数:szPatchName:创建的补丁文件名     szFileName:目标文件名     lpPatchAddress:补丁地址数组  
//	   lpNewByte:补丁原始数据数组       lpNewByte:补丁新数据数组  dwTypeOfPatch:补丁类型
//	   dwPatchNum:补丁数量				ID:补丁模版的资源ID		  bCRC32:是否加入CRC32文件验证
BOOL CreatePatch(TCHAR szPatchName[], TCHAR szFileName[], DWORD lpPatchAddress[],BYTE lpOldByte[],BYTE lpNewByte[],\
				 DWORD	dwTypeOfPatch, DWORD dwPatchNum, DWORD ID, BOOL bCRC32 )
{
	static	char	secName[8] = ".sdata";
	DWORD	CRC32;
	if (bCRC32)		
	{
		CRC32 = GetCRC32(szFileName );
		if (!CRC32)
		{
			MessageBox(NULL, TEXT("CRC32提取出错"), NULL, 0);
			return FALSE;
		}
	}
	
	DWORD	dwResSize;
	PBYTE	lpResData;
	HGLOBAL	hGlobal;
	HRSRC hRes	= FindResource(hInst, MAKEINTRESOURCE(ID), L"PETYPE" );
	if (hRes)
	{
		dwResSize	= SizeofResource(hInst, hRes);
		hGlobal		= LoadResource(hInst, hRes);
		if (hGlobal )
		{
			lpResData	= (PBYTE)LockResource(hGlobal);
			if (lpResData )
			{

///////////////////////////////////开始写入文件并修正补丁中的参数//////////////////////////////////////////

				HANDLE					hFile, hMap;
				PBYTE					lpMemory;
				PIMAGE_NT_HEADERS		lpNtHeaders;
				PIMAGE_SECTION_HEADER	lpSectionHeader;
				PBYTE					lpSectionData;
				DWORD*					lpCRC32;
				DWORD					dwFileSize, dwRead, dwSectionNum;

				if (INVALID_HANDLE_VALUE != ( hFile = CreateFile (szPatchName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
				{
					WriteFile(hFile, lpResData, dwResSize, &dwRead, NULL);    //写入文件
					dwFileSize	= GetFileSize (hFile, NULL);
					//修正数据
					if (dwFileSize)
					{
						hMap	= CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
						if (hMap)
						{
							lpMemory	= (BYTE *)MapViewOfFile (hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
							if (lpMemory)
							{
								lpNtHeaders		= (PIMAGE_NT_HEADERS)(lpMemory + ((PIMAGE_DOS_HEADER)lpMemory)->e_lfanew);
								if (bCRC32)    //写CRC32值
								{
									lpCRC32			= (DWORD*)((PBYTE)(lpNtHeaders)-4);
									*lpCRC32		= CRC32;
								}
								dwSectionNum	= lpNtHeaders->FileHeader.NumberOfSections;
								lpSectionHeader	= (PIMAGE_SECTION_HEADER)(lpNtHeaders + 1);
								
								//查找需修正变量所在区段
								for (DWORD i=0; i < dwSectionNum; i++, lpSectionHeader++)
								{
									if ( !lstrcmpiA( (LPCSTR)lpSectionHeader->Name, secName) )
										break;
								}
								lpSectionData			= lpMemory + RvaToOffset( (PIMAGE_DOS_HEADER)lpMemory, lpSectionHeader->VirtualAddress);
////////////////////////////////////////////////////////////修正变量///////////////////////////////////////////////////////////////////////////
								int x;
								*(DWORD*)lpSectionData	= dwTypeOfPatch;
								*(DWORD*)(lpSectionData+4) = dwPatchNum;
								
								for(x=lstrlen(szFileName); x > 0; x--)
									if(szFileName[x] == TEXT('\\') )
										break;
								
								lstrcpy( (LPWSTR)(lpSectionData+8), &(szFileName[x]) );
								memcpy(lpSectionData+528, lpPatchAddress, ITEM_NUM*sizeof(DWORD));
								memcpy(lpSectionData+592, lpOldByte, ITEM_NUM*sizeof(BYTE));
								memcpy(lpSectionData+608, lpNewByte, ITEM_NUM*sizeof(BYTE));
////////////////////////////////////////////////////////////修正完毕////////////////////////////////////////////////////////////////////////////
								
								UnmapViewOfFile (lpMemory);
								CloseHandle (hMap);
								CloseHandle (hFile);
								return TRUE;
							}

						}

					}

				}
////////////////////////////////////////////////////////////////////////////////////////////////////////////

				return FALSE;
			}
		}
	}
	return FALSE;
}


#endif


 

 

#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdlib.h>
#include "resource.h"
#include "ApiMacro.h"
#include "kernel.h"

//搞掉些烦人的警告
#pragma warning(disable:4244)
#pragma warning(disable:4996)

#define ITEM_NUM	16		//ListView最大项目数
#define CODE_SIZE	1024*32 //自定义补丁代码的最大长度
static  char	g_szBuffer[CODE_SIZE] = { 0 };	//32KB缓存
static  char	g_szUserCode[CODE_SIZE] = { 0 };//32KB用户代码缓存
BOOL	DIY_OK = FALSE;


//添加WIN7风格界面
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0'\
															processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")

////////////////////////////////////////界面辅助函数//////////////////////////////////////////////////////
void SuperClass();	//编辑控件超类化,生成16进制编辑控件
LONG WINAPI ProcEdit(HWND, UINT, WPARAM, LPARAM);	//新编辑控件窗口过程
void PopFileInitialize (HWND hwnd);  //文件对话框初始化
BOOL PopFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName); //打开对话框
BOOL PopFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName); //保存对话框
/////////////////////////////////////////////////////////////////////////////////////////////////////////

//主对话框窗口过程
BOOL CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

//主对话框消息处理函数
BOOL Main_OnNotify	(HWND hwnd, int wParam,	LPNMHDR pnm);
BOOL Main_OnCommand	(HWND hDlg, int id,HWND hCtrl, UINT codeNotify);
BOOL Main_OnInitDialog (HWND hWnd, HWND hWndFocus, LPARAM lParam) ;

//高级设置对话框
BOOL CALLBACK SetDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
//帮助对话框
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) ;
//地址输入对话框
BOOL CALLBACK AddressDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
//定制补丁对话框
BOOL CALLBACK DiyDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
void GetListData( );	//提取ListView的数据存到全局变量中
void GetPeHeader(TCHAR szFileName[]); //获取整个PE头部数据
BOOL CreateDiyPatch();	//定制补丁生成函数



HINSTANCE	hInst;
HWND		g_hWnd;			//主对话框窗口句柄
HWND		g_hListView;   //表格视图句柄
BOOL		g_bOffset = FALSE;  //记录补丁地址是否是用文件偏移,如果是则转换成虚拟地址	
DWORD		g_dwLineOfNum = 0;  //记录ListView数据行数
DWORD		g_dwTypeOfPatch = 1;//记录补丁类型,并赋值默认方法
DWORD		g_dwTypeOfLoader= 1;//记录Loader类型,并赋值默认方法
BOOL		g_bIsPeFile = FALSE;//指示目标文件是否是PE文件
PBYTE		g_lpPeHeader= NULL; //指向整个PE头部,用于RVA与offset之间的转换
DWORD		g_dwAddress = 0;	//地址对话框获得的地址

TCHAR		g_szFileName[MAX_PATH] = { 0 };		//目标文件名
TCHAR		g_szPatchName[MAX_PATH]= { 0 };		//补丁文件名

DWORD		g_pPatchAddress[ITEM_NUM] = { 0 };	//补丁地址
BYTE		g_pOldByte[ITEM_NUM] = { 0 };		//老数据
BYTE		g_pNewByte[ITEM_NUM] = { 0 };		//新数据


int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
{
	hInst = hInstance;
	InitCommonControls();
	SuperClass();//编辑控件超类化
	DialogBoxParam (hInstance, MAKEINTRESOURCE(IDD_DIALOG), NULL, DialogProc, 0);

	return 0;
}


BOOL CALLBACK DialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		HANDLE_MSG(hDlg, WM_INITDIALOG, Main_OnInitDialog);
		HANDLE_MSG(hDlg, WM_COMMAND	, Main_OnCommand	);

		case WM_CLOSE:
			EndDialog (hDlg, 0);
			return true;
	}
	return FALSE;
}

//对话框初始化
BOOL Main_OnInitDialog (HWND hDlg, HWND hWndFocus, LPARAM lParam) 
{
	chSETDLGICONS(hDlg, IDI_ICON1);
	HWND	hEdit1,hEdit2,hEdit3;
	hEdit1	= GetDlgItem(hDlg, IDC_OFFSET);
	hEdit2	= GetDlgItem(hDlg, IDC_OLDBYTE);
	hEdit3	= GetDlgItem(hDlg, IDC_NEWBYTE);
	Edit_LimitText(hEdit1, 8);
	Edit_LimitText(hEdit2, 2);
	Edit_LimitText(hEdit3, 2);

	Edit_LimitText(GetDlgItem(hDlg, IDC_PATH), MAX_PATH);

	g_hWnd	= hDlg;
	PopFileInitialize (hDlg);
	CheckDlgButton(hDlg, IDC_VIRTUALADDRESS, BST_CHECKED);

    //定义表格外观
	g_hListView	= GetDlgItem(hDlg, IDC_LISTVIEW);
	ListView_SetExtendedListViewStyle(g_hListView, LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT );
	//ListView_SetBkColor(g_hListView, 0xa0a0ff);背景色
    ShowWindow (g_hListView, SW_SHOW);

	ListView_InsertCaption(g_hListView, 0, 200, TEXT("虚拟地址") );
	ListView_InsertCaption(g_hListView, 1, 178, TEXT("原始字节") );
	ListView_InsertCaption(g_hListView, 2, 178, TEXT("新的字节") );
	//ListView_AddLine(g_hListView);

	return TRUE;
}


BOOL Main_OnCommand	(HWND hDlg, int id,HWND hCtrl, UINT codeNotify)
{
	static TCHAR	szStr[] = TEXT("选择目标PE文件");
	static TCHAR	szOffset[10];
	static TCHAR	szOldByte[3], szNewByte[3];
	static TCHAR	szStr2[]= TEXT("创建补丁文件");  
	
	TCHAR	szFormat[12] = TEXT("0x00000000");
	int	i;

DWORD a;
	switch(id)
	{
		//case IDC_LISTVIEW:			//列表
		//	return TRUE;

		//case IDC_PATH:				//目标文件路径
		//	return TRUE;

		case IDC_OPEN:				//浏览,打开文件
			if (PopFileOpenDlg(hDlg, g_szFileName, szStr ) )
				if ( IsPeFile(g_szFileName) )
				{
					g_bIsPeFile	= TRUE;
					SetDlgItemText(hDlg, IDC_PATH, g_szFileName);
					GetPeHeader(g_szFileName);   //读取PE头以备偏移转换用
				}
				else
				{
					g_bIsPeFile	= FALSE;
					MessageBox(hDlg, TEXT("文件格式错误"), TEXT("提示"), 0);
				}
			return TRUE;

		case IDC_FILE_OFFSET:		//文件偏移
			g_bOffset	= TRUE;
			ListView_SetCaption(g_hListView, 0, TEXT("文件偏移") );
			
			ListView_DeleteAllItems(g_hListView);
			g_dwLineOfNum = 0;
			return TRUE;

		case IDC_VIRTUALADDRESS:	//虚拟地址
			g_bOffset	= FALSE;
			ListView_SetCaption(g_hListView, 0, TEXT("虚拟地址") );
			
			ListView_DeleteAllItems(g_hListView);
			g_dwLineOfNum = 0;
			return TRUE;

/*		case IDC_OFFSET:	//偏移
			return TRUE;

		case IDC_OLDBYTE:	//老字节
			return TRUE;

		case IDC_NEWBYTE:	//新字节
			return TRUE;
*/
		case IDC_ADD:		//添加
			if (GetDlgItemText(hDlg, IDC_OFFSET, szOffset, sizeof(szOffset)) && GetDlgItemText(hDlg, IDC_OLDBYTE, szOldByte,\
											sizeof(szOldByte)) && GetDlgItemText(hDlg, IDC_NEWBYTE, szNewByte, sizeof(szNewByte)) )
			{
				if ( g_dwLineOfNum < ITEM_NUM)
				{
					szFormat[10-lstrlen(szOffset)] = TEXT('\0');
					lstrcat(szFormat, szOffset);
					i = ListView_AddLine(g_hListView);//添加一行
					ListView_SetItemText(g_hListView, i, 0, szFormat);
					ListView_SetItemText(g_hListView, i, 1, szOldByte);
					ListView_SetItemText(g_hListView, i, 2, szNewByte);
					SetDlgItemText(hDlg, IDC_OFFSET, NULL);
					SetDlgItemText(hDlg, IDC_OLDBYTE, NULL);
					SetDlgItemText(hDlg, IDC_NEWBYTE, NULL);
					g_dwLineOfNum++;
				}
			}
			return TRUE;

		case IDC_DELETE:	//删除选中行
			i = ListView_GetSelectionMark(g_hListView);  //获得选中行索引
			ListView_DeleteItem(g_hListView, i);
			g_dwLineOfNum--;
			return TRUE;

		case IDC_CLEAR:		//清空所有行
			ListView_DeleteAllItems(g_hListView);
			g_dwLineOfNum = 0;
			return TRUE;

		case IDC_SET:		//高级设置
			DialogBox (hInst, MAKEINTRESOURCE (IDD_SET), hDlg, SetDlgProc) ;
			return TRUE;

		case IDC_LOADER:	//创建Loader
			if (g_bIsPeFile )
			{
				lstrcpy(g_szPatchName, TEXT("Loader.exe") );
				if ( PopFileSaveDlg (hDlg, g_szPatchName, szStr2))
				{
					GetListData( );
					if (g_dwTypeOfLoader == DEBUG_PATCH)		//用调试寄存器补丁需要特别处理
					{
						DialogBoxParam (hInst, MAKEINTRESOURCE(IDD_ADDRESS), NULL, AddressDlgProc, 0);
						if (g_dwAddress == 0)   //如果没有指定进行补丁的地址
							break;
						else
						{
							for (int i=g_dwLineOfNum; i > 0; i--)
							{
								g_pPatchAddress[i] = g_pPatchAddress[i-1];
							}
							g_pPatchAddress[0] = g_dwAddress;  //存储指定地址,由补丁数据的存储方式决定
						}
					}
					if (CreatePatch(g_szPatchName, g_szFileName, g_pPatchAddress, g_pOldByte, g_pNewByte, g_dwTypeOfLoader,\
															g_dwLineOfNum, IDR_LOADER, FALSE)  )//ID为资源ID号
						MessageBox( hDlg, TEXT("创建补丁成功"), TEXT("恭喜"), 0);
					else
						MessageBox(hDlg, TEXT("失败,未知错误"), TEXT("悲剧"), 0);
				}
			}
			else
				MessageBox(hDlg, TEXT("请选择目标文件"), TEXT("创建补丁失败"), 0);
			return TRUE;

		case IDC_PATCH:		//创建补丁
			if (g_bIsPeFile )
			{
				lstrcpy(g_szPatchName, TEXT("Patch.exe") );
				if ( PopFileSaveDlg (hDlg, g_szPatchName, szStr2))
				{
					GetListData( );
					if (CreatePatch(g_szPatchName, g_szFileName, g_pPatchAddress, g_pOldByte, g_pNewByte, g_dwTypeOfPatch,\
															g_dwLineOfNum, IDR_PATCH, TRUE)  )//ID为资源ID号
						MessageBox( hDlg, TEXT("创建补丁成功"), TEXT("恭喜"), 0);
					else
						MessageBox(hDlg, TEXT("失败,未知错误"), TEXT("悲剧"), 0);
				}
			}
			else
				MessageBox(hDlg, TEXT("请选择目标文件"), TEXT("创建补丁失败"), 0);
			return TRUE;

		case IDC_DIY:		//定制补丁
			DialogBox (hInst, MAKEINTRESOURCE (IDD_DIY), hDlg, DiyDlgProc) ;
			return TRUE;

		case IDC_CREATE:	//生成定制补丁
			if (!CreateDiyPatch() )
				MessageBox(hDlg, TEXT("创建补丁失败"), TEXT("oh yea"), 0);
			return TRUE;

		case IDM_HELP_ABOUT :
			DialogBox (hInst, MAKEINTRESOURCE (IDD_ABOUT), hDlg, AboutDlgProc) ;
			return TRUE;

		case IDCANCEL:
			EndDialog (hDlg, 0) ;
			return TRUE;
	}
	return FALSE;
}


//高级设置对话框窗口过程
BOOL CALLBACK SetDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static DWORD	dwTmpPatch = 1;
	static DWORD    dwTmpLoader= 1;

	switch(message)
	{
		case WM_INITDIALOG:
			switch(g_dwTypeOfPatch)
			{
				case ADD_LAST_SECTION:
					CheckDlgButton(hDlg, IDC_ADDLAST, BST_CHECKED);
					break;
					
				case ADD_NEW_SECTION:
					CheckDlgButton(hDlg, IDC_ADDNEW, BST_CHECKED);
					break;
					
				case ADD_TO_HEADER:
					CheckDlgButton(hDlg, IDC_ADDHEADER, BST_CHECKED);
					break;

				case BYTE_PATCH:
					CheckDlgButton(hDlg, IDC_ADDFILE, BST_CHECKED);
					break;
			}
			switch(g_dwTypeOfLoader)
			{
				case SLEEP_PATCH:
					CheckDlgButton(hDlg, IDC_THREAD, BST_CHECKED);
					break;

				case DEBUG_PATCH:
					CheckDlgButton(hDlg, IDC_DEBUG, BST_CHECKED);
					break;
			}
			
			return TRUE;
    
		case WM_COMMAND :
			switch (LOWORD (wParam))  		            
			{
				case IDC_ADDLAST:
					dwTmpPatch	= ADD_LAST_SECTION;
					return TRUE;

				case IDC_ADDHEADER:
					dwTmpPatch	= ADD_TO_HEADER;
					return TRUE;

				case IDC_ADDNEW:
					dwTmpPatch	= ADD_NEW_SECTION;
					return TRUE;

				case IDC_ADDFILE:
					dwTmpPatch	= BYTE_PATCH;
					return TRUE;

				case IDC_THREAD:
					dwTmpLoader = SLEEP_PATCH;
					return TRUE;

				case IDC_DEBUG:
					dwTmpLoader = DEBUG_PATCH;
					return TRUE;

				case IDCANCEL:
					EndDialog(hDlg, 0);
					return TRUE;

				case IDOK :	//保存设置
					g_dwTypeOfPatch	= dwTmpPatch;
					g_dwTypeOfLoader= dwTmpLoader;
					EndDialog (hDlg, 0) ;
					return TRUE ;
			}
			return TRUE ;

		case WM_CLOSE:
			EndDialog (hDlg, 0) ;
			return TRUE;
	}
	return FALSE;
}



BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
		case  WM_LBUTTONDOWN:
			PostMessage(hDlg, WM_NCLBUTTONDOWN, HTCAPTION, 0);//实现拖拽效果
			return TRUE ;
    
		case WM_COMMAND :
			switch (LOWORD (wParam))  		            
			{

				case IDOK :
					EndDialog (hDlg, 0) ;
					return TRUE ;
			}
			return TRUE ;

		case WM_CLOSE:
			EndDialog (hDlg, 0) ;
			return TRUE;
	}
	return FALSE;
}

BOOL CALLBACK AddressDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static TCHAR	szBuffer[12];
	static HWND		hEdit;

	switch(message)
	{
		case WM_INITDIALOG:
			hEdit	= GetDlgItem(hDlg, ID_ADDRESS);
			Edit_LimitText(hEdit, 8);
			return TRUE;

		case WM_COMMAND :
			switch (LOWORD (wParam))  		            
			{
				
				case IDOK :
					GetDlgItemText(hDlg, ID_ADDRESS, szBuffer, sizeof(szBuffer) );
					swscanf(szBuffer, L"%x", &g_dwAddress);
					EndDialog (hDlg, 0) ;
					return TRUE ;
			}
			return TRUE ;

		case WM_CLOSE:
			g_dwAddress	= 0;
			EndDialog (hDlg, 0) ;
			return TRUE;
	}
	return FALSE;
}

BOOL CALLBACK DiyDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HWND		hEdit;
	static FILE		*pFile;
	static long		fileSize;
	static BOOL		bFirst = TRUE; //第一次进入?	
	char	ch;
	int		i;

	switch(message)
	{
		case WM_INITDIALOG:
			hEdit	= GetDlgItem(hDlg, IDC_CODE);
			if (bFirst)
			{
				bFirst = FALSE;
				pFile = fopen(".\\template\\patch", "r");
				if (pFile != NULL)
				{
					i=0;
					while((ch = fgetc(pFile) )!= EOF) 
					{
						if (ch == '\n')
							g_szBuffer[i++]='\r';
						g_szBuffer[i++]=ch;
					}
					SetWindowTextA(hEdit, g_szBuffer);
					DIY_OK = TRUE;
					fclose(pFile);
				}
				ShowWindow(g_hWnd, SW_HIDE);
				return TRUE;
			}
			if (DIY_OK)
			{
				ShowWindow(g_hWnd, SW_HIDE);
				SetWindowTextA(hEdit, g_szBuffer);
			}
			else
			{
				MessageBox(g_hWnd,L"读取失败",L"未知错误", NULL);
				EndDialog (hDlg, 0) ;
			}
			return TRUE;

		case WM_COMMAND :
			switch (LOWORD (wParam))  		            
			{
				
				case IDOK :
					GetDlgItemTextA(hDlg, IDC_CODE, g_szUserCode, CODE_SIZE );
					EnableWindow(GetDlgItem(g_hWnd, IDC_CREATE), TRUE);
					ShowWindow(g_hWnd, SW_SHOW);
					EndDialog (hDlg, 0) ;
					return TRUE ;

				case IDCANCEL:
					EnableWindow(GetDlgItem(g_hWnd, IDC_CREATE), FALSE);
					ShowWindow(g_hWnd, SW_SHOW);
					EndDialog (hDlg, 0) ;
					return TRUE;
			}
			return TRUE ;

		case WM_CLOSE:
			ShowWindow(g_hWnd, SW_SHOW);
			EndDialog (hDlg, 0) ;
			return TRUE;
	}
	return FALSE;
}

BOOL CreateDiyPatch()
{
	static TCHAR	szStr[]= TEXT("创建自定义补丁"); 
	static char		szBuffer[1024];
	static TCHAR	szPatch[MAX_PATH];	
	static TCHAR	szCurrentDirectory[MAX_PATH];
	static TCHAR	szOriDirectory[MAX_PATH];
	HANDLE	hAsmFile, hAsmMap;
	PBYTE	lpMemory;
	DWORD	dwFileSize;
	int x;
	if (DIY_OK == FALSE)
		return FALSE;
	if ( PopFileSaveDlg (g_hWnd, g_szPatchName, szStr) )
	{
		GetCurrentDirectory(sizeof(szOriDirectory), szOriDirectory);
		GetModuleFileName(hInst, szPatch, sizeof(szPatch) );
		for(x=lstrlen(szPatch); x > 0; x--)
			if(szPatch[x] == TEXT('\\') )
				break;
		lstrcpy(szCurrentDirectory, szPatch);
		lstrcpy(&(szCurrentDirectory[x]), L"\\template\\");  //获得当前目录
		lstrcpy( &(szPatch[x]), TEXT("\\template\\patch"));
		if (INVALID_HANDLE_VALUE != ( hAsmFile = CreateFile ( szPatch, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ , NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
		{
			dwFileSize	= GetFileSize (hAsmFile, NULL);
			if (dwFileSize)
			{
				hAsmMap	= CreateFileMapping (hAsmFile, NULL, PAGE_READWRITE, 0, 0, NULL);
				if (hAsmMap)
				{
					lpMemory	= (BYTE *)MapViewOfFile (hAsmMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
					if (lpMemory)
					{
						memcpy(lpMemory, g_szUserCode, strlen( (char*)g_szUserCode) ); //复制自定义代码
						
						//下面代码用来隐藏控制台,要不一闪一闪比较难看
						TCHAR	szConsoleTitle[100];//控制台标题
						HWND	hCon;
						AllocConsole();
						GetConsoleTitle(szConsoleTitle, sizeof(szConsoleTitle));
						hCon=FindWindow(NULL, szConsoleTitle);
						ShowWindow(hCon, SW_HIDE);
						
						//设置工作目录并编译连接
						SetCurrentDirectory(szCurrentDirectory);
						system("ml /c /coff DiyPatch >> _1.txt");
						system("link /subsystem:windows DiyPatch.obj DiyPatch.res >> _1.txt");
						system("del DiyPatch.obj");
						MoveFile(L"DiyPatch.exe", g_szPatchName);
						memcpy(lpMemory, g_szBuffer, dwFileSize );//还原模版代码
						UnmapViewOfFile(lpMemory);
						CloseHandle(hAsmMap);
						CloseHandle(hAsmFile);

						FILE *pFile = fopen("_1.txt", "r+");
						int	ch,i=0;
						if (pFile != NULL)
						{
							while((ch = fgetc(pFile) )!= EOF) 
							{
								if (ch == '\n')
									szBuffer[i++]='\r';
								szBuffer[i++]=ch;
							}
							MessageBoxA(g_hWnd, szBuffer, "生成结果", 0);
							fclose(pFile);
						}
						system("del _1.txt");
						SetCurrentDirectory(szOriDirectory); //还原工作目录
						return TRUE;
					}
					else	UnmapViewOfFile(lpMemory);
				}
				else	CloseHandle(hAsmMap);
			}
			else	CloseHandle(hAsmFile);
		}
	}
	return FALSE;
}

//提取数据
void GetListData( )
{
	TCHAR	szBuffer[12] = { 0 };
	DWORD	dwAddress;
	DWORD	dwOldData;
	DWORD	dwNewData;
	for ( DWORD i=0; i < g_dwLineOfNum; i++)
	{
		ListView_GetItemText(g_hListView, i, 0, szBuffer, sizeof(szBuffer) ); 
		swscanf(szBuffer, L"%x", &dwAddress);
		ListView_GetItemText(g_hListView, i, 1, szBuffer, sizeof(szBuffer) );
		swscanf(szBuffer, L"%x", &dwOldData);
		ListView_GetItemText(g_hListView, i, 2, szBuffer, sizeof(szBuffer) );
		swscanf(szBuffer, L"%x", &dwNewData);
		if (g_bOffset)   //统一转换成虚拟地址
			dwAddress	= OffsetToRva( (PIMAGE_DOS_HEADER)g_lpPeHeader, dwAddress);
		
		g_pPatchAddress[i]	= dwAddress;
		g_pOldByte[i]		= (BYTE)dwOldData;
		g_pNewByte[i]		= (BYTE)dwNewData;
	}
}

//读取目标文件PE头
void GetPeHeader(TCHAR szFileName[])
{
	DWORD	dwRead, dwHeaderSize;
	HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if ( hFile == INVALID_HANDLE_VALUE ) 
		return ;

	if (g_lpPeHeader)
		delete g_lpPeHeader;

/////////////////////////////////读取整个PE头/////////////////////////////////////////////
	IMAGE_NT_HEADERS	stNtHeaders;
	SetFilePointer(hFile, 0x3c, 0, FILE_BEGIN);	
	ReadFile(hFile, &dwHeaderSize, 4, &dwRead, NULL);
	SetFilePointer(hFile, dwHeaderSize, 0, FILE_BEGIN);
	ReadFile(hFile, &stNtHeaders, sizeof(IMAGE_NT_HEADERS), &dwRead, NULL);
	dwHeaderSize	= stNtHeaders.OptionalHeader.SizeOfHeaders;
	g_lpPeHeader	= new BYTE[dwHeaderSize];
	SetFilePointer(hFile, 0, 0, FILE_BEGIN);
	ReadFile(hFile, g_lpPeHeader, dwHeaderSize, &dwRead, NULL);
	CloseHandle(hFile);
//////////////////////////////////////////////////////////////////////////////////////////
}


///////////////////////////////////16进制编辑控件实现/////////////////////////////////////////

WNDPROC		g_lpOldProc;   //edit控件的老窗口过程地址

void SuperClass()
{
	WNDCLASSEX	stWC;
	stWC.cbSize	= sizeof(WNDCLASSEX);
	GetClassInfoEx(NULL, TEXT("Edit"), &stWC);
	g_lpOldProc			= stWC.lpfnWndProc;
	stWC.lpfnWndProc	= ProcEdit;
	stWC.hInstance		= hInst;
	stWC.lpszClassName	= TEXT("HexEdit");   //新类名
	RegisterClassEx(&stWC);
}

TCHAR	szHexChar[] = TEXT("0123456789abcdefABCDEF\b");	  //编辑控件允许显示的字符,包括退格键

LONG WINAPI ProcEdit(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)	//新编辑控件窗口过程
{
	int len=lstrlen(szHexChar);
	if (message == WM_CHAR)
	{
		for (int i=0; i < len; i++)
		{
			if (szHexChar[i] == (TCHAR)wParam)
			{
				if (wParam > TEXT('9') )
					wParam &= ~0x20;
				return CallWindowProc(g_lpOldProc, hwnd, message, wParam, lParam);
			}
		}
		return TRUE;
	}

	return CallWindowProc(g_lpOldProc, hwnd, message, wParam, lParam);
}

/////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////文件对话框函数/////////////////////////////////////

static OPENFILENAME ofn ;

void PopFileInitialize (HWND hwnd)
{
     static TCHAR szFilter[] = TEXT ("PE Files (*.exe;*.dll)\0*.exe;*.dll;*.scr;*.fon;*.drv\0")  \
                               TEXT ("All Files (*.*)\0*.*\0\0") ;
     
     ofn.lStructSize       = sizeof (OPENFILENAME) ;
     ofn.hwndOwner         = hwnd ;
     ofn.hInstance         = NULL ;
     ofn.lpstrFilter       = szFilter ;
     ofn.lpstrCustomFilter = NULL ;
     ofn.nMaxCustFilter    = 0 ;
     ofn.nFilterIndex      = 0 ;
     ofn.lpstrFile         = NULL ;          // Set in Open and Close functions
     ofn.nMaxFile          = MAX_PATH ;
     ofn.lpstrFileTitle    = NULL ;          // Set in Open and Close functions
     ofn.nMaxFileTitle     = MAX_PATH ;
     ofn.lpstrInitialDir   = NULL ;
     ofn.lpstrTitle        = NULL ;
     ofn.Flags             = 0 ;             // Set in Open and Close functions
     ofn.nFileOffset       = 0 ;
     ofn.nFileExtension    = 0 ;
     ofn.lpstrDefExt       = NULL; //TEXT ("exe") ;
     ofn.lCustData         = 0L ;
     ofn.lpfnHook          = NULL ;
     ofn.lpTemplateName    = NULL ;
}

BOOL PopFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName)
{
     ofn.hwndOwner         = hwnd ;
     ofn.lpstrFile         = pstrFileName ;
     ofn.lpstrFileTitle    = pstrTitleName ;
     ofn.Flags             = OFN_HIDEREADONLY | OFN_CREATEPROMPT ;
     
     return GetOpenFileName (&ofn) ;
}

BOOL PopFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName)
{
     ofn.hwndOwner         = hwnd ;
     ofn.lpstrFile         = pstrFileName ;
     ofn.lpstrFileTitle    = pstrTitleName ;
     ofn.Flags             = OFN_OVERWRITEPROMPT ;
     
     return GetSaveFileName (&ofn) ;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////


 

 

patc.exe

		.386
		.model flat,stdcall
		option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include		c:\masm32\include\windows.inc

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 导出变量供补丁工具使用
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

PUBLIC	Appendcode_Start	;附加代码起始处	
PUBLIC	Appendcode_End		;附加代码结束处
PUBLIC	Patch_Data		;补丁数据处

		.code
Appendcode_Start LABEL		DWORD

	jmp	_NewEntry

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;重要的函数名,为兼容WIN7 kernelbase.dll,使用LoadLibraryExA函数
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
szLoadLibraryExA db	'LoadLibraryExA',0  
szGetProcAddress db	'GetProcAddress',0

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;补丁功能代码需要的DLL,函数名,字符串等全局变量定义,以下为测试用
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;szUser32	 db	'user32',0
;szMessageBoxA	 db	'MessageBoxA',0
;szCaption	db	'恭喜',0
;szText		db	'代码插入成功!',0


;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;SEH错误Handler
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_SEHHandler proc _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatchertext
	pushad
	mov	esi,_lpExceptionRecord
	assume	esi:ptr EXCEPTIONRECORD
	mov	edi,_lpContext
	assume	edi:ptr CONTEXT
	mov	eax,_lpSEH
	push	[eax+0ch]
	pop	[edi].regEbp
	push	[eax+08]
	pop	[edi].regEip
	push	eax
	pop	[edi].regEsp
	assume	edi:nothing,esi:nothing
	popad
	mov	eax,ExceptionContinueExecution
	ret
_SEHHandler endp


;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;获取kernel32.dll基地址,2种获取方法自行选择
;PS:用PEB获取最好使用LoadLibraryExA函数以兼容WIN7
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;_GetKernel32Base proc uses edi esi ebx _dwEsp
;	call	@F
;	@@:
;	pop	ebx
;	sub	ebx,offset @B
;
;	;安装SEH
;	assume	fs:nothing
;	push	ebp
;	lea	eax, [ebx+offset _safeplace]
;	push	eax
;	lea	eax,[ebx + offset _SEHHandler]
;	push	eax
;	push	fs:[0]
;	mov	fs:[0],esp
;
;	mov	eax,_dwEsp
;	and	eax,0ffff0000h
;
;	.while	eax>=70000000h
;		.if word ptr [eax] == IMAGE_DOS_SIGNATURE
;			mov	edi,eax
;			add	edi,[eax+03ch]
;			.if word ptr [edi] == IMAGE_NT_SIGNATURE
;				jmp	find
;			.endif
;		.endif
;		_safeplace:
;		sub	eax,10000h
;	.endw
;	mov	eax,0
;	find:
;	pop	fs:[0]
;	add	esp,0ch
;	ret
;_GetKernel32Base endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;用PEB获取基址的方法,WIN7中获得的实际是kernelbase.dll的基地址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_GetKernel32Base proc
	local	@dwRet

	pushad
	
	assume fs:nothing
	mov eax,fs:[30h]	;获取PEB所在地址
	mov eax,[eax+0ch]	;获取PEB_LDR_DATA 结构指针
	mov esi,[eax+1ch]	;获取InInitializationOrderModuleList 链表头
				;第一个LDR_MODULE节点InInitializationOrderModuleList成员的指针
	lodsd			;获取双向链表当前节点后继的指针
	mov eax,[eax+8]		;获取kernel32.dll的基地址
	mov @dwRet,eax
	popad
	
	mov eax,@dwRet
	ret
_GetKernel32Base endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;查找导出表获取制定API地址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_GetApi	proc	_hModule,_lpszApi
	local	@dwReturn,@dwSize
	pushad
	
	call	@F
	@@:
	pop	ebx
	sub	ebx,@B
	
	assume	fs:nothing
	push	ebp
	push	[ebx+offset error]
	push	[ebx+offset _SEHHandler]
	push	fs:[0]
	mov	fs:[0],esp
	
	mov	edi,_lpszApi
	mov	ecx,-1
	xor	eax,eax
	cld
	repnz	scasb
	sub	edi,_lpszApi
	mov	@dwSize,edi

	mov	esi,_hModule
	add	esi,[esi+3ch]
	assume	esi:ptr IMAGE_NT_HEADERS
	mov	esi,[esi].OptionalHeader.DataDirectory.VirtualAddress
	add	esi,_hModule
	assume	esi:ptr IMAGE_EXPORT_DIRECTORY

	mov	ebx,[esi].AddressOfNames
	add	ebx,_hModule
	xor	edx,edx
	.while  edx <	[esi].NumberOfNames
		push	esi
		mov	edi,[ebx]
		add	edi,_hModule
		mov	esi,_lpszApi
		mov	ecx,@dwSize
		cld
		repz	cmpsb
		.if	!ecx
			pop	esi
			jmp	@F
		.endif
		next:
		pop	esi
		inc	edx
		add	ebx,4
	.endw
	jmp	error
	@@:
	sub	ebx,[esi].AddressOfNames
	sub	ebx,_hModule
	shr	ebx,1
	add	ebx,[esi].AddressOfNameOrdinals
	add	ebx,_hModule
	movzx	eax,word ptr [ebx]
	shl	eax,2
	add	eax,[esi].AddressOfFunctions
	add	eax,_hModule

	mov	eax,[eax]
	add	eax,_hModule
	mov	@dwReturn,eax
	error:
	pop	fs:[0]
	add	esp,0ch
	assume	esi:nothing
	popad
	mov	eax,@dwReturn
	ret
_GetApi endp

;补丁所需要的函数和全局变量
szCreateThread		db	'CreateThread',0
szGetTickCount		db	'GetTickCount',0
szVirtualProtect	db	'VirtualProtect',0
lpGetTickCount		dd	0
StartCount		dd	0

;以下变量需要补丁程序修正
Patch_Data	LABEL		DWORD
;dwTypeOfPatch	dd	0				;指示补丁类型
dwPatchNum	dd	0				;补丁数量	
dwPatchAddress	dd	16 dup(0)			;补丁地址
byOldData	db	16 dup(0)			;补丁处旧数据和新数据
byNewData	db	16 dup(0) 			

_Thread	proc	_lpVirtualProtect
	local	@lpGetTickCount,@temp,@StartCount,@num

	pushad
	call	@F
	@@:
	pop	ebx
	sub	ebx,@B
	mov	edx,dword ptr [ebx+offset lpGetTickCount]
	mov	@lpGetTickCount,edx
	mov	edx,dword ptr [ebx+offset StartCount]
	mov	@StartCount,edx

	mov	ecx,dword ptr [ebx+offset dwPatchNum]
	mov	@num,ecx
	.while	TRUE
		call	@lpGetTickCount
		sub	eax,@StartCount
		cmp	eax,493e0h		;大于五分钟则超时退出线程
		jg	_exit
		;开始检测补丁地址
		lea	esi,dword ptr [ebx+offset dwPatchAddress]  ;指向补丁地址
		lea	edi,dword ptr [ebx+offset byOldData]	   ;补丁处旧数据
		lea	edx,dword ptr [ebx+offset byNewData]	   ;补丁处新数据
		;检测所有补丁处字节
		mov	ecx,dword ptr [ebx+offset dwPatchNum]
		_peek:
			push	ecx
			mov	ecx,dword ptr [esi]	
			xor	eax,eax
			mov	al,byte  ptr [ecx]		;取补丁处数据
			cmp	al,byte  ptr [edi]		;补丁处是否解码
			jne	_mismatch
			
			;更改页面为读写执行,以确保补丁地址处拥有读写执行权限
			pushad
			lea		eax,@temp
			push	eax
			push	40h
			push	100h
			push	ecx
			call	_lpVirtualProtect
			popad		
			mov	al,byte ptr [edx]	;进行补丁
			mov	byte ptr [ecx],al
			dec	@num
			_mismatch:	
			inc	edi
			inc	edx
			add	esi,4
			pop	ecx
			cmp	@num,0
			je	_exit
		loop	_peek
	.endw
	_exit:
	popad
	ret
_Thread	endp


;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;补丁功能部分
;_dwKernelBase:		kernel32.dll基址
;_lpGetProcAddress:	GetProcAddress地址
;_lpLoadLibraryA	LoadLibraryA或LoadLibraryExA地址
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_Patch	proc	_dwKernelBase,_lpGetProcAddress,_lpLoadLib
		local	@lpVirtualProtect		;local	@hUser32,@lpMessageBoxA
		local	@temp

		pushad
		;以下注释掉的为测试代码
		;lea	edx,dword ptr [ebx+offset szUser32]
		;push	0
		;push	0
		;push	edx
		;call	_lpLoadLib
		;.if	eax
		;	mov	@hUser32,eax
		;	lea	edx,dword ptr [ebx+offset szMessageBoxA]
		;	push	edx
		;	push	eax
		;	call	_lpGetProcAddress
		;	.if	eax
		;		mov	@lpMessageBoxA,eax
		;	.endif
		;.endif
		;.if	@lpMessageBoxA
		;push	MB_YESNO
		;lea	edx,dword ptr [ebx+offset szCaption]
		;push	edx
		;lea	edx,dword ptr [ebx+offset szText]
		;push	edx
		;push	NULL
		;call	@lpMessageBoxA
		;.endif
		lea	edx,dword ptr [ebx+offset szVirtualProtect]
		push	edx
		push	_dwKernelBase
		call	_lpGetProcAddress
		cmp	eax,0
		je	_exit
		mov	@lpVirtualProtect,eax
		lea	edx,@temp
		push	edx
		push	40h
		push	1000h
		lea	edx,dword ptr [ebx+offset lpGetTickCount]
		push	edx
		call	@lpVirtualProtect		;确保全局变量位置可写
		lea	edx,dword ptr [ebx+offset szGetTickCount]
		push	edx
		push	_dwKernelBase
		call	_lpGetProcAddress
		.if	eax
			mov	dword ptr [ebx+offset lpGetTickCount],eax
			call	eax
			mov	dword ptr [ebx+offset StartCount],eax
			lea	edx,dword ptr [ebx+offset szCreateThread]
			push	edx
			push	_dwKernelBase
			call	_lpGetProcAddress
			.if	eax	
				lea		edx,@temp
				push	edx
				push	0
				push	@lpVirtualProtect				;线程参数为VirtualProtect函数的地址
				lea	edx,dword ptr [ebx+offset _Thread]
				push	edx
				push	0
				push	0
				call	eax	;创建监测线程进行补丁
			.endif
		.endif
		_exit:
		popad
		ret
_Patch	endp

_start	proc
	local	@dwKernel32Base
	local	@lpGetProcAddress,@lpLoadLibraryExA
	
	pushad

	call	_GetKernel32Base
	.if	eax
		mov	@dwKernel32Base,eax
		lea	edx,dword ptr [ebx+offset szGetProcAddress]
		push	edx
		push	eax
		call	_GetApi
		mov	@lpGetProcAddress,eax
	.endif
	.if	@lpGetProcAddress
		lea	edx,dword ptr [ebx+offset szLoadLibraryExA]
		push	edx
		push	@dwKernel32Base
		call	@lpGetProcAddress
		.if	eax
			mov	@lpLoadLibraryExA,eax
			push	eax
			push	@lpGetProcAddress
			push	@dwKernel32Base
			call	_Patch
		.endif
	.endif

	popad
	xor	eax,eax
	ret
_start	endp

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;PE文件新入口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_NewEntry:
	call	@F
	@@:
	pop	ebx
	sub	ebx,@B
	call	_start
	;ret
	jmpToStart db 0E9h,0F0h,0FFh,0ffh,0ffh	;需要补丁程序修正
	ret

Appendcode_End LABEL		DWORD
end


 

#ifndef ADDCODE_H
#define ADDCODE_H

#include <windows.h>
#include "RvaToOffset.h"

/////////////////////////下面几种附加代码的补丁应该是能很好的互相兼容(表示我测试过给一文件加了20多个依然正常#_#)///////////////////////////////////

//静态补丁类型
#define ADD_LAST_SECTION 1	//添加代码到最后一个区段	
#define ADD_NEW_SECTION  2  //添加代码到一个新建的区段
#define ADD_TO_HEADER	 3  //添加代码到PE头部
//除了以上3种外还有一种是寻找已存在区段空闲处并插入代码,这种插入PE头的修改更简单,只是比较容易失败,这里就不实现了
//这里再加一种字节补丁,针对一些简单的程序
#define	BYTE_PATCH		 4


///////////////////此文件补丁最多支持16个补丁地址,特别创建一个段用于补丁工具生成补丁时修正变量//////////////////////

#pragma data_seg(".sdata")
DWORD	dwTypeOfPatch = 0 ;					//指示补丁类型
DWORD	dwPatchNum = 0;						//补丁数量
TCHAR	szFileName[MAX_PATH] = { L"load" };

DWORD	dwPatchAddress[16] = { 0 };				//补丁地址
BYTE	byOldData[16] = { 0 };					//补丁处旧数据和新数据
BYTE	byNewData[16] = { 0 };	
#pragma data_seg()
#pragma comment(linker, "/SECTION:.sdata,ERW")

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


/////////////////////从附加代码中引入的变量,需要补丁程序使用和修正//////////////////////

extern "C" DWORD Appendcode_Start;		//附加代码段起始
extern "C" DWORD Appendcode_End;		//附加代码段结束
extern "C" DWORD Patch_Data;			//需要将补丁数据转存到附加代码中

/////////////////////////////////////////////////////////////////////////////////////////

HANDLE	hFile ;//文件句柄
HANDLE	hMap; //映射文件句柄

//////////////////////////////添加代码到最后一个区块/////////////////////////////////////

BOOL AddToLastSection(PBYTE lpMemory, DWORD dwFileSize)
{
	PIMAGE_NT_HEADERS		lpNtHeaders;
	PIMAGE_SECTION_HEADER	lpSectionHeader;
	PIMAGE_SECTION_HEADER	lpLastSectionHeader;
	DWORD					dwNewFileSize;		//最终文件大小
	DWORD					dwFileAlignSize;	//原文件对齐后大小
	DWORD					dwLastSectionAlignSize; //最后区段内存对齐后大小
	DWORD					dwPatchSize;		//补丁大小
	DWORD					dwFileAlign;		//文件对齐粒度
	DWORD					dwSectionAlign;		//内存对齐粒度
	//DWORD					dwLastSectionSize;
	//DWORD					dwPatchStart;	//指定补丁要复制到的文件偏移起始
	PBYTE					lpNewFile;		//最终文件缓存
	DWORD					dwSectionNum;

	lpNtHeaders		= (PIMAGE_NT_HEADERS)( lpMemory + ((PIMAGE_DOS_HEADER)lpMemory)->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(dwFileSize, dwFileAlign);	//求原文件对齐大小
	
	dwPatchSize		= ((DWORD)&Appendcode_End ) - ( (DWORD)&Appendcode_Start );	//获得补丁代码大小

	dwNewFileSize	= Align(dwFileAlignSize + dwPatchSize, dwFileAlign); //获得最终文件对齐后大小
	dwLastSectionAlignSize	= Align(lpLastSectionHeader->Misc.VirtualSize + dwPatchSize, dwSectionAlign); //获得内存中最后区段大小

	lpNewFile		= (PBYTE)VirtualAlloc (NULL, dwNewFileSize, MEM_COMMIT, PAGE_READWRITE);
	if ( !lpNewFile )  //分配内存失败
		return FALSE;

	//复制原文件数据
	memset(lpNewFile, 0, dwNewFileSize);
	memcpy(lpNewFile, lpMemory, dwFileSize);
	//复制完毕,关闭映射文件和句柄 (不关闭后面就无法创建新文件,本来想在AddCode关闭,结果试了抛异常什么的还是没用,只能hMap设置成全局变量然后在这关了,实在不雅观啊‘_’)
	UnmapViewOfFile(lpMemory);
	CloseHandle(hMap);
	CloseHandle(hFile);

	//复制补丁代码前先转储补丁数据
	PBYTE	pBuffer		= (PBYTE)(&Patch_Data);   //指向附加代码补丁数据处
	//(*(DWORD*)pBuffer)	= dwPatchNum; 原本是这句,但是被优化掉后就变成了mov xxx,0  所以对于变量的读写需要特别注意,一不小心就优化掉了,也不想牺牲效率,这里就换成汇编吧虽然不太好看
	_asm
	{
		pushad
		mov	 eax,dwPatchNum
		mov	 ebx,pBuffer
		mov	 dword ptr [ebx], eax
		popad
	}
	memcpy(pBuffer + 4, dwPatchAddress, 16*sizeof(DWORD) );
	pBuffer	+= 4 + 16*sizeof(DWORD);
	memcpy(pBuffer, byOldData, 16);
	memcpy(pBuffer+16, byNewData, 16);

	//复制补丁代码
	memcpy(lpNewFile + dwFileAlignSize, &Appendcode_Start, dwPatchSize); 

	//修正PE头数据
	PIMAGE_NT_HEADERS		lpNewNtHeaders;
	PIMAGE_SECTION_HEADER	lpNewSectionHeader;
	PIMAGE_SECTION_HEADER	lpNewLastSection;
	DWORD*					lpNewEntry;			//指向新入口处
	DWORD					OldEntry;
	
	lpNewNtHeaders		= (PIMAGE_NT_HEADERS)( lpNewFile + ((PIMAGE_DOS_HEADER)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(lpNewFile, lpNewLastSection), dwSectionAlign);//Align(lpNewLastSection->Misc.VirtualSize + dwPatchSize, dwSectionAlign) ;
	
	//修正镜像大小
	lpNewNtHeaders->OptionalHeader.SizeOfImage	= Align(lpNewLastSection->VirtualAddress + lpNewLastSection->Misc.VirtualSize, dwSectionAlign);

	//修正入口地址
	OldEntry	= lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint;
	lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint	= OffsetToRVA( (IMAGE_DOS_HEADER *)lpNewFile, dwFileAlignSize) ;
	
	//修正补丁代码跳回OEP的参数
	lpNewEntry			= (DWORD*)(lpNewFile + dwFileAlignSize + dwPatchSize - 5);
	*lpNewEntry			= OldEntry - (lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint + dwPatchSize - 1); 

	//补丁完毕,写回文件
	HANDLE  hNewFile;
	DWORD	dwRead;
	if (INVALID_HANDLE_VALUE == ( hNewFile = CreateFile (szFileName, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
	{
		VirtualFree(lpNewFile, dwNewFileSize, MEM_RELEASE);
		return FALSE;
	}

	WriteFile(hNewFile, lpNewFile, dwNewFileSize, &dwRead, NULL);
	CloseHandle(hNewFile);

	//释放内存
	VirtualFree(lpNewFile, dwNewFileSize, MEM_RELEASE);
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////



////////////////////////////////新建一个区段并插入代码///////////////////////////////////
//如果节表没有多余的空间

BOOL AddToNewSection(PBYTE lpMemory, DWORD dwFileSize)
{
	PIMAGE_NT_HEADERS		lpNtHeaders;
	PIMAGE_SECTION_HEADER	lpSectionHeader;
	PIMAGE_SECTION_HEADER	lpLastSectionHeader;
	DWORD					dwNewFileSize;		//最终文件大小
	DWORD					dwPatchSize;		//补丁大小
	DWORD					dwPatchSectionSize; //内存中补丁区块的大小
	DWORD					dwPatchFileSize;	//补丁区段文件中大小
	DWORD					dwFileAlign;		//文件对齐粒度
	DWORD					dwSectionAlign;		//内存对齐粒度
	PBYTE					lpNewFile;			//最终文件缓存
	DWORD					dwSectionNum;
	DWORD					dwNewHeaderSize;	//新头部大小
	DWORD					dwOldHeaderSize;	//老头部大小
	DWORD					dwSectionSize;		//内存中所有区块总大小
	BOOL					bChange = FALSE;	//指示新节表的加入是否影响到文件头大小,如有影响,则需修正各区段偏移

	lpNtHeaders			= (PIMAGE_NT_HEADERS)(lpMemory + ((PIMAGE_DOS_HEADER)lpMemory)->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);  //内存中区段总大小

	//获得补丁相关数据
	dwPatchSize			= ((DWORD)&Appendcode_End ) - ( (DWORD)&Appendcode_Start );	//获得补丁代码大小
	dwPatchSectionSize	= Align(dwPatchSize, dwSectionAlign);	//内存中新区段对齐大小
	dwPatchFileSize		= Align(dwPatchSize, dwFileAlign);		//文件中新区段对齐大小
	
	//头部是否能增加一个节表
	DWORD	dwValidSize;	//头部当前有效大小
	dwValidSize			= sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER)*(dwSectionNum + 1) + ((PIMAGE_DOS_HEADER)lpMemory)->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(dwFileSize, dwFileAlign) + dwPatchFileSize, dwFileAlign);
	lpNewFile			= (PBYTE)VirtualAlloc (NULL, dwNewFileSize, MEM_COMMIT, PAGE_READWRITE);

	if ( !lpNewFile )  //分配内存失败
		return FALSE;

	//复制原文件数据
	memset(lpNewFile, 0, dwNewFileSize);
	memcpy(lpNewFile, lpMemory, dwValidSize);	//头部数据复制

	//区段数据复制
	DWORD	dwSize		= lpLastSectionHeader->PointerToRawData + lpLastSectionHeader->SizeOfRawData - dwOldHeaderSize; //文件中所有区段总大小
	memcpy(lpNewFile + dwNewHeaderSize, lpMemory + lpSectionHeader->PointerToRawData, dwSize);

	//复制补丁代码前先转储补丁数据
	PBYTE	pBuffer		= (PBYTE)(&Patch_Data);   //指向附加代码补丁数据处
	_asm
	{
		pushad
		mov	 eax,dwPatchNum
		mov	 ebx,pBuffer
		mov	 dword ptr [ebx], eax
		popad
	}
	memcpy(pBuffer + 4, dwPatchAddress, 16*sizeof(DWORD) );
	pBuffer	+= 4 + 16*sizeof(DWORD);
	memcpy(pBuffer, byOldData, 16);
	memcpy(pBuffer+16, byNewData, 16);

	//补丁数据复制
	memcpy(lpNewFile + dwNewHeaderSize + dwSize, &Appendcode_Start, dwPatchSize);

	//复制完毕,关闭映射文件和句柄
	UnmapViewOfFile(lpMemory);
	CloseHandle(hMap);
	CloseHandle(hFile);

	//开始修正PE头
	PIMAGE_NT_HEADERS		lpNewNtHeaders;
	PIMAGE_SECTION_HEADER	lpNewSectionHeader;
	PIMAGE_SECTION_HEADER	lpNewLastSection;
	DWORD*					lpNewEntry;			//指向新入口处
	DWORD					OldEntry;

	lpNewNtHeaders		= (PIMAGE_NT_HEADERS)( lpNewFile + ((PIMAGE_DOS_HEADER)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*)(lpNewFile + dwNewHeaderSize + dwSize + dwPatchSize - 5);
	*lpNewEntry			= OldEntry - (lpNewNtHeaders->OptionalHeader.AddressOfEntryPoint + dwPatchSize - 1); 

	//补丁完毕,写回文件
	HANDLE  hNewFile;
	DWORD	dwRead;
	if (INVALID_HANDLE_VALUE == ( hNewFile = CreateFile (szFileName, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
	{
		VirtualFree(lpNewFile, dwNewFileSize, MEM_RELEASE);
		return FALSE;
	}

	WriteFile(hNewFile, lpNewFile, dwNewFileSize, &dwRead, NULL);
	CloseHandle(hNewFile);

	//释放内存
	VirtualFree(lpNewFile, dwNewFileSize, MEM_RELEASE);
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////



///////////////////////////添加代码到PE头部///////////////////////////////////////////////
//由于PE头部内存中最大为0x1000字节,所以此方法可能会添加失败

BOOL AddToHeaderSection(PBYTE lpMemory, DWORD dwFileSize)
{
	PIMAGE_NT_HEADERS		lpNtHeaders;
	PIMAGE_SECTION_HEADER	lpSectionHeader;
	PIMAGE_SECTION_HEADER	lpLastSectionHeader;
	DWORD					dwNewFileSize;		//最终文件大小
	DWORD					dwPatchSize;		//补丁大小
	DWORD					dwFileAlign;		//文件对齐粒度
	DWORD					dwSectionAlign;		//内存对齐粒度
	PBYTE					lpNewFile;			//最终文件缓存
	DWORD					dwSectionNum;
	DWORD					dwPatchOffset;		//补丁代码复制到文件的偏移,也即老头部有效数据的大小
	DWORD					dwNewHeaderSize;	//新头部大小
	DWORD					dwOldHeaderSize;	//老头部大小
	DWORD					dwSectionSize;		//所有区块总大小


	lpNtHeaders		= (PIMAGE_NT_HEADERS)( lpMemory + ((PIMAGE_DOS_HEADER)lpMemory)->e_lfanew );
	lpSectionHeader	= (PIMAGE_SECTION_HEADER)(lpNtHeaders + 1);
	dwSectionNum	= lpNtHeaders->FileHeader.NumberOfSections;
	lpLastSectionHeader	= lpSectionHeader + dwSectionNum - 1;      //获得最后一个区块的节表
	dwFileAlign		= lpNtHeaders->OptionalHeader.FileAlignment;
	dwSectionAlign	= lpNtHeaders->OptionalHeader.SectionAlignment;
	dwPatchSize		= ((DWORD)&Appendcode_End ) - ( (DWORD)&Appendcode_Start );	//获得补丁代码大小
	dwOldHeaderSize	= lpSectionHeader->PointerToRawData;					
	
	//dwPatchOffset	= sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER)*(dwSectionNum + 1) + ((PIMAGE_DOS_HEADER)lpMemory)->e_lfanew;  //老PE头有效数据大小,附加代码从此放置
	//原本是使用上面这种方式求有效大小,但考虑到如果PE已有补丁情况下的兼容,所有直接从头最后面往前搜索以取得大小
	
	PBYTE					lpTemp = lpMemory + lpSectionHeader->PointerToRawData - 1;
	DWORD					dwSize = 0;
	while(*lpTemp == 0)  { lpTemp--; dwSize++; }	//获得可填充数据的大小
	dwSize		   -= sizeof(IMAGE_SECTION_HEADER);	//因为可能有个全零的节表,所以减去 
	dwPatchOffset	= lpSectionHeader->PointerToRawData - dwSize ; //老PE头有效数据大小,附加代码从此放置


	dwNewHeaderSize	= Align(dwPatchOffset + dwPatchSize, 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);  

	lpNewFile		= (PBYTE)VirtualAlloc(NULL, dwNewFileSize, MEM_COMMIT, PAGE_READWRITE);
	if (!lpNewFile) //分配内存失败
		return FALSE;

	//复制原文件数据
	memset(lpNewFile, 0, dwNewFileSize);
	memcpy(lpNewFile, lpMemory, dwPatchOffset);		//复制原始PE头
	memcpy(lpNewFile + dwNewHeaderSize, lpMemory + dwOldHeaderSize, dwSectionSize);		//复制区块数据 

	//复制完毕,关闭映射文件和句柄 (不关闭后面就无法创建新文件,本来想在AddCode关闭,结果试了抛异常什么的还是没用,只能hMap设置成全局变量然后在这关了,实在不雅观啊‘_’)
	UnmapViewOfFile(lpMemory);
	CloseHandle(hMap);
	CloseHandle(hFile);

	//复制补丁代码前先转储补丁数据
	PBYTE	pBuffer		= (PBYTE)(&Patch_Data);   //指向附加代码补丁数据处
	_asm
	{
		pushad
		mov	 eax,dwPatchNum
		mov	 ebx,pBuffer
		mov	 dword ptr [ebx], eax
		popad
	}
	memcpy(pBuffer + 4, dwPatchAddress, 16*sizeof(DWORD) );
	pBuffer	+= 4 + 16*sizeof(DWORD);
	memcpy(pBuffer, byOldData, 16);
	memcpy(pBuffer+16, byNewData, 16);

	//复制补丁代码
	memcpy(lpNewFile + dwPatchOffset, &Appendcode_Start, dwPatchSize);

	//开始对头部数据进行修复
	PIMAGE_NT_HEADERS		lpNewNtHeaders;
	PIMAGE_SECTION_HEADER	lpNewSectionHeader;
	lpNewNtHeaders		= (PIMAGE_NT_HEADERS)(lpNewFile + ((PIMAGE_DOS_HEADER)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*)(lpNewFile + dwPatchOffset + dwPatchSize - 5);   //指向要修正的参数
	*lpNewEntry	= dwOldEntry - (dwPatchOffset + dwPatchSize - 1); 

	//补丁完毕,写回文件
	HANDLE  hNewFile;
	DWORD	dwRead;
	if (INVALID_HANDLE_VALUE == ( hNewFile = CreateFile (szFileName, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
	{	
		VirtualFree(lpNewFile, dwNewFileSize, MEM_RELEASE);
		return FALSE;
	}

	WriteFile(hNewFile, lpNewFile, dwNewFileSize, &dwRead, NULL);
	CloseHandle(hNewFile);

	//释放内存
	VirtualFree(lpNewFile, dwNewFileSize, MEM_RELEASE);
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////////

BOOL BytePatch(PBYTE lpMemory,DWORD dwFileSize)
{
	//这里就不需要原来的句柄了,都关掉(其实是因为设置了只读属性,但又不好 加写属性,只好这样,实在不雅观啊啊啊+..+)
	UnmapViewOfFile(lpMemory);
	CloseHandle(hMap);
	CloseHandle(hFile);

	HANDLE  hNewFile, hNewMap;
	PBYTE	lpNewMemory;
	DWORD	num=0;		//记录成功补丁的次数
	DWORD	dwNum;
	if (INVALID_HANDLE_VALUE == ( hNewFile = CreateFile (szFileName, GENERIC_READ | GENERIC_WRITE , FILE_SHARE_READ , NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL) ) )
		return FALSE;
	//开始补丁
	hNewMap	= CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
	if (hMap)
	{
		lpNewMemory	= (BYTE *)MapViewOfFile (hNewMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
		if (lpNewMemory)
		{
			PBYTE   pData=0;
			_asm     //也没想到什么好办法,只有在用到dwPatchNum的地方都用内联汇编实现了
			{
				pushad
				mov	 eax,dwPatchNum
				mov	 dwNum, eax
				popad
			}
			for (DWORD i = 0; i < dwNum; i++)
			{
				pData = (PBYTE)VirtualAddressToPointer( (PIMAGE_DOS_HEADER)lpNewMemory, dwPatchAddress[i]);   //将虚拟地址转换成相对的文件指针
				if ( *pData == byOldData[i])
				{
					*pData = byNewData[i];
					num++;
				}
			}
		}
	}
	UnmapViewOfFile(lpNewMemory);
	CloseHandle(hNewMap);
	CloseHandle(hNewFile);
	if (num != dwNum)   //与需要补丁的数量比较
	{
		MessageBox (GetActiveWindow() , TEXT("补丁数不一致,请检查补丁地址"), NULL, MB_OK);
		return FALSE;
	}
	return TRUE;
}


////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////
//由于下面那个switch跳转会因为变量为常量而被优化掉,所以这里使用#pragma指示来取消优化
#pragma optimize("",off)
BOOL AddCode( )
{
	DWORD	dwFileSize;	//文件大小
	PBYTE	lpMemory;	//内存映射指针
	
	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)
				{
					//使用指定方法打补丁
					switch(dwTypeOfPatch)
					{
						case ADD_LAST_SECTION:
							if (!AddToLastSection(lpMemory, dwFileSize) )
								return FALSE;
							break;

						case ADD_NEW_SECTION:
							if (!AddToNewSection(lpMemory, dwFileSize) )
								return FALSE;
							break;

						case ADD_TO_HEADER:
							if (!AddToHeaderSection(lpMemory, dwFileSize) )
								return FALSE;
							break;

						case BYTE_PATCH:
							if (!BytePatch(lpMemory, dwFileSize))
								return FALSE;
							break;

					}
					
					return TRUE;
				}
				else
					MessageBox (GetActiveWindow() , TEXT("目标文件打开失败"), NULL, MB_OK);
			}
			else
				MessageBox ( GetActiveWindow() , TEXT("目标文件打开失败"), NULL, MB_OK);
		}
	}
	else
		MessageBox (GetActiveWindow() , TEXT("目标文件打开失败"), NULL, MB_OK);
	
	

	return FALSE;
}
//函数结束,恢复优化
#pragma optimize("",on)

#endif 


 

#include <windows.h>
#include "resource.h"
#include "AddCode.h"

#pragma comment(linker, "/SECTION:.text,ERW")    //添加写属性,后面要修正参数

///////////////////////////设置对图标////////////////////////////////
inline void chSETDLGICONS(HWND hWnd, int idi) {
   SendMessage(hWnd, WM_SETICON, ICON_BIG,  (LPARAM) 
      LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
         MAKEINTRESOURCE(idi)));
   SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) 
      LoadIcon((HINSTANCE) GetWindowLongPtr(hWnd, GWLP_HINSTANCE), 
      MAKEINTRESOURCE(idi)));
}

BOOL CALLBACK DialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);	//主窗口过程

BOOL IsFileModified( );  //检测文件是否已补丁或损坏
DWORD CRC32(BYTE* ptr,DWORD Size);  //获取目标文件CRC32校验值,和本补丁程序存储的校验值进行对比(校验值为PE头前4个字节)

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{

	DialogBoxParam (hInstance, MAKEINTRESOURCE(IDD_DIALOG), NULL, DialogProc, 0);

    return 0 ;
}

BOOL CALLBACK DialogProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	static	BOOL	bBackup = TRUE;
	static  TCHAR	szBakFileName[MAX_PATH] ;  

	switch (message)
	{
		case WM_INITDIALOG:
			chSETDLGICONS(hDlg, IDI_ICON1);
			CheckDlgButton(hDlg, IDC_BACKUP, BST_CHECKED);
			return TRUE;

		case WM_PAINT:
			return FALSE;

		case WM_COMMAND:
			switch (LOWORD (wParam))
			{
				case IDC_BACKUP:	//备份选项
					if(SendDlgItemMessage(hDlg, IDC_BACKUP, BM_GETCHECK, 0, 0) == BST_CHECKED)
						bBackup	= TRUE;
					else
						bBackup = FALSE;
					break;

				case IDOK:
					if ( !IsFileModified() )
					{
						MessageBox(hDlg, TEXT("已补丁或文件损坏,放弃"), TEXT("提示"), 0);
						break;
					}
					if (bBackup )   //进行文件备份
					{
						lstrcpy(szBakFileName,szFileName);
						lstrcat(szBakFileName,TEXT(".bak") );
						CopyFile(szFileName,szBakFileName,TRUE);
					}
					if (AddCode( ) )	//为目标PE打补丁(给PE添加代码)
						MessageBox(hDlg, TEXT("补丁成功"), TEXT("提示"), 0);
					else
						MessageBox(hDlg, TEXT("补丁失败"), TEXT("提示"), 0);
					break;
				
				case IDCANCEL:
					EndDialog(hDlg, 0);
					break;
			}
			return TRUE;
	}

	return FALSE;
}


////////////////////////////////////////////////////////////////
// 打开文件判断CRC32值是否正确
//

BOOL IsFileModified()
{
	PIMAGE_DOS_HEADER	    pDosHeader=NULL;
    PIMAGE_NT_HEADERS       pNtHeader=NULL;
    PIMAGE_SECTION_HEADER   pSecHeader=NULL;

	DWORD fileSize,OriginalCRC32,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];     // 申请内存,也可用VirtualAlloc等函数申请内存
	ReadFile(hFile,pBuffer, fileSize, &NumberOfBytesRW, NULL);//读取文件内容
	CloseHandle(hFile);  //关闭文件

	pDosHeader=(PIMAGE_DOS_HEADER)pBuffer;

///////////////定位到本文件PE头前4个字节读取目标CRC32的值/////////////////////////////
	TCHAR	szMyName[MAX_PATH];
	DWORD	dwRead;
	GetModuleFileName(NULL, szMyName,MAX_PATH);
	HANDLE hMyFile = CreateFile(szMyName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if ( hMyFile == INVALID_HANDLE_VALUE ) 
	{
		delete pBuffer;
		return FALSE;
	}

	//定位到PE头前4个字节处并读取
	SetFilePointer(hMyFile, 0x3c, 0, FILE_BEGIN);	
	ReadFile(hMyFile, &OriginalCRC32, 4, &dwRead, NULL);
	SetFilePointer(hMyFile, OriginalCRC32-4, 0, FILE_BEGIN);
	ReadFile(hMyFile, &OriginalCRC32, 4, &dwRead, NULL); 	
	CloseHandle(hMyFile);
//////////////////////////////////////////////////////////////////////////////////////////

	fileSize=fileSize-DWORD(pDosHeader->e_lfanew);//将PE文件头前那部分数据去除

	if (CRC32((BYTE*)(pBuffer+pDosHeader->e_lfanew),fileSize) == OriginalCRC32 )
	{
		delete pBuffer;
		return TRUE;
	}
	else
	{
		delete pBuffer;
		return FALSE;
	}

}
////////////////////////////////////////////////////////////////
// 计算字符串的CRC32值
// 参数:欲计算CRC32值字符串的首地址和大小
// 返回值: 返回CRC32值

DWORD CRC32(BYTE* ptr,DWORD Size)
{

	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(Size--)
	{
		crcTmp2 = ((crcTmp2>>8) & 0x00FFFFFF) ^ crcTable[ (crcTmp2^(*ptr)) & 0xFF ];
		ptr++;
	}
		
	return (crcTmp2^0xFFFFFFFF);
}


loader.exe

#include <windows.h>
#include "resource.h"

//动态补丁类型
#define SLEEP_PATCH 1	
#define DEBUG_PATCH 2

//动态补丁,最多支持16个补丁地址,特别创建一个段用于补丁工具生成补丁时修正变量

#pragma data_seg(".sdata")
DWORD	dwTypeOfPatch = 0;					//指示补丁类型
DWORD	dwPatchNum  = 0 ;						//补丁数量
TCHAR	szFileName[MAX_PATH] = { 0 };

DWORD	dwPatchAddress[16] = { 0 };				//补丁地址
BYTE	byOldData[16] = { 0 };					//补丁处旧数据和新数据
BYTE	byNewData[16] = { 0 };	

#pragma data_seg()
#pragma comment(linker, "/SECTION:.sdata,ERW")

//时间记录
DWORD	dwTickCount;

///////////////////////////类型1,利用进程读写机制不断测试补丁位置///////////////////////////
////////////////好处是能无视一些壳,弊端是仅仅是停一段时间检测,可能失败,不稳定//////////////
void Patch1()
{
	STARTUPINFO     	si;
	PROCESS_INFORMATION pi;
	
	BYTE      ReadBuffer = 0;
	BOOL      bContinueRun=TRUE;
	
	ZeroMemory(&si, sizeof(STARTUPINFO)) ;
	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)) ;

    if( !CreateProcess(szFileName, NULL, NULL,  NULL,  FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi) ) 
	{
        MessageBox(NULL, TEXT("请确定补丁是否在目标文件目录下."), TEXT("提示"), MB_OK); 
        return ; 
    } 
	
	DWORD	i, dwCount=0;
	DWORD	dwCurrentTick, OldPro;
	while (bContinueRun) {
		
		ResumeThread(pi.hThread); 
		Sleep(10);
		SuspendThread(pi.hThread);
		for (i=0; dwPatchAddress[i] ; i++)
		{
			ReadProcessMemory(pi.hProcess, (LPVOID)dwPatchAddress[i], &ReadBuffer, 1, NULL);
			
			if( byOldData[i] == ReadBuffer )    //判断地址数据是否正确
			{	
				VirtualProtectEx(pi.hProcess, (LPVOID)dwPatchAddress[i], 1, PAGE_EXECUTE_READWRITE, &OldPro);
				WriteProcessMemory(pi.hProcess, (LPVOID)dwPatchAddress[i],&byNewData[i], 1,NULL);
				dwCount++;
				ResumeThread(pi.hThread); 
			}	
		}
		_asm
		{
			mov	ebx,dwPatchNum
			cmp	dwCount,ebx
			jne	_exit
			mov	bContinueRun,0
			_exit:
		}
		//if (dwCount == dwPatchNum)  //所有补丁完毕,退出循环
		//	bContinueRun	= FALSE;
		dwCurrentTick	= GetTickCount();
		if ( (dwCurrentTick - dwTickCount) > 6*1000)  //如果10秒还没退出,超时
		{
			MessageBox( NULL, TEXT("超时,补丁失败,指示的补丁数据可能有错误"), 0, 0);
			TerminateProcess(pi.hProcess, 0);
			bContinueRun	= FALSE; ;
		}
	}

    CloseHandle(pi.hProcess); 
    CloseHandle(pi.hThread); 
}

/////////////////////////////////////////利用调试寄存器打补丁///////////////////////////////////////////////////////
/////////////打此类补丁应在补丁地址第一个地址填上希望中断的地址以确保所有地址数据已解码,以保证补丁正确性///////////
void Patch2()
{
	STARTUPINFO				si ;
	PROCESS_INFORMATION		pi ;
	ZeroMemory(&si, sizeof(STARTUPINFO)) ;
	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)) ;
	si.cb = sizeof(STARTUPINFO) ;
	
	BOOL	WhileDoFlag=TRUE;
	BYTE    ReadBuffer=0;
	
	
	if( !CreateProcess(szFileName, NULL, NULL, NULL, FALSE, DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi) ) 
	{
        MessageBox(NULL, TEXT("请确定补丁是否在目标文件目录下."), TEXT("提示"), MB_OK);
        return ; 
    } 
	
	DEBUG_EVENT		DBEvent ;
	CONTEXT			Regs ;
	DWORD			dwSSCnt , dwNum;
	
	dwSSCnt = 0 ;

	Regs.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS ;

	//使进程在Single Step模式下运行,每执行一条指令就会给调试进程发送EXCEPTION_SINGLE_STEP
	//需要注意的是,收到这个消息后,如果还想继续让程序Single Step下去,是需要重新设置一次SF位的
	GetThreadContext(pi.hThread,&Regs);
	Regs.EFlags|=0x100;
	SetThreadContext(pi.hThread,&Regs);

	ResumeThread(pi.hThread);

	DWORD	i,OldPro,dwCount=0;
	while (WhileDoFlag) {
		WaitForDebugEvent (&DBEvent, INFINITE);
		switch (DBEvent.dwDebugEventCode)
		{
		case	EXCEPTION_DEBUG_EVENT:

			switch (DBEvent.u.Exception.ExceptionRecord.ExceptionCode)
			{
			case	EXCEPTION_SINGLE_STEP :
				{
					++dwSSCnt ;
					if (dwSSCnt == 1)
					{   
						//当收到第一个EXCEPTION_SINGLE_STEP异常信号,表示中断在程序的第一条指令,即入口点
						//把Dr0设置成程序的入口地址
						
						GetThreadContext(pi.hThread,&Regs);
						
						Regs.Dr0=Regs.Eax;
						Regs.Dr7=0x101;
						
						SetThreadContext(pi.hThread,&Regs);
						
					}
					else if (dwSSCnt == 2)
					{
						//第二次中断在起先设置的补丁点,设置硬件断点,后面所有的地址均在运行到此处时补丁

						GetThreadContext(pi.hThread, &Regs) ;
						Regs.Dr0 = dwPatchAddress[0];
						Regs.Dr7 = 0x101 ;
						SetThreadContext(pi.hThread, &Regs) ;
					}
					else if (dwSSCnt == 3)
					{
						//第三次中断,己到补丁指示的地址
						GetThreadContext(pi.hThread, &Regs) ;
						Regs.Dr0 = Regs.Dr7 = 0 ;
					
						//从下标1开始才是需要打补丁的地址
						for (i=1; dwPatchAddress[i] ; i++)
						{
							ReadProcessMemory(pi.hProcess, (LPVOID)dwPatchAddress[i], &ReadBuffer, 1, NULL);
			
							if( byOldData[i-1] == ReadBuffer )    //判断地址数据是否正确
							{	
								VirtualProtectEx(pi.hProcess, (LPVOID)dwPatchAddress[i], 1, PAGE_EXECUTE_READWRITE, &OldPro);
								WriteProcessMemory(pi.hProcess, (LPVOID)dwPatchAddress[i],&byNewData[i-1], 1,NULL); 
								dwCount++;
							}	
						}
						_asm
						{
							push eax
							mov	eax,dwPatchNum
							mov	dwNum,eax
							pop eax
						}
						if (dwPatchNum != dwNum)   //补丁成功数与需要补丁数不同
							MessageBox(NULL, TEXT("补丁字节数不一致,补丁可能失败"), TEXT("提示"), 0);
						
						SetThreadContext(pi.hThread, &Regs) ;
					}
					break ;
				}				 
			}
			break ;

		case	EXIT_PROCESS_DEBUG_EVENT :
				WhileDoFlag=FALSE;
				break ;
		}
		
		ContinueDebugEvent(DBEvent.dwProcessId, DBEvent.dwThreadId, DBG_CONTINUE) ;
	}
	
	CloseHandle(pi.hProcess) ;
	CloseHandle(pi.hThread)  ;
}

//////////////////////////////提升当前进程权限(调试权限)///////////////////////////////////
BOOL EnablePrivilege(PCTSTR szPrivilege, BOOL fEnable) {

   BOOL fOk = FALSE;    
   HANDLE hToken;

   if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, 
      &hToken)) {

      TOKEN_PRIVILEGES tp;
      tp.PrivilegeCount = 1;
      LookupPrivilegeValue(NULL, szPrivilege, &tp.Privileges[0].Luid);
      tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
      AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
      fOk = (GetLastError() == ERROR_SUCCESS);

      CloseHandle(hToken);
   }
   return(fOk);
}

//取消优化以免跳转无效
#pragma optimize("",off)
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow)
{
	dwTickCount	= GetTickCount();
	EnablePrivilege(SE_DEBUG_NAME, TRUE);
	switch(dwTypeOfPatch)
	{
		case SLEEP_PATCH:
			Patch1();
			break;

		case DEBUG_PATCH:
			Patch2();
			break;
	}
	EnablePrivilege(SE_DEBUG_NAME, FALSE);

    return 0 ;
}
#pragma optimize("",on)


/*
void Patch2()
{
	STARTUPINFO				si ;
	PROCESS_INFORMATION		pi ;
	ZeroMemory(&si, sizeof(STARTUPINFO)) ;
	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)) ;
	si.cb = sizeof(STARTUPINFO) ;
	
	BOOL	WhileDoFlag=TRUE;
	BYTE    ReadBuffer={0};
	BYTE    dwINT3code[1]={0xCC};
	BYTE    dwOldbyte[1]={0};

	HANDLE	hFile;
	DWORD	dwEntry, dwRead;
	IMAGE_NT_HEADERS stNtHeaders;
	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)
		{
			SetFilePointer(hFile, 0x3c, 0, FILE_BEGIN);
			ReadFile(hFile, &dwEntry, 4, &dwRead, NULL);
			SetFilePointer(hFile, dwEntry, 0, FILE_BEGIN);
			ReadFile(hFile, &stNtHeaders, sizeof(IMAGE_NT_HEADERS), &dwRead, NULL);
			dwEntry	= stNtHeaders.OptionalHeader.AddressOfEntryPoint;
			dwEntry+= stNtHeaders.OptionalHeader.ImageBase;
			CloseHandle(hFile);
		}
	}
	else
	{
        MessageBox(NULL, TEXT("请确定补丁是否在目标文件目录下."), NULL, MB_OK);
        return ; 
	}

	if( !CreateProcess(SZFILENAME, NULL, NULL, NULL, FALSE, DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &si, &pi) ) 
	{
        MessageBox(NULL, TEXT("请确定补丁是否在目标文件目录下."), NULL, MB_OK);
        return ; 
    } 
	
	DEBUG_EVENT		DBEvent ;
	CONTEXT			Regs ;
	DWORD			dwState,Oldpp;


	Regs.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS ;
	
	DWORD	i, dwCount=0;
	DWORD	dwCurrentTick, OldPro;

	while (WhileDoFlag) {
		WaitForDebugEvent (&DBEvent, INFINITE);
		dwState = DBG_EXCEPTION_NOT_HANDLED ;
		switch (DBEvent.dwDebugEventCode)
		{
			case CREATE_PROCESS_DEBUG_EVENT:
				//如果进程开始,则将断点地址的代码改为INT3中断,同时备份原机器码
				ReadProcessMemory(pi.hProcess, (LPCVOID)(dwEntry), &dwOldbyte, sizeof(dwOldbyte), NULL) ;
				VirtualProtectEx(pi.hProcess, (LPVOID)dwEntry, 1, PAGE_EXECUTE_READWRITE, &Oldpp);
				WriteProcessMemory(pi.hProcess, (LPVOID)dwEntry,&dwINT3code, 1,NULL);	//打补丁
				dwState = DBG_CONTINUE ;
				break;			
				
			case	EXIT_PROCESS_DEBUG_EVENT :
				WhileDoFlag=FALSE;
				break ;
			
			case	EXCEPTION_DEBUG_EVENT:
				switch (DBEvent.u.Exception.ExceptionRecord.ExceptionCode)
				{
					case	EXCEPTION_BREAKPOINT:
					{
						GetThreadContext(pi.hThread, &Regs) ;
						if(Regs.Eip==dwEntry+1){
							//中断触发异常事件,恢复原机器码,并读出数据
							Regs.Eip--;
							WriteProcessMemory(pi.hProcess, (LPVOID)dwEntry,&dwOldbyte, 1,NULL);
						
							for (i=0; dwPatchAddress[i] ; i++)
							{
								ReadProcessMemory(pi.hProcess, (LPVOID)dwPatchAddress[i], &ReadBuffer, 1, NULL);
			
								if( byOldData[i] == ReadBuffer )    //判断地址数据是否正确
								{	
									VirtualProtectEx(pi.hProcess, (LPVOID)dwPatchAddress[i], 1, PAGE_EXECUTE_READWRITE, &OldPro);
									WriteProcessMemory(pi.hProcess, (LPVOID)dwPatchAddress[i],&byNewData[i], 1,NULL);
									dwCount++;
									ResumeThread(pi.hThread); 
								}	
							}
					
							SetThreadContext(pi.hThread, &Regs) ;
						}
						dwState = DBG_CONTINUE ;
						break;
					}
				}
				break;
		}
		
		ContinueDebugEvent(DBEvent.dwProcessId, DBEvent.dwThreadId, dwState) ;
	} //.end while
	
	CloseHandle(pi.hProcess) ;
	CloseHandle(pi.hThread)  ;
}*/


 

YAXBPC1.6.1 老朽痴拙汉化版(一款 xdelta3 的补丁生成器 GUI) 软件推荐:xdelta、xdelta3 “非常实用”用的二进制diff 与patch 工具,此工具只需母盘和很小的xdelta 补丁文件,即可方便的生成和母盘差异不大的目标文件,在大文件领域非常有用, 如大型游戏到ROM,大型软件文件、镜像 xdelta3用法: 1. 生成补丁:xdelta3 -v -e -s [母盘文件] [目标文件] 生成的差异文件[补丁文件] 2. 应用补丁:xdelta3 -v -d -s [母盘文件] 生成的差异文件[补丁文件] [目标文件] 3. 下载地址:http://xdelta.googlecode.com/files/xdelta3.0u.x86-32.exe 4. 官方主页:http://xdelta.org/ 5. 其中的-e -s 和-d -s 是必备参数,-v 输出详细处理信息;每个文件之间都有一个空隔着。 xdelta 用法: 1. 生成补丁:xdelta delta [母盘文件] [目标文件] 生成的差异文件[补丁文件] 2. 应用补丁:xdelta patch 生成的差异文件[补丁文件] [母盘文件] [目标文件] 3. 下载地址:http://evanjones.ca/software/xdelta.exe 4. 官方主页:http://evanjones.ca/ 5. 其中delta是生成补丁文件必备参数patch 是应用补丁的必备参数;-m用来设置内存缓冲大小 特别说明: 1. Xdelta的命令简单、方便处理小于2G的文件;Xdelta3命令复杂/功能强大, 2. 能方便的处理大于2G 的文件,在大文件横行的当今也不可或缺,以上仅给出了最简单的补丁生成和应用命令. 3. 更多的命令和参数选项您可以使用-help 命令自己查看。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值