XCTF_MOBILE16_boomshakalaka-3

初见

题目附件为一个apk。

提示信息为:play the game, get the highest score

在SDK创建的模拟器下按照apk后运行失败:

想到电脑里还装了雷电模拟器,apk在雷电模拟器下可以正常按照运行。

运行app后,是一个打飞机的游戏,然后我就玩了两把。。。。。。

暂时不清楚这个游戏和解题有什么关系,题目的提示信息说要拿到最高分,但解题方法应该不是玩到最高分^_^。

接下来静态分析apk文件看看。

静态分析apk

用jadx分析apk,发现apk中有两个包:

第一个包,com.example.plane应该是作者开发的。主要有两个类,a和FirstTest。

第二个包,org.cocos2dx.lib,看名字就像个第三方的,百度一下,发现是个游戏开发的库,包括“我是MT”等许多游戏都是用这个库开发的。作者应该就是用这个库开发了这个打飞机的游戏。

没有发现MainActivity,需要先确定app的主Activity是哪个。故解码AndroidManifest.xml看看哪个是主Activity。

主Activity

apk内的xml文件都是编码过的,需要用apktool解码:

apktool.bat d C:\Users\leo\Desktop\9ff03a7a4b364ca3be452d08771ae7fb.apk

解码后,AndroidManifest.xml部分内容为:

<activity android:configChanges="orientation" android:label="Plane" android:name=".FirstTest" android:screenOrientation="portrait" android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

可以看到主Activity是FirstTest类。

FirstTest类

FirstTest类继承自Cocos2dxActivity类:

public class FirstTest extends Cocos2dxActivity{
    ... ...
}

加载了cocos2dcpp库:

    static {
        System.loadLibrary("cocos2dcpp");
    }

可以猜测,继承Cocos2dxActivity类应该是使用cocos2dx库开发游戏的方式。

FirstTest类内部只有两个方法:

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        new a(this, "flag").d("YmF6aW5nYWFhYQ==");
        new a(this, "Cocos2dxPrefsFile").d("N0");
    }

    public Cocos2dxGLSurfaceView onCreateView() {
        Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
        new a(this, "Cocos2dxPrefsFile").d("MG");
        glSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);
        return glSurfaceView;
    }

这两个方法都是对Cocos2dxActivity类中方法的重载,应该是在游戏运行的特定阶段会被调用的。名字都有Create,应该是在游戏运行的开始阶段被调用。

这两个方法内部都用到了a类的d方法,接下来看一看这个方法。

a类

a类的主要功能是使用SharedPreferences接口进行数据存储:

public class a {
    private SharedPreferences editor;

    public a(Context arg1, String arg2) {
        this.editor = null;
        this.editor = arg1.getSharedPreferences(arg2, 0);
    }

    public void d(String arg1) {
        this.editor.edit().putString("DATA", String.valueOf(String.valueOf(c())) + arg1).commit();
    }
}

SharedPreferences接口是一个轻量级的存储类,用于保存软件配置参数。使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下。

shared_prefs目录

我来到雷电模拟器的/data/data/com.example.plane/shared_prefs目录下,果然看到了xml文件。
文件名就是FirstTest类调用a类构造函数的第二个参数:

这里有两个xml文件。

flag.xml

 首先看了下flag.xml文件,内容为:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="DATA">YmF6aW5nYWFhYQ==</string>
</map>

这就是FirstTest类的onCreate方法调用a类的d方法时传入的参数,也印证了a类就是用SharedPreferences接口进行数据存储的。

这里“YmF6aW5nYWFhYQ==”有明显的BASE64特征,解码为:bazingaaaa。提交错误,这不是flag。

接下来重点看看Cocos2dxPrefsFile.xml。

Cocos2dxPrefsFile.xml

这个文件内容为:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <int name="HighestScore" value="34300" />
    <string name="DATA">MGN0ZntDMGNvUzJkX0FuRHJvMWdz99ZntDMGNvUzJkX0FuRHJvMWRzRVdz99</string>
    <boolean name="isHaveSaveFileXml" value="true" />
</map>

里面记录了我在游戏里的最高分“34300”,还有一串DATA字符串。

这个字符串的部分内容是由FirstTest类写入的,FirstTest类的onCreate和onCreateView分别往Cocos2dxPrefsFile.xml文件写入了“N0”和“MG”。正是这段字符串的前四个字符。

我之后重启了几遍app,发现,每次启动app都会在DATA字符串中追加“MG”和“N0”。

“MGN0”之后的字符串分为多组,每组的固定模式为:

ZntDMGNvUzJkX0FuRHJv + xxx + dz99

其中xxx会变化。

通过多次打游戏,并分析Cocos2dxPrefsFile.xml文件内容的变化,可以得到。每次超过前一次的最高分时,都会在该xml文件中增加一条上述模式的内容。

想找一找增加这些内容的代码。

在jadx中没找到关于“ZntDMGNvUzJkX0FuRHJv”和“dz99”字符串,估计在lib中,接下来使用ida分析apk里的“libcocos2dcpp.so”。

libcocos2dcpp.so

分析这个库文件有点懵,因为我本身没有游戏开发经验。想找一篇合适的介绍cocos2d的文章也没找到,看了多篇资料后,得到一些粗略的理解:

  1. 所谓动态的游戏,就是一个一个画面的交替。
  2. cocos2dx通过定时器来定时更新我们看到的画面。
  3. cocos2dx中负责响应定时器的函数名叫update()。

也就是我们移动飞机,敌机位置变化,子弹移动,消灭敌机,增加分数等都是和update()函数有关。

update()

在IDA搜索了一下update()函数,发现有许多函数名中有update的,但函数名为update的只有一个:GameLayer::update()。

浏览一下GameLayer::update()函数的伪代码,发现一个名字为ControlLayer::updateScore()的函数。

感觉这到题目和这个分数有很大的关系,题目的提示信息也说了要得到最高分,ControlLayer::updateScore()应该是个重要函数。

但这两个函数一个是GameLayer类的,一个是ControlLayer类的。搜索了以下游戏层和控制层的关系,大致就是游戏层负责显示相关,控制层负责事件处理(触摸屏幕、子弹移动到敌机从而消灭飞机)。这只是粗略的理解,因为没有对cocos2d进行广泛学习。

updateScore()

这个函数的反编译代码很多,粗略浏览一下,发现一些有趣的数字操作:

if ( (unsigned int)a2 <= 1000000000 )
  {
    ... ...
    switch ( a2 )
    {
      case 100:
        v6 = cocos2d::CCUserDefault::sharedUserDefault(v5);
        std::operator+<char>(v22, v20, "MW");
        cocos2d::CCUserDefault::setStringForKey(v6, &v33, v22);
        v7 = v22;
        break;
      case 600:
        v8 = cocos2d::CCUserDefault::sharedUserDefault(v5);
        std::operator+<char>(v23, v20, "Rf");
        cocos2d::CCUserDefault::setStringForKey(v8, &v33, v23);
        v7 = v23;
        break;

猜测这里的a2就是获得的分数,这个1000000000应该就是最高分。

在switch分支中可以看到,当分数为100时,给一个字符串附加“MW”,想这会不会就是Cocos2dxPrefsFile.xml中ZntDMGNvUzJkX0FuRHJv + xxx + dz99里的xxx呢。

与其看代码,不如来一把游戏。。。。。。

因为分数为历史最高分时才会在Cocos2dxPrefsFile.xml中写入内容,所以我先在模拟器中将Cocos2dxPrefsFile.xml中的最高分修改为0,并删除现有的DATA字符串内容:

 重新运行游戏,达到100分,也就是消灭一个小飞机(小飞机100分,大飞机600分):

 此时Cocos2dxPrefsFile.xml内容为:

 DATA的内容正好是“ZntDMGNvUzJkX0FuRHJv + MW + dz99”。

同样道理,得分为600时(必须是消灭6个小飞机,不能是一个小飞机+一个大飞机,分数必须有过600这个状态),DATA内容为“ZntDMGNvUzJkX0FuRHJv + MWRf + dz99”。

按照这个逻辑,一直玩到1000000000分,经过updateScore()内的各个分支(switch和之后的if),将得到一个最长DATA字符串。我们按照分数高低拼接出这个字符串,内容为:

ZntDMGNvUzJkX0FuRHJv + MWRfRzBtRV9Zb1VfS24w + dz99

这东西和答案有什么关系呢,得猜一下。

虽然这段字符串结尾没有“=”,但全是可显字符,再结合flag.xml里的DATA字符串是BASE64编码的,所以怀疑这个也是BASE64编码的。

试一下,发现BASE64解码失败。

这时看到了DATA字符串最前面的MGN0,也就是app运行时填入的4个字符,加上这四个字符,完整字符串为:“MGN0ZntDMGNvUzJkX0FuRHJvMWRfRzBtRV9Zb1VfS24wdz99”。

对这个完整串BASE64解码成功,结果为“0ctf{C0coS2d_AnDro1d_G0mE_YoU_Kn0w?}”。

总结

这道题的难点在cocos2d游戏库相关知识,短期内想查到解题有用资料费不少功夫。

我现在也还是只了解了个大概皮毛。所以对这个题目的so库文件、so库文件如何与java部分的代码配合,还不了解。

如果是懂游戏开发的大神,应该能给出原理更清晰的解答。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值