91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何淺析iOS手游逆向和保護

發布時間:2021-12-18 18:18:07 來源:億速云 閱讀:291 作者:柒染 欄目:網絡安全

本篇文章為大家展示了如何淺析iOS手游逆向和保護,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

背景介紹

隨著手游的發展,隨之而來的手游逆向破解技術也越來越成熟,尤其是Andorid方面,各種破解文章比比皆是,相對而言,iOS方面關于手游的逆向分析文章比較少,網易易盾移動安全專家呂鑫垚將通過分析一款unity游戲和一款cocos-lua游戲來剖析一般向的游戲破解及保護思路。

識別Unity游戲

iOS平臺的ipa包可以通過壓縮軟件解壓,一般來說Unity的游戲有如下文件目錄特征:

如何淺析iOS手游逆向和保護

破解思路

Unity游戲會在   \Data\Managed\Metadata下生產資源文件global-metadata.dat。游戲中使用的字符串都被保存在了一個global-metadata.dat的資源文件里,只有在動態運行時才會將這些字符串讀入內存。這使得用IDA對游戲進行靜態分析變得更加困難。那么為了解決這個困難,有人造了輪子,即Il2CppDumper。此可讀取global-metadata.dat文件中的信息,并與可執行文件結合起來。

github:https://github.com/Perfare/Il2CppDumper   打開Il2CppDumper,會彈出一個窗口,第一個選擇macho執行程序,第二個選擇global-metadata.dat,然后選擇對應的模式一般選auto,然后會生成如下的dump.cs  里面就是這個游戲用到的c#的接口。

如何淺析iOS手游逆向和保護有了接口以后,我們就可以搜索一般游戲修改的關鍵字battle,player,maxhp,fight等,然后我們定位到如果所示的類FightRoleData是我們戰斗的時候角色數據來源,還有一個叫battlemanager的類,這個類是一個戰斗管理者,包括開始戰斗,暫停戰斗,結束戰斗。

public class FightRoleData : ICloneable // TypeDefIndex: 2414
{
    // Fields
    public long Sid; // 0x10
    public long OwnerId; // 0x18
    public long Uid; // 0x20
    public int Power; // 0x28
    public int Level; // 0x2C
    public int Sex; // 0x30
    public int FlagType; // 0x34
    public int RoleUnit; // 0x38
    public int Sit; // 0x3C
    public int AttackType; // 0x40
    public int Race; // 0x44
    public int Professional; // 0x48
    public int Star; // 0x4C
    public int Quality; // 0x50
    public int Impression; // 0x54
    public int Awaken; // 0x58
    public int IsNpc; // 0x5C
    public int Soul; // 0x60
    public int Formation; // 0x64
    public int SkinID; // 0x68
    public int AwakenLv; // 0x6C
    public int[][] Skills; // 0x70
    public int[] Runes; // 0x78
    public double Hp; // 0x80
    public double MaxHp; // 0x88
    public double Rage; // 0x90
    public double MaxRage; // 0x98
    public double Aggro; // 0xA0
    public double MoveSpeed; // 0xA8
    public double Attack; // 0xB0
    public double PhysisDefense; // 0xB8
    public double MagicDefense; // 0xC0
    ...
    ...
    ...
    // Properties
    public ERolePosType PostitionType { get; }
    public ERoleGender Gender { get; }
    public bool IsAwaken { get; }

    // Methods
    public virtual void Init(ErlArray erlData); // RVA: 0x100EDDB68 Offset: 0xEDDB68
    private static double _getProperty(ErlArray attrData, int index, bool[] checker, ERoleProperty property); // RVA: 0x100EDE8A0 Offset: 0xEDE8A0
    public ERolePosType get_PostitionType(); // RVA: 0x100EDE93C Offset: 0xEDE93C
    public ERoleGender get_Gender(); // RVA: 0x100EDE964 Offset: 0xEDE964
    public bool get_IsAwaken(); // RVA: 0x100EDE97C Offset: 0xEDE97C
    public object Clone(); // RVA: 0x100EDE98C Offset: 0xEDE98C
    public void .ctor(); // RVA: 0x100EDE994 Offset: 0xEDE994
}


// Namespace: 
public class BattleManager : MonoBehaviour // TypeDefIndex: 3127
{
    // Fields
    ...
    ...
    ...
    // Properties
    public Camera GameCamera { get; set; }
    public GameObject CameraBase { get; }
    public bool Loading { get; set; }
    public BattleView battleView { get; set; }
    public string BattleMusic { get; }
    public Dictionary`2<string, RoleModelConfig> RoleModelConfigDic { get; }
    public int TargetFrame { get; }
    public static BattleManager Instance { get; }
    public DragonBallBattle Battle { get; }
    public bool Pause { get; set; }
    public List`1<BattleRoleController> BattleRoleControllers { get; }
    public bool IsSkipSuperSkill { get; }
    private bool _startAnimPlaying { get; }

