JAVA实战训练营Day.2 ——“假如我是游戏设计师”
目录
前言
一、Day.2的目标
二、任务所涉及的知识点(参考资料)
1.JAVA 进制转换的几个方法
2.IDEA配置Java环境及基本使用
三、实现任务的工具
1.《植物大战僵尸》游戏文件
2.IDEA(社区版)
3.exe4j和inno setup complier
4.一个工具人(QAQ)
四、开始实战
1.Java实现修改关卡
2.Java实现修改金币
3.完整源码
4.个人对游戏数据渗透与风控的拙见(手动卑微)
五、总结
前言
?
?昨天我们通过Hex Editor Neo完成了对《植物大战僵尸》游戏数据的修改,那我们今天就尝试使用Java语言来实现对游戏数据的修改。并且还要自己当游戏设计,站在风控和渗透工程师的视角再回顾任务,改进这款游戏。
一、Day.2的目标
修改游戏《植物大战僵尸》,完成以下目标:
1.Java语言实现对游戏数据的修改
2.对游戏进行风控设计,改进游戏
二、任务所涉及的知识点(参考资料)
1.Java进制转换的几个方法
https://blog.csdn.net/m0_37961948/article/details/80438113https://blog.csdn.net/m0_37961948/article/details/80438113https://blog.csdn.net/m0_37961948/article/details/80438113
2.IDEA配置Java环境及基本使用
Java环境的配置:
https://blog.csdn.net/weixin_39536315/article/details/112793788?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163532650016780261946696%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163532650016780261946696&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-112793788.pc_search_all_es&utm_term=idea%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83&spm=1018.2226.3001.4187https://blog.csdn.net/weixin_39536315/article/details/112793788?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163532650016780261946696%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163532650016780261946696&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-112793788.pc_search_all_es&utm_term=idea%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83&spm=1018.2226.3001.4187https://blog.csdn.net/weixin_39536315/article/details/112793788?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163532650016780261946696%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163532650016780261946696&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-112793788.pc_search_all_es&utm_term=idea%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83&spm=1018.2226.3001.4187
Java打包封装:
链接里exe4j和inno setup complier分享失效了,可以自行在CSDN内下载。
(会员好贵啊,官方大大啥时候发放福利QAQ)
https://blog.csdn.net/weixin_42562514/article/details/106603392https://blog.csdn.net/weixin_42562514/article/details/106603392https://blog.csdn.net/weixin_42562514/article/details/106603392
三、实现任务的工具
1.《植物大战僵尸》游戏文件
https://pan.baidu.com/s/1aNyrPxklN8NViut3bC8LNghttps://pan.baidu.com/s/1aNyrPxklN8NViut3bC8LNghttps://pan.baidu.com/s/1aNyrPxklN8NViut3bC8LNg
提取码:lput
2.IDEA(社区版)
下载地址:
IntelliJ IDEA: The Capable & Ergonomic Java IDE by JetBrainshttps://www.jetbrains.com/idea/
使用VScode是同样可实现的,因为我这个小菜鸟之前没使用过IDEA,所有这里我们选择使用IDEA。
VScode下载地址附上:
https://code.visualstudio.com/https://code.visualstudio.com/https://code.visualstudio.com/
附赠配置Java环境的保姆级教程(买一送一,服务到家):
https://blog.csdn.net/qq_41119146/article/details/103835651?ops_request_misc=&request_id=&biz_id=102&utm_term=vscode%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-5-103835651.pc_search_all_es&spm=1018.2226.3001.4450https://blog.csdn.net/qq_41119146/article/details/103835651?ops_request_misc=&request_id=&biz_id=102&utm_term=vscode%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-5-103835651.pc_search_all_es&spm=1018.2226.3001.4450https://blog.csdn.net/qq_41119146/article/details/103835651?ops_request_misc=&request_id=&biz_id=102&utm_term=vscode%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~sobaiduweb~default-5-103835651.pc_search_all_es&spm=1018.2226.3001.4450
3.exe4j和inno setup complier
打包封装软件请自行在站内下载哦!
4.一个工具人(QAQ)
四、开始实战
?
?我的总体思路:将游戏文件用十六进制的方式打开,再修改相关数据,最后保存。
1.Java实现修改关卡
public void update() {
Scanner input = new Scanner(System.in);
System.out.println("跳转关卡1-1 输入1 跳转关卡2-1 输入11 类推");
System.out.println("输入当前关卡");
tempNum = decToHexStringDiv(Integer.parseInt(input.next()));
System.out.println("输入想跳转到达的关卡:");
tempNum2 = decToHexStringDiv(Integer.parseInt(input.next()));
System.err.println("16进制当前关卡:" + tempNum);
System.err.println("16进制更改的关卡:" + tempNum2);
if (Integer.parseInt(tempNum2, 16) > 50) {
System.out.println("你个憨包儿");
System.exit(0);
}
pattern = new String[][] {{tempNum, tempNum2}};
}
为了防止同学们输错关卡,我设计了一个触发彩蛋,欢迎大家解锁成就<我是憨包儿>(斜眼笑)
2.Java实现修改金币
private void updateMoney() {
Scanner input = new Scanner(System.in);
System.out.println("输入当前金币数量:");
tempNum = decToHexStringDiv(Integer.parseInt(input.next()));
System.out.println("输入更改的金币数量:");
tempNum2 = decToHexStringDiv(Integer.parseInt(input.next()));
System.out.println("16当前金币:" + tempNum);
System.out.println("16更改金币:" + tempNum2);
// 08位置能存储的金币最大值是ffH*10=2550D,通过昨天的测试我们知道是多位地址存储金币数量的
if (Integer.parseInt(tempNum, 16) > 255) {
System.out.println("输入的字节大于255");
System.out.println("要查找的字符:" + reverseHex(tempNum));
tempNum = reverseHex(tempNum);
}
if (Integer.parseInt(tempNum2, 16) > 255) {
for (int i = 0; i <= tempNum2.length() - tempNum.length(); i++) {
// 因为是多位地址存储金币数量的,所以我们要计算更改的数据长度
tempNum += "0";
}
System.err.println("16进制当前金币:" + tempNum);
System.err.println("16进制更改的金币:" + tempNum2);
tempNum2 = reverseHex(tempNum2);
System.err.println("16进制低字节转换:" + tempNum2);
}
pattern = new String[][] {{tempNum, tempNum2}};
}
?
这个就很鸡肋,我们输入的是十进制,但是文件是以十六进制的形式打开的,所以是金币数量是D到H的变化。
3.完整源码
参考了不少资料,虽然没有bug了,但还会报一些警告,还请多多指教。
package com.ghcare.demo;
import java.io.*;
import java.util.Scanner;
class MainTool {
private final String RO_HOME =
"C:\\ProgramData\\PopCap Games\\PlantsVsZombies\\userdata\\"; // 游戏文件的位置
private final String FILE = "user1"; // 我们要修改的文件名
private final String BAK_FILE = FILE + "_BAK.dat"; // 文件的备份扩展名
private final String PATCH_FILE = FILE + ".dat"; // 文件的扩展名
String tempNum = null;
String tempNum2 = null;
private String[][] pattern = {{"ff00", "0a1a"}};//main方法
public static void main(String[] args) throws IOException {
MainTool tool = new MainTool();
tool.patch();
}
// Java的16进制的高低字节序转化
public static String reverseHex(String hex) {
char[] charArray = hex.toCharArray();
int length = charArray.length;
int times = length / 2;
for (int c1i = 0; c1i < times; c1i += 2) {
int c2i = c1i + 1;
char c1 = charArray[c1i];
char c2 = charArray[c2i];
int c3i = length - c1i - 2;
int c4i = length - c1i - 1;
charArray[c1i] = charArray[c3i];
charArray[c2i] = charArray[c4i];
charArray[c3i] = c1;
charArray[c4i] = c2;
}
return new String(charArray);
}
//十进制转十六进制,小于16的十进制前面补一个0
public static String decToHexStringDiv(int n) {
int flag = n;
StringBuffer s = new StringBuffer();
String a;
char[] b = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
while (n != 0) {
s = s.append(b[n % 16]);
n = n / 16;
}
a = s.reverse().toString();
if (flag < 16) {
a = 0 + a;
}
return a;
}
// 备份文件恢复
public void restore() {
if (isExistBackup()) {
new File(RO_HOME + PATCH_FILE).delete();
new File(RO_HOME + BAK_FILE).renameTo(new File(RO_HOME + PATCH_FILE));
System.out.println("[文件恢复成功]");
} else {
System.out.println("文件恢复失败");
System.exit(0);
}
}
public void init() {
// 初始化操作
if (new File(RO_HOME + PATCH_FILE).exists()) {
System.out.println("[读取到备份文件]");
} else {
System.out.println("未读取到备份文件");
}
// 备份原始文件
if (!isExistBackup()) {
new File(RO_HOME + PATCH_FILE).renameTo(new File(RO_HOME + BAK_FILE));
}
System.out.println("[少年请选择你的英雄]");
System.out.println("1:开始数据修改");
System.out.println("2:恢复修改前文件");
System.out.println("选择执行,输入1或2:");
}
public void success() {
System.out.println();
System.out.println("[修改成功,玩的开心OVO]");
System.out.println("[CSDN第八期Java实战训练营]");
}
//进行16进制替换,将输入的16进制字符串替换为已定义的模式
public String replace(String original) {
for (int i = 0; i < pattern.length; i++) {
original = original.replaceAll(pattern[i][0].toLowerCase(), pattern[i][1].toLowerCase());
if (original.contains(pattern[i][0].toLowerCase())) {
System.out.println("替换成功");
}
}
return original;
}
//将文件读取为16进制,读取原始文件并将转换为16进制字符串
public String readOriginal2Hex() throws IOException {
FileInputStream fin = new FileInputStream(new File(RO_HOME + BAK_FILE));
StringWriter sw = new StringWriter();
int len = 1;
byte[] temp = new byte[len];
//16进制的方式打开文件
for (; (fin.read(temp, 0, len)) != -1; ) {
if (temp[0] > 0xf && temp[0] <= 0xff) {
sw.write(Integer.toHexString(temp[0]));
} else if (temp[0] >= 0x0 && temp[0] <= 0xf) {
// 对于只有1位的16进制数前边补0
sw.write("0" + Integer.toHexString(temp[0]));
} else {
// int<0的位转化为16进制的特殊处理,Java没有Unsigned int,所以这个int可能为负数
sw.write(Integer.toHexString(temp[0]).substring(6));
}
}
return sw.toString();
}
//将替换后的16进制字符串写回文件
public void writeNew2Binary(String replaced) throws NumberFormatException, IOException {
FileOutputStream fout = new FileOutputStream(RO_HOME + PATCH_FILE);
for (int i = 0; i < replaced.length(); i = i + 2) {
fout.write(Integer.parseInt(replaced.substring(i, i + 2), 16));
}
}
public void writeTest(String temp) throws IOException {
FileOutputStream fout = new FileOutputStream(RO_HOME + "test.txt");
for (int i = 0; i < temp.length(); i++) {
fout.write(temp.charAt(i));
}
}
public boolean isExistBackup() {
return new File(RO_HOME + BAK_FILE).exists();
}
public void patch() throws IOException {
init();
String input = new BufferedReader(new InputStreamReader(System.in)).readLine();
if (input.equals("1")) {
System.out.println("输入1更改关卡,输入2更改金币");
String selectOption = new BufferedReader(new InputStreamReader(System.in)).readLine();
if (selectOption.equals("1")) {
update();
} else if (selectOption.equals("2")) {
updateMoney();
}
String temp = null;
temp = readOriginal2Hex();
temp = replace(temp);
writeNew2Binary(temp);
success();
} else if (input.equals("2")) {
restore();
} else {
System.out.println("该功能还处于研发中");
System.exit(0);
}
}
// 修改金币
private void updateMoney() {
Scanner input = new Scanner(System.in);
System.out.println("输入当前金币数量:");
tempNum = decToHexStringDiv(Integer.parseInt(input.next()));
System.out.println("输入更改的金币数量:");
tempNum2 = decToHexStringDiv(Integer.parseInt(input.next()));
System.out.println("16当前金币:" + tempNum);
System.out.println("16更改金币:" + tempNum2);
// 08位置能存储的金币最大值是ffH*10=2550D,通过昨天的测试我们知道是多位地址存储金币数量的
if (Integer.parseInt(tempNum, 16) > 255) {
System.out.println("输入的字节大于255");
System.out.println("要查找的字符:" + reverseHex(tempNum));
tempNum = reverseHex(tempNum);
}
if (Integer.parseInt(tempNum2, 16) > 255) {
for (int i = 0; i <= tempNum2.length() - tempNum.length(); i++) {
// 因为是多位地址存储金币数量的,所以我们要计算更改的数据长度
tempNum += "0";
}
System.err.println("16进制当前金币:" + tempNum);
System.err.println("16进制更改的金币:" + tempNum2);
tempNum2 = reverseHex(tempNum2);
System.err.println("16进制低字节转换:" + tempNum2);
}
pattern = new String[][]{{tempNum, tempNum2}};
}
// 修改关卡
public void update() {
Scanner input = new Scanner(System.in);
System.out.println("跳转关卡1-1 输入1 跳转关卡2-1 输入11 类推");
System.out.println("输入当前关卡");
tempNum = decToHexStringDiv(Integer.parseInt(input.next()));
System.out.println("输入想跳转到达的关卡:");
tempNum2 = decToHexStringDiv(Integer.parseInt(input.next()));
System.err.println("16进制当前关卡:" + tempNum);
System.err.println("16进制更改的关卡:" + tempNum2);
if (Integer.parseInt(tempNum2, 16) > 50) {
System.out.println("你个憨包儿");
System.exit(0);
}
pattern = new String[][]{{tempNum, tempNum2}};
}
}
好了,我们把程序打包好。(参照参考资料——Java的打包封装)
(打包带回家,夜宵有着落了)
至此任务1完成。
4.个人对游戏数据渗透与风控的拙见(手动卑微)
这些是训练营的老师提供的一些方法论:
?
关于我个人的拙见(就以植物大战僵尸为例):
之前没有接触过渗透与风控,对数据安全的知识了解的也非常有限。
所以这个做的不尽人意,多多包涵。
小结:
网络游戏就把数据统统放在服务器,再给数据包加密,加上各种反外挂操作。
(例如:鹅厂的TP,懂得都懂) 单机游戏就算加密原始数据,也只是读出来是错误的数据。
但不管是什么形式的保护措施,只要有耐心、花时间也是可以破解开的,
毕竟没有绝对的加密解密。
或许,只有在不停的攻防中达到一种动态平衡,才是真正的安全。
至此任务2完成。
五、总结
今天这个任务就提升难度了,比如16进制的高低字节转化的环节,尽管参考了一些大佬的代码,但尝试多次都是报错,反反复复的改bug。代码我给写了注释,如果有不理解的地方或者更好的方法欢迎大家评论留言。
关于渗透与风控,我想用这句话来概括一下:
“放眼去看我们的世界,看到我们的岁月静好,是因为有人为我们负重前行!”
向各位致力于保护大家信息数据安全的工程师们表示最高敬意!
Respect!!!
(完结撒花)
|