tictactoe wp almost finished

This commit is contained in:
taoky
2020-11-06 00:22:28 +08:00
parent 3be0b82f79
commit 87e169cbaf
4 changed files with 210 additions and 4 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

+6 -4
View File
@@ -24,13 +24,15 @@ bool success = false; // human wins?
char input[128] = {}; // input is large and it will be ok.
```
那么我们能不能在 `gets` 的时候,就把 `success` 弄成 `true` 呢?对于本题给出的二进制程序文件,答案是可以的!
那么我们能不能在 `gets` 的时候,就把 `success` 弄成 `true` 呢?对于本题给出的二进制程序文件,答案是可以的!(视编译器、编译选项的不同,这个答案可能会发生变化,所以要以给定的二进制文件为准)
### 分析
为了判断栈上 `input``success` 这两个变量的位置,我们需要使用调试工具,或者反编译工具去判断。
为了判断栈上 `input``success` 这两个变量的位置,我们需要使用调试工具,或者反编译工具去判断。这里以 IDA 为例。
[TBD]
![Variables on stack](README.assets/1.png)
可以看到,变量 `input` 距离栈帧基址 (BP) 有 0x90 bytes,变量 `success` 距离栈帧基址有 0x1 bytes,所以输入 143 个字符之后,输入值为 0x1 的字符(注意,不是 `'1'`),将 `success` 覆盖为 `true` (1) 即可。
## 升上天空
@@ -53,4 +55,4 @@ tictactoe: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically
所以考虑使用 ROP 来获取服务器的 shell。由于这个程序是静态链接的,被链接的 C 库中就包含了很多可以被利用的 gadgets,所以考虑使用 ROPgadget 来自动生成 payload。
[TBD]
ROPgadget 生成的 payload 前面需要加上 padding,使得 payload 能够正好从函数返回地址开始覆盖。从上图我们可以看到,为了从 r(返回地址)开始,我们的 padding 长度应该是 0x8 - (-0x90) = 152。(s 代表是保存的寄存器等调用者函数的现场)
Binary file not shown.
@@ -0,0 +1,204 @@
#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
#include <limits.h>
#include <assert.h>
#define EMPTY 0
#define BLACK 1
#define WHITE 2
#ifdef EXAMPLE
// Send payload to server to get flag plz
#define FLAG "\x76\x43\x49\x4C\x09\x5A\x4A\x55" \
"\x41\x41\x4E\x54\x11\x46\x5C\x14" \
"\x46\x53\x45\x4E\x5C\x48\x1B\x48" \
"\x52\x1E\x58\x25\x35\x62\x25\x28" \
"\x24\x21\x67\x38\x25\x30"
#else
// flag{easy_gamE_but_can_u_get_my_shel1}
#define FLAG "\x43\x4A\x46\x4F\x52\x4F\x4A\x5F" \
"\x54\x71\x48\x51\x5C\x77\x6C\x56" \
"\x40\x42\x68\x5B\x58\x54\x64\x49" \
"\x62\x59\x5A\x34\x1E\x2F\x3A\x1B" \
"\x36\x2E\x22\x24\x78\x37"
#endif
#define COMPUTER BLACK
#define HUMAN WHITE
#define MIN_SCORE -100
char flag[] = FLAG;
int board[3][3] = {};
void flag_decode(void) {
for (int i = 0; flag[i] != '\0'; i++) {
flag[i] ^= (i + 23333);
}
}
int check(void) {
for (int i = 0; i < 3; i++) {
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != EMPTY) {
return board[i][0];
}
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != EMPTY) {
return board[0][i];
}
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != EMPTY) {
return board[0][0];
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != EMPTY) {
return board[0][2];
}
return false;
}
bool full(void) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == EMPTY) {
return false;
}
}
}
return true;
}
void print(void) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
char out = '_';
if (board[i][j] == WHITE) {
out = 'X';
} else if (board[i][j] == BLACK) {
out = 'O';
} else if (board[i][j] != EMPTY) {
out = '?'; // well if you see this something may be wrong.
}
printf("%c", out);
}
puts("");
}
}
int opp(int p) {
if (p == BLACK)
return WHITE;
else if (p == WHITE)
return BLACK;
assert(0);
}
int minimax(int player) {
int winColor = check();
if (winColor != EMPTY) {
return winColor == player ? 1 : -1;
}
int movex = -1, movey = -1;
int max_score = MIN_SCORE;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == EMPTY) {
board[i][j] = player;
int score = -minimax(opp(player));
if (score > max_score) {
max_score = score;
movex = i; movey = j;
}
board[i][j] = EMPTY;
}
}
}
if (movex == -1) {
return 0;
}
return max_score;
}
void ai(int *x, int *y) {
// Make sure that human cannot win
// I heard that there's an algorithm named "Minimax"
int movex = -1, movey = -1;
int max_score = MIN_SCORE;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == EMPTY) {
board[i][j] = COMPUTER;
int score = -minimax(HUMAN);
if (score > max_score) {
max_score = score;
movex = i; movey = j;
}
board[i][j] = EMPTY;
}
}
}
*x = movex;
*y = movey;
}
int main(void) {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
bool success = false; // human wins?
char input[128] = {}; // input is large and it will be ok.
puts("Welcome to Tic Tac Toe! Computer first!");
puts("You're 'X' and I'm 'O'!");
while (!success) {
// computer: BLACK
int x, y;
puts("Now computer goes!");
ai(&x, &y);
board[x][y] = BLACK;
print();
if (check()) {
success = false;
break;
}
if (full()) {
break;
}
puts("Now human goes!");
// human: WHITE
// example input: (0,1)
while (true) {
printf("Your turn. Input like (x,y), such as (0,1): ");
gets(input);
x = input[1] - '0';
y = input[3] - '0';
printf("You wanna put X on (%d,%d)...\n", x, y);
if (!(x >= 0 && x < 3 && y >= 0 && y < 3)) {
puts("Wrong input! Please try again!");
continue;
}
if (board[x][y] != EMPTY) {
puts("This pos has already been occupied!");
continue;
}
break;
}
board[x][y] = WHITE;
print();
if (check()) {
success = true;
break;
}
if (full()) {
break;
}
}
if (success) {
puts("What? You win! Here is your flag:");
flag_decode();
puts(flag);
} else {
puts("You failed! See you next time~");
}
return 0;
}