VC外挂教程.docx
- 文档编号:10803042
- 上传时间:2023-02-23
- 格式:DOCX
- 页数:230
- 大小:113.65KB
VC外挂教程.docx
《VC外挂教程.docx》由会员分享,可在线阅读,更多相关《VC外挂教程.docx(230页珍藏版)》请在冰豆网上搜索。
VC外挂教程
VC外挂教程
初级篇1.1.1教学目标:
模拟鼠标操作
1.1.1、游戏数据分析(SPY++)
a、取得窗口相对坐标
b、读出游戏窗口信息GetWindowRect
c、移动鼠标指针SetCursorPos
HWNDFindWindow(
LPCTSTRlpClassName,//窗口类名
LPCTSTRlpWindowName//窗口标题
);
教学过程:
取游戏标题:
QQ游戏-连连看角色版
取开局所在坐标:
x=655;y=577//lparam0x0241028f
拦截消息:
WM_LBUTTONDOWN,WM_LBUTTONUP
API-FindWindow(NULL,"QQ游戏-连连看角色版");
打开VC,新建-工程-MFCEXE-工程名是LLKWG,然后选择基本对话框,完成即可
[attachment=535]
还要在编辑框中关联变量,建立类向导
[attachment=536]
在游戏开局按钮输入代码
//Thesystemcallsthistoobtainthecursortodisplaywhiletheuserdrags
//theminimizedwindow.
HCURSORCLlk_wgDlg:
:
OnQueryDragIcon()
{
return(HCURSOR)m_hIcon;
}
HWNDgameh;//游戏窗口句柄
RECTr1;//RECT结构表示一个矩形区域
voidCLlk_wgDlg:
:
*****tartGame()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
gameh=:
:
FindWindow(NULL,"QQ游戏-连连看角色版");//获取游戏窗口句柄
:
:
GetWindowRect(gameh,&r1);//这里取坐标,双冒号是全局的意思
this->m_x=r1.left;this->m_y=r1.top;//读出窗口左上角坐标,this是关联变量
UpdateData(false);//显示到编辑框
//设置鼠标指针位置取开局所在坐标:
x=655;y=577//lparam0x0241028f
SetCursorPos(655+r1.left,577+r1.top);//当前窗口坐标+开局按钮坐标
}
1.1.2用VC++写个最简单的外挂(实现游戏开局)
a、鼠拟鼠标单击mouse_event
b、鼠标指针移动还原
c、集成到startgame函数里
教学过程:
//模拟鼠标的单击(鼠标按下/鼠标抬起)
//鼠标在当前位置按下
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
//鼠标在当前位置抬起
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
小结:
mouse_event,Sleep,SetCursorPos
这次用到了鼠标点击的函数mouse_event鼠标硬件模拟,如果调用不成功则延时一下Sleep(200),然后再将鼠标移回原位SetCursorPos这个是位置设置函数
调用成功后,将函数放在一个.h头文件里,方便以后调用.新建c/c++HeaderFile,文件名GameProc
#include"stdafx.h"
//游戏功能函数
HWNDgameh;
RECTr1;
POINTp;//x,y
voidstartGame()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
//获取游戏窗口句柄
gameh=:
:
FindWindow(NULL,"QQ游戏-连连看角色版");
:
:
GetWindowRect(gameh,&r1);
//保存当前鼠标指针
//取得当前鼠标位置
GetCursorPos(&p);
//设置鼠标指针位置取开局所在坐标:
x=655;y=577//lparam0x0241028f
SetCursorPos(655+r1.left,577+r1.top);
//模拟鼠标的单击(鼠标按下/鼠标抬起)
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);//鼠标在当前位置按下
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//鼠标在当前位置抬起
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
Sleep(200);//过一段时间再执行后边的代码
SetCursorPos(p.x,p.y);//还原鼠标位置
}
当然还要将这个.h文件包涵进主函数里
#include"stdafx.h"
#include"llk_wg.h"
#include"llk_wgDlg.h"
#include"GameProc.h"
然后这样调用
voidCLlk_wgDlg:
:
*****tartGame()
{
startGame();
}
1.2.1、CE中的数据类型
a、数据类型:
Bit,Byte,Word,Dword,float,double
b、用CE查找出坐位号;
c、保存分析数据
教学过程:
a、数据类型:
Bit,Byte,Word,Dword,float,double
C++数据类型
bit/位1位取值范围:
0..1
byte(字节)=0..11111111(2进制)=0..255(10进制)=0..FF(16进制)
WORD(单字)=2Byte=0..65535(10进制)=0..FFFF(16进制)
DWORD(双字)=2WORD=4Byte=0..4294967295=0..FFFFFFFF
float(浮点数)double(双浮点数)
int(4字节),long(4字节),WORD,DWORD(4字节),float(4字节),double(8字节)
b、用CE查找出坐位号;
打开QQ游戏连连看
猜测0..5,1..6,顺时针
0..7
坐位号地址:
0x00B8D8E0
因为游戏当中不同的座位号数据不一样,所以首先要找到座位号.
用CE搜索游戏是字节型来查找游戏的座位号,一桌是6个位置,猜测是从0到5,上面是0,顺时针方向加1.所以我们更换座位号后再继续用CE搜索相应的位置号,注意附加的进程可不要出错哦.QQ连连看的进程名是KYODAI~1.EXE,可以用任务管理器找出.
找到后将CE的数据保存一下,方便下次调用.
1.2.2、编程读出坐位号;
a、远程读取进程数据
b、打开远程进程
c、读取远程进程数据
教学过程:
API函数介绍
1、FindWindow//获取窗口句柄
2、GetWindowThreadProcessId//获取窗口进程ID
3、OpenProcess//打开指定进程
4、ReadProcessMemory//读指定进程内存数据
游戏进程名:
KYODAI~1.EXE
游戏窗口标题:
"QQ游戏-连连看角色版"
HWNDFindWindow(
LPCTSTRlpClassName,//NULL忽略
LPCTSTRlpWindowName//窗口标题
);
BOOLReadProcessMemory(
HANDLEhProcess,//进程句柄
LPCVOIDlpBaseAddress,
//基址0x00B8D8E0
LPVOIDlpBuffer,//存放数据缓冲区
DWORDnSize,//要读取数据的字节数
LPDWORDlpNumberOfBytesRead
//实际读取的字节数
);
这次需要几个函数来取得窗口/句柄/进程/内存等信息
几个函数要联合运用,前一个函数的返回值就是后一个函数的参数
在VC代码里添加座位号的变量m_Num类型是UINT,新增一个按钮,添加如下代码
[attachment=537]
c*****tPCHARgameCaption="QQ游戏-连连看角色版";
voidCLlk_wgDlg:
:
OnButton2()
{
//游戏窗口标题:
"QQ游戏-连连看角色版"
//1、FindWindow//获取窗口句柄
//2、GetWindowThreadProcessId//获取窗口进程ID
//3、OpenProcess//打开指定进程
//4、ReadProcessMemory//读指定进程内存数据
//获取窗口句柄
HWNDgameh=:
:
FindWindow(NULL,gameCaption);
//获取窗口进程ID
DWORDprocessid;
:
:
GetWindowThreadProcessId(gameh,&processid);
//打开指定进程
HANDLEprocessH=:
:
OpenProcess(PROCESS_ALL_ACCESS,false,processid);
//读指定进程内存数据
DWORDbyread;
LPCVOIDpbase=(LPCVOID)0x00B8D8E0;//读取当前的指针,强制转换为LPCVOID指针
LPVOIDnbuffer=(LPVOID)&m_num;//保存当前的指针,强制转换为LPVOID指针
:
:
ReadProcessMemory(processH,pbase,nbuffer,4,&byread);
UpdateData(false);//更新变量的值到编辑框
}
1.2.3、用CE查出棋盘基址;
a、找棋盘数据基址
b、分析棋盘数据结构
19宽*11高:
数组bytea[19][11]
byte0..255//00..FF
0x0012A508//棋盘数组基址
db地址//以字节方式显示指定地址内存里的数据
前面座位号的基址已经找出了,所以这次要找出棋盘的数据,
要先查一下棋盘的2维排列,是19宽*11高,用QQ截图查找一下长和宽,然后计算出每一格的大小.猜测棋盘的棋子也是字节的,我们查找左上角第一棋子的数据.
用CE查找,如果有棋子就是大于0,变化了就再搜索更改的数值,没有棋子就是0,多次查找就找到第一棋子的地址了.自己找个座位坐下来,加个密码不让别人进,多按几次"练习"按钮,这样棋盘变化就方便找棋子数据了.
老师也查找出错了,再来一次吧.这次找到了..
找到后用OD加载一下看看,这样的数据排序看的比较清楚
1.2.4、读出当前棋盘数据
a、编程读出棋盘数据
b、棋盘数据显示出来
参考章节:
1.2.3,1.2.2
19宽*11高:
数组bytea[11][19]//a[y][x]
byte0..255//00..FF
0x0012A508//棋盘数组基址
db地址//以字节方式显示指定地址内存里的数据
itoa(要转换的整数,存放字符数组,要转换的字符进制)
for(inty=0;y<=10;y++)//y++y:
=Y+1;
添加了一个编辑框用来显示棋盘数据,再关联一个变量m_ChessdataCstring类型
[attachment=538]
增加”更新棋盘数据”按钮,添加代码如下:
bytechessdata[11][19];//a[y][x]
voidCLlk_wgDlg:
:
OnBtnReadchess()
{
//TODO:
Addyourcontrolnotificationhandlercodehere
//获取窗口句柄
HWNDgameh=:
:
FindWindow(NULL,gameCaption);
//获取窗口进程ID
DWORDprocessid;
:
:
GetWindowThreadProcessId(gameh,&processid);
//打开指定进程
HANDLEprocessH=:
:
OpenProcess(PROCESS_ALL_ACCESS,false,processid);
//读指定进程内存数据
DWORDbyread;
LPCVOIDpbase=(LPCVOID)0x0012A508;//棋盘数据基址
LPVOIDnbuffer=(LPVOID)&chessdata;//存放棋盘数据
:
:
ReadProcessMemory(processH,pbase,nbuffer,11*19,&byread);
charbuf[11];///显示棋盘数据
m_chessdata="";//先清空编辑
for(inty=0;y<=10;y++)//一列一列的读,FOR循环:
Y=0是循环的起始值,Y<=10是终止值,Y++是每次Y+1
{
for(intx=0;x<=18;x++)//一行一行的读
{
itoa(chessdata[y][x],buf,16);//itoa整型转换成字串
m_chessdata+=buf;
m_chessdata+="";
}
m_chessdata+="\r\n";//换行
}
UpdateData(false);//更新数据
}
这节课讲了VC编程,注意细节,数据是按照[Y][X]排序的,注意棋盘数据是2位数的,要将charbuf改成charbuf[11].
1.3.1分析棋子与棋盘坐标关系
a、鼠标软件模拟,函数SendMessage
b、分析窗口内棋子相对坐标X,Y
c、软件模拟点击棋盘坐标x,y处的棋子
1、SendMessage;
SendMessage(hwnd,WM_LBUTTOMDOWN,0,YX);//hwnd=FindWindow(NULL,游戏标题);
SendMessage(hwnd,WM_LBUTTOMUP,0,YX);//PostMessage/mouse_event
2、获取棋盘左上角棋盘第一格坐标.
棋盘第一格坐标x=21,y=192
intx=22,y=187;
hwnd=FindWindow(NULL,游戏标题);
SendMessage(hwnd,WM_LBUTTONDOWN,0,(y<<16)+x);//
SendMessage(hwnd,WM_LBUTTONUP,0,(y<<16)+x);//
3、计算棋盘的宽度*高度
589*385
棋盘第一格
坐标x=21,y=192
31*35棋子宽度,高度
SendMessage(hwnd,WM_LBUTTONDOWN,0,(y<<16)+x+31*2);//
SendMessage(hwnd,WM_LBUTTONUP,0,(y<<16)+x);//
//SendMessage鼠标模拟,//WM_LBUTTONDOWN鼠标左键按下//WM_LBUTTONUP鼠标左键抬起
//<<左移指令
前面都是直接移动了鼠标,这次要改发送鼠标消息了,这样鼠标不移动也会点击游戏的开始按钮.SendMessage的参数是相对坐标,mouse_event的参数是绝对坐标
再次打开SPY++,找到棋盘第一格的位置,X=21,Y=187
新增一个按钮”点击棋盘第一格”方便测试,添加代码如下:
voidCLlk_wgDlg:
:
OnButton3()//按钮函数
{
intx=22,y=187;//定义座标点
HWNDhwnd=:
:
FindWindow(NULL,gameCaption);//查找窗口
intlparam;//定义座标点变量
lparam=(y<<16)+x+31*2;//表示指定格,Y<<16是左移16位,发消息用的Y座标点
:
:
SendMessage(hwnd,WM_LBUTTONDOWN,0,lparam);//鼠标按下消息
:
:
SendMessage(hwnd,WM_LBUTTONUP,0,lparam);//鼠标抬起消息
}
用QQ抓图,查找棋子格子数大小,31*35的大小,以便计算出所有格子的座标点.
1.3.2消掉一对棋子的算法框架
a、遍历棋盘同类型棋子配对
b、构建算法框架
//遍历整个棋盘找相同一对棋子
//检测这一对棋子是否可以消除
//如果可以消除,则模拟鼠标点击这2点
最多一条三条连线能够形式一条路线就表示可消除
更改”点击棋盘第一格”代码如下:
voidClearPiar()//消除一对棋子
{
//读出棋盘数据至chessdata11,19
updatdChess();
//遍历整个棋盘找出相同类型一对棋子
POINTp1,p2;//定义两个点,座标型
intx1,y1,x2,y2;//定义座标点
for(y1=0;y1<11;y1++)//点1循环,从Y列开始
for(x1=0;x1<19;x1++)//点2循环,从X行开始
{for(y2=y1;y2<11;y2++)//这是点2的Y循环,由Y1开始
for(x2=x1;x2<19;x2++)//这是点2的X循环,由X1开始
//棋子1与棋子2类型是否相同,要求点1与点2相等则假,就是座标不能相同.
if((chessdata[y1][x1]==chessdata[y2][x2])&&(!
((x1==x2)&&(y1==y2))))
{
p1.x=x1;p1.y=y1;
p2.x=x2;p2.y=y2;
//检测相同的2个棋子是否可消掉
if(check2p(p1,p2))//如果可消除则返回真
{
//click2p鼠标模拟点击p1,p2
click2p(p1,p2);
}
}
}
}
1.3.3(Check2p)大致框架(算法核心)
a、在这一对棋子间找相通路径的原理
b、(Check2p函数)框架代码
c、(CheckLine函数)检测2点是否有连通
LineNull(p1,p2);//是否在棋盘上的2个点之前是否有一条全为0的直线,如有true,否则false
1、剪贴游戏图;
Y坐标相同的情况下p1,p2
lineNull(p1.right,p2.left)//可消除
X坐标相同的情况下p1,p2
LineNull(p1.down,p2.up)//可消除
X与Y坐标都不相情况下p1,p2
lineNll(p1.down,pa),LineNull(p2.down,pb),LineNull(pa,pb)
//可消除
现在就要来分析游戏了,连连看大家都知道,是需要判断两点间是否可以连接的,比如有直连,有一折后的连接,最多是二折后的连接.
如果是直连的话,就要判断中间是否所有的数据都为0,判断的思路很主要,一定要搞清楚.而有折的连接则需要多条直线,最多是三条直线,这个需要遍历,不断的向下判断.
将VC代码打开,插入一个类GenericClass,名称为CChessPoint
添加代码如下:
classCChessPoint
{
public:
POINTp;//临时点
POINTup;//上点
POINTdown;//下点
POINTleft;//左点
POINTright;//右点
CChessPoint(POINTpxy);//构造函数
virtual~CChessPoint();
};
还要在Cchesspoint.cpp实现部分修改代码
CChessPoint:
:
CChessPoint(POINTpxy)
{up=pxy;down=pxy;left=pxy;right=pxy;//将座标初始化
//向上下左右扩展
p=pxy;
up.y=pxy.y-1;
down.y=pxy.y+1;
left.x=pxy.x-1;
right.x=pxy.x+1;
//这样处理完之后每个棋子就包涵了上下左右中五个点的属性
}
CChessPoint:
:
~CChessPoint()
{
}
在按钮部分增加代码如下:
boollineNull(POINTp1,POINTp2)
{
returntrue;//先写个空的,下节课继续.
}
boolcheck2p(POINTp1,POINTp2)
{//检测p1,p22个棋子是否可以消除
//Y坐标相同的情况下p1,p2
//lineNull(p1.right,p2.left)//可消除
if(p1.y==p2.y)//如果列相同则执行
{CChessPointpa(p1),pb(p2);//先建立类,初始化两点
if(lineNull(pa.down,pb.up))returntrue;//先将两个点类化,可消除返回真
}
//X坐标相同的情况下p1,p2
//LineNull(p1.down,p2.up)//可消除
if(p1.x==p2.x)//如果行相同则执行此句
{CChessPointpa(p1),pb(p2);
if(lineNull(pa.down,pb.up))returntrue;
}
//X与Y坐标都不相情况下p1,p2
//lineNull(p1.down,pa),LineNull(p2.down,pb),LineNull(pa,pb)
//可消除
returntrue;
}
1.3.4CheckLine实现
a、CheckLine函数实现
b、Check2p核心代码架构
boolCheckLine(POINTp1;POINTp2)
{
//x坐标相同
//p1.ytop2.y
//Y坐标相同
//p1.xtop2.x
}
首先还是要添加修改两点间是直线的判断函数
boolCheckLine(POINTp1,POINTp2)//检测2点间是否连通(存在一条全为0的直线路径)
{
intx,y;
if(p1.x==p2.x)//两点X坐标相同
{
for(y=p1.y;y<=p2.y;y++)
{
//假如ChessData[y][p1.x]Y的某一个点大于0则说明有棋子,就返回false;
if(chessdata[
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- VC 外挂 教程