旅行的青蛙Unity遊戲逆向修改Android&iOS

2021-01-14 看雪學院


前言


最近旅行的青蛙甚火,便來試一試Android和iOS上面Unity3D遊戲C#腳本和C/C++函數的分析與修改,由於Android大部分是mono模式如果C#腳本沒有加密的話是直接可以拿過來分析修改的,但是iOS大都使用IL2CPP模式把C#腳本轉成C/C++代碼了,直接分析的話要難很多,所以本文先從Android開始分析然後結合Android再分析iOS。



C#腳本


首先將Android apk安裝包解壓,查看assets/bin/Data/Managed文件夾下面的dll文件,其中Assembly-CSharp.dll便是遊戲中的C#腳本,使用Reflector或者dnSpy進行反編譯都可以,如何出現反編譯出錯的情況,那可能就是腳本被加密了,一般在嘗試在libmono.so中的 mono_image_open_from_data_with_name 中獲取解密後的dll腳本,而當前分析的遊戲C#腳本並沒有加密。在dnSpy中的反編譯結果如下:

 

 

左側就可以看到對應的類,點擊類就可以看到反編譯出來的代碼,下面先跟遊戲裡面的一些特徵來分析反編譯出來的腳本。



漢化


首先使用 adb install tabikaeru.apk 命令安裝apk文件,然後打開遊戲可以看到如下界面:

 

 

首先來看看怎麼修改顯示的文字以達到漢化的效果,使用dnSpy點擊編輯->搜索程序集搜索右邊選擇數字/字符串然後搜索名前:

 

 

點擊CallTutorial可以找到剛剛在屏幕上面顯示的文字:

 

 

滑鼠點擊右鍵選擇編輯IL指令,修改剛剛看到的文字為漢字:

 

 

然後點擊應用即可看到修改後的效果:

 

 

點擊文件->全部保存替換 assets/bin/Data/Managed 文件夾下面對應的文件即可,然後將文件打包成apk使用jarsinger、signapk.jar或Android助手重籤名安裝到手機就能看到修改後的效果了。

 



修改三葉草數


要修改三葉草數可以從購買的時候入手,比如提示三葉草不足的時候:

 

 

直接搜索足找到目標代碼:

 

 

根據找到的如下代碼來分析:

if (SuperGameMaster.CloverPointStock() >= itemDataFormat.price)

