Versions Compared

Key

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

...

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

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

      • setAIFunction(), setHUMANFunction() 함수를 이용해 할당 받은 영역에 호출할 함수의 주소를 저장합니다.

      • 각 구조체의 Play 포인터 함수에 저장되는 주소는 다음과 같습니다.
        • AI→Play(0x405040)→0x40290A
        • HUMAN→Play(0x405020)→0x402C12
    • 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
             + (double)(LODWORD(endTime.tv_sec) - LODWORD(startTime.tv_sec));
    if ( rowNumber == -1 || colNumber == -1 )
      break;
    if ( rowNumber != -2 && colNumber != -2 )
    {
      *(double *)&gPlayerGameInfo.board[playerNum - 1 + 14LL] = *(double *)&gPlayerGameInfo.board[playerNum - 1 + 14LL]
                                                              - playTime;
      if ( *(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(HUMAN→Play(0x405020)→0x402C12)


  • 해당 함수는 다음과 같은 기능을 합니다.

    • callPrintBoard() 함수에 의해 Borad가 출력됩니다.

    • scanf() 함수를 이용해 사용자로 부터 좌표 값 또는 명령어를 입력 받습니다.

    • 입력 받은 명령어에 따라 다음과 같이 값을 설정합니다.

      • surrender : col = -1, row = -1

      • regret : col = -2, row = -2

    • 명령어가 아닐 경우 좌표 값이 저장됩니다.


Code Block
unsigned __int64 __fastcall UserInput(__int64 a1, GameInfo *gameInfo, __int64 playerNum, signed int *row, signed int *col)
{
  bool areaOverflow; // al
  char chCol; // [rsp+37h] [rbp-9h]
  unsigned __int64 v10; // [rsp+38h] [rbp-8h]

  v10 = __readfsqword(0x28u);
  callPrintBoard(gameInfo);
  memset(cmd, 0, 0xCuLL);
  if ( scanf("%10s", cmd) != 1 )
    print("Er?");
  if ( !strcmp("surrender", cmd) )
  {
    *col = -1;
    *row = *col;
  }
  else if ( !strcmp("regret", cmd) )
  {
    *col = -2;
    *row = *col;
  }
  else
  {
    if ( sscanf(cmd, "%c%d", &chCol, row) != 2 )
      print("Input like 'A19'");
    *col = chCol - 65;
    *row = 19 - *row;
    areaOverflow = (unsigned __int8)checkBoardArea(*row) ^ 1 || (unsigned __int8)checkBoardArea(*col) ^ 1;
    if ( areaOverflow )
      print("No overflow plz.");
  }
  return __readfsqword(0x28u) ^ v10;
}

regret()


  • 해당 함수는 다음과 같은 기능을 합니다.


    • historyCnt 변수의 값이 0 일 경우 해당 함수는 종료됩니다.
    • historyCnt 변수의 값이 0 아닐 경우 다음과 같은 코드를 실행합니다.
      • DeletePlayHistory() 함수를 이용해 gHistory[] 에 맨 마지막에 플레이한 GameInfo를 삭제 합니다.
        • AI, Human의 플레이 기록을 삭제합니다.
      • AI가 마지막에 플레이한 GameInfo를 추출해 gPlayerGameInfo에 저장합니다.


Code Block
signed __int64 __cdecl regret()
{
  GameInfo *history; // rax

  if ( historyCnt <= 1 )
    return 0LL;
  DeletePlayHistory();                          // AI play history
  DeletePlayHistory();                          // Human play history
  if ( ::gHistory[historyCnt - 1] )
  {
    history = (GameInfo *)::gHistory[historyCnt - 1];
    gPlayerGameInfo.board[0] = history->board[0];
    gPlayerGameInfo.board[1] = history->board[1];
    gPlayerGameInfo.board[2] = history->board[2];
    gPlayerGameInfo.board[3] = history->board[3];
    gPlayerGameInfo.board[4] = history->board[4];
    gPlayerGameInfo.board[5] = history->board[5];
    gPlayerGameInfo.board[6] = history->board[6];
    gPlayerGameInfo.board[7] = history->board[7];
    gPlayerGameInfo.board[8] = history->board[8];
    gPlayerGameInfo.board[9] = history->board[9];
    gPlayerGameInfo.board[10] = history->board[10];
    gPlayerGameInfo.board[11] = history->board[11];
    *(_QWORD *)&gPlayerGameInfo.rowNumber = *(_QWORD *)&history->rowNumber;
    gPlayerGameInfo.player = history->player;
    gPlayerGameInfo.playTimeFor[0] = history->playTimeFor[0];
    gPlayerGameInfo.playTimeFor[1] = history->playTimeFor[1];
  }
  return 1LL;
}




SetMarkForBoard

  • 해당 함수는 다음과 같은 기능을 합니다.

    • player 변수의 값을 이용해 플레이어의 마크를 결정합니다.

    • saveMarkofBoard() 함수와 마크를 저장할 좌표 값(row, col)을 이용해gameinfo.Board[] 영역에 값을 저장합니다.

    • 각종 함수를 이용해 플레이어가 마크를 저장하길 원하는 위치 값이 타당한지 확인합니다.

      • CheckBoardArea(), GetMarkForBoard(), checkLocation(), ...

    • 입력한 위치 값이 정상적이지 않으면 메시지를 출력하고 프로그램을 종료합니다.
    • 입력한 위치 값가 정상적이라면 해당 gameInfo를 gHistory[]에 저장합니다.
      • "operator new(0x80)" 코드에 의해 Heap 영역을 할당합니다.
      • 할당된 Heap 영역의 주소 값을 gHistory[]변수에 저장합니다.
  • 취약성은 여기서 발생합니다.
    • GameInfo 구조체를 사용하는 gHistory[]의 크기는 364 입니다.
    • gHistory[] 배열에 저장된 번호가 364를 넘는지에 대한 확인이 없습니다.
    • 즉, 유저가 입력한 값이 364회가 넘으면 gPlayerGameInfo 영역에 Overflow됩니다.

...