Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
titlePlay game
autolycos@ubuntu:~/CTF/HITCON/OmegaGo$ ./omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac 
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 180.00

A19
   ABCDEFGHIJKLMNOPQRS
19 X..................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ..................O
Time remain: O: 180.00, X: 173.38

regret
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 180.00
surrender
This AI is too strong, ah?
Play history? (y/n)
y
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
piece O play at J10
Time remain: O: 180.00, X: 180.00

Play again? (y/n)
y
   ABCDEFGHIJKLMNOPQRS
19 ...................
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 180.00

Main()

  • 해당 함수는 while() 함수를 이용하여 MainFunction()함수를 계속 호출합니다.
Code Block
languagecpp
titlemain()
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 result; // rax@3
  __int64 v4; // rbx@3
  __int64 v5; // [rsp+8h] [rbp-18h]@1

  v5 = *MK_FP(__FS__, 40LL);
  alarm(0xB4u);
  setvbuf(stdout, 0LL, 2, 0LL);
  while (MainFunction());
  result = 0LL;
  v4 = *MK_FP(__FS__, 40LL) ^ v5;
  return result;
}

MainFunction(0x401738)

  • 해당 함수는 다음과 같은 기능을 합니다.
    • DefaultSet() 함수를 통해 게임 진행시 필요한 변수의 초기화를 선언합니다.

    • "operator new(8uLL)" 코드를 이용하여 vtable정보를 저장할 Heap 영역을 할당합니다.

      • 할당된 Heap의 크기는 0x20byte 입니다.
    • 할당된 Heap 영역의 주소값은 Computer, Human Class 변수에 저장합니다.
    • while()을 통해 게임을 플레이하기 위한 기능을 실행합니다.

    • gettimeofday() 함수를 이용하여 플레이어의 게임 플레이 시간을 계산합니다.

    • rowNumber, colNumber 의 값이 -1과 같다면 게임을 종료합니다.

    • rowNumber, colNumber 의 값이 -2일 경우에는 게임 턴을 한차례 돌리는 regret()함수를 호출합니다.
    • 그외의 rowNumber, colNumber 값이 입력 되면 플레이어의 플레이시간을 저장합니다.
      • 총 플레이시간이 0.0보다 작으면 게임을 종료합니다.
      • 총 플레이시간이 0.0보다 크면 SetMarkForBoard()함수를 이용하여 게임 보드 rowNumber,colNumber 위치에 표시합니다.

Struct

  • 해당 바이너리를 분석하기 위해 다음과 같은 구조체들이 필요합니다.
    • 해당 바이너리는 C++로 개발되어 있으며 vtable을 사용하고 있습니다.
  • 아래 구조체는 AI Class의 play함수를 표현하는 구조체 입니다.


Code Block
struct __attribute__((aligned(8))) Method
{
  void (__fastcall **Play)(Method *a, GameInfo *state, signed int player_number, uint32_t *row, uint32_t *col);
  _QWORD empty;
};
  • 아래 구조체는 OmegaGo의 게임 정보를 저장하는 구조체 입니다.
Code Block
struct GameInfo
{
  _QWORD board[12];
  _DWORD rowNumber;
  _DWORD colNumber;
  _QWORD player;
  double playTimeForAI;
  double playTimeForHuman;
};


Main()

  • 해당 함수는 while() 함수를 이용하여 MainFunction()함수를 계속 호출합니다.
Code Block
languagecpp
titleMainFunctionmain()
bool MainFunction()
{
  Class1 *v0; // rax@1
  Class1 *Computer; // rbx@1
  Class2 *v2; // rax@1
  Class2 *Human; // rbx@1
  bool__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 result; // al@13rax@3
  __int64 v5v4; // rcx@13rbx@3
  unsigned int rowNumber__int64 v5; // [rsp+Ch8h] [rbp-64h18h]@3@1

  v5 = *MK_FP(__FS__, 40LL);
  alarm(0xB4u);
  setvbuf(stdout, 0LL, 2, 0LL);
  while (MainFunction());
  result = 0LL;
  v4 = *MK_FP(__FS__, 40LL) ^ v5;
  return result;
}

MainFunction(0x401738)

  • 해당 함수는 다음과 같은 기능을 합니다.
    • setDefGameinfo() 함수를 통해 게임 진행시 필요한 변수의 초기화를 선언합니다.

    • "operator new(8uLL)" 코드를 이용하여 AI, HUMAN 변수에 Heap 영역(8byte)을 할당합니다.

      • 각 Class 별 할당되는 Heap의 크기는 16byte 입니다.
      • setAIFunction(), setHUMANFunction() 함수를 이용해 할당 받은 영역에 호출할 함수의 주소를 저장합니다.

    • while()을 통해 게임을 플레이하기 위한 기능을 실행합니다.

      • gettimeofday() 함수를 이용하여 플레이어의 게임 플레이 시간을 계산합니다.

      • rowNumber, colNumber 의 값이 -1과 같다면 게임을 종료합니다.

      • rowNumber, colNumber 의 값이 -2일 경우에는 게임 턴을 한차례 돌리는 regret()함수를 호출합니다.
      • 그외의 rowNumber, colNumber 값이 입력 되면 플레이어의 플레이시간을 저장합니다.
        • 총 플레이시간이 0.0보다 작으면 게임을 종료합니다.
        • 총 플레이시간이 0.0보다 크면 SetMarkForBoard()함수를 이용하여 게임 보드 rowNumber,colNumber 위치에 표시합니다.
Code Block
languagecpp
titleMainFunction()
__int64 OmegaGo()
{
  Method *AI; // rbx
  Method *HUMAN; // rbx
  unsigned int rowNumber; // [rsp+Ch] [rbp-64h]
  unsigned int colNumber; // [rsp+10h] [rbp-60h]
  int playerNum; // [rsp+14h] [rbp-5Ch]
  double playTime; // [rsp+18h] [rbp-58h]
  struct timeval startTime; // [rsp+20h] [rbp-50h]
  struct timeval endTime; // [rsp+30h] [rbp-40h]
  Method *player[2]; // [rsp+40h] [rbp-30h]
  unsigned __int64 v10; // [rsp+58h] [rbp-18h]

  v10 = __readfsqword(0x28u);
  setDefGameinfo();
  playerNum = 1;
  AI = (Method *)operator new(8uLL);
  AI->Play = 0LL;
  setAIFunction(AI);
  player[0] = AI;
  HUMAN = (Method *)operator new(8uLL);
  HUMAN->Play = 0LL;
  setHUMANFunction(HUMAN);
  player[1] = HUMAN;
  while ( !((unsigned __int8)sub_401202(playerNum, &gPlayerGameInfo) ^ 1) )
  {
    gettimeofday(&startTime, 0LL);
    (*player[playerNum - 1]->Play)(player[playerNum - 1], &gPlayerGameInfo, playerNum, &rowNumber, &colNumber);
    gettimeofday(&endTime, 0LL);
    playTime = (double)(LODWORD(endTime.tv_usec) - LODWORD(startTime.tv_usec)) / 1000000.0
unsigned int colNumber; // [rsp+10h] [rbp-60h]@3
  unsigned int playerInfo; // [rsp+14h] [rbp-5Ch]@1
  double playTime; // [rsp+18h] [rbp-58h]@3
  struct timeval startTime; // [rsp+20h] [rbp-50h]@3
  struct timeval endTime; // [rsp+30h] [rbp-40h]@3
  Class1 *Computer_1; // [rsp+40h] [rbp-30h]@1
  Class2 *Human_1; // [rsp+48h] [rbp-28h]@1
  __int64 canary; // [rsp+58h] [rbp-18h]@1

  canary = *MK_FP(__FS__, 40LL);
  DefaultSet();
  playerInfo = 1;
  operator new(8uLL);
  Computer = v0;
  v0->vtable = 0LL;
  class::ctor(v0);
  Computer_1 = Computer;
  operator new(8uLL);
  Human = v2;
  v2->vtable = 0LL;
  class2::ctor(v2);
  Human_1 = Human;
  while ( !((unsigned __int8)sub_401202(playerInfo, &gameInfo) ^ 1) )
  {
    gettimeofday(&startTime, 0LL);
    ((void (__fastcall *)(Class1 *, GameInfo *, _QWORD, unsigned int *, unsigned int *))(&Computer_1)[playerInfo - 1]->vtable->Method_1)(
      (&Computer_1)[playerInfo - 1],
      &gameInfo,
      playerInfo,
      &rowNumber,
      &colNumber);
    gettimeofday(&endTime, 0LL);
    playTime = (double)(LODWORD(endTime.tv_usec) - LODWORD(startTime.tv_usec)) / 1000000.0
             + (double)(LODWORD(endTime.tv_sec) - LODWORD(startTime.tv_sec));
    if ( rowNumber == -1 || colNumber == -1 )   // surrender
      break;
    if ( rowNumber != -2 && colNumber != -2 )   // Default
    {
      *(double *)&gameInfo.board[(signed int)(playerInfo - 1) + 14LL] = *(double *)&gameInfo.board[(signed int)(playerInfo - 1)
                                                                                                 + 14LL]
                                 + (double)(LODWORD(endTime.tv_sec) - LODWORD(startTime.tv_sec));
    if ( rowNumber == -1 || colNumber == -1 )
      break;
    if ( rowNumber != -2 && colNumber != -2 )
     - playTime;{
      if ( *(double *)&gameInfogPlayerGameInfo.board[(signed int)(playerInfoplayerNum - 1) + 14LL] < 0.0 )
    = *(double *)&gPlayerGameInfo.board[playerNum - 1 + 14LL]
    print("Time's up");
      SetMarkForBoard(&gameInfo, rowNumber, colNumber, playerInfo, 0);
      playerInfo ^= 3u;
    }
    else if ( (unsigned __int8)regret() ^ 1 )   // regret
    {
      print("No you cant't");
    }
  }
  CheckResults();
 - PlayHistory()playTime;
  result = PlayAgain();
  v5if =( *MK_FP(__FS__, 40LL) ^ canary;
  return result;
}

vtable

  • 해당 프로그램은 C++ 로 개발되어 있으여 클래스를 생성해서 사용하고 있습니다.
    • 그리고 해당 클래스에서는 virtual function를 선언해서 사용합니다.
  • 다음과 같은 형태를 가집니다.
Code Block
languagecpp
titleClass information
class Player {
 public:
  virtual void Play(GameInfo *info, _QWORD playerInfo, unsigned int *row, unsigned int *col) {};
};

class Computer : public Player { ... };
class Human : public Player { ... };
  • Computer, Human Class에 호출하는 Player 함수의 주소는 다음과 같습니다.

...

Class

...

Call vtable function

...

Computer

...

40290A()

...

Hummand

...

402C12()

...

(double *)&gPlayerGameInfo.board[playerNum - 1 + 14LL] < 0.0 )
        print("Time's up");
      SetMarkForBoard(&gPlayerGameInfo, rowNumber, colNumber, playerNum, 0);
      playerNum ^= 3u;
    }
    else if ( (unsigned __int8)regret() ^ 1 )
    {
      print("No you cant't");
    }
  }
  CheckResults();
  PlayHistory();
  return PlayAgain();
}

UserInput

  • 해당 함수는 다음과 같은 기능을 합니다.
    • PrintGameBoard_0() 함수를 이용하여 Go board를 출력합니다.
    • scanf() 함수를 이용하여 최대 10개의 문자를 입력 받습니다.
      • 입력받은 문자열은 다음과 같이 데이터 처리 합니다.
        • 입력한 문자열이 "surrender"는 *colNumber, *rowNumber 변수에 -1을 저장합니다.
        • 입력한 문자열이 "regret"는 *colNumber, *rowNumber 변수에 -2을 저장합니다.
        • 앞에 두 문자열과 같지 않을 경우 다음과 같이 데이터를 처리합니다.
          • sscanf() 함수를 이용하여 문자와 숫자 값을 분리 합니다.
          • 그리고 해당 값은 CheckBoardArea()함수를 이용하여 Go board 범위 안에 포함되는지 확인합니다.

...

Code Block
languagecpp
titlePrintGameBoard()
__int64 __fastcall PrintGameBoard(GameInfo *gameInfo, char a2)
{
 char v2; // al@6
 signed int row; // [rsp+20h] [rbp-10h]@1
 signed int col; // [rsp+24h] [rbp-Ch]@5
 __int64 v6; // [rsp+28h] [rbp-8h]@1

 v6 = *MK_FP(__FS__, 40LL);
 puts(" ABCDEFGHIJKLMNOPQRS");
 for ( row = 0; row <= 18; ++row )
 {
 	if ( 19 - row <= 9 )
 		putchar(' ');
 	else
 		putchar('1');
 		putchar((19 - row) % 10 + 48);
 		putchar(' ');
 	for ( col = 0; col <= 18; ++col )
 	{
 		v2 = GetMarkForBoard(gameInfo, row, col);
 		putchar(v2);
 	}
 	puts(&byte_404E87);
 }
 if ( a2 )
 	fprintf(
	 stdout,
	 "piece %c play at %c%d\n",
	 LODWORD(gameInfo->colNumber),
	 (unsigned int)(HIDWORD(gameInfo->rowNumber) + 'A'),
	 (unsigned int)(19 - LODWORD(gameInfo->rowNumber)));
 fprintf(
 stdout,
 "Time remain: %c: %6.2lf, %c: %6.2lf\n",
 'O',
 'X',
 *(double *)&gameInfo->player,
 gameInfo->playTimeForCom);
 puts(&byte_404E87);
 return *MK_FP(__FS__, 40LL) ^ v6;
}

struct GameInfo

  • 다음은 해당 프로그램에서 사용하는 Struct 입니다.
  • 해당 Struct는 게임 플레이 정보를 기록하는 Struct입니다.
    • board[12] 변수에는 Go game board 내용을 저장합니다.

    • rowNumber는 row의 좌표값을 저장합니다.

    • colNumber는 Col의 좌표값을 저장합니다.
    • Player는 player의 Mark를 저장합니다.(O, X)
    • playTimeForCom는 컴퓨터의 Play 시간을 저장합니다.

    • playTimeForHum는 사람의 Play 시간을 저장합니다.
Code Block
languagecpp
titlestruct GameInfo{ ... }
struct GameInfo
{
  _QWORD board[12];
  _DWORD rowNumber;
  _DWORD colNumber;
  _DWORD player;
  _DWORD tmp;
  double playTimeForCom;
  double playTimeForHum;
}; at %c%d\n",
	 LODWORD(gameInfo->colNumber),
	 (unsigned int)(HIDWORD(gameInfo->rowNumber) + 'A'),
	 (unsigned int)(19 - LODWORD(gameInfo->rowNumber)));
 fprintf(
 stdout,
 "Time remain: %c: %6.2lf, %c: %6.2lf\n",
 'O',
 'X',
 *(double *)&gameInfo->player,
 gameInfo->playTimeForCom);
 puts(&byte_404E87);
 return *MK_FP(__FS__, 40LL) ^ v6;
}

ResetGameinfo - 0x04010AE

...

Code Block
languagecpp
titleResetGameinfo_04010AE(GameInfo *gameInfo)
__int64 __fastcall ResetGameinfo(GameInfo *gameInfo)
{
  GameInfo *tmp; // ST08_8@1
  __int64 canary; // ST18_8@1

  tmp = gameInfo;
  canary = *MK_FP(__FS__, 40LL);
  memset(gameInfo, 0, 0x60uLL);
  gameInfo->colNumber = -1;
  tmp->rowNumber = tmp->colNumber;
  LODWORD(gameInfo->player) = 0;
  *(_QWORD *)&gameInfo->playTimeForHum = 4640537203540230144LL;
  tmp->playTimeForCom = tmp->playTimeForHum;
  return *MK_FP(__FS__, 40LL) ^ canary;
}

SetMarkForBoard - 0x04025B2

  • 해당 함수는 다음과 같은 기능을 합니다.
    • GetMarkForBoard() 함수를 이용하여 유저가 입력한 좌표의 값이 '.' 문자와 같은지 확인합니다.

    • playerInfo의 값을 이용하여 board에 표시할 mark 문자를 설정합니다.
    • 그리고 사용자로 부터 입력받은 값이 정상적인 값인지 확인합니다.
    • 마지막으로 그 값이 정상적인 값이라면 다음과 같은 처리를 진행합니다.
      • "operator new(0x80uLL)" 코드에 의해 Heap 영역을 할당합니다.
        • 할당된 Heap 메모리의 크기는 0x90 byte 입니다.
      • gameInfo에 보관된 값을 할당된 Heap 영역에 저장합니다.
      • 할당된 Heap 영역의 주소 값을 history[]변수에 저장합니다.

...