Rất buồn là trong giải này mình không làm ra flag bài nào huhu ;-; Sau khi upsolve lại thì mình nhận ra nhiều vấn đề mình không nên để gặp phải lúc thi
# Intarsteller
## Stage 1: Time seed
```c
puts("Don't you go?");
fgets(input, 160, stdin);
memset(board, 0, 0x46uLL);
v14 = time(0LL) % 10000000;
ptr = malloc(0xCuLL);
if ( ptr )
{
buildKey(v14, (__int64)&ptr);
hashMD5((const char *)ptr, (__int64)base_flag);
for ( i = 0; i <= 15; ++i )
sprintf(&s1[2 * i], "%02x", base_flag[i]);
byte_555555558420 = 0;
if ( !strcmp(s1, "3f263d86f20c90b1d83cefa0e0cf0512") )
{//...
```
* seed được lấy theo time(0), lấy 7 số cuối
* Hàm build_seed(seed, ptr) gán ptr = "Wana" + reverse(seed)
* hashMD5 ptr lưu vào key
* Tạo xâu s1 từ key
* So sánh s1 phải khớp với `3f263d86f20c90b1d83cefa0e0cf0512`
Vậy làm sao để s1 khớp với đoạn trên? Viết script python tìm seed:
```py
import hashlib
res = "3f263d86f20c90b1d83cefa0e0cf0512"
for seed in range(10000000):
s = f"{seed:07d}"
s = s[::-1]
s = f"Wana{s}".encode()
s = hashlib.md5(s).hexdigest()
if s == res:
print(seed)
exit(0)
```
Output: `5022101`
hex: `0x4ca195`
Vậy có thể dùng lệnh chỉnh sửa thời gian của hệ thống để gọi chạy hoặc dùng debug đặt breakpoint ở đây để sửa rdx thành 0x4ca195

