VC++制作连连看辅助经验分享

来源:互联网 发布:金蝶软件公司简介 编辑:程序博客网 时间:2024/04/29 16:31

最近看了郁金香老师的《VC++外挂编程》系列视频,试着按这个思路写了一个笔者自己的连连看外挂。试验了一下,比较成功,已经从0分刷到了近20000分。下面来分享一下经验。

 

首先说明一下需要用到的工具:

1、  CE(cheat engine),笔者用的是cheat engine6.2,中文补丁。

2、  VC++ 6.0

 

好了,废话不多说,开始详细讲解整个制作过程。首先打开QQ游戏客户端,选择QQ连连看并选好座位。

打开CE,这是如果不出意外会弹出QQ游戏崩溃发送错误报告的提示。笔者从网上查找资料得知,QQgame启动了某个线程来检测CE,所以只要检测到CE打开了,即便你什么都没做,QQ游戏也会崩溃而推出。

既然知道原因了,那就有对策。过检测的方法就不讲了,这边引用一篇看雪的文章,有详细解释。地址:http://bbs.pediy.com/showthread.php?t=147811&highlight。

要是闲麻烦也没关系,笔者根据上面这篇文章写的一个小工具,可以在打开QQ游戏的时候直接干掉这个检测线程。网盘下载地址:http://vdisk.weibo.com/s/bhIhB。

运行如果提示成功,那接下来就可以放心运行CE了。

 

游戏分析部分

 

我们首先是要找出棋盘的内存地址。内存中存放棋盘的方法可以假定为,如果该格子上有图片,那么数据是大于0的某个值;而如果该格子上没有图片,那么数据应该是等于0。

我们先来验证一下以上假设是否正确。笔者的方法是查找棋盘最左上角的那个格子。具体过程,打开CE,点选择进程,选择连连看进程。

在scan type处选择ExactValue,Value type处选择Byte。最后,在Value处填入0(在游戏还没开局前,这个格子对应的内存数据应该是0,表示该格子没有图片)。其它设置默认即可。填写完后如下:

然后点击FirstScan,第一次搜索完后会发现内存中为0的数据成千上万。没关系,继续搜索即可。

点击连连看的练习,视棋盘左上角的格子而定,如果格子上有图片,则在scan type处选择Bigger Than,然后在Value处填入0,点击Next Scan;如果棋盘左上角的格子没有图片,则在scan type处选择Exact Value,然后在Value处填入0,点击Next Scan。

搜索完一次之后,再点一下练习。一直重复上面步骤,最后会发现有5个值是一直符合上述情况的:


(5个可能会有出入,具体是当时情况而定)

这几个值并非都是我们想要的。关闭连连看,再打开连连看,重新在CE载入连连看进程,点击练习,这是会发现又有几个值和情况不符合了,直接在地址出把他们删掉。


如图,第二个和第三个已经已经不符和了,删掉即可。

再多按几次练习切换游戏棋盘数据,最终只留下一个正确的数值如图,只有第一个地址是符合的。


我们把这个地址记下来。笔者是64位的系统,内存地址可能有所出入。具体视自己找出来的地址为准。

 

程序编写部分

 

有了棋盘的内存地址,接下去就可以开始编写程序了。打开VC++,新建工程,选择MFC AppWizard(exe),选择基于对话框的应用程序,接下来全部默认即可。

 

首先我们要做的是把游戏棋盘数据从内存中读取出来放入我们自己程序的数组中。在读取内存之前先列举一下等下要用到的几个关键函数以及他们的作用。

 

