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

2019-04-01 17:53

//让游戏窗口置顶

::SetWindowPos(gameh,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); //窗口置顶函数 }

else //如果没有窗口被找到 {

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

::SetWindowPos(gameh,HWND_NOTOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); //取消置顶 } }

再继续添加棋子数功能void CLlk_wgDlg::OnClearAll() 函数在上面教案里,这个就是秒杀功能.但是还需要读出棋子数,函数是int ReadChessNum(),在上面教案里.然后再调整一下秒杀功能.然后再调整一下置顶功能, //让游戏窗口置顶

if (gametop)//新建的变量方便设置 {//选中则置顶

SetWindowPos(gameh,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } else

{//未选中则不置顶

SetWindowPos(gameh,HWND_TOP,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); }

1.4.4 初级篇小结

a、游戏分析小结 b、编程小结 知识点:

1、GetWindowRect//窗口信息的获取 BOOL GetWindowRect(

HWND hWnd, // handle to window

LPRECT lpRect // 存放返回值的首地址 RECT );

2、SetCursorPos//设置鼠标指针 BOOL SetCursorPos( int X, //X int Y //Y );

3、mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//硬件模拟鼠标 4、FindWindow //获取窗口句柄 HWND FindWindow(

LPCTSTR lpClassName, //窗口类名 NULL LPCTSTR lpWindowName //窗口标题 NULL );

5、GetWindowThreadProcessId //获取窗口进程ID DWORD GetWindowThreadProcessId(

HWND hWnd, // handle to window

LPDWORD lpdwProcessId // 指向变量的指针 用来返回进程PID );

6、OpenProcess //打开指定进程 HANDLE OpenProcess(

DWORD dwDesiredAccess, // 访问权限 标记 BOOL bInheritHandle, // false;

DWORD dwProcessId // lpdwProcessId 进程ID标识 );

7、ReadProcessMemory //读指定进程 内存数据 BOOL ReadProcessMemory(

HANDLE hProcess, // HANDLE OpenProcess返回值 LPCVOID lpBaseAddress,

// 读取 进程起始地址 基址 LPVOID lpBuffer, // 存放数据的缓冲区 DWORD nSize, // 要读出的字节数 LPDWORD lpNumberOfBytesRead

// 实际读出字节数 );

8、WriteProcessMemory //写内存,参数同读内存 9、SendMessage //可以软模拟 鼠标 键盘操作 10、SetTimer UINT SetTimer(

HWND hWnd, // 指向窗口的句柄 UINT nIDEvent, // 定时器 标识ID

16

UINT uElapse, // 时间间隔(毫秒) TIMERPROC lpTimerFunc //回调函数 );

