吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 25150|回复: 59
收起左侧

[游戏安全] 【游戏漏洞】旅行青蛙 | 破解分析从内存到存档再到改包

  [复制链接]
Gslab 发表于 2018-2-2 10:18
本帖最后由 Gslab 于 2018-2-2 10:52 编辑

最近朋友圈里出现了一款日本的游戏,十分火爆,于是忍不住想去破解看看。分析后发现这个游戏的破解并不难,但是可以多种思路进行,是个很好的学习样本,于是决定写一篇文章分享给初学者们。本文分三个方向进行破解分析,分别为内存修改,存档修改,apk修改。文章涉及的修改较为简单,主要目的是给大家提供多元的分析思路,接下来我们一个一个来进行具体分析。
所使用样本为 旅行青蛙 1.0.4版本(目前最新版本)。

目录0x1.内存修改 → GG修改器修改数值,需root
0x2.存档修改 → 存档十六进制修改,无需root;原创apk用于修改存档,无需root
0x3.apk修改   → Unity3D游戏脚本修改,无需root
0x4.总结         → 文章整体思路和方向概况

正文0x1.内存修改思路:这个方式是用在已经root的手机上,也就是我们接触比较多的修改器通过搜索来确认关键数值的内存地址,然后将其修改,达到破解目的。
工具:GG修改器 / 需要ROOT权限 因为比较简单,这部分尽量简要讲。打开GG修改器和游戏,进游戏后查看当前三叶草数量,GG修改器附加游戏进程,并搜索该数量。
      1.jpg 2.jpg