HWND FindWindow(LPCTSTR lpClassName,      // 指向窗口类名(in)LPCTSTR lpWindowName     // 指向窗口名称(in));

我们用这个函数来通过窗口标题名称获得窗口的句柄


DWORD GetWindowThreadProcessId(HWND hWnd,               //窗口句柄(in)LPDWORD lpdwProcessId    //进程ID(out));

我们用这个函数来通过窗口句柄获得它的进程ID

 

HANDLE OpenProcess(DWORD fdwAccess,         //希望获得的访问权限标志(in)                         //这里用PROCESS_ALL_ACCESSBOOL fInherit,           //是否继承句柄,这里用FALSE(in)DWORD IDProcess          //进程ID(in));

我们用这个函数来通过进程ID来打开进程

 

BOOL ReadProcessMemory(HANDLE hProcess,                   //远程进程句柄(in)LPCVOID lpBaseAddress,             //远程进程中内存地址,机上面CE分析出的地址(in)LPVOID lpBuffer,                //本地进程中内存地址(out)DWORD nSize,                    //要传送的字节数(in)LPDWORD lpNumberOfBytesRead     //实际传送的字节数(out));

我们用这个函数来远程访问内存并读取指定字节数的内存

 

你可能已经发现以上几个函数是一环扣一环的,即前一个函数的返回值正好是后一个函数的参数。

 

然后我们还要用到VC++6.0自带的一个小工具,SPY++来获得游戏窗口标题。详细方法在文章最后讲述,获得的窗口标题为"QQ游戏 - 连连看角色版"。

 

清楚了这几个函数的参数即功能之后,我们就可以开始读取棋盘的内存数据了。

 

由于本篇不是介绍MFC编程的,所以设计具体编写的就不讲了,下面把辅助制作的几个关键函数讲一下。

 

读取棋盘数据函数

#define GameCaption "QQ游戏 - 连连看角色版"#define MAX_X 11  #define MAX_Y 19 BOOL  UpdateChessData(){         HWND hgame =::FindWindow(NULL, GameCaption);         //获得游戏窗口进程ID         DWORD pid;         GetWindowThreadProcessId(hgame,&pid);         //打开指定进程         HANDLE hndProcess;         hndProcess =OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);         DWORD bfread;         LPVOID  nbuffer = (LPVOID)&chessdata;    //存放棋盘数据         //MAX_X*MAX_Y = 11*19,通过观察发现棋盘大小是19*11的,所以应该读取这么字节数         if(!ReadProcessMemory(hndProcess, LPCVOID(0x0018A3BC), nbuffer, MAX_X*MAX_Y,&bfread))         {#ifdef _DEBUG                   TRACE0("readchessdata failed");#endif                   return FALSE;         }         return TRUE;}

 

有了棋盘数据之后,通过构造一个算法来找出其中一对能消除的即可。

 

BOOL  ClearPair(POINT&point1, POINT &point2);

这个函数是本函数最关键的函数,也是核心算法。但是由于篇幅关系,这里不再详细讲述,百度一下连连看消除算法,都能找到的。

 

通过上述函数,就能获得一对可以消除的图片。

 

之后我们要做的就是像鼠标一样在窗口上消除一对。通过模拟鼠标的移动的移动和点击是一种方法,但是不太可取。因为我们开辅助的时候要想做自己的事情,鼠标就不能由程序来控制。所以我们采取第二种方法,向游戏窗口发送鼠标消息。函数如下:

 

BOOL Click2p(POINT p1,POINT p2){         //点击p1         HWND hwnd =::FindWindow(NULL, GameCaption);         int lparam;         //每个小方格的长为35,宽为31         lparam=((p1.y*35+192)<<16)+(p1.x*31+21);         //鼠标按下         ::SendMessage(hwnd,WM_LBUTTONDOWN,0x1,lparam);         //鼠标松开         ::SendMessage(hwnd,WM_LBUTTONUP,0x0,lparam);         //点击p2         lparam=((p2.y*35+192)<<16)+(p2.x*31+21);         ::SendMessage(hwnd,WM_LBUTTONDOWN,0x1,lparam);         ::SendMessage(hwnd,WM_LBUTTONUP,0x0,lparam);        return true;}

 

解释下SendMessage函数

LRESULT SendMessage(HWND hWnd,               //要发送消息的目标窗口UINT Msg,                //消息类型WPARAM wParam,           //指定附加的消息特定信息LPARAM lParam            //指定附加的消息特定信息);

 

通过spy++可以捕获消息的附加消息信息,这个在最后介绍捕获消息的方法。发现鼠标按下时,wParam的值为0x1,lParam的高16位是y坐标,低16位是x坐标。

 

最后通过一个定时器每隔一定时间触发一下ClearPair,然后调用Click2P函数,即可实现自动消除方块了。

 

附连连看辅助可执行程序(64位版,32位已测试失败)。

附Spy++使用方法。

1、获得窗口标题

 

VC++   工具  SPY ++。

选择工具,按住,把鼠标移到连连看游戏窗口上,显示


Caption即我们上面FindWindow函数要用到的第二个参数。选择Properties,点击OK后把它复制下来。

 

2、截获窗口消息

 

前面方法相同,之后选择Messages即可,messages  options 的Messages标签下选择


这两个点击OK即可。截取到足够消息后点击即可停止。双击消息可以查看消息的附加信息(lParam和wParam);

原创粉丝点击