VOID CALLBACK TimerProc(

HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // 当前系统时间 );

11、KillTimer() BOOL KillTimer(

HWND hWnd, // 指向窗口的句柄 UINT uIDEvent // 定时器 标识ID );

12、SetWindowPos //HWND_TOPMOST 窗口置顶 控件讲解:

CButton slider//滑块控件

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); //选中复选框 基础知识:

a、数据类型:Bit,Byte,Word,Dword,float,double b、用CE查找数据 c、CE工具使用技巧 d、OD调试

老师总结,我也总结.初级班的课程我已经全部看完了, 相对于DELPHI班来讲记录的也比较仔细,相信我的记录能够帮助其它同学和网友,其实记录主要就是记一下课程的内容,方便以后回忆学习.总体来讲,VC班的初级教程内容比较多,相对于DELPHI班初级教程来讲,游戏也复杂了些,编写代码就更加复杂,就算是有DELPHI基础,想学起VC代码来我也感觉到非常吃力.不过我也觉着这个学习的顺序是对的,如果一开始就学VC,那恐怕我就没有信心了.想起自己在D班里的感想,弄外挂,编程会则可成. 2.1.1、CALL的概念(远程调用CALL)

a、写个调用示例(假想游戏客户端)

b、用OD找CALL,初探(用OD找出我们自己写的CALL) c、代码注入器,远程CALL调用 1、先写一个假想的游戏客户端. //VC++

SetWindowText(h,\窗口标题\ char s[33];

itoa(BoolValue,s,10);//整型转字串 m_edt1.SetWindowText(s); //找加血CALL

1//CE找的 血值 基址:416680 2 OD硬件 断点 hw 416680 3 //代码注入器 call 402360 //加血

call 4023d0 //减血 40108c call 40108c //减血 //

HANDLE CreateRemoteThread(

HANDLE hProcess, // OpenProcess

LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全结构指针 NULL DWORD dwStackSize, // 0

LPTHREAD_START_ROUTINE lpStartAddress, // 指向我们的CALL 地址 LPVOID lpParameter, // 传递的参数指针 NULL DWORD dwCreationFlags, // 0

LPDWORD lpThreadId // 返回一个 线程ID标识 );

首先讲一下CALL的基本知识,在VC里新建一个窗口,并增加编辑框,关联一个控件类型的变量m_edt1 int BoolValue=3000;//新建一个血值,假设为3000 HWND edit_hwnd; //设置全局变量 然后添加如下代码

void addBlood()//加血代码 {

char s[33]; //建立缓冲变量 BoolValue+=22;//每次加22血

itoa(BoolValue,s,10); //整型转换为10进制字符串

17

SetWindowText(edit_hwnd,s); //设置编辑框数值 }

void decBlood()//减血代码 {

char s[33]; BoolValue-=22; itoa(BoolValue,s,10);

SetWindowText(edit_hwnd,s); }

[attachment=548]

那么我们就要用CE来查找这个加血和减血按钮的CALL,打开CE,搜索一下当前的血值,再改变一下血值再搜索,如果找到的地址是绿色的,那么就证明其值是全局变量,也就是一级基址/主基址,改变一下血值,就找到其地址了,那么我们在OD里下一个内存断点 Hw 416680 , 被断下之后在00402385位置,向上看,我们来到函数的头部,也就是上面全是int 3的第一句那么我们找到的加血的CALL就是402360. [attachment=549]

打开代码注入器,输入 call 402360 发现确实加血了 同理,再查找出减血的 CALL 4023d0

当然除此之外,在此函数头部的上一句是一个JMP语句,而这句也可以调用

最后又讲了一下CreateRemoteThread创建线程函数的用法,参数在上面教案里有详解. 2.1.2、远程CALL调用代码实现

a、CreateRemoteThread API函数

b、无参数的远程CALL调用(代码实现) 1、先写一个假想的游戏客户端. call 402360 //加血

call 4023d0 //减血 40108c call 40108c //减血

HANDLE CreateRemoteThread(

HANDLE hProcess, // OpenProcess

LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全结构指针 NULL DWORD dwStackSize, // 0

LPTHREAD_START_ROUTINE lpStartAddress, // 指向我们的CALL 地址 LPVOID lpParameter, // 传递的参数指针 NULL (stdcall) DWORD dwCreationFlags, // 0

LPDWORD lpThreadId // 返回一个 线程ID标识 int lptid;=(LPDWORD) &lptid; );

HWND h=FindWindow(NULL,\DWORD id;LPDWORD pid=&id;

GetWindowThreadProcessId(h,Pid);

HANDLE hp=OpenProcess(PROCESS_ALL_ACCESS,false,Pid); DWORD tid;

CreateRemoteThread(hp,NULL,0, (LPTHREAD_START_ROUTINE)(0x402360 ) ,NULL,0,&tid);

上节课我们是用的代码注入器来调用的CALL,那么这次我们自己编写代码来调用CALL,这需要几个函数的联合运用,参数的详解在上面的教案中. 1, FindWindow

2, GetWindowThreadProcessId 3, OpenProcess

4, CreateRemoteThread 新建一个窗口 [attachment=551] 具体的调用代码如下:

void RemoteCall(int CallAddr)//整理出来的CALL调用函数 {

HWND h; //窗口变量

h=::FindWindow(NULL,\查找窗口句柄需要全局标识符 DWORD id; //进程ID

LPDWORD Pid=&id; //定义临时变量方便调用

::GetWindowThreadProcessId(h,Pid); //取得指定窗口的进程ID 存放到变量id里边 HANDLE hp=OpenProcess(PROCESS_ALL_ACCESS,false,id);//获取访问进程权限存放至hp DWORD tid; //定义临时变量方便调用 //在进程里调用 CallAddr

CreateRemoteThread(hp,NULL,0,(LPTHREAD_START_ROUTINE)CallAddr ,NULL,0,&tid); }

void CCALLDlg::OnRemoteAdd() //加血调用 {

RemoteCall(0x402360); //加血CALL 16进制写法 }

void CCALLDlg::OnBTNDecBlood() //减血调用 {

RemoteCall(0x40108c );//减血CALL 16进制写法

18

}

备注:LPTHREAD_START_ROUTINE 指向的函数是回调函数,并且必须由宿主应用程序的编写器实现。

因为我有DELPHI基础,上面的4个函数我在DELPHI当中可是经常用,所以很熟悉,我只需要了解VC中的写法即可 2.1.3、调试工具OD简介(人物角色)血值,魔力值; a、CE找出当前血值偏移 b、OD 分析出魔力值 c、导出游戏关键代码 血值 基址 0x45657F0 +4: 魔力值 +8: 持久力 +C:血值上限 +10:魔力值上限

+14:持久力上限 1000 +1C:善恶度 +18:当前经验值

+20:升级到下一级所需经验值 +24: 灵兽 持有数量 上限2 +2C:历练值 +30:心 +34:力 +38:体 +3C:身

终于要正式搞游戏了,下载RXJH吧,首先从血值入手,用CE查找当前的血值631,打两下怪,再CE查找当前的血值564,再吃点药,又变成631了,继续用CE一搜就找到了,基址都是绿色的,当前搜索到的值不是绿色,那么这就是一个变量的值,还好我们找到了一个绿色地址045657F0 [attachment=553]

再用OD附加一下,确定此位置,显示一下内存区域 [attachment=554]

通过猜测,我们找到了相关的血值上限和蓝值等,详细在上面的教案中.但是我们需要找到一级基址,我们在血值处下一个断点,被断在0058957C处,但是没再继续找,下节课再见.

我发现RXJH的各类偏移真的很好找,至少比50好找,如果各位朋友想练习查找基址的基本功,那就拿个别的游戏再练练手. 2.1.4、游戏基址概念;

a、基址+偏移 概念

b、读写内存函数 参数简介

c、编程实现读出(血值,魔力值) 血值 基址 45657F0 :一级 全局结构变量 首地址 +4: 魔力值 //45657F4 二级基址 +8: 持久力 +C:血值上限 +10:魔力值上限

+14:持久力上限 1000 +1C:善恶度 +18:当前经验值

+20:升级到下一级所需经验值 +24: 灵兽 持有数量 上限2 +2C:历练值 +30:心 +34:力 +38:体 +3C:身

//API函数 远程读取数据的方法

上一节的基址还没找完,本节课继续,先建一个小程序用来读取血值和魔值,我们通过API函数来读取 其实就是四个函数的联合运用,前面都有讲过 1, FindWindow

2, GetWindowThreadProcessId 3, OpenProcess

4, ReadProcessMemory

为了方便调用, 再新建一个头文件GameProc.h(他总愿意建这个名字的) 将游戏的标题定义好#define GameCaption \在窗口中关联变量 m_role_bloodvalue m_role_MagicValue 然后编写具体代码如下:

void CReadRoleBloodDlg::OnBtnReadblood() //读取血值函数 {

// TODO: Add your control notification handler code here HWND gameh=::FindWindow(NULL,GameCaption); //获取窗口句柄 DWORD processid; //建立ID变量

::GetWindowThreadProcessId(gameh,&processid); //获取窗口进程ID

HANDLE processH=::OpenProcess(PROCESS_ALL_ACCESS,false,processid); //打开指定进程

19

//读指定进程内存数据

DWORD byread; //读取字节数变量 LPCVOID pbase=(LPCVOID)0x45657F0; //读取血值的基址

LPVOID nbuffer=(LPVOID)&m_role_bloodvalue; //存放读出数据的缓冲区 ::ReadProcessMemory(processH,pbase,nbuffer,4,&byread);//读血值 pbase=(LPCVOID)(0x45657F0+4); //读取的基址

nbuffer=(LPVOID)&m_role_MagicValue; //存放读出数据的缓冲区 UpdateData(false); }

void CReadRoleBloodDlg::OnBtnMagicValue()//读取魔值函数 {

// 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)0x45657F0; //读取血值的基址

LPVOID nbuffer=(LPVOID)&m_role_bloodvalue; //存放读出数据的缓冲区 pbase=(LPCVOID)(0x45657F0+4); //读取的基址

nbuffer=(LPVOID)&m_role_MagicValue; //存放读出数据的缓冲区 ::ReadProcessMemory(processH,pbase,nbuffer,4,&byread); UpdateData(false); }

[attachment=555]

试验一下血和魔已经读取正确了,那么我们如何才能够时时的读取呢,还可以用以前用过的时间函数. 2.1.5、常用汇编指令详解(VC++内联汇编) a、Mov指令的几种形式

b、汇编指与高级语言的转换 c、push指令

eax,ebx,ecx,edx,edi,esi;//32位=4字节 esp,ebp; eax //

ax,bx,cx,dx,di,si,sp,bp低16位 al,bl,cl,dl 低8位

void mycall(int x,int y) {

_asm {

// mov eax,33 // eax=a // mov ebx,11 // ebx=b // mov b,eax // b=eax // mov a,ebx // a=ebx mov edi,edi mov edi,edi mov eax,x

mov a,eax // a=b mov eax,y mov b,eax } }

本节课讲解汇编基础知识,也就是VC++内联汇编,就是在VC代码中直接调用汇编代码,新建一个MFC基础对话框,用于测试汇编代码,并对几个寄存器进行了介绍,先介绍一下MOV指令,添加汇编代码在按钮事件中: _asm {

Mov edi,edi Mov edi,edi Mov a,11 Mov b,33 }

测试成功之后,再来到OD里反汇编看一下 [attachment=556]

20


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

下一篇:组织行为学重点

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

马上注册会员

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