声明:本文只是为了初学C++的,能够做出一些实用的东西,跳出管理系统的束缚,提升学习的兴趣,在这里选取了单机游戏,请不要尝试在线游戏,违发而已未必可行。
序:首先我们需要一个Qt+VS环境
Qt从http://download.qt.io/archive/中下载,第一个和第三个,在里面选择对应版本。然后就是配环境了,这里提供2013+Qt5.5.1的环境配置,如果环境不同,请自行百度。这点解决问题能力都没有,就别学C++了...
我的环境是2013+Qt5.5.1,不同版本可能略有差异,不过大同小异。
首先打开VS创建工程,点确定,然后一直下一步

选择QWidget,点完成

然后就创建了一个工程,我们来说说这个工程。

一:main函数和C++的很像

然后再看看刚才创建的Qt窗口类

先是头文件

然后是CPP文件

再看看一些“奇怪”的文件

下面这两个是Qt的moc文件,是编译时自动生成和更新的,所以不用管

下面第一个是资源文件的代码CPP,自动生成的,不用管

第二个是UI文件的头文件,自动生成的,不用管

资源文件,用来加载图片等一些资源,这里没用到,不用管

这个就是上面提到的UI文件,相当于可视化界面设计器,用来设计界面的。

双击点开XXXXX.UI文件

二:接下来开始界面设计

在控件盒子中左键选中一个文件标签,一个文字输入框,一个按钮,然后往界面设计器里面拖。文件标签在左,文字输入框在中,一个按钮在右。

ctrl+鼠标左键点选三个控件,然后在任意一个控件上右键,选择布局->水平布局

开始界面布局

右键大窗口,选择布局->垂直布局

然后鼠标放到界面设计器的边框边缘,按住左键拖动到合适大小

在对象查看器里左键点选大窗口,然后属性窗口往下拖,在WindowTitle里修改窗口标题

双击控件修改控件的文本

记录控件的对应关系,把金钱技能和属性对应的输入框和按钮记录下来

在这里我金钱的输入框是lineEdit,金钱修改按钮是pushButton,技能点则分别是lineEdit_2和pushButton_2,属性点则是lineEdit_3和pushButton_3

然后点保存,注意一定要保存

三:实现前的知识普及

1:游戏内存修改的知识普及

一般游戏数据有一个地址值,但是这个地址值是动态的,每次游戏重启都会发生变化,所以我们要找到不变的一级基址,和两个不变的偏移量,来得到最新的游戏数据地址。

2:Qt信号槽知识普及

①Qt信号

信号是指一种通知,形象地比喻下:比如你带了许多巧克力去公司,然后在群里告诉大家,“我带了很多巧克力,要的来我工位拿”,这里公司群就是你的应用程序,群员就是程序里的实例化对象,你说的话这就是一种信号;可能有些人会无视,有些人根本没看见,有些人会来要,有些人会转告其他人,你只负责发出一个通知,你不关心别人看到你的通知会作何反应。

② Qt槽函数

槽指的是一种行为函数,定义了收到信号通知后,应该做出何种反应,上面巧克力的例子,无视,转告和要巧克力,都是一种对于信号通知的响应行为。

③ Qt的connect函数

就是对信号和槽进行关联,A发的信号通知B做出某种响应行为。

④ Qt的QTimer

定时器,按照你设置的时间间隔,不间断发出timeout()信号通知。

⑤ QMessageBox::information()

显示一个提示窗口

⑥ ui控件的指针怎么找

UI控件的指针和objectname同名,而objectname就是在界面设计器点选对应控件,属性里第一个

使用的时候用ui.objectname或者ui->objectname,用哪种取决于h文件里的ui变量是对象还是指针。这里就是ui.objectname。

四:基址和偏移量查找

①现在我们要用到一个软件,名字叫cheat engine,我的是6.6中文版。游戏以骑马与砍杀为例,首先修改金钱。

②把金钱数据输入ce,点击新的扫描

③想办法改变金钱数,输入CE,点击再次扫描,不断重复这条,直到数据只有一个

(注意:有可能会遇到一直有2个的情况,这样的情况试着改下数据就行了,哪个生效就是哪个)

④这里得到的就是一个游戏数据内存,可以改游戏数据值,但他是动态的,游戏重启就失效了,我们需要找的是基址。

⑤鼠标右键这个游戏数据地址,查找什么改变了这个值。

⑥然后出现这个界面,一开始是没有数据的,需要改变下游戏数据(这里是金钱数)

⑦双击这条数据,这里的5D0就是第一个偏移量,4B4C1024就是下一个要查找的地址。