-> nhảy được vào trong for
## Stage 2: Build hole
```c
v7 = 0;
num_queries = 0;
num_xor_val = 0;
for ( j = 0; j < strlen(input); ++j )
{
if ( input[j] == '_' )
{
v7 = 1;
}
else
{
if ( v7 )
{
v4 = num_queries++;
v5 = &query[v4];
}
else
{
v6 = num_xor_val++;
v5 = &xor_val[v6];
}
*v5 = input[j];
}
}
if ( query[num_queries - 1] == '\n' )
--num_queries;
if ( v7 && num_xor_val > 6 )
{
for ( k = 0; k <= 7; ++k )
hole[k] ^= xor_val[k];
for ( m = 0; m <= 7; ++m )
{
for ( n = 0; n <= 7; ++n )
{
if ( m > 0 && (((int)hole[m - 1] >> (7 - n)) & 1) != 0 )
zzzz[8 * m + n] ^= 8u;
if ( m <= 6 && (((int)hole[m + 1] >> (7 - n)) & 1) != 0 )
zzzz[8 * m + n] ^= 2u;
if ( n > 0 && (((int)hole[m] >> (8 - n)) & 1) != 0 )
zzzz[8 * m + n] ^= 1u;
if ( n <= 6 && (((int)hole[m] >> (6 - n)) & 1) != 0 )
zzzz[8 * m + n] ^= 4u;
if ( zzzz[8 * m + n] )
{
puts("Think more, you're quite near...");
free(ptr);
return 0LL;
}
}
}
```
Phân tích:
* mảng xor_val được gán mởi phần kí tự input trước dấu `_`, mảng query được gán bởi các kí tự input đằng sau dấu `_`.
* column_val được đem đi xor với mảng xor_val (8 kí tự).
* Kiểm tra bằng một mảng zzzz[8*8]
Mảng zzzz và hole ban đầu đã được gán sẵn các giá trị, và mục tiêu là phải làm cho toàn bộ mảng bằng 0. Vậy cần tìm input mảng xor_val sao cho hole sau khi được xor phải bằng 0 hết. Mảng hole này đóng vai trò là các vách tường cho game phía sau.
```py
zzz = '06 06 07 05 05 05 07 03 06 0F 0B 09 0A 0C 0E 0B 0E 0D 09 05 02 05 0E 0B 0A 09 0A 06 08 05 0E 0B 0A 05 06 03 09 06 0E 0B 0A 05 0C 09 05 06 0F 0B 0A 03 0A 0A 06 0E 0F 0B 0C 05 05 05 05 0D 0D 09'
zzz = bytes.fromhex(zzz)
hole = [0] * 8
dx = [0, 1, 0, -1]
dy = [8, 7, 6, 7]
for i in range(8):
for j in range(8):
for d in range(4):
if zzz[8 * i + j] >> d & 1:
hole[i + dx[d]] |= (1 << (dy[d] - j))
xor_val = [21, 208, 152, 255, 215, 216, 182, 139]
for i in range(8):
xor_val[i] ^= hole[i]
print(chr(xor_val[i]), end = '')
```
Output: `j3stdo1t`
-> input có dạng `j3stdo1t_...`
## Stage 3: Main game
```c
toa_do = 0x300000001LL;
for ( ii = 0; ii < num_queries; ++ii )
{
if ( query[ii] != base_board[8 * (int)toa_do + HIDWORD(toa_do)] )
--point;
if ( query[ii] == 'C' )
print_flag(toa_do, (unsigned int *)&point);
if ( query[ii] == 'A' )
point += (unsigned __int8)update_board();
if ( query[ii] == 'R' )
++HIDWORD(toa_do);
if ( query[ii] == 'L' )
--HIDWORD(toa_do);
if ( query[ii] == 'U' )
LODWORD(toa_do) = toa_do - 1;
if ( query[ii] == 'D' )
LODWORD(toa_do) = toa_do + 1;
checker(toa_do, &point);
if ( point <= 0 )
{
puts("Mario...You're in black hole!...");
free(ptr);
return 0LL;
}
++board[8 * (int)toa_do + HIDWORD(toa_do)];
}
```
1. Ban đầu:
* point ban đầu được 1 điểm
* tọa độ ban đầu ở (3, 1)
* mảng board[8*8] ban đầu khởi tạo toàn 0, được cộng lên sau mỗi truy vấn
2. Query:
* So sánh query với mảng res_arr[64] (8*8) cố định, nếu khác nhau thì trừ --point.
* `C` là để in ra flag
* `A` là để point += update_board();
* `RLUD` thay đổi biến `toa_do` là một pair di chuyển phải trái lên xuống
* phải duy trì biến point luôn dương
* sau mỗi bước đều gọi checker() và ++board[x][y] với x, y lấy từ toa_do.
3. Hàm print_flag kiểm tra các điều kiện:
* point > 0
* num_queries == 150
* num_xor_val == 8
* toado == 0 (0, 0)
* vậy input có dạng: `j3stdo1t_[queries 150 kí tự CARLUD]`
4. Hàm update_board()
* lấy min cách board[pos[i]] với pos[i] là các vị trí đặt biệt được khởi tạo trước
* gán lại các board[pos[i]] = 0
* point được cộng lên một lượng bằng giá trị min đó
5. Hàm checker()
* 2 tọa độ phải nằm trong khoảng 0..7,
* nếu đi vào hole thì bị trừ --point.
* point phải luôn dương
## Tóm tắt:
Cần tìm một đường đi xuất phát từ ô (3, 1), di chuyển `RLUD` qua 4 ô kề cạnh, đi theo các hướng trong mảng base_board để không bị trừ point. Đi vào hole hoặc đi khác base_board thì point trừ 1, phải duy trì luôn dương. Có thể dùng `A` để cộng point bằng min số lần đi qua ở các ô trong mảng pos. Mục tiêu là đến được ô (0, 0) và dùng `A` để print_flag.
Như vậy để không bị trừ điểm, ta có thể thử đi theo chính xác các hương trong mảng base_board. Script python:
```py
x = 1
y = 3
print(board[x * 8 + y])
for i in range(150):
c = board[x * 8 + y]
print(c, end = '')
if c == 'U':
x -= 1
elif c == 'D':
x += 1
elif c == 'L':
y -= 1
elif c == 'R':
y += 1
```
Output:`DDLLDDDRRRUURUUULLDDLLDDDRRRUURUUULLDDLLDDDRRRUURUUULLDDLLDDDRRRUURUUULLDDLLDDDRRRUURUUULLDDLLDDDRRRUURUUULLDDLLDDDRRRUURUUULLDDLLDDDRRRUURUUULLDDLLDD`
Như vậy thì nó đang đi lòng vòng bên trong mà không thể thoát ra được.