    // Methods
    ...
    ...
    ...
    public void StartBattle(); // RVA: 0x101BBB1EC Offset: 0x1BBB1EC
    public void SkipBattle(); // RVA: 0x101BE18B0 Offset: 0x1BE18B0
    ...
    ...
    ...
}

至此,我們可以很容易實現兩個功能跳過戰斗,修改我們角色的攻擊力,第一個功能可以通過hook StartBattle()方法然后獲得this指針也就是BattleManager對象,然后我們根據BattleManager對象來調用SkipBattle()方法就可以了,第二個方式的話我們可以修改FightRoleData的數據來實現,那我們我們首先來看下FightRoleData在哪些地方被用到了,通過搜索可以發現這么個類:

// Namespace: BattleSystem
public static class BattleAPI // TypeDefIndex: 2490
{
    // Methods
    private static T _GetConfig(long id); // RVA: 0x1000E98B4 Offset: 0xE98B4
    public static DragonBallBattle Create(BattleScene scene, string hexData); // RVA: 0x100B06CFC Offset: 0xB06CFC
    public static DragonBallBattle Create(BattleScene scene, byte[] dataBytes); // RVA: 0x100B0950C Offset: 0xB0950C
    public static DragonBallBattle Create(BattleScene scene, BattleData data, optional CallBack`1<DragonBallBattle> beforeInit); // RVA: 0x100B06E04 Offset: 0xB06E04
    public static BattleRole CreateBattleRole(BattleRoleConfig roleConfig, FightRoleData roleData, BattleScene scene, DragonBallBattle battle, Dictionary`2<long, List`1<int[]>> seqCache, optional double initialCD, optional double autoCD); // RVA: 0x100B0B3A0 Offset: 0xB0B3A0
    private static int[] _getUniqueAttackSequence(int[] seq, long sid, Dictionary`2<long, List`1<int[]>> cache, YKRandom random); // RVA: 0x100B0CC28 Offset: 0xB0CC28
    private static BattleRole _createBattleRolePartner(BattlePartnerConfig partnerConfig, BattleScene scene, int[] level, DragonBallBattle battle); // RVA: 0x100B0A4B4 Offset: 0xB0A4B4
    public static void ApplyProperty(BattleRoleData roleData, FightRoleData netData); // RVA: 0x100B0CDB0 Offset: 0xB0CDB0
    private static BattleRole[] _getFormatBattleRoles(BattleScene scene, List`1<FightRoleData> data, BattleFormation formatiom, int battleIndex, DragonBallBattle battle, Dictionary`2<long, List`1<int[]>> seqCache, double[] initialCDModifier, double[] autoCD); // RVA: 0x100B09C1C Offset: 0xB09C1C
    public static int ServerIndexToConfigIndex(int index, ERolePosType posType); // RVA: 0x100B0E2A8 Offset: 0xB0E2A8
    public static void ImportConfig(IConfigImporter importer); // RVA: 0x100B0E390 Offset: 0xB0E390
}

其中CreateBattleRole這個函數用到了FightRoleData的數據,那么我們可以通過hook CreateBattleRole這個函數,同時修改第三個參數(第一個參數是this指針)對應的roledata的偏移里面的數值比如0xB0偏移位置的attack的值達到修改攻擊力的目的。

防護

Unity游戲在iOS中雖然將il轉成了cpp的形式,這在一定程度上增大了逆向難度,因為轉成了匯編形式不容易從代碼層面去分析功能。但是因為il2cpp本身的冗余性,太多的字符串、符號信息被保留了。分析者很容易通過這些信息找到突破口,所以這里給出幾點意見:

  1. 加密global-metadata.dat

  2. 在c#層面進行函數符號混淆(由于函數符號混淆容易出錯所以建議對核心的幾個類進行混淆)

  3. 字符串加密,代碼混淆

  4. 服務端不要信任客戶端,增加對數據的校驗,比如我上面修改了攻擊力,服務器在下發roledata的時候就需要對下發的roledata進行簽名,如果我客戶端修改了數據,服務器校驗的時候就數據簽名異常,不予以信任。

談了點Unity游戲,現在我們來談談一款cocos-lua游戲。

識別Lua游戲

一般來說通過這兩方面來看是不是lua腳本游戲,首先解壓ipa,然后進入資源目錄一般來說是src或者res,里面有類似lua,luac后綴,保險一點我們把二進制拖進ida看下:

如何淺析iOS手游逆向和保護搜索lua luajit關鍵字得到如圖信息。

如何淺析iOS手游逆向和保護判定是lua腳本游戲。我們把lua腳本拖進游戲看下一般來說肯定是加密了,或者編譯為luac/luajit形式,不然就太容易被破解了。

如何淺析iOS手游逆向和保護

根據以上結果來看,不是明文存儲做了加密,而且看頭幾個字節很有可能是采用了xxtea這種加密方式(這種方式是cocos官方提供的而且特征很明顯,加密后將sign追加在文件頭部作為標識。加密的key則是直接寫在代碼里面的)

破解思路

Lua游戲的話一般來說這么2種思路:

  1. 獲取lua腳本,替換lua腳本

  2. 因為lua腳本的動態特性,我們只需要通過lua引擎去加載我們的lua腳本就能達到劫持數據的作用

我們這邊通過dump的方式來獲取腳本,可以通過hook   luaL_loadbuffer來獲取解密后的腳本,但是iOS跟安卓還是有些不同,因為安卓lua是通過so來加載的,所以必定有導出函數luaL_loadbuffer。但是iOS   lua已經集成到二進制中了,所以符號自然就被strip掉了,這個時候我們可以通過字符串配合lua源碼來定位,比如我這邊選擇的字符串是”error  loading module '%s' from file",然后向上追溯就很容易找到這個函數。

如何淺析iOS手游逆向和保護對比下f5內容與luaL_loadbuffer原型

int luaL_loadbuffer (lua_State *L, const char *buff, size_t sz, const char *name);

現在我們就開始編寫代碼來dump腳本,這邊我用frida來實現,原因是frida對于這些一次性的需求實在是太好用了,不需要編譯,不需要重啟設備,開箱即用。

script = session.create_script("""

var baseAddr = Module.findBaseAddress('QuickMud-mobile');
var luaL_loadbuffer = baseAddr.add(0x2DF644);

Interceptor.attach(luaL_loadbuffer, {
    onEnter: function(args) {
        var name = Memory.readUtf8String(args[3]);
        var obj = {}
        obj.size = args[2].toInt32()
        obj.name = name;
        obj.content = Memory.readCString(args[1], obj.size);
        send(obj);
    }
} );

""")

def write(path, content):
    print('write:', path)
    folder = os.path.dirname(path)
    if not os.path.exists(folder):
        os.makedirs(folder)
    open(path, 'w').write(content)

def on_message(message, data):
    if message['payload']['name']:  
        name = message['payload']['name']
        name = “/Add/Your/Dump/Path/"+ name
        content = message['payload']['content'].encode('utf-8')
        dirName = os.path.dirname(name)
        if not os.path.exists(dirName):
            os.makedirs(os.path.dirname(name))
        if name.endswith('.lua'):
            write(name, content)

script.on('message', on_message)
script.load()
sys.stdin.read()

如何淺析iOS手游逆向和保護如何淺析iOS手游逆向和保護如何淺析iOS手游逆向和保護

有了解密后的腳本我們就可以通過修改腳本達到作弊的效果,因為有了源碼我們甚至可以寫一個脫機掛出來,這對游戲的危害極大。

防護

可以看到lua腳本如果只加密危害是很大的,所以lua游戲需要保障lua腳本的安全可以從以下幾點入手:

  1. 對lua編譯為luac 或者 luajit 然后在此基礎上對lua引擎修改opcode,然后修改luajit的bytecode增大逆向的難度

  2. iOS雖然strip了符號,但是由于lua是開源的很容易定位到luaL_loadbuff,所以有必要加上字符串加密和代碼邏輯混淆來保護游戲的安全。

上述內容就是如何淺析iOS手游逆向和保護,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

ios
AI

梁平县| 奉化市| 桃江县| 兴业县| 柘城县| 拉孜县| 青浦区| 交口县| 南宁市| 清镇市| 叙永县| 垫江县| 鄄城县| 离岛区| 安福县| 银川市| 奎屯市| 太仆寺旗| 泗阳县| 漯河市| 洪泽县| 睢宁县| 罗山县| 石门县| 调兵山市| 崇明县| 板桥市| 抚宁县| 乐亭县| 泾阳县| 通城县| 忻州市| 铜梁县| 鲜城| 青岛市| 宣城市| 毕节市| 浙江省| 遂宁市| 化德县| 屏南县|