...
- 해당 함수는 다음과 같은 기능을 합니다.
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에 저장합니다.
- DeletePlayHistory() 함수를 이용해 gHistory[] 에 맨 마지막에 플레이한 GameInfo를 삭제 합니다.
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됩니다.
...