안드로이드 게임 앱 리버싱 문제다.


1945같은 간단한 비행 게임인데 문제 설명란에 최고점수를 얻으라고 나와있다.


대충 점수조작을 하면 될것같다 생각했고 일단 자바코드부터 보면 아래와 같이 SharedPreferences를 통해 앱 데이터 로컬 영역에 임의의 값을 저장하고 있었다.



위 값들은 앱 실행 시 초기 값을 담는 정도였고 자바코드에선 더이상 볼게 없어서 네이티르 코드를 분석해봤다.


점수와 관련된 코드를 찾아보니 updateScore라는 함수가 존재했고 해당 함수 코드는 아래와 같았다.


cocos2d::CCUserDefault *__fastcall ControlLayer::updateScore(cocos2d::CCUserDefault *result, unsigned int a2)

{

  int v2; // r3

  const char *v3; // r4

  int v4; // r7

  cocos2d::CCUserDefault *v5; // r0

  int v6; // r5

  int v7; // r3

  char *v8; // r0

  int v9; // r5

  int v10; // r3

  int v11; // r5

  int v12; // r3

  int v13; // r5

  int v14; // r3

  int v15; // r5

  int v16; // r3

  int v17; // r5

  int v18; // r3

  int v19; // r5

  int v20; // r3

  int v21; // r5

  int v22; // r3

  int v23; // r5

  int v24; // r3

  int v25; // r5

  int v26; // r3

  int v27; // r0

  int v28; // [sp+0h] [bp-68h]

  cocos2d::CCUserDefault *v29; // [sp+4h] [bp-64h]

  int v30; // [sp+8h] [bp-60h]

  char v31; // [sp+Ch] [bp-5Ch]

  char v32; // [sp+10h] [bp-58h]

  char v33; // [sp+14h] [bp-54h]

  char v34; // [sp+18h] [bp-50h]

  char v35; // [sp+1Ch] [bp-4Ch]

  char v36; // [sp+20h] [bp-48h]

  char v37; // [sp+24h] [bp-44h]

  char v38; // [sp+28h] [bp-40h]

  char v39; // [sp+2Ch] [bp-3Ch]

  char v40; // [sp+30h] [bp-38h]

  char v41; // [sp+34h] [bp-34h]

  char v42; // [sp+38h] [bp-30h]

  char v43; // [sp+3Ch] [bp-2Ch]

  int v44; // [sp+40h] [bp-28h]

  char v45; // [sp+44h] [bp-24h]

  int v46; // [sp+48h] [bp-20h]

  char v47; // [sp+4Ch] [bp-1Ch]


  v44 = 1635017060;

  v2 = 0;

  v45 = 0;

  v47 = 0;

  v29 = result;

  v3 = (const char *)a2;

  v46 = 0;

  do

  {

    *((_BYTE *)&v46 + v2) = *((_BYTE *)&v44 + v2) ^ 0x20;

    ++v2;

  }

  while ( v2 != 4 );

  if ( a2 <= 0x3B9ACA00 )

  {

    v4 = cocos2d::CCUserDefault::sharedUserDefault(result);

    sub_3A34D8(&v33, &unk_3F92A0, &v31);

    cocos2d::CCUserDefault::getStringForKey(&v32, v4, &v46, &v33);

    v5 = (cocos2d::CCUserDefault *)sub_3A1DDC(&v33);

    if ( v3 == (const char *)&dword_64 )

    {

      v6 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v34, &v32, "MW");

      cocos2d::CCUserDefault::setStringForKey(

        v6,

        (const char *)&v46,

        (const char **)&v34,

        v7,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v34;

    }

    else if ( v3 == (const char *)&stru_254.st_value )

    {

      v9 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v35, &v32, "Rf");

      cocos2d::CCUserDefault::setStringForKey(

        v9,

        (const char *)&v46,

        (const char **)&v35,

        v10,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v35;

    }

    else if ( v3 == (const char *)&stru_2B4.st_size )

    {

      v11 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v36, &v32, "Rz");

      cocos2d::CCUserDefault::setStringForKey(

        v11,

        (const char *)&v46,

        (const char **)&v36,

        v12,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v36;

    }

    else if ( v3 == (const char *)&stru_BB4.st_value )

    {

      v13 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v37, &v32, "Bt");

      cocos2d::CCUserDefault::setStringForKey(

        v13,

        (const char *)&v46,

        (const char **)&v37,

        v14,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v37;

    }

    else if ( v3 == (const char *)&stru_15D4.st_info )

    {

      v15 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v38, &v32, "RV");

      cocos2d::CCUserDefault::setStringForKey(

        v15,

        (const char *)&v46,

        (const char **)&v38,

        v16,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v38;

    }

    else if ( v3 == (const char *)&stru_26A4.st_size )

    {

      v17 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v39, &v32, "9Z");

      cocos2d::CCUserDefault::setStringForKey(

        v17,

        (const char *)&v46,

        (const char **)&v39,

        v18,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v39;

    }

    else if ( v3 == (const char *)&stru_4644.st_info )

    {

      v19 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v40, &v32, "b1");

      cocos2d::CCUserDefault::setStringForKey(

        v19,

        (const char *)&v46,

        (const char **)&v40,

        v20,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v40;

    }

    else if ( v3 == (const char *)&stru_15AD4.st_info )

    {

      v21 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v41, &v32, "Vf");

      cocos2d::CCUserDefault::setStringForKey(

        v21,

        (const char *)&v46,

        (const char **)&v41,

        v22,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v41;

    }

    else if ( v3 == (const char *)&stru_18694.st_info )

    {

      v23 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v42, &v32, "S2");

      cocos2d::CCUserDefault::setStringForKey(

        v23,

        (const char *)&v46,

        (const char **)&v42,

        v24,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v42;

    }

    else

    {

      if ( v3 != (const char *)1000000000 )

      {

LABEL_25:

        v27 = cocos2d::CCString::createWithFormat((cocos2d::CCString *)"%d", v3);

        (*(void (__fastcall **)(_DWORD, _DWORD))(**((_DWORD **)v29 + 66) + 428))(

          *((_DWORD *)v29 + 66),

          *(_DWORD *)(v27 + 20));

        return (cocos2d::CCUserDefault *)sub_3A1DDC(&v32);

      }

      v25 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v43, &v32, "4w");

      cocos2d::CCUserDefault::setStringForKey(

        v25,

        (const char *)&v46,

        (const char **)&v43,

        v26,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v43;

    }

    sub_3A1DDC(v8);

    goto LABEL_25;

  }

  return result;

}


대충보니 점수가 1000000000점이면 될 것 같아서 디버깅을 통해 해당 함수의 인자값을 1000000000점으로 바꿔보니 게임이 클리어되면서 잠겨있는 xml파일을 보라고 했다.


shared_prefs 폴더 내에서 Cocos2dxPrefsFile.xml 파일을 확인해보니 base64 인코딩된 값이 존재해서 이걸 디코딩해봤는데 중간의 값들이 깨져있었다. 


0ctf{C0coS2d_AnDro1gs��g�36�3&E��G&�w?}


단순히 게임 클리어만 하는 방식이 아닌 것 같아서 파일 내에 저장되는 base64 인코딩 값이  어떻게 생성되는지 코드를 좀 더 자세히 봤다.


코드 내 분기문들을 보니 게임 클리어과정에서 아래와 같은 형태로 특정 점수들을 지나갈때마다 최종 base64 인코딩 값의 일부 값들이 저장되고 있었다.


if ( v3 == (const char *)&dword_64 )

    {

      v6 = cocos2d::CCUserDefault::sharedUserDefault(v5);

      std::operator+<char,std::char_traits<char>,std::allocator<char>>(&v34, &v32, "MW");

      cocos2d::CCUserDefault::setStringForKey(

        v6,

        (const char *)&v46,

        (const char **)&v34,

        v7,

        v28,

        (int)v29,

        v30,

        *(int (__fastcall **)(int))&v31);

      v8 = &v34;

    }


각 점수별로 저장하는 값들을 다 더해줬고 맨 앞 맨 뒤에 기본적으로 저장되는 값들을 디버깅으로 구해 최종 base64인코딩 값을 만들었다. 이를 디코딩해보면 정상적인 플래그 값이 나온다.


FLAG = 0ctf{C0coS2d_AnDro1d_G0mE_YoU_Kn0w?}






















'CTF > Writeup' 카테고리의 다른 글

Pico CTF 2018 keygen-me-2  (0) 2019.03.22
Tokyo-Westerns-3rd-2017 CTF Rev Rev Rev  (0) 2019.03.15
SSCTF 2016 : Re1  (0) 2019.03.13
Sharif University CTF 2016 : Serial  (0) 2019.03.11
35C3 CTF 2018 COREBOT  (0) 2019.02.26
블로그 이미지

JeonYoungSin

메모 기록용 공간

,