附加后我们进行搜索,搜索37这个数值。
3.jpg 4.jpg    
搜索结果比较多,我们需要筛选,回到游戏使用三叶草买东西,数值变化为27,然后我们搜索27来确认三叶草数量的内存地址。
5.jpg 6.jpg    
修改最终搜索到的值为27000,回到游戏就可以看到三叶草数量已经变化。
7.jpg 8.jpg       
其他物品及抽奖券等数量均可用该方式修改,请大家自己尝试。这种方式非常方便,但是有个弊端就是需要我们有ROOT权限,对于目前大部分安卓手机来讲,ROOT权限的获取越来越难,接下来我们来分析不需要ROOT权限的两种修改方法。
0x2.存档修改思路:通过存档文件分析和修改完成关键数值修改工具:十六进制编辑器单机游戏都会有存档,旅行青蛙当然也不例外,我们按照常规路径去找一下,发现游戏的存档都在Tabikaeru.sav文件中,路径请看图:
9.jpg 我们使用十六进制编辑器将其打开,编辑器可以用PC端的也可以用手机端的,自行选择。打开后我们根据目前的三叶草数量27000进行搜索,27000的十六进制为0x6978,所以我们在十六进制文件中可以进行hex搜索,搜索 69 78 或 78 69。(通常在十六进制中的数值都是倒序记录,比如0x6978会保存为 78 69,在旅行青蛙1.0.1版本的存档中就是这么保存的,不过在1.0.4版本的存档中,已经变为了正序,即69 78)
10.jpg
经过搜索我们找到了三叶草的数量,接下来我们将其修改验证一下,将69 78 修改为 FF FF,保存后放回手机中存档的文件夹中,重新启动,发现三叶草数量已经变更:
11.jpg
其他数值修改,比如抽奖券或者其他物品数量等,均可依照此方法进行,此处不再赘述,请大家自己尝试。另外还可以在每次数值有较明显变化后保存存档文件,进行对比分析,来找到更多物品的数值。为了更简便的进行修改,我们做一个专用修改器apk用来在未root手机上专门完成此修改过程,源码如下:
package com.example.frog;
import android.content.Context;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;import android.os.Bundle;
import android.util.Log;
import android.view.View;import android.view.inputmethod.InputMethodManager;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;
import java.io.BufferedOutputStream;import java.io.File;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class MainActivity extends AppCompatActivity {   
    private EditText editText;
    private EditText editText2;
    private Button button;
    private InputMethodManager inputMethodManager;
    private static final String FILE_PATH = Environment.getExternalStorageDirectory() + File.separator + "Android/data/jp.co.hit_point.tabikaeru/files/Tabikaeru.sav";
    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = (EditText) findViewById(R.id.editText);
        editText2 = (EditText) findViewById(R.id.editText2);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View v) {
                if (editText.getText().toString().equals("") || editText2.getText().toString().equals("")) {
                    return;                }
                String cloverHex = String.format("%06X",  Integer.valueOf(editText.getText().toString()));
                String couponHex = String.format("%06X",  Integer.valueOf(editText2.getText().toString()));
                Log.d("123", " " + cloverHex);
                Log.d("123", " " + couponHex);
                writeToFile(cloverHex, couponHex);
            }        });    }
    public void writeToFile(String cloverHex, String couponHex) {
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        File file = new File(FILE_PATH);
        File newFile = new File(FILE_PATH);
        byte[] cloverByteArray = hexStringToByte(cloverHex);
        byte[] couponByteArray = hexStringToByte(couponHex);
        if (!file.exists()) {
            Log.d("123", "未找到文件Tabikaeru.sav");
            return;
        }
        try {
            fileInputStream = new FileInputStream(file);
            byte[] arrayOfByte = new byte[fileInputStream.available()];
            Log.d("123", "文件大小" + arrayOfByte.length);
            fileInputStream.read(arrayOfByte);
            if (arrayOfByte.length > 29) {
                file.delete();
                Log.d("123", "删除旧文件");
                createFile(newFile);                //三叶草
                arrayOfByte[23] = cloverByteArray[0];//Byte.valueOf(cloverHex.substring(0, 2));
                arrayOfByte[24] = cloverByteArray[1];//Byte.valueOf(cloverHex.substring(2, 4));
                arrayOfByte[25] = cloverByteArray[2];//Byte.valueOf(cloverHex.substring(4, 6));
                //抽奖券
                arrayOfByte[27] = couponByteArray[0];//Byte.valueOf(couponHex.substring(0, 2));
                arrayOfByte[28] = couponByteArray[1];//Byte.valueOf(couponHex.substring(2, 4));
                arrayOfByte[29] = couponByteArray[2];//Byte.valueOf(couponHex.substring(4, 6));
                Log.d("123", " " + arrayOfByte.length);
                for (int i = 0; i <arrayofbyte.length; i++) {</arrayofbyte.length; i++) {
                    Log.d("123", " " + arrayOfByte);
                }
                fileOutputStream = new FileOutputStream(newFile);
                fileOutputStream.write(arrayOfByte);
            }
        } catch (Exception e) {
           e.printStackTrace();
        } finally {
            Toast.makeText(this, getString(R.string.saved), Toast.LENGTH_SHORT).show();
            hideSoftInput();
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            }
catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public void createFile(File file){
        try{
            file.getParentFile().mkdirs();
            file.createNewFile();
        }catch (IOException e){
           e.printStackTrace();
        }    }
    public void hideSoftInput(){
        if(inputMethodManager == null) {
           inputMethodManager = (InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
        }
        inputMethodManager.hideSoftInputFromWindow(editText.getWindowToken(), 0);
        editText.clearFocus();
        inputMethodManager.hideSoftInputFromWindow(editText2.getWindowToken(), 0);
        editText2.clearFocus();    }     /**     * 把16进制字符串转换成字节数组     */
   public static byte[] hexStringToByte(String hex) {
        int len = (hex.length() / 2);
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {            int pos = i * 2;
            result = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
            if (result == 0) {
               result = 00;
            }
        }
        return result;
    }
    private static int toByte(char c) {
        byte b = (byte) "0123456789ABCDEF".indexOf(c);
        return b;
    }}
上述代码实现了存档的直接修改,界面如下,不需要ROOT权限:
12.png
输入数值后,点击修改即可完成三叶草及抽奖券的修改,更多物品修改请自行尝试。 0x3.apk修改思路:分析apk包,找到脚本文件,反编译后找到关键method进行修改,然后重新打包工具:Android Killer,DnSpyAndroid Killer相关操作这里不再赘述,反编译后我们发现这是一个mono框架的Unity3D游戏,Unity3D游戏的脚本文件都存放在Assembly-CSharp.dll或Assembly-CSharp-firstpass.dll文件中,很显然,旅行青蛙的脚本文件位于Assembly-CSharp.dll,我们使用Dnspy进行分析看看。
13.jpg
我们搜索三叶草的英文clover,发现getCloverPoint可能是我们需要找的关键method。
14.jpg
根据getCloverPoint的源码,我们发现这个method的功能是在三叶草数量发生变化时在三叶草数量进行增减运算,那么我们可以对函数内部增加数量的这句代码进行修改,修改为发生变化增加固定数量的三叶草,比如10000。(抽奖券相关修改也在SuperGameMaster中可以找到,method名为getTicket,此处不作演示,请大家自己尝试修改)
15.jpg
修改后函数变更为:
16.jpg
保存后打包apk运行,只要三叶草数量发生变化(如收割三叶草或者购买物品),三叶草的数量就会增加10000。 0x4.总结本文通过多种思路对旅行青蛙的修改进行了分析,内容较为简单,主要目的是分享游戏破解分析的思路,有兴趣的可以尝试更多物品数量的修改。


文章已获原作者【黯夏子风】同意转载,如需转载请联系原作者
*转载请注明来自游戏安全实验室(GSLAB.QQ.COM)

点评

H2o
第二个方法修改比较方便 修改成功  发表于 2018-2-2 14:18

免费评分

参与人数 24吾爱币 +22 热心值 +23 收起 理由
粉藍弟 + 1 + 1 我很赞同!
purplewall + 1 + 1 我很赞同!
gunxsword + 1 + 1 谢谢@Thanks!
kingkiller + 1 + 1 我很赞同!
江青山 + 1 + 1 谢谢@Thanks!
电竞灰原 + 1 + 1 谢谢@Thanks!
NaZio + 1 + 1 我很赞同!受教了。
cpk1ng + 1 手机版 hexedit 请看38 楼
丿凉生丶Amen + 1 + 1 我很赞同!
xiaowanzi + 1 谢谢@Thanks!
kxp555 + 1 + 1 谢谢@Thanks!
修神者 + 1 + 1 用心讨论,共获提升!
丶提莫 + 1 + 1 我很赞同!
sunnylds7 + 1 + 1 谢谢@Thanks!
li5832 + 1 + 1 热心回复!
wushaominkk + 2 + 1 鼓励转贴优秀软件安全工具和文档!
cn52pojie + 1 热心回复!
人满为患 + 1 + 1 谢谢@Thanks!
用户APTX4869 + 1 + 1 用心讨论,共获提升!
jcily + 1 + 1 这么久唯一一篇高质量帖
寒蝉鸣泣之时 + 1 + 1 热心回复!
zach14c + 1 谢谢@Thanks!
别说我 + 1 + 1 谢谢@Thanks!
后知后觉丶 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

山高人为蜂 发表于 2018-2-2 21:27
本帖最后由 山高人为蜂 于 2018-2-2 22:15 编辑

用葫芦娃修改器都找不到三叶草的数据。安卓版养蛙1.01版的。就算是改存盘文件,游戏会加载不了。我下载的1.01版养蛙,存盘文件是GameData.sav、GameData.save.back共两个,真奇怪。
今晚再次修改了一下这两个文件,居然可以加载到了。存盘文件GameData.sav、GameData.save.back的0x0c70地址,就是三叶草、礼券的数量。
我用的是WINHEX修改的。两个存盘文件都要修改,然后保存。再将它们复制到游戏的存盘目录中,代替原来的文件。
!!!!!!!建议修改前,要做好原来存盘文件的备份工作,以免误操作损坏文件,造成不必要的损失。!!!!!!!!!!

无标题.png

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
他化自在天子 + 1 + 1 我很赞同!

查看全部评分

Rain_Personal 发表于 2018-2-2 15:43
实测十六进制修改那个,1.0.4小米市场下载的应用,由于玩过后存在Tabikaeru.sav.back了能重启无法完成修改,所以建议重新安装,完成教程后再次进入此时应该是509个三叶草,然后退出游戏,拷贝file文件下仅存的那个Tabikaeru.sav,进行十六进制修改,然后复制替换,重新打开。

免费评分

参与人数 1吾爱币 +1 收起 理由
cpk1ng + 1 https://storage.evozi.com/apk/dl/17/04/22/com.myprog.hexedit.apk?h=hkl

查看全部评分

j542936 发表于 2018-2-2 10:29
兔子我是胡萝卜 发表于 2018-2-2 10:31
老大 图挂了
头像被屏蔽
信誉最高点 发表于 2018-2-2 10:32
提示: 作者被禁止或删除 内容自动屏蔽
Cubo 发表于 2018-2-2 10:33
老大 图挂了
流水 发表于 2018-2-2 10:34
应该是从看雪转载的吧
xxy419255334 发表于 2018-2-2 10:43
谢谢楼主好心分享。
jinger666888 发表于 2018-2-2 11:11
6666666666
km852753951 发表于 2018-2-2 11:16
支持大神 学习了
bobwuai 发表于 2018-2-2 11:20
牛啊,谢谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-23 22:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表