槃星—第2天任务—JAVA实现游戏数据修改以及风控设计

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!!!

(完结撒花)

  • 34
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

槃星Panxing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值