diff --git a/official/生活在博弈树上/README.assets/1.png b/official/生活在博弈树上/README.assets/1.png new file mode 100644 index 0000000..4a0dfc0 Binary files /dev/null and b/official/生活在博弈树上/README.assets/1.png differ diff --git a/official/生活在博弈树上/README.md b/official/生活在博弈树上/README.md index 3b8521b..1727003 100644 --- a/official/生活在博弈树上/README.md +++ b/official/生活在博弈树上/README.md @@ -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] \ No newline at end of file +ROPgadget 生成的 payload 前面需要加上 padding,使得 payload 能够正好从函数返回地址开始覆盖。从上图我们可以看到,为了从 r(返回地址)开始,我们的 padding 长度应该是 0x8 - (-0x90) = 152。(s 代表是保存的寄存器等调用者函数的现场) \ No newline at end of file diff --git a/official/生活在博弈树上/src/tictactoe b/official/生活在博弈树上/src/tictactoe new file mode 100755 index 0000000..b9cc889 Binary files /dev/null and b/official/生活在博弈树上/src/tictactoe differ diff --git a/official/生活在博弈树上/src/tictactoe.c b/official/生活在博弈树上/src/tictactoe.c new file mode 100644 index 0000000..8689f8a --- /dev/null +++ b/official/生活在博弈树上/src/tictactoe.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include + +#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; +} \ No newline at end of file