美文网首页
Unity游戏逆向思路

Unity游戏逆向思路

作者: 约你一起偷西瓜 | 来源:发表于2020-05-26 12:17 被阅读0次

    拆弹专家,原谷歌游戏
    downloadUrl:https://ww.lanzous.com/id06yjc(有广告版本)
    本文只作为思路分享文章,无逆向成品,仅供学习交流
    拿到apk首先还是解压一下看一下文件目录,看看游戏引擎

    Unity游戏.png
    这里就先借用一下Perfare大神的工具:Il2CppDumper
    该工具用来分析so里面游戏逻辑的方法枚举,虽然没有函数体,但是通过函数名很大程度上还是可以帮助我们分析游戏的逻辑,工具很香,可以配合IDA使用,了解详情的可以自行查看GitHub文档
    (7.2版本IDA直接去运行ida.py并加载script.json即可实现方法名的导入)

    其次就是对反编译libil2cpp的dll文件可以使用Reflector或者dnSpy来查看(其实和查看dump.cs没有太大的区别,主要的差别在于使用Reflector可以直接去查看空命名空间的代码,过掉一些系统级别代码)

    举例一下Reflector结合Frida的使用

    Reflector示例.png

    这里我们可以知道偏移地址(实际地址=基地址+偏移地址+ thumb指令?1:0))

    • showDilog() → 0x547484
    • showMessage() → 0x547F30

    通过这种方式我们就可以轻松的批量断点我们希望断点的方法了
    以下为一个简单的批量断点脚本示例:

    function start(){
        //com.izyplay.defusethebomb.bazhang
        var arrayAddr = [0x54728C,0x547310,0x54745C,0x547DF8,0x547484,0x548218,0x547F30,0x55DF40
            ,0x679798,0x6798B4,0x687428,0x687350]; 
        var arrayName = ["AndroidDialog Create","AndroidDialog Create1","AndroidDialog init"
            ,"AndroidMessage Create","showDialog","CallStatic","showMessage","SetPressedState"
            ,"NativeDialog","NativeMessage","ToggleButton","OnClick"]; 
        
        var soAddr = Module.findBaseAddress("libil2cpp.so");
        console.error('\nsoAddr:' + soAddr + "\n");
    
        for (var index = 0; index < arrayAddr.length; index++) {  
            console.log("-------------------------");
            var currentAddr = soAddr.add(arrayAddr[index]);
            console.log('currentAddr:' + currentAddr);
            funcTmp(currentAddr,soAddr,index,arrayName);
            console.log("\t\t---->"+index,arrayAddr[index]+" is prepared ");  
        } 
        console.log("\n")
    }
    
    function funcTmp(currentAddr,soAddr,index,arrayName){
        Interceptor.attach(currentAddr, {
            onEnter: function(args){
                console.log("called : "+arrayName[index]+"  ----- addr : " + currentAddr.sub(soAddr) +"\n");
            },
            onLeave: function(retval){
    
            }
        });
    }
    

    已上是对so的一个简单处理分析
    我们知道Unity游戏与Java的通信是通过UnitySendMessage()之类的函数来实现的
    不同的代码可能写法不一样,但是这里注意几个关键词就是了
    “Unity”,“Send”,“Message”,“Reward”,“Video”(拿到国内的谷歌游戏都是添加了广告的,自然是有一个video来展示广告,获取奖励Reward等等)自己排列组合,总能发现点东西

    使用Jadx搜索关键字.png

    随便点进去一个跟进代码不难发现其实最终就是去调用了Native方法




    这里就不继续跟进了,回到初衷是要搞这个游戏的奖励
    这里游戏原来的处理逻辑是点击观看广告视频,然后就可以成功获取奖励,上面说了,游戏与unity的通信是通过UnitySendMessage来实现的,这里我们只用再找到在哪里打开视频,打开视频看完了必然也会有一个成功回调,修改smali处理一下这个逻辑就搞定,于是我们又重新搜索关键字Reward,Video ... 发现如下

    奖励获取.png

    这里可以看到每种广告播放状态都向Unity发了一条消息(UnitySendMessage 是一个 public static方法),简单分析一下逻辑,成功后向Unity发的是什么消息,剩下的就是对这个smali为所欲为了

    public static void UnitySendMessage(String str, String str2, String str3) {
            if (!m.c()) {
                f.Log(5, "Native libraries not loaded - dropping message for " + str + "." + str2);
                return;
            }
            try {
                nativeUnitySendMessage(str, str2, str3.getBytes("UTF-8"));
            } catch (UnsupportedEncodingException unused) {
            }
        }
    
    
        const-string v0, "onRewardedVideoAdRewarded"
    
        const-string v1, "123"
    
        invoke-direct {p0, v0, v1}, Lcom/ironsource/unity/androidbridge/AndroidBridge;->sendUnityEvent(Ljava/lang/String;Ljava/lang/String;)V
    

    至于以上我们是怎么找到这些关键点的话,我们还是使用我们的Frida大法,使用基于Frida的Objection来完成Class批量断点,非常好用,当然你也可以选择手写Frida批量下断脚本

    Objection批量断点.png

    想进行方法调用的测试,我们可以使用Frida的远程方法调用,静态变量值的获取等等

            Java.choose("com.ironsource.unity.androidbridge.AndroidBridge",{
                onMatch: function(obj){
                   var ss = {"reward_amount":1,"placement_name":"DefaultRewardedVideo","reward_name":"Virtual Item"};
                    obj.sendUnityEvent("onRewardedVideoAdRewarded","onRewardedVideoAdRewarded");
                },
                onComplete: function(){
    
                }
            });
    
            Java.choose("com.ironsource.mediationsdk.model.RewardedVideoConfigurations",{
                onMatch: function(obj){
                    console.log("主动调用onRewardedVideoAdRewarded")
                    var mRVPlacements = obj.mRVPlacements.value;
                    console.log(mRVPlacements.size());
                    console.log(mRVPlacements.get(0));
                },
                onComplete: function(){
    
                }
            });
    

    最后来一个小tips:想不看广告只用对app重新签名就是,但是广告播放成功的回调自然也是失效了,所以还是需要稍微改改smali

    以上就是这个Unity游戏的简单逆向过程

    总结一下:
    一般游戏逆向分为unity游戏,cocos游戏,或者一些自己写的游戏

    unity主要可以看成两类,dll游戏和libil2cpp游戏,dll游戏比较简单,由于c#类似Js的语言特性,几乎就是可以明文随便篡改(dpy/),为了安全性的提升,所以才应运而生了libil2cpp用来转换dll to so ,但实际上逆向的时候使用ida分析libil2cpp的时候也差不多吧,有点汇编基础基本不难看懂,或者是结合frida去动态短点一些位置,或者是使用dwarf去动态调试一些位置,so我们只能修改不能新增指令,解决这个问题我们可以考虑用inlinehook完成新增。对于libil2cpp的情况,咋们可以用我分享的工具dps.py查找关键词函数,使用dpoint.js批量断点我们想查看的函数,动态断点方便我们快速找到想要hook的关键点后,使用inlinehook对其实现本地化为所欲为。对于一些自己写的游戏没有了dumper,但是我们可以考虑使用frida脚本枚举导出函数进行批批量hook,同时筛选函数名实现上述类似工作,至于cocos游戏也可以参照上述思路,咋们就简单分个类 cocos js 和 cocos lua,由于这篇文章主要介绍unity游戏,这里就不多说cocos,大概就这样哇 ~

    相关文章

      网友评论

          本文标题:Unity游戏逆向思路

          本文链接:https://www.haomeiwen.com/subject/gesmahtx.html