另外程序代码里对“自动扫雷”按钮有2套代码,分别是OnBnClickedBtnAuto 和 OnBnClickedBtnAuto2。区别在于OnBnClickedBtnAuto使用了早期些的一些代码,而OnBnClickedBtnAuto2是独立写的。他们也分别调用了HitBlock和HitBlock2,他们区别是前者是单一的点击方块功能,后者集布雷、自动点击所有方块(当然排除是雷的方块)于一体,所以HitBlock需要在循环里调用,传入所需的行和列参数,HitBlock2只需要调用一次,它的参数个数同HitBlock一样,但是含义不同,HitBlock2参数的含义是首次点击方块的位置,这个位置用于布雷。
还有一点,这2个函数的执行都是很快很快的,这里看不出有什么不对,如果在中间加上Sleep问题就明显了,扫雷窗口在函数执行完毕之前是不更新的,也就是在扫雷过程中你看不到方块被一个一个的点开。导致问题的原因可能是DLL线程和扫雷主线程为同一个线程,在DLL执行过程中阻碍了扫雷主线程更新UI。可以使用SetTimer来修改代码,其实这也是我最先实现了的一种方式,后来删除了。另外也可以试着创建个新线程来执行扫雷。
// GameForm.cpp : implementation file
//
#include "stdafx.h"
#include "MineInjectDll.h"
#include "GameForm.h"
#include "afxdialogex.h"
// GameForm dialog
IMPLEMENT_DYNAMIC(GameForm, CDialog)
GameForm::GameForm(CWnd* pParent /*=NULL*/)
: CDialog(GameForm::IDD, pParent)
, m_MineInf(_T(""))
, mineInf(0)
, mineCount(0)
, row(0)
, column(0)
, m_VATOP(0)
, m_VAFUN(0)
, m_VAFUN2(0)
, m_VAFUN3(0)
{
}
GameForm::~GameForm()
{
if(mineInf!=NULL)
delete[] mineInf;
}
void GameForm::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT_MINEINF, m_MineInf);
}
BEGIN_MESSAGE_MAP(GameForm, CDialog)
ON_BN_CLICKED(IDC_BTN_GETINF, &GameForm::OnBnClickedBtnGetinf)
ON_BN_CLICKED(IDC_BTN_AUTO, &GameForm::OnBnClickedBtnAuto2)
END_MESSAGE_MAP()
// GameForm message handlers
void GameForm::OnBnClickedBtnGetinf()
{
HMODULE hMine = GetModuleHandle(_T("Minesweeper.exe"));
if(hMine==NULL)
{
AfxMessageBox(_T("无法找到模块mineswpeer"));
return;
}
//模块句柄即为基址
DWORD mineBaseAddr = (DWORD)hMine;
const int RVA_top = 0x868B4;
DWORD VA_top = mineBaseAddr + (DWORD)RVA_top;
int lmineCount=0,lrow=0,lcolumn=0;
DWORD* mineTable = 0;
_asm
{
mov ecx,VA_top ;一级基址
mov ecx,[ecx]
mov ecx,[ecx+0x10]
mov ebx,[ecx+0x4]
mov lmineCount,ebx ;地雷总数
mov ebx,[ecx+0x8]
mov lrow,ebx ;行
mov ebx,[ecx+0x0c]
mov lcolumn,ebx ;列
mov ebx,[ecx+0x44]
mov ebx,[ebx+0x0C] ;ebx是一个地址表首地址
mov mineTable,ebx
}
mineCount = lmineCount;
row = lrow;
column = lcolumn;
if(mineInf!=NULL)
delete[] mineInf,mineInf=NULL;
mineInf = new char[row*column];
memset(mineInf,0,column*row);
DWORD mineColumn=0;
char* mineColumnTable=0;
for(int i=0;i<column;i++)
{
for(int j=0;j<row;j++)
{
//第I列
mineColumn = mineTable[i];
mineColumnTable = (char*)(*((DWORD*)(mineColumn + 0x0C)));
mineInf[j*column+i] = mineColumnTable[j];
}
}
CString str;
for(int r=0;r<row;r++)
{
for(int col=0;col<column;col++)
{
if(mineInf[r*column+col]==1)
str+=_T("M");
else
str+=_T("O");
str+=_T(" ");
}
str+=_T("\r\n");
}
m_MineInf = str;
UpdateData(false);
}
void GameForm::HitBlock(int arow, int acol, bool bFirst)
{
int va_top = m_VATOP;
int va_fun = m_VAFUN;
int va_fun2 = m_VAFUN2;
int va_fun3 = m_VAFUN3;
_asm
{
push eax //保留参数
push ecx
push ebx
mov eax,va_top
mov eax,[eax]
mov [eax+0xC5],1
mov ecx,va_top
mov ecx,[ecx]
push arow
push acol
xor bl,bl
mov eax,va_fun
call eax //调用点击方块CALL
test eax,eax
jg here_
jmp end_
here_:
push eax
mov eax,va_fun2
call eax //后续处理CALL
end_:
pop ebx
pop ecx
pop eax
}
}
void GameForm::OnBnClickedBtnAuto()
{
HMODULE hMine = GetModuleHandle(_T("Minesweeper.exe"));
if(hMine==NULL)
{
AfxMessageBox(_T("无法找到模块mineswpeer"));
return;
}
//模块句柄即为基址
DWORD mineBaseAddr = (DWORD)hMine;
const int RVA_top = 0x868B4; //数据基址 RVA
const int RVA_fun = 0x21418; //扫雷CALL RVA
const int RVA_fun2 = 0x26BCD; //后续函数 RVA
const int RVA_fun3 = 0x200BB; //布雷函数 RVA
m_VATOP = mineBaseAddr + (DWORD)RVA_top;
m_VAFUN = mineBaseAddr + (DWORD)RVA_fun;
m_VAFUN2 = mineBaseAddr + (DWORD)RVA_fun2;
m_VAFUN3 = mineBaseAddr + (DWORD)RVA_fun3;
//第一次点击,同时生成布雷信息
HitBlock(0,0,true);
//在第一次点击之后才会有布雷信息,此时获取雷信息
OnBnClickedBtnGetinf();
//开始扫雷
for(int r=0;r<row;r++)
{
for(int col=0;col<column;col++)
{
if(mineInf[r*column+col]!=1)
{
HitBlock(r,col,false);
}
}
}
}
void GameForm::OnBnClickedBtnAuto2()
{
HMODULE hMine = GetModuleHandle(_T("Minesweeper.exe"));
if(hMine==NULL)
{
AfxMessageBox(_T("无法找到模块mineswpeer"));
return;
}
//模块句柄即为基址
DWORD mineBaseAddr = (DWORD)hMine;
const int RVA_top = 0x868B4; //数据基址 RVA
const int RVA_fun = 0x21418; //扫雷CALL RVA
const int RVA_fun2 = 0x26BCD; //后续函数 RVA
const int RVA_fun3 = 0x200BB; //布雷函数 RVA
m_VATOP = mineBaseAddr + (DWORD)RVA_top;
m_VAFUN = mineBaseAddr + (DWORD)RVA_fun;
m_VAFUN2 = mineBaseAddr + (DWORD)RVA_fun2;
m_VAFUN3 = mineBaseAddr + (DWORD)RVA_fun3;
HitBlock2(0,0);
}
void GameForm::HitBlock2(int arow, int acol)
{
int va_top = m_VATOP;
int va_fun = m_VAFUN;
int va_fun2 = m_VAFUN2;
int va_fun3 = m_VAFUN3;
_asm
{
mov esi,va_top
mov esi,[esi]
mov eax,[esi+0x10]
mov edi,arow //行
mov ebx,acol //列
// mov [eax+0x18],0 //edi+18=0,zf=1,第一次
mov [esi+0xC5],1
mov ecx,esi
push edi
push ebx
xor bl,bl
mov eax,va_fun
call eax //第一次点击方块
mov edi,0
mov ebx,0
/* mov eax,[esi+0x10]*/
/* mov [eax+0x18],1*/
row_:
push edi
column_:
push ebx
mov eax, dword ptr [esi+0x10]
mov eax, dword ptr [eax+0x44]
mov eax, dword ptr [eax+0x0C]
mov eax, dword ptr [eax+ebx*4]
mov eax, dword ptr [eax+0x0C]
xor ecx, ecx
cmp byte ptr [edi+eax], cl // 判断是不是雷
jnz end_ // 雷跳转到最后
mov [esi+0xC5],1
mov ecx,esi
push edi
push ebx
xor bl,bl
mov eax,va_fun
call eax //调用点击方块CALL
test eax,eax
jg here_
jmp end_
here_:
push eax
mov eax,va_fun2
call eax //后续处理CALL
push 200
mov eax,Sleep
call eax
end_:
pop ebx
inc ebx
mov eax,[esi+0x10]
cmp ebx,[eax+0x0C]
jb column_
pop edi
mov ebx,0
inc edi
cmp edi,[eax+0x08]
jb row_
}
}
// Dialog
IDD_GAMEFORM DIALOGEX 0, 0, 299, 123
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg", 0, 0, 0x0
BEGIN
EDITTEXT IDC_EDIT_MINEINF,7,7,175,109,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | NOT WS_TABSTOP
PUSHBUTTON "获取MINE信息",IDC_BTN_GETINF,195,7,97,21
DEFPUSHBUTTON "自动扫雷",IDC_BTN_AUTO,195,39,97,21
END
评论