最近朋友圈里出現(xiàn)了一款日本的游戲,十分火爆,于是忍不住想去破解看看,。分析后發(fā)現(xiàn)這個游戲的破解并不難,但是可以多種思路進行,,是個很好的學習樣本,于是決定寫一篇文章分享給初學者們,。
本文分三個方向進行破解分析,,分別為內(nèi)存修改,存檔修改,,apk修改,。文章涉及的修改較為簡單,主要目的是給大家提供多元的分析思路,,接下來我們一個一個來進行具體分析,。
所使用樣本為 旅行青蛙 1.0.4版本(目前最新版本)。 鏈接: https://pan.baidu.com/s/1dSqHK6 密碼: qmvg
目錄
0x1.內(nèi)存修改→ GG修改器修改數(shù)值,,需root
0x2.存檔修改→ 存檔十六進制修改,,無需root;原創(chuàng)apk用于修改存檔,,無需root
0x3.apk修改 → Unity3D游戲腳本修改,,無需root
0x4.總結 → 文章整體思路和方向概況
正文
0x1.內(nèi)存修改
思路:這個方式是用在已經(jīng)root的手機上,也就是我們接觸比較多的修改器通過搜索來確認關鍵數(shù)值的內(nèi)存地址,,然后將其修改,,達到破解目的。
工具:GG修改器 / 需要ROOT權限
因為比較簡單,,這部分盡量簡要講,。
打開GG修改器和游戲,進游戲后查看當前三葉草數(shù)量,,GG修改器附加游戲進程,,并搜索該數(shù)量。
附加后我們進行搜索,,搜索37這個數(shù)值,。
搜索結果比較多,我們需要篩選,,回到游戲使用三葉草買東西,,數(shù)值變化為27,然后我們搜索27來確認三葉草數(shù)量的內(nèi)存地址,。
修改最終搜索到的值為27000,,回到游戲就可以看到三葉草數(shù)量已經(jīng)變化。
其他物品及抽獎券等數(shù)量均可用該方式修改,,請大家自己嘗試,。
這種方式非常方便,但是有個弊端就是需要我們有ROOT權限,,對于目前大部分安卓手機來講,,ROOT權限的獲取越來越難,接下來我們來分析不需要ROOT權限的兩種修改方法,。
0x2.存檔修改
思路:通過存檔文件分析和修改完成關鍵數(shù)值修改
工具:十六進制編輯器
單機游戲都會有存檔,,旅行青蛙當然也不例外,我們按照常規(guī)路徑去找一下,,發(fā)現(xiàn)游戲的存檔都在Tabikaeru.sav文件中,,路徑請看圖:
我們使用十六進制編輯器將其打開,編輯器可以用PC端的也可以用手機端的,,自行選擇,。
打開后我們根據(jù)目前的三葉草數(shù)量27000進行搜索,27000的十六進制為0x6978,,所以我們在十六進制文件中可以進行hex搜索,,搜索 69 78 或 78 69。
(通常在十六進制中的數(shù)值都是倒序記錄,,比如0x6978會保存為 78 69,,在旅行青蛙1.0.1版本的存檔中就是這么保存的,不過在1.0.4版本的存檔中,,已經(jīng)變?yōu)榱苏?,?9 78)
經(jīng)過搜索我們找到了三葉草的數(shù)量,接下來我們將其修改驗證一下,,將69 78 修改為 FF FF,,保存后放回手機中存檔的文件夾中,重新啟動,,發(fā)現(xiàn)三葉草數(shù)量已經(jīng)變更:
其他數(shù)值修改,,比如抽獎券或者其他物品數(shù)量等,均可依照此方法進行,,此處不再贅述,,請大家自己嘗試。另外還可以在每次數(shù)值有較明顯變化后保存存檔文件,,進行對比分析,,來找到更多物品的數(shù)值。
為了更簡便的進行修改,,我們做一個專用修改器apk用來在未root手機上專門完成此修改過程,,源碼如下(完整project參考附件):
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++) {
Log.d("123", " " + arrayOfByte[i]);
}
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進制字符串轉換成字節(jié)數(shù)組
*/
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[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
if (result[i] == 0) {
result[i] = 00;
}
}
return result;
}
private static int toByte(char c) {
byte b = (byte) "0123456789ABCDEF".indexOf(c);
return b;
}
}
上述代碼實現(xiàn)了存檔的直接修改,,界面如下,不需要ROOT權限:
輸入數(shù)值后,,點擊修改即可完成三葉草及抽獎券的修改,,更多物品修改請自行嘗試。
0x3.apk修改
思路:分析apk包,,找到腳本文件,,反編譯后找到關鍵method進行修改,然后重新打包
工具:Android Killer,,DnSpy
Android Killer相關操作這里不再贅述,,反編譯后我們發(fā)現(xiàn)這是一個mono框架的Unity3D游戲,Unity3D游戲的腳本文件都存放在Assembly-CSharp.dll或Assembly-CSharp-firstpass.dll文件中,,很顯然,,旅行青蛙的腳本文件位于Assembly-CSharp.dll,我們使用Dnspy進行分析看看,。
我們搜索三葉草的英文clover,,發(fā)現(xiàn)getCloverPoint可能是我們需要找的關鍵method。
根據(jù)getCloverPoint的源碼,,我們發(fā)現(xiàn)這個method的功能是在三葉草數(shù)量發(fā)生變化時在三葉草數(shù)量進行增減運算,,那么我們可以對函數(shù)內(nèi)部增加數(shù)量的這句代碼進行修改,修改為發(fā)生變化增加固定數(shù)量的三葉草,,比如10000,。
(抽獎券相關修改也在SuperGameMaster中可以找到,method名為getTicket,,此處不作演示,,請大家自己嘗試修改)
修改后函數(shù)變更為:
保存后打包apk運行,只要三葉草數(shù)量發(fā)生變化(如收割三葉草或者購買物品),,三葉草的數(shù)量就會增加10000,。
0x4.總結
本文通過多種思路對旅行青蛙的修改進行了分析,內(nèi)容較為簡單,,主要目的是分享游戲破解分析的思路,,有興趣的可以嘗試更多物品數(shù)量的修改。