Để ý các bước cuối: `...LLDDLLDD` thì sau đoạn `LL` nó đã về lại ô start, 6 kí tự cuối mình nghĩ là nên thay đổi để nó đi được về ô (0, 0). Nhưng thay đổi thì chắc chắn bị trừ point, vậy nên cần phải được cộng lên bằng một truy vấn `A`, đến được đây thì cộng được 9 điểm rồi, Tính cả bước cuối phải là `C` để print_flag, thì ta còn 4 bước để duy chuyển. Vậy có khoảng 4 cách để đi, trong đó phải tìm cách ít bị trừ điểm nhất:
* Chắc chắn phải qua hole 3 lần
* Gọi `A` một lần và gọi `C` một lần
Vậy đã chắc chắn bị trừ 5 điểm, còn 4 điểm mà với 4 bước di chuyển khác với base_board thì khi đến được print_flag thì point đã bằng 0. Nhưng trong đó có một đường giảm được một lần trừ vì đi đúng hướng ở `L`.

Vậy đã chỉnh 6 kí tự cuối thành `AULLLC` là được.

## Vấn đề
Trong quá trình phân tích code đến đoạn mảng zzzz thì mình đã note sai rằng mảng zzzz khởi tạo bằng 0 ;-; như vậy thì xor_val phải bằng với hole và mình đi giải mã cái hole ra một chuỗi không đọc được. Sau mọi cố gắng debug mình vẫn không nhận ra ;-; Đến lúc biết được thì còn lại 2 tiếng.
Mình đã thực sự không chú ý đến hàm update_board vì Pseudocode của nó lấy min mà gán giá trị ban đầy là -1 trong khi mảng board sẽ luôn dương mà không hề xem code assembly (0xFF). Đây là sai lầm lớn nhất của mình. Đến đây mình lại qua làm các bài khác nên đã bỏ lỡ mất cơ hội.
# 니가/니가
Khi được cho đến 7686 file thì mình biết đây là dạng viết script auto-rev. Mở một file lên đọc và phân tích thì mình biết được nó cho key và cipher sau khi mã hóa RC4. Mà RC4 là khóa đối xứng nên có thể decript lại dễ dàng. Mở thử các file khác lên thì nó cũng y hệt như vậy.
Thử giải mã file đầu tiên thì mình được một chuỗi bytes như sau: `b'\x89PNG\r\n\x1a\n'`. Vậy khả năng sau khi ghép tất cả file này lại sẽ được một ảnh png. Đến đây thì nghe có vẻ dễ nên mình ngồi code tiếp bài này mà bỏ qua luôn bài khia trong khi đã đến được bước cuối cùng ;-;
Nhưng mọi thứ bắt đầu khó khăn khi mình không dùng được các thư viện trong `capstone` để dump file ra mà lấy key với cipher, huhu. Trước thi một tuần thì mình có cài lại máy vì bị một số vấn đề với acc fb và disc, cài lại đủ thứ mà quên mất cái này ;-; Vì thấy mất thời gian mà cài không được nên mình dump luôn bằng `xxd`...
Mất khá nhiều thời gian và các biến cố sảy ra khi các lệnh ở các file có vài chỗ khác nhau nên mình đã dump sai nhiều trường hợp và tất nhiên là không mở được ảnh.
Sau giải khi về nhà bình tĩnh lại mà làm thì cuối cùng cũng có được flag.