⑧开启一个新的扫描

⑨选择需要的的地址查找是什么访问了这个地址,有时候有很多个,一般是比较特殊的那个(就是其他地址开头都是一样的,就他不一样),或者一个个看,有数据的就是我们需要的那个地址(注意无需改变游戏数据就有数据)

⑩随便双击一个mov指令数据,这里的140EC就是第二个偏移值,48D2E010就是下一个要查找的地址

①①用新拿到的推荐地址重复第⑧步,查找的绿色地址就是一级基址了

①②开始效验这个基址

①③ 修改这个地址的数值,如果钱发生变化的话就找对了

同理,用这个方法查找技能点

找出来的一级基址是009D5E2C,偏移是5D0 2BC,发现没有,一级基址和第二次偏移是一样的,所以之后查找,找一次偏移就可以了。

【特别注意】网上有些攻略说一级基址+第二次偏移量+第一次偏移量就是游戏数据地址,其实是错的,应该是一级基址里保存的值+第二次偏移量得到二级基址,二级基址里保存的值+第一次偏移量才是游戏数据地址。

五:代码实现部分,教程以注释展现

只实现了技能点和金钱

  1. #ifndef GAMEEDITOR_H
  2. #define GAMEEDITOR_H
  3.  
  4. #include <QtWidgets/QWidget>
  5. #include "ui_gameeditor.h"
  6. //读写游戏内存所必须的头文件
  7. #include <windows.h>
  8.  
  9. class QTimer;
  10. class GameEditor : public QWidget
  11. {
  12. Q_OBJECT
  13.  
  14. public:
  15. GameEditor(QWidget *parent = 0);
  16. ~GameEditor();
  17.  
  18. //slots就是表示槽函数
  19. protected slots:
  20. void connectGame();
  21. void updateGameMoney();
  22. void updateGamePerks();
  23.  
  24. private:
  25. Ui::GameEditorClass ui;
  26. //计时器指针,见5-2-4
  27. QTimer* timer;
  28. //金钱地址
  29. DWORD moueyAdress;
  30. //技能点地址
  31. DWORD perksAdress;
  32. //进程PID
  33. DWORD pid;
  34. HWND hwnd;
  35. //进程句柄
  36. HANDLE handle;
  37. };
  38.  
  39. #endif // GAMEEDITOR_H

  

  1. #include "gameeditor.h"
  2. //定时器头文件
  3. #include <QTimer>
  4. //提示框头文件
  5. #include <QMessageBox>
  6.  
  7. GameEditor::GameEditor(QWidget *parent)
  8. : QWidget(parent)
  9. {
  10. ui.setupUi(this);
  11. pid = 0;
  12. hwnd = 0;
  13. handle = 0;
  14. //金钱一级基址
  15. moueyAdress = 0x009D5E2C;
  16. //技能点一级基址
  17. perksAdress = 0x009D5E2C;
  18. //创建一个定时器,见5-2-4
  19. timer = new QTimer;
  20. //设置时间间隔为1000毫秒
  21. timer->setInterval(1000);
  22. //timeout为计时器内置信号,时间一到自动发送
  23. //connect为关联信号槽,详细见前面的知识普及内容
  24. //connect(信号发送者指针,SIGNAL(信号), this, SLOT(槽实现函数));
  25. connect(timer, SIGNAL(timeout()), this, SLOT(connectGame()));
  26. //clicked为按钮内置信号,点击自动发送
  27. //还记得吗,ui.pushButton是金钱修改按钮,ui.pushButton_2是技能点修改按钮
  28. connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(updateGameMoney()));
  29. connect(ui.pushButton_2, SIGNAL(clicked()), this, SLOT(updateGamePerks()));
  30. ui.label_4->setText(QString::fromLocal8Bit("正在等待游戏程序...."));
  31. //计时器开始计时
  32. timer->start();
  33. }
  34.  
  35. GameEditor::~GameEditor()
  36. {
  37.  
  38. }
  39.  
  40. void GameEditor::connectGame()
  41. {
  42. //查找窗口并返回窗口句柄
  43. hwnd = FindWindow(0, L"Mount&Blade Warband");
  44.  
  45. if (!hwnd)
  46. {
  47. return;
  48. }
  49.  
  50. //通过窗口句柄获取pid
  51. GetWindowThreadProcessId(hwnd, &pid);
  52.  
  53. if (!pid)
  54. {
  55. return;
  56. }
  57.  
  58. //通过pid打开一个进程并获取进程句柄
  59. handle = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
  60.  
  61. if (!handle)
  62. {
  63. return;
  64. }
  65.  
  66. //通过金钱一级基址读取里面的数据
  67. DWORD newMoneyAdress = 0;
  68. ReadProcessMemory(handle, (LPVOID)moueyAdress, &newMoneyAdress, sizeof(newMoneyAdress), 0);
  69. //读取不到说明你开了游戏,但是没有开始
  70. if (moueyAdress <= 0)
  71. {
  72. return;
  73. }
  74. ui.label_4->setText(QString::fromLocal8Bit("游戏程序连接成功!"));
  75. //停止计时器
  76. timer->stop();
  77.  
  78. //金钱一级基址里的值+第二个偏移量=金钱二级基址
  79. moueyAdress = newMoneyAdress;
  80. moueyAdress += 0x140EC;
  81. //通过金钱二级基址读取里面的数据
  82. ReadProcessMemory(handle, (LPVOID)moueyAdress, &newMoneyAdress, sizeof(newMoneyAdress), 0);
  83.  
  84. //金钱二级基址里的值+第一个偏移量=游戏数据地址(动态),游戏数据地址里的值就是游戏数据
  85. moueyAdress = newMoneyAdress;
  86. moueyAdress += 0x5D0;
  87.  
  88. //同理通过技能点一级基址和偏移量获取游戏数据地址
  89. DWORD newPerksAdress = 0;
  90. ReadProcessMemory(handle, (LPVOID)perksAdress, &newPerksAdress, sizeof(newPerksAdress), 0);
  91. perksAdress = newPerksAdress;
  92. perksAdress += 0x140EC;
  93. ReadProcessMemory(handle, (LPVOID)perksAdress, &newPerksAdress, sizeof(newPerksAdress), 0);
  94. perksAdress = newPerksAdress;
  95. perksAdress += 0x2BC;
  96. }
  97.  
  98. //金钱的修改按钮被点击
  99. void GameEditor::updateGameMoney()
  100. {
  101. //还记得吗,ui.lineEdit就是金钱的文本输入框,text()表示获取输入框的文本,toInt()表示转化为int数据
  102. DWORD money = ui.lineEdit->text().toInt();
  103. //修改游戏数据地址里的值
  104. DWORD pref=WriteProcessMemory(handle, (LPVOID)moueyAdress, &money, sizeof(money), 0);
  105. //QString是Qt里的字符串
  106. QString informationStr;
  107. if (pref)
  108. {
  109. informationStr = QString::fromLocal8Bit("修改成功");
  110. }
  111. else
  112. {
  113. informationStr = QString::fromLocal8Bit("修改失败");
  114. }
  115. //信息提示框,第一个参数是父窗口,填写this或者0都可以,第二个参数是标题,第三个参数是提示内容
  116. QMessageBox::information(this, QString::fromLocal8Bit("提示"), informationStr);
  117. }
  118.  
  119. //技能点的修改按钮被点击
  120. void GameEditor::updateGamePerks()
  121. {
  122. DWORD perks = ui.lineEdit_2->text().toInt();
  123. DWORD pref = WriteProcessMemory(handle, (LPVOID)perksAdress, &perks, sizeof(perks), 0);
  124.  
  125. QString informationStr;
  126. if (pref)
  127. {
  128. informationStr = QString::fromLocal8Bit("修改成功");
  129. }
  130. else
  131. {
  132. informationStr = QString::fromLocal8Bit("修改失败");
  133. }
  134. //信息提示框,第一个参数是父窗口,填写this或者0都可以,第二个参数是标题,第三个参数是提示内容
  135. QMessageBox::information(this, QString::fromLocal8Bit("提示"), informationStr);
  136. }

  

