Description

Category: Reversing

Source: HDCON 2017 Quals.(Febuary)

Points: 100

Author: Jisoon Park(js00n.park)

Description:

가위바위보에 연속적으로 이겨서 플래그를 획득하시오

Write-up

apk 파일이 주어진 것으로 보아, android 리버싱 문제이다. 코드 분석에 앞서, Android 기기에 파일을 올려서 동작을 확인해 본다.

가위바위보를 하는 단순한 게임인데, Gole Score(Goal Score의 오타인듯)가 17916인걸로 봐서, 17916번을 연속으로 이기면 flag를 획득할 수 있는것으로 보인다.
brute force 같은건 생각하지 말고, 코드 분석으로 넘어가자.

apk 파일은 zip 파일과 같은 형태이므로, unzip을 이용해서 압축을 풀고, classes.dex 파일을 jar 파일로 변환한 뒤, jd-gui를 통해 코드 분석을 실시하면 된다.

$ unzip rock.apk
   ...
$ dj2-dex3jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar
$ jd-gui classes-dex2jar.jar

열심히 뜯어볼 것도 없이, 친절하게 CallMe.class라는 클래스가 있다. 이 클래스가 호출되도록 하면 되는 것 같다. 이 클래스가 하는 일은 onReceive() 메소드에 기록되어 있다.

문자열을 갖고 조합하는 코드인 것 같은데, 한땀한땀 파악하긴 귀찮으니 그냥 그대로 C 코드로 옮긴다. ck라는 함수는 Native API이다. IDA에서 decompile 해서 코드로 옮기면 된다.

마지막으로, paramIntent에서 'mm'이라는 태그를 통해 넘어온 문자열을 찾아야 하는데, 가위바위보를 이겼을 경우 CallMe가 호출될 것이므로, MainActivity에서 이 부분을 찾아보면 Native 함수인 rps_calc() 함수의 반환 값이 전달되는 것을 알 수 있다.

rps_calc() 함수를 decompile 해서 살펴보자.
어떤 변수(k)가 이 게임의 목표 점수였던 17916보다 큰 값을 가질때 뭔가를 수행하는 부분을 찾을 수 있다.

이왕 코드로 옮기기로 한 것, 이 부분도 따와서 붙여넣도록 한다.

최종 코드는 아래와 같이 완성된다.

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

signed short CallMe_ck(signed int a3, signed short a4, signed short a5)
{
  signed short result; // ax@1

  result = a5;
  //LOBYTE(result) = a5;

  switch ( a3 % 6 )
  {
    case 0:
      result = ((a4 & 0xFFF0u) >> 4) ^ a5;
      goto LABEL_8;
    case 1:
      result = ((a4 & 0xFFE0u) >> 5) ^ a5;
      goto LABEL_8;
    case 2:
      result = ((a4 & 0xFF80u) >> 7) ^ a5;
      goto LABEL_8;
    case 3:
      result = ((a4 & 0xFFC0u) >> 6) ^ a5;
      goto LABEL_8;
    case 5:
      result = a5 ^ 0xF;
      goto LABEL_8;
    case 4:
LABEL_8:
      result = (char)result;
      break;
    default:
      result = 67;
      break;
  }
  return result;
}

void onReceive(char* v10)
{
  ///CallMe_onReceive///
  register int i;
  char* paramContext = v10;
  char arrayOfChar[] = "SBtbhfle_7tg]Runsj5]io_MBmi";
  char paramIntent[100] = {0};

  for (i = 0; i < strlen(arrayOfChar); i++)
  {
    if (i < 16) {
      paramIntent[i] = CallMe_ck(i, paramContext[i], arrayOfChar[i]);
    } else {
      paramIntent[i] = CallMe_ck(i, paramContext[i - 16], arrayOfChar[i]);
    }
  }

  printf("%s\n", paramIntent);

  return;
}

int main(void)
{
  ///rps_1calc///
  register size_t v6; // esi@4
  signed int v10; // [sp+8h] [bp-354h]@1
  signed int v11; // [sp+Ch] [bp-350h]@1
  signed int v12; // [sp+10h] [bp-34Ch]@1
  signed int v13; // [sp+14h] [bp-348h]@1
  char v14;

  v14 = 0;
  v13 = 658317629;
  v12 = 540877373;
  v11 = 1581080381;
  v10 = 0x533D5458;

  printf ("%08x %08x %08x %08x %08x\n", (unsigned int)&v10, (unsigned int)&v11, (unsigned int)&v12, (unsigned int)&v13, (unsigned int)&v14);

  if ( strlen((const char *)&v10) )
  {
    //v9 = 0x10;
    v10 = (v10 & (~0xff)) ^ (0x10 ^ 0x58);
    if ( strlen((const char *)&v10) >= 2 )
    {
      v6 = 1;
      do
        *((unsigned char *)&v10 + v6++) ^= 0x10;
      while ( v6 < strlen((const char *)&v10) );
    }
  }
  printf("%s\n", (char*)&v10);

  onReceive(&v10);

  return 0;
}

위 코드를 컴파일하여 실행하면 flag 값을 얻을 수 있다.

Flag : W@tching_7th_Sunse7_in_B@li

'writeups > Reversing' 카테고리의 다른 글

Local News  (0) 2019.11.25
KeyGenMe  (0) 2019.11.25
pyc decompile  (0) 2019.11.25
Easyhaskell  (0) 2019.11.25
EASY_CrackMe  (0) 2019.11.25

+ Recent posts