Tìm hiểu thêm về `qemu` thì nó có thể giả lập các môi trường khác nhau để chạy file, ở bài này mình có thể dùng nó để chạy arm này để check xem output từng file đã đúng chưa rồi debug từng đoạn.
# Simple
Mình mở bài này lên thì nó dùng rất nhiều hàm má hóa trong thư viện được gửi kèm theo. Vì đang bận tâm 2 bài kia nên trong giải mình chưa phân tích kĩ bài này. Chỉ biết là nó qua khá nhiều lớp:
* input 48 kí tự
* rand()
* Xor
* RC4
* base 64
* output: `Aq6A5/+BYzEMpE9tavt8zuc/yI59hrstzUYxLnQ6010WHM9aoBjcPFu1UjCQiGJH`
Hình như là nó không có yêu cầu nhập input gì, nên cũng đã thử debug step vượt từng các anti để coi nó làm gì thì gặp khá nhiều lỗi nên bỏ cuộc thôi ;-;
```c
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v3; // rcx
__int64 v4; // r8
__int64 v5; // r9
_QWORD v7[4]; // [rsp+0h] [rbp-240h] BYREF
char v8[256]; // [rsp+20h] [rbp-220h] BYREF
char s[37]; // [rsp+120h] [rbp-120h] BYREF
char v10; // [rsp+145h] [rbp-FBh]
unsigned int v11; // [rsp+22Ch] [rbp-14h]
unsigned int seed; // [rsp+230h] [rbp-10h]
unsigned int seconds; // [rsp+234h] [rbp-Ch]
unsigned int v14; // [rsp+238h] [rbp-8h]
unsigned int v15; // [rsp+23Ch] [rbp-4h]
strcpy((char *)v7, "Look at me !!!!!!");
v14 = alarm(0);
seconds = v14;
puts("Welcome !!!!!");
puts("Enter input to encrypt: ");
sleep(v14);
fgets(s, 64, stdin);
v15 = strlen(s);
if ( (int)v15 > 0 && s[v15 - 1] == 10 )
s[--v15] = 0;
if ( v15 != 48 )
{
puts("Nah not that input.");
exit(0);
}
seed = O____O((unsigned int)s[13], (unsigned int)v10, (unsigned int)v10, v3, v4, v5, v7[0], v7[1], v7[2]);
srand(seed);
O__O(s, v15);
O_O(s, v15, v7, 17LL);
v11 = UwwwU(s, v15);
v11 = O_____O(v11);
UwwwwwU(s, v15, v11);
sub_404056(s, v15);
UwU(s, v15, v8);
puts(v8);
return 0LL;
}
```
Phân tích từng hàm trong file libfunctions.so
1. Tính seed
```c
__int64 __fastcall O____O(int a1, int a2)
{
unsigned int v3; // [rsp+Ch] [rbp-Ch]
int v4; // [rsp+10h] [rbp-8h]
int i; // [rsp+14h] [rbp-4h]
v3 = (-1640531527 * a2) ^ a1;
v4 = -1548630313;
for ( i = 0; i <= 3; ++i )
{
v3 ^= v3 + v4;
v4 = (v4 << 21) | (v4 >> 11);
}
return v3;
}
```
Khởi tạo random theo seed vừa tạo: srand(seed)
2.
```c
__int64 __fastcall O__O(__int64 a1, int a2)
{
__int64 result; // rax
int v3; // [rsp+10h] [rbp-Ch]
unsigned int i; // [rsp+14h] [rbp-8h]
v3 = 30;
for ( i = 0; ; ++i )
{
result = i;
if ( (int)i >= a2 )
break;
*(_BYTE *)((int)i + a1) ^= v3 % 256;
*(_BYTE *)((int)i + a1) = ((int)*(unsigned __int8 *)((int)i + a1) >> (8 - (v3 % 7 + 1))) | (*(_BYTE *)((int)i + a1) << (v3 % 7 + 1));
*(_BYTE *)((int)i + a1) = 3 * *(_BYTE *)((int)i + a1) + 5;
v3 = 1337 * v3 % 256;
}
return result;
}
```
3.
```c
__int64 __fastcall O_O(__int64 a1, int a2, char *a3, int a4)
{
__int64 result; // rax
char v5; // [rsp+26h] [rbp-12h]
char v6; // [rsp+27h] [rbp-11h]
int i; // [rsp+28h] [rbp-10h]
int j; // [rsp+2Ch] [rbp-Ch]
unsigned int k; // [rsp+30h] [rbp-8h]
int v10; // [rsp+34h] [rbp-4h]
if ( a4 )
{
for ( i = 0; i < a2; ++i )
{
v10 = (i * (unsigned __int8)a3[i % a4] + (unsigned __int8)a3[(i + 1) % a4]) % a2;
v6 = *(_BYTE *)(i + a1);
*(_BYTE *)(i + a1) = *(_BYTE *)(v10 + a1);
*(_BYTE *)(a1 + v10) = v6;
}
v5 = *a3;
for ( j = 0; j < a2; ++j )
{
v5 ^= j + a3[j % a4];
*(_BYTE *)(j + a1) ^= v5;
*(_BYTE *)(j + a1) = a3[(j + 2) % a4] ^ ((*(_BYTE *)(j + a1) >> 3) | (32 * *(_BYTE *)(j + a1)));
}
for ( k = 0; ; ++k )
{
result = k;
if ( (int)k >= a2 )
break;
*(_BYTE *)((int)k + a1) = a3[(int)(k + 1) % a4] * *(_BYTE *)((int)k + a1) + a3[(int)(k + 3) % a4];
*(_BYTE *)((int)k + a1) ^= *(_BYTE *)((int)(k + 2) % a2 + a1) * *(_BYTE *)((int)(k + 1) % a2 + a1);
}
}
return result;
}
```
4.
```c
__int64 __fastcall UwwwU(__int64 a1, int a2)
{
unsigned int v3; // [rsp+10h] [rbp-Ch]
int v4; // [rsp+14h] [rbp-8h]
int i; // [rsp+18h] [rbp-4h]
if ( !a2 )
return 0LL;
v3 = 0;
v4 = -889275714;
for ( i = 0; i < a2; ++i )
{
v3 = (int)((31
* (((*(_DWORD *)(4LL * i + a1) << ((char)i % 5)) | (*(int *)(4LL * i + a1) >> ((char)i % 3 + 1))) ^ v3)
+ *(_DWORD *)(4LL * i + a1)) ^ (v4 >> (i & 3)))
% 1000000007;
v4 = (33 * v4) ^ (*(_DWORD *)(4LL * i + a1) + (v4 >> 5));
}
return v3;
}
```
5.
```c
__int64 __fastcall O_____O(int a1, int a2)
{
int v3; // [rsp+4h] [rbp-24h]
int v4; // [rsp+Ch] [rbp-1Ch]
int v5; // [rsp+10h] [rbp-18h]
int v6; // [rsp+14h] [rbp-14h]
int i; // [rsp+18h] [rbp-10h]
int v8; // [rsp+1Ch] [rbp-Ch]
int v9; // [rsp+24h] [rbp-4h]
v3 = a1;
if ( !a1 )
v3 = 1;
v4 = (unsigned __int16)v3;
v5 = v3 >> 16;
v6 = -1548630313;
for ( i = 0; i <= 5; ++i )
{
v8 = v4 + (v6 ^ 0x9E3779B9) + i;
if ( a2 )
v8 = -1640531535 * (v8 >> 7);
v9 = ((1026727936 * (v8 ^ v5)) | ((1540483477 * (v8 ^ v5)) >> 19)) ^ v4;
v4 = v5;
v5 = v9;
v6 = (v6 << 21) | (v6 >> 11);
}
return v5 | (unsigned int)(v4 << 16);
}
```
6.
```c
__int64 __fastcall UwwwwwU(__int64 a1, int a2, int a3)
{
__int64 result; // rax
int v4; // [rsp+18h] [rbp-8h]
int i; // [rsp+1Ch] [rbp-4h]
if ( a2 )
{
v4 = -559038737;
for ( i = 0; ; ++i )
{
result = (unsigned int)i;
if ( i >= a2 )
break;
*(_DWORD *)(4LL * i + a1) ^= (*(int *)(4LL * i + a1) >> ((char)i % 5 + 1)) ^ ((a3 << ((char)i % 7)) + v4);
*(_DWORD *)(4LL * i + a1) = ((*(int *)(4LL * i + a1) >> ((char)i % 9)) | (*(_DWORD *)(4LL * i + a1) << (32 - (char)i % 9))) ^ ((*(int *)(4LL * i + a1) >> (32 - (char)i % 11)) | (*(_DWORD *)(4LL * i + a1) << ((char)i % 11))) ^ (32 * *(_DWORD *)(4LL * i + a1));
*(_DWORD *)(4LL * i + a1) = (-1640523343 * *(_DWORD *)(4LL * i + a1) - (*(int *)(4LL * i + a1) >> 7)) & 0x7FFFFFFF;
*(_DWORD *)(4LL * i + a1) = 1284865837 * (*(_DWORD *)(4LL * i + a1) ^ 0x9E3779B9)
- 144211633
- 1000000007
* ((0x5851F42D4C957F2DLL * (unsigned __int64)(*(_DWORD *)(4LL * i + a1) ^ 0x9E3779B9)
+ 0x14057B7EF767814FLL)
/ 0x3B9ACA07);
*(_DWORD *)(4LL * i + a1) ^= a3 & (v4 ^ *(_DWORD *)(4LL * i + a1)) | ((*(int *)(4LL * i + a1) >> 5) | (*(_DWORD *)(4LL * i + a1) << 27)) & ((*(int *)(4LL * i + a1) >> 29) | (8 * *(_DWORD *)(4LL * i + a1)));
*(_DWORD *)(4LL * i + a1) ^= (*(_DWORD *)(4LL * i + a1) >> 1) & 0x55555555 | (2 * *(_DWORD *)(4LL * i + a1)) & 0xAAAAAAAA;
if ( i > 0 )
*(_DWORD *)(4LL * i + a1) ^= (v4 >> ((char)i % 7)) ^ ((4 * *(_DWORD *)(4LL * i - 4 + a1)) | (*(int *)(4LL * i - 4 + a1) >> 3));
v4 = (*(_DWORD *)(4LL * i + a1) ^ (33 * v4)) + (a3 >> 2);
}
}
return result;
}
```
7. sub_404056(a1)