只需要一点点C++基础,新手也可以制作单机游戏内存修改器的更多相关文章

  1. Android基础新手教程——3.1 基于监听的事件处理机制

    Android基础新手教程--3.1.1 基于监听的事件处理机制 标签(空格分隔): Android基础新手教程 本节引言: 第二章我们学习的是Android的UI控件,我们能够利用这些控件构成一个精 ...

  2. Android基础新手教程——1.5.2 Git之使用GitHub搭建远程仓库

    Android基础新手教程--1.5.2 Git之使用GitHub搭建远程仓库 标签(空格分隔): Android基础新手教程 本节引言: 在上一节中.我们学习了怎样使用Git.构建我们的本地仓库.轻 ...

  3. Android基础新手教程——3.8 Gestures(手势)

    Android基础新手教程--3.8 Gesture(手势) 标签(空格分隔): Android基础新手教程 本节引言: 周六不歇息,刚剪完了个大平头回来.继续码字~ 好的,本节给大家带来点的是第三章 ...

  4. Android基础新手教程——1.10 反编译APK获代替码&amp;资源

    Android基础新手教程--1.10 反编译APK获代替码&资源 标签(空格分隔): Android基础新手教程 本节引言: "反编译Apk".看上去好像好像非常高端的样 ...

  5. Android基础新手教程——1.6 .9(九妹)图片怎么玩

    Android基础新手教程--1.6 .9(九妹)图片怎么玩 标签(空格分隔): Android基础新手教程 1.本节引言: 可能有的一些疑问: 1.什么是.9图片? 答:图片后缀名前有.9的图片,如 ...

  6. Android基础新手教程——4.1.3 Activity登堂入室

    Android基础新手教程--4.1.3 Activity登堂入室 标签(空格分隔): Android基础新手教程 本节引言: 好的,在学习了两节的Activity后相信大家已经知道怎样去使用Acti ...

  7. Android基础新手教程——4.1.2 Activity初窥门径

    Android基础新手教程--4.1.2 Activity初窥门径 标签(空格分隔): Android基础新手教程 本节引言: 上一节中我们对Activity一些主要的概念进行了了解,什么是Activ ...

  8. Android基础新手教程——4.4.1 ContentProvider初探

    Android基础新手教程--4.4.1 ContentProvider初探 标签(空格分隔): Android基础新手教程 本节引言: 本节给大家带来的是Android四大组件中的最后一个--Con ...

  9. Android基础新手教程——3.7 AnsyncTask异步任务

    Android基础新手教程--3.7 AnsyncTask异步任务 标签(空格分隔): Android基础新手教程 本节引言: 本节给大家带来的是Android给我们提供的一个轻量级的用于处理异步任务 ...

随机推荐

  1. 自己动手写一个自动登录脚本gg

    1.下载一个sshpass工具 2.安装sshpass,安装到tools文件夹 3.把tools文件夹的路径加入到/etc/bashrc vim   /etc/bashrc 最后一行  : expor ...

  2. asp.net core 中灵活的配置方式

    asp.net core支持外部文件和命令行参数方式来配置系统运行所需要的配置信息,我们从下面两个常用场景来具体说下具体使用方法. 一.监听地址及端口配置 1,命令行方式 asp.net core系统 ...

  3. Python 操作 MYSQL

    本文介绍了 Python 操作 MYSQL.执行 SQL 语句.获取结果集.遍历结果集.取得某个字 段.获取表字段名.将图片插入数据库.执行事务等各种代码实例和详细介绍,代码居多, 是一桌丰盛唯美的代 ...

  4. 简单地总结几种常见web攻击手段及其防御方式

    web攻击手段有几种,本文简单介绍几种常见的攻击手段及其防御方式 XSS(跨站脚本攻击) CSRF(跨站请求伪造) SQL注入 DDOS XSS 概念 全称是跨站脚本攻击(Cross Site Scr ...

  5. js实现两个输入框中的数字相乘并自动将结果显示在第三个输入框

    <script type="text/javascript"> function cal(ida,idb,idc) { var numa=Number(document ...

  6. (转)ManyToMany注解

    @ManyToMany  注释:表示此类是多对多关系的一边,mappedBy 属性定义了此类为双向关系的维护端,注意:mappedBy 属性的值为此关系的另一端的属性名. 例如,在Student类中有 ...

  7. Django学习(八)---修改文章和添加文章

    博客页面的修改文章和添加新文章 从主页点击不同文章的超链接进入文章页面,就是传递了一个id作为参数,然后后台代码根据这个参数从数据库中取出来对应的文章,并把它传递到前端页面 修改文章和添加新文章,是要 ...

  8. Linux常用命令及shell技巧

    这里列出一些个人在工作中常使用的各种linux命令,每一个不详细讲参数,只写经常用的参数.希望快速获得在linux命令行工作的能力的朋友可以看看.本人一直觉的,不使用linux 图形界面,以xshel ...

  9. 【学习笔记】C# 封装和继承

    封装 封装是实现面向对象程序设计的第一步 封装就是将数据.方法等集合在一个个单元中,我们称之为类 封装的意义在于保护代码/数据,屏蔽复杂性 继承 继承是所有面向对象语言不可缺少的部分 继承是为了实现类 ...

  10. nyoj_61: 传纸条(一)

    题目链接 使用双线dp,假设两个人同时从左上角移动到右下角,且满足路线不交叉,另k=x1+y1=x2+y2压缩状态进行优化.每次状态转移满足 x1,x2,y1,y2都在矩阵范围内,且(x2,y2)在相 ...