郁金香VC++初级和中级篇, 最全的手记及琢字翻译(3)

2019-04-01 17:53

SendMessage(hwnd,WM_LBUTTONDOWN,0,lparam);// SendMessage(hwnd,WM_LBUTTONUP,0,lparam);// //点击p2

lparam=((p2.y*35+192)<<16)+(p2.x*31+21); SendMessage(hwnd,WM_LBUTTONDOWN,0,lparam);// SendMessage(hwnd,WM_LBUTTONUP,0,lparam);// return true; }

前面的代码只是分析出可消除的棋子,并没有实现真正消除,所以本节课将模拟鼠标点击,发送鼠标信息来实现自动消除棋子 bool Click2p(POINT p1,POINT p2)//点击两点函数 {

//点击p1

HWND hwnd=FindWindow(NULL,gameCaption);//查找游戏窗口 int lparam;//定义参数

//192是棋盘距离窗口上面的距离,21是棋盘距离左边的距离

lparam=((p1.y*35+192)<<16)+(p1.x*31+21);//通过棋子格子计算位置 SendMessage(hwnd,WM_LBUTTONDOWN,0,lparam);//鼠标按下 SendMessage(hwnd,WM_LBUTTONUP,0,lparam);//鼠标抬起 //点击p2

lparam=((p2.y*35+192)<<16)+(p2.x*31+21);//计算配对棋子位置 SendMessage(hwnd,WM_LBUTTONDOWN,0,lparam);// SendMessage(hwnd,WM_LBUTTONUP,0,lparam);// return true; }

代码添加完之后挂上游戏测试了一下,发现开始可以消除棋子,后面就不动了,再继续查找代码,分析哪里出错. 将bool CLlk_wgDlg::ClearPiar() 函数的 for (x1=x1;x1<19;x1++) 改为

for (x1=0;x1<19;x1++)//不要限制其格子数 还要将for (x2=x1;x2<19;x2++) 改成for (x2=0;x2<19;x2++) 这回可以消除所有棋子了 1.3.7 挂机/秒杀/ a、自动开局 b、挂机下棋 c、秒杀 UINT SetTimer(

HWND hWnd, // 指向窗口句柄 UINT nIDEvent, // 时钟标识

UINT uElapse, // 时间间隔 (毫秒) TIMERPROC lpTimerFunc // 指向回调函数的地址 );

KillTimer(UINT nIDEvent); // 时钟标识 VOID CALLBACK playproc(

HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // current system time ) {

ClearPiar(); }

VOID CALLBACK strartproc(

HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // current system time ) {

startGame(); //自动开局 }

c*****t PLAYID=111; c*****t STARTID=112;

void CLlk_wgDlg::OnCheck1() {

// TODO: Add your control notification handler code here UpdateData(true);//更新窗口内容至变量

11

if (m_autoplay) {

SetTimer(PLAYID,1500,&playproc); } else {

KillTimer(PLAYID); } }

void CLlk_wgDlg::OnCheck2() {

// TODO: Add your control notification handler code here UpdateData(true);//更新窗口内容至变量 if (m_autoplay) {

SetTimer(STARTID,3*1000,&strartproc); } else {

KillTimer(STARTID); } }

//////////////上面都是教案中的内容,下面是我记录的内容/////////

在窗口上添加两个复选框CheckBox,设置为”自动开局”和”自动挂机”,再关联两个变量 M_autoplay / M_autostart

为了运用这两个变量在函数中,又重新调整了一下头llk_wgDlg.cpp中的函数,都是在定义全局变量/标识符一类的代码调整,对我这个没有VC++基础的人来讲,有点看不懂. 这是添加复选框后的界面 [attachment=543]

在”自动挂机”和”自动开局”中添加如下代码

c*****t PLAYID=111;//定义一个数值方便调用,不要重复 c*****t STARTID=112;//可以随便取数,不要重复

void CLlk_wgDlg::OnCheck1() //如果复选框被选中则执行 {

// TODO: Add your control notification handler code here UpdateData(true);//更新窗口内容至变量 if (m_autoplay)//如果变量被设置则运行 {