{

    if (SuperGameMaster.FindItemStock(shopDataFormat.itemId) < 99)

    {

        base.GetComponent<FlickCheaker>().stopFlick(true);

        ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();

        if (itemDataFormat.type == Item.Type.LunchBox)

        {

            confilm.OpenPanel_YesNo(string.Concat(new object[]

            {

                itemDataFormat.name,

                "\nを買いますか?\n(所持數\u3000",

                SuperGameMaster.FindItemStock(shopDataFormat.itemId),

                ")"

            }));

        }

        else

        {

            confilm.OpenPanel_YesNo(itemDataFormat.name + "\nを買いますか?");

        }

        confilm.ResetOnClick_Yes();

        confilm.SetOnClick_Yes(delegate

        {

            confilm.ClosePanel();

        });

        confilm.SetOnClick_Yes(delegate

        {

            this.GetComponent<FlickCheaker>().stopFlick(false);

        });

        confilm.SetOnClick_Yes(delegate

        {

            this.BuyItem();

        });

        confilm.ResetOnClick_No();

        confilm.SetOnClick_No(delegate

        {

            confilm.ClosePanel();

        });

        confilm.SetOnClick_No(delegate

        {

            this.GetComponent<FlickCheaker>().stopFlick(false);

        });

    }

    else

    {

        base.GetComponent<FlickCheaker>().stopFlick(true);

        ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();

        confilm.OpenPanel("もちものがいっぱいです");

        confilm.ResetOnClick_Screen();

        confilm.SetOnClick_Screen(delegate

        {

            confilm.ClosePanel();

        });

        confilm.SetOnClick_Screen(delegate

        {

            this.GetComponent<FlickCheaker>().stopFlick(false);

        });

    }


當三葉草的存儲小於當前商品的價格的時候就會彈出這個提示,點擊SuperGameMaster.CloverPointStock()得到如下代碼:


public static int CloverPointStock()

{

    return SuperGameMaster.saveData.CloverPoint;

}


這裡返回的是一個從saveData.CloverPoint中獲取的整數,可以嘗試將其修改成返回一個特定的數字每次查詢三葉草的數目都是這麼多,右鍵點擊編輯IL指令,然後點擊重置並右鍵刪除第一條指令,因為只是返回一個數字的話兩條指令就夠了:

 

 

然後修改第二條指令為一個數字,具體的指令代表的類型可以查看IL指令說明:

 

 

點擊確定後就能看到修改代碼的效果:

public static int CloverPointStock()

{

    return 9999;

}


重新籤名安裝之後每次購買商品之後三葉草的數目也不會減少,一直是9999。



修改抽獎券


修改抽獎券也是同樣的套路,搜索足找到目標代碼,其實剛剛搜索的時候就已經看到了。

 

if (SuperGameMaster.TicketStock() < 5)

{

    ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();

    confilm.OpenPanel("ふくびき券が足りません");

    confilm.ResetOnClick_Screen();

    confilm.SetOnClick_Screen(delegate

    {

        confilm.ClosePanel();

    });

    return;

}


點擊 SuperGameMaster.TicketStock() 也是差不多的代碼同樣修改返回數字即可:

public static int TicketStock()

{

    return SuperGameMaster.saveData.ticket;

}


public static int TicketStock()

{

    return 9999;

}



修改抽獎概率


每次抽獎都是抽到白球就不是很開心了,怎麼能夠提供抽獎的概率呢?抽到其它顏色的球的時候會提示:

 

 

搜索白玉找到如下代碼:

public static readonly Dictionary<Rank, string> PrizeBallName = new Dictionary<Rank, string>

{

    {

        Rank.White,

        "白玉"

    },

    {

        Rank.Blue,

        "青玉"

    },

    {

        Rank.Green,

        "緑玉"

    },

    {

        Rank.Red,

        "赤玉"

    },

    {

        Rank.Gold,

        "黃玉"

    }

};


右鍵點擊PrizeBallName選擇分析找到在那裡被使用的:

 

 

雙擊PrizeBallName得到如下代碼:


public void PushRollButton()

{

    if (SuperGameMaster.TicketStock() < 5)

    {

        ConfilmPanel confilm = this.ConfilmUI.GetComponent<ConfilmPanel>();

        confilm.OpenPanel("ふくびき券が足りません");

        confilm.ResetOnClick_Screen();

        confilm.SetOnClick_Screen(delegate

        {

            confilm.ClosePanel();

        });

        return;

    }

    SuperGameMaster.GetTicket(-5);

    SuperGameMaster.set_FlagAdd(Flag.Type.ROLL_NUM, 1);

    base.GetComponentInParent<UIMaster>().freezeObject(true);

    base.GetComponentInParent<UIMaster>().blockUI(true, new Color(0f, 0f, 0f, 0.3f));

    this.LotteryCheck();

    this.ResultButton.GetComponent<RollResultButton>().CngImage((int)this.result);

    this.ResultButton.GetComponent<RollResultButton>().CngResultText(Define.PrizeBallName[this.result] + "がでました");

    this.LotteryWheelPanel.GetComponent<LotteryWheelPanel>().OpenPanel(this.result);

    SuperGameMaster.SetTmpRaffleResult((int)this.result);

    SuperGameMaster.SaveData();

    SuperGameMaster.audioMgr.PlaySE(Define.SEDict["SE_Raffle"]);

    this.BackFunc();

}


從代碼來看就是剛剛抽獎結果提示的地方,來分析下這個結果是怎麼生成的,可以看到 Define.PrizeBallName[this.result] 這個result決定了抽到的是什麼顏色的球,那麼看看這個result是在哪裡生成的。在當前文件中看到了兩處賦值:

public void LotteryCheck()

{

    int num = Random.Range(0, Define.PrizeBalls[Rank.RankMax]);

    this.result = Rank.White;

    int i = 0;

    int num2 = 0;

    while (i < 5)

    {

        num2 += Define.PrizeBalls[(Rank)i];

        if (num < num2)

        {

            this.result = (Rank)i;

            break;

        }

        i++;

    }

}

 

ublic void SetTmpResult()

{

    this.result = (Rank)SuperGameMaster.GetTmpRaffleResult();

    this.BackFunc();

}


下面是在UIMaster_raffle.UI_Start中調用的,判斷之前的獎品有沒有領取,LotteryCheck這個剛好是在顯示結果之前調用的,猜測就是這裡控制的不同顏色的球的出現概率,分析下這段代碼:

public void LotteryCheck()

{

    int num = Random.Range(0, Define.PrizeBalls[Rank.RankMax]); //Rank.RankMax是5,Define.PrizeBalls[Rank.RankMax]是100,從0-100隨機生成一個數

    this.result = Rank.White;  //默認都是白色的球

    int i = 0;

    int num2 = 0;

    while (i < 5)

    {

        num2 += Define.PrizeBalls[(Rank)i];  //根據PrizeBalls的值小於60是白色,大於等於60並且少於87是藍色,大於等於87小於96是綠色,大於等於96小於99是紅色,大於等於99是金色。

        if (num < num2)

        {

            this.result = (Rank)i;

            break;

        }

        i++;

    }

}


從上面代碼分析PrizeBalls中就定義了不同顏色的球搖出概率:

public static readonly Dictionary<Rank, int> PrizeBalls = new Dictionary<Rank, int>

{

    {

        Rank.White,

        60

    },

    {

        Rank.Blue,

        27

    },

    {

        Rank.Green,

        9

    },

    {

        Rank.Red,

        3

    },

    {

        Rank.Gold,

        1

    },

    {

        Rank.RankMax,

        100

    }

};


所以直接修改這裡的數字就控制搖出的概率了,比如不出白球,其它球的概率都一樣:

public static readonly Dictionary<Rank, int> PrizeBalls = new Dictionary<Rank, int>

{

    {

        Rank.White,

        0   //不存在白球

    },

    {

        Rank.Blue,

        25

    },

    {

        Rank.Green,

        25

    },

    {

        Rank.Red,

        25

    },

    {

        Rank.Gold,

        25

    },

    {

        Rank.RankMax,

        100

    }

};



修改農場四葉草數


農場裡面大部分都是三葉草、四葉草的概率是很小的,那麼怎麼修改這個呢?在界面上面好像不太好找關聯,先試試搜索三葉草的英語clover,點擊checkCloverCreate看起來就是控制生成的函數:

 

public void checkCloverCreate()

{

    this.cloverList = SuperGameMaster.GetCloverList();

    bool flag = false;

    if (this.cloverList.Count == 0) 

    {

        flag = true;

        //這句話翻譯的意思就是:有了四葉草的初期化標誌。四葉草生成

        //也就是flag為true的時候會生成四葉草

        Debug.Log("[CloverFarm] クローバーの初期化フラグが立ちました。四葉を生成します");

    }

    if (this.cloverList.Count < this.cloverMax)

    {

        Debug.Log(string.Concat(new object[]

        {

            "[CloverFarm] クローバーの數を調整します:",

            this.cloverList.Count,

            " > ",

            this.cloverMax

        }));

    }

    while (this.cloverList.Count < this.cloverMax)

    {

        CloverDataFormat cloverDataFormat = new CloverDataFormat();

        cloverDataFormat.lastHarvest = new DateTime(1970, 1, 1);

        cloverDataFormat.timeSpanSec = -this.cloverList.Count - 1;

        cloverDataFormat.newFlag = true;

        this.cloverList.Add(cloverDataFormat);

    }

    if (this.cloverList.Count > this.cloverMax)

    {

        Debug.Log(string.Concat(new object[]

        {

            "[CloverFarm] クローバーの數を調整します:",

            this.cloverList.Count,

            " > ",

            this.cloverMax

        }));

        this.cloverList.RemoveRange(this.cloverMax - 1, this.cloverList.Count - this.cloverMax);

    }

    List<GameObject> list = new List<GameObject>();

    for (int i = 0; i < this.cloverList.Count; i++)

    {

        if (!this.cloverList[i].newFlag && this.cloverList[i].timeSpanSec <= 0)

        {

            list.Add(this.LoadCloverObject(i, this.cloverList[i]));

        }

    }

    int num = 0;

    for (int j = 0; j < this.cloverList.Count; j++)

    {

        if (this.cloverList[j].newFlag)

        {

            //這裡根據flag調用不同的函數,

            if (!flag)

            {

                //生成三葉草

                list.Add(this.NewCloverObject(j, this.cloverList[j], list));

            }

            else

            {

                //生成四葉草,不同的是第四個參數為true

                list.Add(this.NewCloverObject(j, this.cloverList[j], list, true));

                flag = false;

            }

            this.cloverList[j].x = list[list.Count - 1].transform.localPosition.x;

            this.cloverList[j].y = list[list.Count - 1].transform.localPosition.y;

            Clover component = list[list.Count - 1].GetComponent<Clover>();

            this.cloverList[j].element = component.element;

            this.cloverList[j].spriteNum = component.spriteNum;

            this.cloverList[j].point = component.point;

            this.cloverList[j].newFlag = false;

            num++;

        }

    }

    foreach (GameObject gameObject in list)

    {

        int num2 = this.cloverOrderInLayer;

        foreach (GameObject gameObject2 in list)

        {

            if (gameObject.transform.position.y < gameObject2.transform.position.y)

            {

                num2++;

            }

        }

        gameObject.GetComponent<SpriteRenderer>().sortingOrder = num2;

    }

    Debug.Log(string.Concat(new object[]

    {

        "[CloverFarm] クローバー生成完了:",

        list.Count,

        "\u3000/ (新規:",

        num,

        ")"

    }));

}


首先看this.NewCloverObject(j, this.cloverList[j], list)這個內部調用就是四個參數的,只不過第四個參數是false:

public GameObject NewCloverObject(int index, CloverDataFormat cloverData, List<GameObject> cloversObj)

{

    return this.NewCloverObject(index, cloverData, cloversObj, false);

}


也就是第四個參數為true就是四葉草,點進去看看:

public GameObject NewCloverObject(int index, CloverDataFormat cloverData, List<GameObject> cloversObj, bool fourLeafFlag)

{

    Vector2 size = base.GetComponent<BoxCollider2D>().size;

    PolygonCollider2D component = base.GetComponent<PolygonCollider2D>();

    Vector2 vector;

    vector..ctor(base.GetComponent<BoxCollider2D>().offset.x - size.x / 2f, base.GetComponent<BoxCollider2D>().offset.y - size.y / 2f);

    int num = 0;

    bool flag;

    Vector3 vector2;

    do

    {

        flag = false;

        vector2 = new Vector2(vector.x + Random.Range(0f, size.x), vector.y + Random.Range(0f, size.y));

        if (!component.OverlapPoint(vector2 + base.transform.position))

        {

            flag = true;

        }

        else

        {

            for (int i = 0; i < cloversObj.Count; i++)

            {

                Vector2 size2 = cloversObj[i].GetComponent<BoxCollider2D>().size;

                if (Mathf.Abs(vector2.x - cloversObj[i].transform.localPosition.x) < size2.x / 2f && Mathf.Abs(vector2.y - cloversObj[i].transform.localPosition.y) < size2.y / 4f)

                {

                    flag = true;

                }

            }

            num++;

            if (num >= 100)

            {

                break;

            }

        }

    }

    while (flag);

    GameObject gameObject = Object.Instantiate<GameObject>(this.basePrefab, Vector3.zero, Quaternion.identity);

    CloverDataFormat cloverDataFormat = new CloverDataFormat();

    cloverDataFormat.point = 1;

    cloverDataFormat.element = 0;

    //如果第四個參數是false,這裡就是四葉草生成的概率,是fourLeaf_percent是1那這個概率就是1/100

    if (Random.Range(0f, 10000f) < this.fourLeaf_percent * 100f)

    {

        cloverDataFormat.element = 1;

    }

    //如果傳了這個參數為true直接生成四葉草

    if (fourLeafFlag)

    {

        cloverDataFormat.element = 1;

    }

    int element = cloverDataFormat.element;

    if (element != 0)

    {

        if (element == 1)

        {

            cloverDataFormat.spriteNum = Random.Range(0, this.fourCloverSprite.Length);

            gameObject.GetComponent<SpriteRenderer>().sprite = this.fourCloverSprite[cloverDataFormat.spriteNum];

        }

    }

    else

    {

        cloverDataFormat.spriteNum = Random.Range(0, this.cloverSprite.Length);

        gameObject.GetComponent<SpriteRenderer>().sprite = this.cloverSprite[cloverDataFormat.spriteNum];

    }

    gameObject.GetComponent<Clover>().SetCloverData(index, cloverDataFormat);

    gameObject.transform.parent = base.transform;

    gameObject.transform.localScale = Vector3.one;

    gameObject.transform.localPosition = vector2;

    return gameObject;

}


那麼這裡既可以修改四葉草生成的概率也可以修改 fourLeafFlag 這個為true,第一種方式和上面一樣,看看第二種。右鍵fourLeafFlag編輯IL指令:

 

 

這裡首先取fourLeafFlag判斷然後跳轉,所以修改為true即可。修改 ldarg.s fourLeafFlag為ldc.i4 1,修改後的代碼如下:

if (true)

{

    cloverDataFormat.element = 1;

}



Android篇總結


Android的修改的部分就到這裡了,其它的大家可以自己去嘗試,總結下分析Unity3D遊戲過程,當然這篇文章講的是最簡單的情況,還有使用保護將dll腳本加密的,就需要hook函數或者從內存查找dump,還有通過C#腳本調用lua腳本來實現的,邏輯在lua腳本裡面,lua腳本又加密了的情況等等。


那麼沒保護的情況的話,一般可以從界面顯示搜索來分析,然後根據一些特定單詞去搜索查找關鍵代碼部分。這篇主要是Android上面使用mono模式的情況,如果是使用IL2CPP的話比如iOS上面,C#腳本都轉成了cpp文件c代碼的形式的話分析起來就會麻煩很多,下面來看看iOS上面修改hook代碼。


iOS


上面主要也是為下面做鋪墊吧,因為在iOS現在都是IL2CPP模式,C#腳本已經被轉成了C/C++代碼。所以要單獨分析iOS的話難度會大很多,如果從Android的C#腳本入手的話,因為iOS和Android腳本都是一樣的話,可以從Android分析的函數名來對應iOS的c函數然後進行hook修改。


提取ipa


首先從越獄設備上面提取旅行青蛙的ipa包,使用frida-ios-dump一鍵提取即可。由於是日文名字,先通過./dump.py -l把名字列出來,然後複製名字或者通過bundle id去dump就可以了。


IL2CPP符號還原


由於使用IL2CPP選項編譯unity遊戲,會生成cpp的代碼,直接使用IDA看是看不到函數和函數名的,而且遊戲中使用的字符串都被保存在global-metadata.dat的資源文件裡。


首先要通過提取global-metadata.dat文件裡面的字符串對對應的c函數進行符號還原。具體也有現成的文章:還原使用IL2CPP編譯的unity遊戲的symbol,github上面也有線程的項目也做這件事情Il2CppDumper。


下載release的工具,運行Il2CppDumper.exe並依次選擇il2cpp的可執行文件和global-metadata.dat文件,然後選擇Auto(Plus)模式,將生成dump.cs文件和script.py腳本。使用IDA打開可執行文件然後使用script.py腳本即可還原符號。

 

Making method name...

Make method name done

Setting String...

Set string done

Making function...

Make function done, please wait for IDA to complete the analysis

Script finish !



根據函數hook代碼


還原之後就可以根據之前分析到的函數名來hook對應的代碼了,首先是三葉草的數目通過SuperGameMaster.CloverPointStock()獲取的,在IDA搜索CloverPointStock如下:

 

 

接著就可以直接hook這個函數了,由於要inline hook目前是在越獄機器上面,後面會講到非越獄機器hook的方案。使用MonkeyDev新建一個Logos Tweak項目,清空.xm的內容並寫入如下內容:

#import <substrate.h>

#import <dlfcn.h>

#import <mach-o/dyld.h>

 

int (*old_clover_point_stock)(void);

 

int new_clover_point_stock(void)

{

    return 9999;

}

 

%ctor

{

    @autoreleasepool

    {

        unsigned long clover_point_stock = _dyld_get_image_vmaddr_slide(0) + 0x100093A2C;

        MSHookFunction((void *)clover_point_stock, (void *)&new_clover_point_stock, (void **)&old_clover_point_stock);

 

 

    }

}


然後在build settings裡面設置埠和設備密碼然後command + b安裝就能看到效果了,其它函數的hook也是一樣的:

#import <substrate.h>

#import <dlfcn.h>

#import <mach-o/dyld.h>

 

int (*old_clover_point_stock)(void);

 

int new_clover_point_stock(void)

{

    return 9999;

}

 

int (*old_ticket_stock)(void);

 

int new_ticket_stock(void)

{

    return 9999;

}

 

void (*old_lotterycheck)(uint64_t obj);

 

void new_lotterycheck(uint64_t obj)

{

    *(int*)(obj + 80) = rand() % 4 + 1;

}

 

uint64_t (*old_new_clover_object)(uint64_t obj, int index, uint64_t cloverData, uint64_t cloversObj, int fourLeafFlag);

 

uint64_t new_new_clover_object(uint64_t obj, int index, uint64_t cloverData, uint64_t cloversObj, int fourLeafFlag)

{

    return old_new_clover_object(obj,index,cloverData,cloversObj,1);

}

 

%ctor

{

    @autoreleasepool

    {

        unsigned long clover_point_stock = _dyld_get_image_vmaddr_slide(0) + 0x100093A2C;

        MSHookFunction((void *)clover_point_stock, (void *)&new_clover_point_stock, (void **)&old_clover_point_stock);

 

        unsigned long ticket_stock = _dyld_get_image_vmaddr_slide(0) + 0x100093AA4;

        MSHookFunction((void *)ticket_stock, (void *)&new_ticket_stock, (void **)&old_ticket_stock);

 

        unsigned long lotterycheck = _dyld_get_image_vmaddr_slide(0) + 0x100086CF4;

        MSHookFunction((void *)lotterycheck, (void *)&new_lotterycheck, (void **)&old_lotterycheck);

 

        unsigned long new_clover_object = _dyld_get_image_vmaddr_slide(0) + 0x100037100;

        MSHookFunction((void *)new_clover_object, (void *)&new_new_clover_object, (void **)&old_new_clover_object);

    }

}


這裡有一個函數RaffelPanel$$LotteryCheck要修改裡面的result的值,就要根據彙編或者偽代碼來看result的賦值是在什麼位置了,該函數通過F5獲得的偽代碼如下:

__int64 __fastcall RaffelPanel__LotteryCheck(__int64 a1)

{

  __int64 v1; // x19

  __int64 v2; // x0

  __int64 v3; // x0

  int v4; // w20

  int v5; // w23

  signed int v6; // w24

  __int64 v7; // x0

  __int64 result; // x0

 

  v1 = a1;

  if ( !(byte_10137EDBB & 1) )

  {

    sub_100DEAD34(6810LL);

    byte_10137EDBB = 1;

  }

  v2 = qword_101439198;

  if ( *(_BYTE *)(qword_101439198 + 266) & 1 && !*(_DWORD *)(qword_101439198 + 188) )

  {

    sub_100DFF71C();

    v2 = qword_101439198;

  }

  if ( !*(_QWORD *)(*(_QWORD *)(v2 + 160) + 192LL) )

LABEL_17:

    sub_100DE28B4();

  v3 = sub_1000FB954();

  v4 = Random__Range_71094(0LL, 0LL, v3, 0LL);

  v5 = 0;

  *(_DWORD *)(v1 + 80) = 0;   this.result = Rank.White;  //默認都是白色的球

  v6 = -1;

  while ( 1 )

  {

    v7 = qword_101439198;

    if ( *(_BYTE *)(qword_101439198 + 266) & 1 )

    {

      if ( !*(_DWORD *)(qword_101439198 + 188) )

      {

        sub_100DFF71C();

        v7 = qword_101439198;

      }

    }

    if ( !*(_QWORD *)(*(_QWORD *)(v7 + 160) + 192LL) )

      goto LABEL_17;

    result = sub_1000FB954();

    v5 += result;

    if ( v4 < v5 )  //if (num < num2)

      break;   

    if ( ++v6 >= 4 )

      return result;

  }

  *(_DWORD *)(v1 + 80) = v6 + 1;  //this.result = (Rank)i;

  return result;

}


這裡的*(_DWORD *)(v1 + 80)的位置其實就是this.result所以直接修改80偏移位置的值就可以了。



iOS總結


總結來說的話,要分析iOS裡面轉換後的腳本C代碼還是不容易的,如果能夠根據Android C#腳本分析的結果然後對iOS的符號進行恢復一下的話,就可以直接根據Android分析到的函數直接來Hook iOS對應的函數來修改參數或者值了。不過這裡還是在越獄設備上面進行的hook,然後可能還會寫個非越獄設備同樣進行靜態的hook操作。





本文由看雪翻譯小組 AloneMonkey 原創

轉載請註明來自看雪社區


熱門閱讀


點擊閱讀原文/read,

更多乾貨等著你~



相關焦點

  • 旅行青蛙可以設置成中文嗎?旅行青蛙中文翻譯
    青蛙旅行是一個青蛙養成記的遊戲,在日本爆紅。許多網友表示沒有中文版青蛙旅行,其實可以看看本文提供的翻譯來玩這個遊戲。下面,我們來看看旅行青蛙怎麼玩?。【嫌找翻譯太麻煩的朋友可以閱讀:安卓:旅行青蛙漢化版哪裡可以下載?| 蘋果ios:旅行青蛙ios漢化哪裡可以下載?】
  • 旅行青蛙中文翻譯 養青蛙的遊戲叫什麼漢化版怎麼下載
    新手攻略 旅行青蛙這款遊戲目前很多朋友都在玩,雖然漢化版已經出了,不過只有安卓用戶能夠使用,各大網站均有下載,ios的朋友則需要頁面翻譯,下面小編為大家帶來遊戲攻略和旅行青蛙中文翻譯。
  • 蘋果手機旅行青蛙怎麼設置中文 旅行青蛙漢化版ios日語翻譯
    青蛙旅行遊戲怎麼設置語言?蘋果手機旅行青蛙iOS版如何弄成中文顯示?旅行青蛙這個遊戲目前在IOS還是只能用日語,那麼旅行青蛙iOS怎麼漢化改成中文,看完本文你就知道什麼情況了。  因為這個是日本的遊戲,旅行青蛙只是日文翻譯過來的中文意思,iPhone手機用戶朋友們下載的時候只有日語版的,旅行青蛙日語版看不懂怎麼辦還如何玩?哪邊能設置中文顯示呢?
  • 「旅行青蛙」被曝暗藏病毒,蛙媽們看過來了
    出漢化了嗎?ios怎麼玩漢化/破解版?但是,現在市面上出現的很多所謂的「旅行青蛙漢化」「旅行青蛙ios破解版」卻利用一些急於為蛙提供最好裝備的蛙媽們的母愛,在程序中暗藏危機。在此呼籲各位蛙媽,千萬不要隨意下載百度搜索的下載連結、更不要購買淘寶上的收費破解版本。
  • 旅行青蛙彩蛋大揭秘!如何快速佛系養蛙,青蛙養成記拿走不謝!
    最近,又有一個小遊戲刷爆朋友圈啊!一隻只會吃飯、看書、睡覺的青蛙,經常任性出去旅行的青蛙,真的是自帶萌點啊,青蛙養成記,大家都知道怎麼玩嗎?不知道的趕緊跟著小編來了解一下吧!  目前安卓除了漢化版,ios的小夥伴,要想養蛙,只能靠著感覺走了啊。
  • 旅行青蛙為什麼火?養青蛙的遊戲叫什麼名字?
    最近大家的朋友圈和各大自媒體都被一隻青蛙刷屏了,這到底是一款怎麼樣的遊戲呢,養青蛙的遊戲叫什麼名字呢。很多朋友都好奇,這款遊戲為什麼突然這麼火,旅行青蛙為什麼突然就火了呢,且聽小編分析下。  一隻綠色的青蛙是什麼遊戲:  這款遊戲是近期在國內非常火的一款經營養成類遊戲,中文遊戲名字叫做旅行青蛙。
  • 類似旅行青蛙的遊戲有哪些 類似青蛙旅行好玩手遊推薦
    想必大家最近被一款佛系養蛙遊戲刷屏,網上許多小夥伴都深陷其中不能自拔,不少玩家覺得意猶未決。小編在這裡給大家推薦幾款類似旅行的青蛙的遊戲,希望大家喜歡。  旅行青蛙下載導航  安卓用戶:安卓手機旅行青蛙漢化中文版下載地址  iOS用戶:蘋果iPhone手機旅行青蛙下載地址
  • 淘寶旅行青蛙怎麼玩 淘寶旅行青蛙入口介紹
    淘寶旅行青蛙怎麼玩?隨著淘寶推出中國版旅行青蛙,很多想玩旅行青蛙的朋友表示找不到入口,不清楚在哪裡進入旅行青蛙遊戲。下面就介紹一下淘寶旅行青蛙的入口和玩法。旅行青蛙系列軟體最新版本下載旅行青蛙漢化版電腦版軟體版本:1.0.4手遊電腦版立即查看旅行青蛙漢化版軟體版本:1.0.4Android遊戲立即查看旅行青蛙軟體版本:1.0.1iOS遊戲立即查看淘寶旅行青蛙進入教程:目前來說,大家可以通過以下幾種方法進入淘寶旅行青蛙的遊戲:1、在淘寶搜索欄搜索「旅行青蛙」
  • 魔道祖師:當旅行的青蛙變成了旅行的忘羨,這款遊戲真的有毒
    於是,當這批才華橫溢的人拿起畫筆,打開PS,那真的會是「改變世界」,就包括對旅行青蛙這款遊戲善意地改編。當旅行的青蛙變成了旅行的忘羨,這款遊戲真的有毒(作圖大神:藍莓慕斯貓)。但是,這組圖的背景其實是一款叫「旅行青蛙」的遊戲,曾經還席捲過朋友圈,很是火了一陣子。就連小醬醬這種不怎麼玩遊戲的人,都被朋友安利去下載下來玩過幾天。只是,因為小醬醬實在是玩不轉日語版,最後不了了之。
  • 旅行青蛙遊戲日文翻譯成中文圖文教程
    文/郭一傑導讀:最近有一款叫做【旅行青蛙】的遊戲火了,但是這個遊戲全程都是日文,很多玩家表示完全看不懂啊!接下來,小編為大家帶來了【旅行青蛙】日文翻譯成中文的圖文教程,大家一起來看看吧!遊戲下載完成後,點擊遊戲圖標會出現下面的開機畫面,等待遊戲加載完畢。會出現遊戲的演示教程,由於是日文,所以大部分玩家看不懂是什麼意思!
  • 旅行青蛙怎麼玩?旅行青蛙最全中文圖文攻略
    旅行青蛙怎麼玩?旅行青蛙最全中文圖文攻略  近日,一款佛系養兒子遊戲火爆異常,朋友圈又掀起了一輪養兒子狂潮!那麼這款旅行青蛙手遊怎麼玩,下面為大家帶來旅行青蛙手遊的中文翻譯,希望能幫到大家。
  • 遊戲評測:《旅行青蛙》給我帶來不同的遊戲體驗和人生感悟
    遊戲評測:《旅行青蛙》給我帶來不同的遊戲體驗和人生感悟哈嘍大家好,我是蕾蕾,你的男朋友有沒有因為你先是管手機裡四個不知道哪來的野男人叫老公,全然不顧男友就在身邊,舔屏的時候,連瞧都懶得瞧他一眼的行為而生氣過呢?
  • 《旅行青蛙》攻略技巧 旅行青蛙串門的小動物蝸牛蜜蜂烏龜吃什麼
    值得一提的是,至今《旅行青蛙》這款遊戲還沒有開發出適合iOS系統的漢化版本。這也讓人不免產生了一個疑問,這款遊戲到底是有怎樣的魔力,可以讓玩家如此痴迷?這一次的《旅行青蛙》恰好就踏準了這個點。作為一款放置類遊戲,《旅行青蛙》沒有任何養成元素,也沒有任何遊戲目標。在遊戲中,你將擁有一隻寵物青蛙。你只要給它收拾好行囊,它就會自動出門遠行。經過一段時間之後,青蛙會帶回來一些土特產,它也會在旅行途中給你郵寄一些明信片。但是,它出不出門、何時出門、何時歸來,全都是隨機的。
  • 旅行青蛙會死嗎_旅行青蛙會死的很悽慘是真的嗎
    旅行青蛙會死嗎?旅行青蛙可能會死?最近一日本網友爆料旅行青蛙會死,而且死得很悽慘,是不是真的,一起來看看吧。  》》》》點擊下載【3733遊戲盒子】進入APP搜索【旅行青蛙破解版】即可暢玩漢化版、無限獎券哦!  上周五,一個日本網友的帖子在養蛙圈激起了軒然大波。
  • 有青蛙的是什麼遊戲_養青蛙的遊戲叫什麼
    有青蛙的是什麼遊戲?養青蛙的遊戲叫什麼?這款養青蛙遊戲最近火爆全網,簡直人手一隻青蛙在養,那麼這款養青蛙的遊戲去哪裡下載呢?一起和小編來看看吧。>  其實這是一款日系遊戲「旅かえる」,中文名叫「旅行青蛙」。
  • 旅行青蛙界面漢化翻譯大全 旅行青蛙道具食物怎麼用
    旅行青蛙中文漢化版界面翻譯旅行青蛙界面漢化翻譯,旅行青蛙中文菜單意思。最近一款日本的遊戲的在國內慢慢火起來了,也是好評如潮,很多小夥伴都在玩,但是目前都是日文的玩起來很不舒服,雖然遊戲比較簡單,所以下面就來為大家介紹一下旅行青蛙的中文漢化版的界面。
  • 《旅行的青蛙》遊戲現實原型蛙曝光
    導 讀 旅行青蛙現實原型是什麼蛙,原型是什麼樣子的。
  • 旅行青蛙中文版翻譯攻略:旅行青蛙最全圖文攻略來了!
    旅行青蛙是一個近期非常熱門的休閒遊戲,所謂佛系遊戲。旅行青蛙中玩家需要給自己的青蛙準備出門旅行的包裹便當物品,還需要給它接受文件,招待它的好友。它會隨時給你寄來旅行的明信片見聞,它的一切行動不受你的控制,養青蛙兒子需要一些隨緣。
  • 旅行青蛙可能會死嗎?旅行青蛙為什麼最後會死?
    更新:原新聞是洋蔥新聞,「旅行青蛙」最後會死是假消息。旅行青蛙可能會死亡?這個佛系的遊戲是很多小夥伴都在玩的遊戲,最近在朋友圈一直很火,但是大家可能會不知道這個遊戲的結局,結局可能有點悲傷,小編就為大家詳細的介紹下這個遊戲的結局!
  • 《旅行青蛙》青蛙會死嗎 青蛙會不會死
    《旅行青蛙》青蛙會死嗎?青蛙會不會死?