VC通过读写内存实现Win7自动扫雷程序

2012-02-24 22:47:48|?次阅读|上传:wustguangh【已有?条评论】发表评论

关键词:C/C++, 游戏, MFC|来源:唯设编程网

3、扫雷按钮响应事件:

void CMemSearchDlg::OnBnClickedButton4()
{
	//开始自动扫雷,使此按钮无效,以免自动点击到此按钮上,导致可能的循环发生
	GetDlgItem(IDC_BTN_AUTO)->EnableWindow(FALSE);

	//DWORD m_pID;	//进程的ID
	//HANDLE g_hProcess;	//进程的句柄

	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,m_pID);
	if( hProcessSnap == INVALID_HANDLE_VALUE )
	{
		AfxMessageBox(_T("试图枚举扫雷进程模块时出错!"));
		return ;
	}

	//重定向一级基址
	const int RVA_top = 0x868B4;
	int MineBaseAddr=0;
        //注意名称要小写,_tcscmp是cs的
	TCHAR MineName[] = _T("minesweeper.exe");	
	MODULEENTRY32 me;
	me.dwSize=sizeof(MODULEENTRY32);
	BOOL bSuccess = Module32First(hProcessSnap,&me);
	if(!bSuccess)
	{
		AfxMessageBox(_T("试图枚举扫雷进程模块时出错!"));
		goto RTN;
	}
	do
	{
		if(_tcscmp(me.szModule,MineName)==0)
		{
			MineBaseAddr = (int)me.modBaseAddr;
			break;
		}
	}while(Module32Next(hProcessSnap,&me));

	if(MineBaseAddr==0)
	{
		AfxMessageBox(_T("目标进程中无法找到模块minesweeper.exe"));
		goto RTN;
	}

	int VA_top = MineBaseAddr + RVA_top;

	//开始读取地雷信息
	int mineCount=0,row=0,column=0;
	unsigned char* MineInf = NULL;
	DWORD var = 0;

	::ReadProcessMemory(g_hProcess,(LPVOID)VA_top,&var,4,NULL);
	::ReadProcessMemory(g_hProcess,(LPVOID)(var+0x10),&var,4,NULL);
	if(	!::ReadProcessMemory(g_hProcess,LPVOID(var+0x04),&mineCount,4,NULL) ||
		!::ReadProcessMemory(g_hProcess,LPVOID(var+0x08),&row,4,NULL) ||
		!::ReadProcessMemory(g_hProcess,LPVOID(var+0x0c),&column,4,NULL))
	{
		AfxMessageBox(_T("读取内存数据失败!"));
		goto RTN;
	}

	//因为计算缩放情况下的方块位置需要行和列信息,所以在取得行和列后才开始发生第一个按键。
	int firstX = 0;
	int firstY = 0;
	int sizeBlock = 0;		//每个方块的边长
	int x=0,y=0;
	POINT pt;

	//获得扫雷的窗口位置
	RECT rt;
	if(!::GetClientRect(pWnd->GetSafeHwnd(),&rt))
	{
		AfxMessageBox(_T("取得扫雷窗口位置失败!"));
		goto RTN;
	}
	pt.x = rt.left,pt.y=rt.top;
	pWnd->ClientToScreen(&pt);

	sizeBlock = (rt.right-rt.left)/(column+4) + 1;
	//also	sizeBlock = (rt.bottom-rt.top)/(row+4);

	firstX = 2*sizeBlock + sizeBlock/2 + pt.x;
	firstY = (rt.bottom-rt.top-row*sizeBlock)/2 + sizeBlock/2 + pt.y;

	::SetForegroundWindow(pWnd->GetSafeHwnd());
	//发送一次按键以生成地雷信息
	x = firstX,y = firstY;
	::GetCursorPos(&pt);		//备份当前鼠标位置
	::SetCursorPos(x,y);
	//////////////////////////////////////////////////////////
	::mouse_event(MOUSEEVENTF_LEFTDOWN,x,y,0,0);
	::mouse_event(MOUSEEVENTF_LEFTUP,x,y,0,0);
	/*::Sleep(50);*/
	::mouse_event(MOUSEEVENTF_LEFTDOWN,x,y,0,0);
	::mouse_event(MOUSEEVENTF_LEFTUP,x,y,0,0);
	::Sleep(50);
	//////////////////////////////////////////////////////////
	/*另一种模拟鼠标左键双击的方法,但是测试没有成功*/
	//////////////////////////////////////////////////////////
	//发送第一次按键使得第一个方块获得焦点
	//pWnd->SendMessage(WM_LBUTTONDOWN,0,(y<<16)|x);		
	//pWnd->SendMessage(WM_LBUTTONUP,0,(y<<16)|x);
	//发送第二次按键使得第一个方块被按下
	//pWnd->SendMessage(WM_LBUTTONDOWN,0,(y<<16)|x);		
	//pWnd->SendMessage(WM_LBUTTONUP,0,(y<<16)|x);
	///////////////////////////////////////////////////////////	

	////暂停下以等待MineSweeper程序生成地雷信息
	//Sleep(100);	
    //地址表基址 DWORD table[column]
	::ReadProcessMemory(g_hProcess,LPVOID(var+0x44),&var,4,NULL);
	::ReadProcessMemory(g_hProcess,LPVOID(var+0x0C),&var,4,NULL); 

	//为存储地雷信息分配内存空间
	MineInf = new unsigned char[row*column];
	int step = 0;
	SIZE_T byteReaded = 0;
	unsigned char* flag = MineInf;
	DWORD var2 = 0;

	//保存地雷信息
	while(step<column)
	{
		::ReadProcessMemory(g_hProcess,LPVOID(var+step*4),&var2,4,NULL);
		::ReadProcessMemory(g_hProcess,LPVOID(var2+0x0C),&var2,4,NULL);

		::ReadProcessMemory(g_hProcess,(LPVOID)var2,(LPVOID)flag,row,&byteReaded);
		if(byteReaded!=row)
		{
			AfxMessageBox(_T("读取内存数据失败!"));
			goto RTN;
		}
		byteReaded = 0;
		flag += row;
		step++;
	}

	//开始自动扫雷
	::GetCursorPos(&pt);		//备份当前鼠标位置
	for(int arow=0;arow<row;arow++)
	{
		for(int acol=0;acol<column;acol++)
		{
			//根据arow,acol计算方块坐标
			x = firstX + sizeBlock*acol;
			y = firstY + sizeBlock*arow;

			//在发送按键消息前需要设置下鼠标位置,扫雷程序似乎是根据鼠标位置
			//确定点击的方块的,而不是鼠标消息的参数
			//所以PostMessage在这里也不可以使用
			::SetCursorPos(x,y);
			//判断是否是雷,不是雷才执行鼠标点击动作
			if(MineInf[acol*row+arow]==1){
				/*::mouse_event(MOUSEEVENTF_RIGHTDOWN,x,y,0,0);
				::mouse_event(MOUSEEVENTF_RIGHTUP,x,y,0,0);	*/
				pWnd->SendMessage(WM_RBUTTONDOWN,0,(y<<16)|x);
				pWnd->SendMessage(WM_RBUTTONUP,0,(y<<16)|x);
			}else{
				/*::mouse_event(MOUSEEVENTF_LEFTDOWN,x,y,0,0);
				::mouse_event(MOUSEEVENTF_LEFTUP,x,y,0,0);*/				
				pWnd->SendMessage(WM_LBUTTONDOWN,0,(y<<16)|x);
				pWnd->SendMessage(WM_LBUTTONUP,0,(y<<16)|x);
			}				
			//休息5毫秒
			Sleep(1);
		}
	}
	//::SetCursorPos(pt.x,pt.y);	//恢复鼠标位置

RTN:
	if(MineInf!=NULL)
	{
		delete[] MineInf;
		MineInf = NULL;
	}

	if(hProcessSnap!=NULL)
		CloseHandle(hProcessSnap);

	//恢复按钮
	GetDlgItem(IDC_BTN_AUTO)->EnableWindow(TRUE);
}

本方法比较冗长,主要思路是根据其它工具获取的基址进行内存读取,读取内存数据主要使用 了::ReadProcessMemory函数,根据读取的参数判断对应位置是否是地雷,然后通过::moue_event模拟鼠标事件,是地雷则模拟鼠 标右键进行操作,否则模拟鼠标左键进行操作即可。

<12>
发表评论0条 】
网友评论(共?条评论)..
VC通过读写内存实现Win7自动扫雷程序