SetTimer(PLAYID,1500,&playproc);//自动挂机 } else {

KillTimer(PLAYID);//关掉定时器,不执行回调函数 } }

void CLlk_wgDlg::OnCheck2() {

// TODO: Add your control notification handler code here UpdateData(true);//更新窗口内容至变量 if (m_autoplay) //如果变量被设置则运行 {

SetTimer(STARTID,3*1000,&strartproc);//自动开局 } else//如果没有被选中的话 {

KillTimer(STARTID); } }

1.3.8 游戏外挂界面美化 a、添加进度条 b、界面调整

c、Slider控件属性设置 BOOL MoveWindow(

HWND hWnd, // 窗口句柄 int X, // 水平坐标X int Y, // 垂直坐标Y int nWidth, // 宽度 int nHeight, // 高度

BOOL bRepaint // 是否重画窗口 true,false );

//::MoveWindow(this->m_hWnd,0,0,330,200,true);

12

MoveWindow(0,0,330,200,true); 滑块(slider)

void SetTicFreq(int nFreq); int GetPos() c*****t;

void SetRange(int nMin, int nMax, BOOL bRedraw = FALSE); MoveWindow(0,0,330,200,true);

this->m_ctl_slider.SetRange(50,3000); //设置滑块的 最小值 最大值 this->m_ctl_slider.SetTicFreq(150); //分隔线 宽度 this->m_ctl_slider.SetPos(1000); //滑块 位置 this->m_ctl_check.SetCheck(true); //选中复选框

如果对窗口的调整还不满意,就用编程来实现,因为用代码设置窗口及控件大小最精确了.注意MoveWindow函数,如果是全局使用则是6个参数,局部使用就是5个参数

再给滑块条设置一下分隔线:建立控件变量m_ctl_slider,然后查看一下该变量的定义,用于参考代码写法,这几个就是滑块的类成员函数 滑块(slider)

void SetTicFreq(int nFreq); int GetPos() c*****t;

void SetRange(int nMin, int nMax, BOOL bRedraw = FALSE); 设置好手放在窗口的初始化处,也就是llk_wgDlg.cpp里的 BOOL CLlk_wgDlg::OnInitDialog() 函数 这样在启动时就会设置好控件样式

在MoveWindow(0,0,330,200,true);下面添加代码

this->m_ctl_slider.SetRange(50,3000); //设置滑块的 最小值 最大值 this->m_ctl_slider.SetTicFreq(150); //分隔线 宽度 this->m_ctl_slider.SetPos(1000); //滑块 位置 用编辑框测试一下滑块,调整好样式.

再设置一下”挂机速度调节”复选框来开启/关闭 滑块功能

这个需要先建立一个BOOL类型的变量m_sliderenable 来进行控制,添加代码如下 void CLlk_wgDlg::OnCheck3() {

// TODO: Add your control notification handler code here UpdateData(true);

::EnableWindow(m_ctl_slider.m_hWnd,m_sliderenable);//通过这个复选框来控制滑块条是否可用 }

还要设置这个挂机速度调节初始默认为开启,还需要在该控件中建立一个变量m_ctl_check 最终的代码设置如下

在MoveWindow(0,0,330,200,true);下面添加代码

this->m_ctl_slider.SetRange(50,3000); //设置滑块的 最小值 最大值 this->m_ctl_slider.SetTicFreq(150); //分隔线 宽度 this->m_ctl_slider.SetPos(1000); //滑块 位置 this->m_ctl_check.SetCheck(true); //选中复选框 [attachment=544]

设置的代码都在上面的教案里.感觉VC++的MFC不如DELPHI的窗体/控件用着方便,而且是非常的麻烦,当然也有的人用BCB,可能也是这个原因. 1.3.9 倒计时与棋子数(基址查找)

a、查找棋子数 //为了优化 自动开局

b、查找倒计时 //这个倒计时有点讨厌,去掉它 c、开局的标志: 1、棋子数:$001166E0

1Byte,变更棋子数 :扫描类型 :精确数值 2、倒计时基址:$00118088查找 0

t=t-n; //3000-10;

1、棋子数: $001166E0 //没开局之前 0, 开局之后 大于0 2、倒计时基址:$00118088 //毫秒

3、开局的标志:01C3A7B4,01C4EF74,01C61F9C,01C84CF4 //boolean ///////////////////////////

前面的代码虽然可以自动下棋了,但是有关于自动开局这里还不智能,所以要增加一个棋子数的判断,当棋子为空时就要点击”开始”按钮,多找几次就找到了剩余棋子数的地址.同时为了丰富功能,还要查找倒计时功能,将倒计时去掉.而倒计时是个进度条,没有确切的数值,所以我们只好猜测,假设未开局时是0,如果刚一开始的时候估计其值可能会大于100,然后再查找不断减少的数值,最后走完就是0,注意这个值是4字节的.

找到了这两个数值我们如何做呢?可以将倒计时的数值锁定,或者将其时间控件给去掉;棋子数是我们用来监测是否在开局中,那么我们再来查找一下在游戏中是否有开局的数值,我们猜测未开局时是0,开局后是1,因为这都是编程时的真与假的设置 1.4.1 优化自动开局函数StartGame a、让游戏窗口高高在上 b、优化开局函数 参考 1.3.9

3、开局的标志:01C3A7B4,01C4EF74,01C61F9C,01C84CF4 //开局 时为1 未开局为0 int flag;//这个值为0时 执行 StartGame; HWND gameh=::FindWindow(NULL,gameCaption);

13

//AfxMessageBox(\

if (gameh==0) { return;} //没有找到游戏窗口 //让游戏窗口置顶

SetWindowPos(gameh,HWND_TOP,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); //AfxMessageBox(\ //

DWORD pid;

::GetWindowThreadProcessId(gameh,&pid); long flag,byReadSize;

HANDLE hp=OpenProcess(PROCESS_ALL_ACCESS,false,pid);

::ReadProcessMemory(hp,(LPCVOID)(0x01C3A7B4),(LPVOID)(&flag),4,(LPDWORD) (&byReadSize)); if (byReadSize==0) {AfxMessageBox(\未成功读出数据\if ((flag==0)&&(byReadSize>0)) { startGame();} //自动开局

将上节课中找到的游戏开局4个地址写入代码中,写在计时器的回调函数里 在这个函数里

VOID CALLBACK strartproc(

HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // current system time )

添加如下代码

{ HWND gameh=::FindWindow(NULL,gameCaption); if (gameh==0) { return;} //没有找到游戏窗口 //让游戏窗口置顶,最后参数是忽略掉前面的参数

SetWindowPos(gameh,HWND_TOP,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); DWORD pid;//建立进程ID变量

::GetWindowThreadProcessId(gameh,&pid);//通过窗口找进程ID,参数1窗口句柄,参数2得到的ID long flag,byReadSize;//定义变量用于指针

HANDLE hp=OpenProcess(PROCESS_ALL_ACCESS,false,pid);//打开线程,参数1为打开权限,参数2为进程派生,参数3为打开句柄ID

::ReadProcessMemory(hp,(LPCVOID)(0x01C3A7B4),(LPVOID)(&flag),4,(LPDWORD) (&byReadSize));//读取内存,参数1进程句柄,参数2被读进程指针,参数3保存数据指针,参数4读取字节浸透,参数5实际字节数

if (byReadSize==0) {AfxMessageBox(\未成功读出数据\检测数据是否读出 if ((flag==0)&&(byReadSize>0)) { startGame();} //自动开局 }

1.4.2 去掉游戏倒计时限制 a、找到计时代码

b、动态修改游戏代码(OD使用初探) c、去掉计时限制 参考:1.3.9

2、倒计时基址:$00118088 //秒 分析写入 倒计时的代码:

0042646d:mov [eax+000047E4],edx //初始化 倒计时的值 00426526:mov [eax+000047E4],ecx //更新 倒计时的值 =减1 00426526:

byte acode[6]={0x90,0x90,0x90,0x90,0x90,0x90};//要将代码NOP掉 bool ClearCode() {

HWND gameh=::FindWindow(NULL,gameCaption); if (gameh==0) { return;} //没有找到游戏窗口 DWORD pid;

::GetWindowThreadProcessId(gameh,&pid); long byWriteSize;

HANDLE hp=OpenProcess(PROCESS_ALL_ACCESS,false,pid);

::WriteProcessMemory(hp,(LPCVOID)(0x00426526),(LPVOID)(acode),6,(LPDWORD) (&byWriteSize));//写入NOP代码 }

先将写入倒计时找到,用OD附加游戏,来到修改倒计时的汇编代码处,多试几次,看看哪行代码是关键的?试试将关键代码NOP掉看看 [attachment=545]

也就是在00426526这里连续写入6个90的数值,90就是NOP,也就是空操作.接下来在VC里编写代码,让咱们的外挂自己修改游戏的关键地址为6个90,代码已经在教案里了(上面的蓝字)

至于写入的函数WriteProcessMemory的参数,与ReadProcessMemory函数的参数是一样的,只是读出字节数和写入字节数的区别(详细附后). 再新增一个复选框”去掉游戏倒计时”,用来开启修改掉倒计时代码 void CLlk_wgDlg::OnCleartimer() {

// TODO: Add your control notification handler code here

if (ClearCode()) {m_ctl_cleartime.EnableWindow(false);} //禁用它 else { m_ctl_cleartime.SetCheck(false);}

14

}

WriteProcessMemory 函数原型:

Declare Function WriteProcessMemory Lib \(ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long 作用:写内存 说明:

hProcess , 进程的句柄

lpBaseAddress, 写入进程的位置(地址) lpBuffer, 数据当前存放地址 nSize, 数据的长度

lpNumberOfBytesWritten,实际数据的长度 nSize以字节为单位,一个字节Byte等于8位 基本数据类型的长度 ShortInt 8位 = 1Byte SmallInt 16位 = 2Byte Integer 16位 = 2Byte LongInt 32位 = 4Byte Word 16位 = 2Byte LongWord 32位 = 4Byte Boolean 8位 = 1Byte WordBool 16位 = 2Byte LongBool 32位 = 4Byte

比如要写入Integer类型的数据,那么Integer长度2Byte 所以nSize = 2 1.4.3 编写完整外挂 a、功能测试 b、修改完善外挂

秒杀: 1、棋子数: $001166E0 //没开局之前 0, 开局之后 大于0 int chessnum=ReadChessNum(); void KillAll()//秒杀 {

// TODO: Add your control notification handler code here while (chessnum!=0) {

ClearPiar();

// Sleep(1); //0x001166E0 棋子数=0时退出 chessnum=ReadChessNum(); } }

int ReadChessNum() //读出当前 棋子数 {

// TODO: Add your control notification handler code here //获取窗口句柄

HWND gameh=::FindWindow(NULL,gameCaption); //获取窗口进程ID DWORD processid;

::GetWindowThreadProcessId(gameh,&processid); //打开指定进程

HANDLE processH=::OpenProcess(PROCESS_ALL_ACCESS,false,processid); //读指定进程内存数据 DWORD byread;

LPCVOID pbase=(LPCVOID)0x001166E0 ; //棋子数据基址 int ChessNum;

LPVOID nbuffer=(LPVOID)&ChessNum; //存放棋子数据 ::ReadProcessMemory(processH,pbase,nbuffer,4,&byread); return ChessNum; }

再继续完善我们之前的代码,修改一下窗口置顶的代码,因为置顶的选项里没有关联变量,所以不能控制该选项框,新增一个变量m_gametop ,然后再加入代码

void CLlk_wgDlg::OnGameTop() {

UpdateData(true); //更新窗口数据至变量 gametop=m_gametop;

if( m_gametop)//当变量值为真时则置顶 {

HWND gameh=::FindWindow(NULL,gameCaption); if (gameh==0) { return;} //没有找到游戏窗口

15


郁金香VC++初级和中级篇, 最全的手记及琢字翻译(3).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:组织行为学重点

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: