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
      • 할당된 Heap 영역은 게임을 재시작해도 해제되지 않습니다.
    • while()을 통해 게임을 플레이하기 위한 기능을 실행합니다.

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

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

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

...

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;
}

DeletePlayHistory

  • 해당 함수는 다음과 같은 기능을 합니다.
    • gHistory[]에 저장된 heap 영역을 해제합니다.
Code Block
unsigned __int64 DeletePlayHistory()
{
  __int64 v1; // [rsp+0h] [rbp-10h]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( gHistory[historyCnt - 1] )
  {
    v1 = sub_401EB6((GameInfo *)gHistory[historyCnt - 1]);
    operator delete(gHistory[historyCnt - 1]);
    sub_402FE6(&unk_607220, &v1);
    --historyCnt;
  }
  return __readfsqword(0x28u) ^ v2;
}

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됩니다.

...