8. Base 64
```c
_BYTE *__fastcall UwU(__int64 a1, int a2, __int64 a3)
{
int v3; // eax
int v4; // edx
int v5; // eax
int v6; // eax
char v7; // cl
int v8; // eax
char v9; // cl
int v10; // eax
_BYTE *result; // rax
int v13; // [rsp+18h] [rbp-10h]
int v14; // [rsp+1Ch] [rbp-Ch]
int v15; // [rsp+1Ch] [rbp-Ch]
int v16; // [rsp+1Ch] [rbp-Ch]
int v17; // [rsp+24h] [rbp-4h]
v13 = 0;
v14 = 0;
while ( v13 < a2 )
{
if ( a2 <= v13 + 1 )
v3 = 0;
else
v3 = *(unsigned __int8 *)(v13 + 1LL + a1) << 8;
v4 = v3 + (*(unsigned __int8 *)(v13 + a1) << 16);
if ( a2 <= v13 + 2 )
v5 = 0;
else
v5 = *(unsigned __int8 *)(v13 + 2LL + a1);
v17 = v4 + v5;
*(_BYTE *)(v14 + a3) = base64_table[((v4 + v5) >> 18) & 0x3F];
v6 = v14 + 1;
v15 = v14 + 2;
*(_BYTE *)(v6 + a3) = base64_table[(v17 >> 12) & 0x3F];
if ( a2 <= v13 + 1 )
v7 = 61;
else
v7 = base64_table[(v17 >> 6) & 0x3F];
v8 = v15;
v16 = v15 + 1;
*(_BYTE *)(v8 + a3) = v7;
if ( a2 <= v13 + 2 )
v9 = 61;
else
v9 = base64_table[v17 & 0x3F];
v10 = v16;
v14 = v16 + 1;
*(_BYTE *)(v10 + a3) = v9;
v13 += 3;
}
result = (_BYTE *)(4 * ((a2 + 2) / 3) + a3);
*result = 0;
return result;
}
```
Theo như đoạn Pseoducode thì qua rất nhiều lớp mã hóa như này, cuối cùng flag được base64 ở buối cuối. Nhưng trong IDA mình còn thấy sự xuất hiện của các hàm khác:

Mình đoán là không phải tự nhiên mà nó có, liệu chương trình có nhảy vào các hàm này không? Để biết được thì mình đã thử debug lại và bypass từng đoạn xem có step vào từng hàm được không: