2012-02-24 22:47:48|?次阅读|上传:wustguangh【已有?条评论】发表评论
关键词:C/C++, 游戏, MFC|来源:唯设编程网
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模拟鼠标事件,是地雷则模拟鼠 标右键进行操作,否则模拟鼠标左键进行操作即可。