...
해당 함수는 다음과 같은 기능을 합니다.
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; } |
...
- 해당 함수는 다음과 같은 기능을 합니다.
- 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; } |
...
- 해당 함수는 다음과 같은 기능을 합니다.
- 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; } |
...
해당 함수는 다음과 같은 기능을 합니다.
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됩니다.
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; } |
...