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