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模拟鼠标事件,是地雷则模拟鼠 标右键进行操作,否则模拟鼠标左键进行操作即可。