## Lavie
Bài này có 2 file:

Thử chạy trên cmd thì thấy chương trình yêu cầu nhập passcode:

Hướng làm đầu tiên là mình cố gắng đọc code trong IDA để xem có thể hiểu được luôn chương trình đang làm gì hay không.
Decomplie lavie.exe bằng IDA được hàm main như sau:
```c
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int64 v4; // rax
FILE *v5; // rbx
size_t v6; // rbx
size_t v7; // rdi
FILE *v8; // rax
__int64 v9; // rdx
__int64 v10; // rbx
char *v11; // rcx
__int64 v12; // r8
int i; // eax
int v14; // r9d
char *v15; // r10
int v16; // r11d
char *v17; // rcx
FILE *Stream; // [rsp+20h] [rbp-188h] BYREF
char v20[40]; // [rsp+28h] [rbp-180h] BYREF
char v21[64]; // [rsp+50h] [rbp-158h] BYREF
char v22[256]; // [rsp+90h] [rbp-118h] BYREF
if ( argc < 2 )
{
sub_140001020("./lavie.exe command");
exit(1);
}
sub_140001020("Enter ur passcode: ");
sub_140001080("%32s");
v4 = -1i64;
do
++v4;
while ( v20[v4] );
if ( v4 < 0x20 )
exit(1);
if ( fopen_s(&Stream, argv[1], "rb") )
{
perror("Failed to open file");
v5 = Stream;
}
else
{
fseek(Stream, 0, 2);
v6 = ftell(Stream);
rewind(Stream);
v7 = v6;
v8 = (FILE *)malloc(v6);
v5 = v8;
if ( v8 )
fread(v8, 1ui64, v7, Stream);
else
perror("Failed to allocate memory");
fclose(Stream);
}
if ( (unsigned int)sub_140001830(v20, v5) )
{
sub_140001020("Ultimate secret box is not only the name muahahaha");
}
else
{
v10 = 0i64;
v11 = v22;
LODWORD(v12) = 0;
for ( i = 0; i < 256; ++i )
*v11++ = i;
v14 = 0;
v15 = v22;
do
{
v16 = (unsigned __int8)*v15;
v12 = (v16 + (unsigned __int8)v20[v14 & 0x1F] + (_DWORD)v12) & 0x800000FF;
if ( (int)v12 < 0 )
v12 = ((unsigned __int8)(v12 - 1) | 0xFFFFFF00) + 1;
v17 = &v22[(int)v12];
++v14;
*v15++ = *v17;
*v17 = v16;
}
while ( v14 < 256 );
sub_140001470(v22, v9, v12, v21);
sub_140001020("Cracked...\n");
do
{
sub_140001020("%c");
++v10;
}
while ( v10 < 64 );
}
return 0;
}
```
* argc < 2 (chỉ chạy file lavie mà không có command thì chương trình sẽ dừng).
* sub_140001020 là hàm print, sub_140001080 là hàm input
* Nhập vào xâu v20, v4 là độ dài, nếu < 0x20 (32) thì thoát.
* Đọc dữ liệu từ file command lưu vào v5 và xử lí một vài thứ gì đó
* Mục tiêu có lẽ là phải tìm cách cho chương trình chạy được đến đoạn này:

Để đến được đó thì phải vượt qua được cái if này:
```c
if ( (unsigned int)sub_140001830(v20, v5) )
{
sub_140001020("Ultimate secret box is not only the name muahahaha");
}
else
{
// Cracked trong này
}
```
nghĩa là phải cho hàm ```sub_140001830(v20, v5)``` trả về 0. Trong đó v20 là input mình nhập vào, v5 là dữ liệu từ file command.
Decomplie hàm ```sub_140001830(v20, v5)``` được như sau:
```c
__int64 __fastcall sub_140001830(char *a1, char *a2)
{
unsigned int *v2; // rdi
unsigned int v3; // eax
unsigned int v5; // r14d
char *v6; // rbp
unsigned int v7; // ebx
unsigned int *v8; // rax
unsigned int *v9; // rcx
unsigned int v10; // esi
unsigned int v11; // ebx
unsigned int *v12; // rcx
unsigned int *v13; // rcx
unsigned int *v14; // rax
unsigned int v15; // esi
unsigned int v16; // ebx
unsigned int *v17; // rcx
unsigned int *v18; // rcx
unsigned int *v19; // rax
unsigned int v20; // ebx
unsigned int *v21; // rax
unsigned int v22; // esi
int v23; // ebx
unsigned int *v24; // rcx
int *v25; // rcx
unsigned int *v26; // rax
unsigned int v27; // esi
unsigned int v28; // ebx
unsigned int *v29; // rcx
unsigned int *v30; // rcx
unsigned int *v31; // rax
int v32; // eax
unsigned int v34; // [rsp+58h] [rbp+10h]
v2 = 0i64;
v3 = *a2 - 17;
v5 = v34;
v6 = a2 + 1;
while ( 2 )
{
switch ( v3 )
{
case 0u:
v7 = *v6;
v8 = (unsigned int *)malloc(0x10ui64);
++v6;
*((_QWORD *)v8 + 1) = v2;
v2 = v8;
*v8 = v7;
goto LABEL_34;
case 1u:
if ( v2 )
{
v9 = v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v5 = *v9;
free(v9);
}
else
{
v5 = 0x80000000;
}
goto LABEL_34;
case 2u:
if ( v2 )
{
v12 = v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v10 = *v12;
free(v12);
if ( v2 )
{
v13 = v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v11 = *v13;
free(v13);
}
else
{
v11 = 0x80000000;
}
}
else
{
v10 = 0x80000000;
v11 = 0x80000000;
}
v14 = (unsigned int *)malloc(0x10ui64);
*((_QWORD *)v14 + 1) = v2;
v2 = v14;
*v14 = v10 * v11;
goto LABEL_34;
case 3u:
if ( v2 )
{
v17 = v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v15 = *v17;
free(v17);
if ( v2 )
{
v18 = v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v16 = *v18;
free(v18);
}
else
{
v16 = 0x80000000;
}
}
else
{
v15 = 0x80000000;
v16 = 0x80000000;
}
v19 = (unsigned int *)malloc(0x10ui64);
*((_QWORD *)v19 + 1) = v2;
v2 = v19;
*v19 = v16 + v15;
goto LABEL_34;
case 4u:
sub_140001020("%c", v5);
goto LABEL_34;
case 5u:
v20 = *a1;
v21 = (unsigned int *)malloc(0x10ui64);
*((_QWORD *)v21 + 1) = v2;
v2 = v21;
*v21 = v20;
goto LABEL_34;
case 6u:
if ( v2 )
{
v24 = v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v22 = *v24;
free(v24);
if ( v2 )
{
v25 = (int *)v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v23 = *v25;
free(v25);
}
else
{
v23 = 0x80000000;
}
}
else
{
LOBYTE(v22) = 0;
v23 = 0x80000000;
}
v26 = (unsigned int *)malloc(0x10ui64);
*((_QWORD *)v26 + 1) = v2;
v2 = v26;
*v26 = (v23 >> v22) & 1;
goto LABEL_34;
case 7u:
if ( v2 )
{
v29 = v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v27 = *v29;
free(v29);
if ( v2 )
{
v30 = v2;
v2 = (unsigned int *)*((_QWORD *)v2 + 1);
v28 = *v30;
free(v30);
}
else
{
v28 = 0x80000000;
}
}
else
{
v27 = 0x80000000;
v28 = 0x80000000;
}
v31 = (unsigned int *)malloc(0x10ui64);
*((_QWORD *)v31 + 1) = v2;
v2 = v31;
*v31 = v27 ^ v28;
goto LABEL_34;
case 8u:
++a1;
LABEL_34:
v32 = *v6++;
v3 = v32 - 17;
if ( v3 > 9 )
goto LABEL_35;
continue;
case 9u:
return v5;
default:
LABEL_35:
sub_140001020("WTF are u doinggg...");
exit(1);
}
}
}
```
Những gì đọc được:
* Hàm main đã truyền input và command vào a1 và a2
* Một vòng lặp vô hạn (sẽ có break)
* Có thể đoán là chương trình đang sử lí các truy vấn trong command gốm 10 loại (0..9) mỗi truy vấn làm một thao tác nào đó
* v3 trỏ vào từng phần tử của command, là giá trị (0..9) sau khi -17 của loại truy vấn.
* Loại 0: thêm phần tử vào stack
* Loại 1: lấy ra phần tử cuối stack
* Loại 2: lấy ra 2 phần tử cuối (*) nhân với nhau rồi push vào lại
* Loại 3: lấy ra 2 phần tử cuối (+) cộng với nhau rồi push vào lại
* Loại 4: inra byte kí tự
* Loại 5: push vào stack kí tự thứ i của input
* Loại 6: lấy ra u, v cuối stack và lấy bit thứ u của v push vào lại
* Loại 7: lấy ra 2 phần tử cuối (^) xor với nhau rồi push vào lại
* Loại 8: i++
* Loại 9: break
* Nếu không thì in ra "WTF are u doinggg..." và thoát
Có thể viết lại bằng C++:
```cpp
int calculator(vector<char> input, vector<int> query) {
stack<int> st;
vector<char>::iterator input_pointer = input.begin();
vector<int>::iterator query_pointer = query.begin();
int output_value = 0;
while ( 2 )
{
int query_type = *query_pointer - 17;
switch ( query_type )
{
case 0: {
int query_val = *++query_pointer;
st.push(query_val);
break;
}
case 1: {
output_value = st.top();
st.pop();
break;
}
case 2: { // lấy hai số cuối stack ra nhân lại và push vào cuối
int u = st.top(); st.pop();
int v = st.top(); st.pop();
st.push(u * v);
break;
}
case 3: { // lấy hai số cuối stack ra cộng lại và push vào cuối
int u = st.top(); st.pop();
int v = st.top(); st.pop();
st.push(u + v);
break;
}
case 4: {
cout << char(output_value);
break;
}
case 5: {
char input_val = *input_pointer;
st.push(input_val);
break;
}
case 6: { // lấy bit thứ v của u (2 số cuối stack)
int u = st.top(); st.pop();
int v = st.top(); st.pop();
st.push((u << v) & 1);
break;
}
case 7: { // lấy hai số cuối stack ra xor lại và push vào cuối
int u = st.top(); st.pop();
int v = st.top(); st.pop();
st.push(u ^ v);
break;
}
case 8: {
++input_pointer;
break;
}
case 9: {
break;
}
default: {
cout << "WTF are u doinggg...";
exit(1);
}
}
++query_pointer;
}
return output_value;
}
```
Từ đó có thể thấy, chương trình đang thực hiện tính giá trị của biểu thức theo kiểu postfix ([biểu thức hậu tố](https://www.iostream.co/article/ung-dung-stack-bieu-thuc-hau-to-postfix-uqu1Q)).
Vậy mình sẽ chuyển từ dạng postfix sang infix để có thể có được biểu thức dễ đọc hơn và mục tiêu của mình là tìm input sao cho kết quả của biểu thức này ra 0.
Sau khi viết script để chuyển, mình thấy nó có dạng như sau:
```
(xorval1 ^ x1 * (a1 >> b1 & 1) + x2 * (a1 >> b2 & 1) + ... x8 * (a1 >> b8 & 1))
+
(xorval2 ^ k1 * (a2 >> v1 & 1) + k2 * (a2 >> v2 & 1) + ... k8 * (a2 >> v8 & 1))
+
...
```
với x1, x2, ... x8, k1, ..., k8 là các hằng số; a1, a2, ..., a8 là 8 byte của input; b1, b2, ... b8, v1, ..., v8 là các giá trị từ 0 tới 7 (vì một kí tự lưu dưới dạng 1 byte = 8 bit, phép toán ```b >> i & 1``` là để lấy bit thứ i của b).
Mà vì các giá trị là không âm và chỉ có phép + * ^ nên từng cụm ```x1 * (a1 >> b1 & 1) + x2 * (a2 >> b2 & 1) + ... x8 * (a8 >> b8 & 1) ^ xorval``` phải bằng với 0 và hoàn toàn có thể cày trâu thay thế từng kí tự (33..127) để từng đoạn này bằng 0.
Vì nhận ra C++ mà để code đoạn tính giá trị biểu thức thì mình lại phải chuyển từ dạng infix thành postfix rồi code khá dài dòng phức tạp :v nên mình chuyển sang code bằng python để có sẵn hàm tính giá trị đoạn biểu thức dài dài kia.
```python
query = open("command", "rb").read()
i = 0
j = 1
typ = query[0] - 17
infix = ""
stack = []
while 1:
match typ:
case 0:
stack.append(str(query[j]))
j += 1
case 1:
infix = stack.pop()
case 2:
u = stack.pop()
v = stack.pop()
val = f'{v} * {u}'
stack.append(val)
case 3:
u = stack.pop()
v = stack.pop()
val = f'{v} + {u}'
stack.append(val)
# case 4:
# infix
case 5:
stack.append(f'a{i}')
case 6:
u = stack.pop()
v = stack.pop()
val = f'({v} >> {u} & 1)'
stack.append(val)
case 7:
u = stack.pop()
v = stack.pop()
val = f'({v} ^ {u})'
stack.append(val)
case 8:
i += 1
case 9:
break
typ = query[j] - 17
j += 1
print(infix)
a = infix.split(") + (")
for i in range(32):
for val in range(33, 128):
s = a[i].replace(f'a{i}', str(val))
if i == 0:
s += ')'
elif i == 31:
s = '(' + s
if eval(s) == 0:
print(chr(val), end = '')
break
```
Kết quả thu được một xâu khá giống flag:
```
((a0 >> 0 & 1) * 2 + (a0 >> 1 & 1) * 3 + (a0 >> 2 & 1) * 67 + (a0 >> 3 & 1) * 37 + (a0 >> 4 & 1) * 41 + (a0 >> 5 & 1) * 11 + (a0 >> 6 & 1) * 13 + (a0 >> 7 & 1) * 89 ^ 70) + ((a1 >> 0 & 1) * 2 + (a1 >> 1 & 1) * 3 + (a1 >> 2 & 1) * 67 + (a1 >> 3 & 1) * 5 + (a1 >> 4 & 1) * 7 + (a1 >> 5 & 1) * 47 + (a1 >> 6 & 1) * 61 + (a1 >> 7 & 1) * 29 ^ 56) + ((a2 >> 0 & 1) * 2 + (a2 >> 1 & 1) * 67 + (a2 >> 2 & 1) * 37 + (a2 >> 3 & 1) * 7 + (a2 >> 4 & 1) * 43 + (a2 >> 5 & 1) * 11 + (a2 >> 6 & 1) * 13 + (a2 >> 7 & 1) * 31 ^ 70) + ((a3 >> 0 & 1) * 97 + (a3 >> 1 & 1) * 3 + (a3 >> 2 & 1) * 41 + (a3 >> 3 & 1) * 73 + (a3 >> 4 & 1) * 11 + (a3 >> 5 & 1) * 13 + (a3 >> 6 & 1) * 53 + (a3 >> 7 & 1) * 29 ^ 77) + ((a4 >> 0 & 1) * 97 + (a4 >> 1 & 1) * 67 + (a4 >> 2 & 1) * 3 + (a4 >> 3 & 1) * 11 + (a4 >> 4 & 1) * 43 + (a4 >> 5 & 1) * 13 + (a4 >> 6 & 1) * 47 + (a4 >> 7 & 1) * 83 ^ 74) + ((a5 >> 0 & 1) * 67 + (a5 >> 1 & 1) * 5 + (a5 >> 2 & 1) * 37 + (a5 >> 3 & 1) * 71 + (a5 >> 4 & 1) * 7 + (a5 >> 5 & 1) * 11 + (a5 >> 6 & 1) * 89 + (a5 >> 7 & 1) * 29 ^ 90) + ((a6 >> 0 & 1) * 2 + (a6 >> 1 & 1) * 3 + (a6 >> 2 & 1) * 5 + (a6 >> 3 & 1) * 11 + (a6 >> 4 & 1) * 13 + (a6 >> 5 & 1) * 83 + (a6 >> 6 & 1) * 53 + (a6 >> 7 & 1) * 61 ^ 87) + ((a7 >> 0 & 1) * 2 + (a7 >> 1 & 1) * 3 + (a7 >> 2 & 1) * 7 + (a7 >> 3 & 1) * 71 + (a7 >> 4 & 1) * 43 + (a7 >> 5 & 1) * 83 + (a7 >> 6 & 1) * 29 + (a7 >> 7 & 1) * 31 ^ 82) + ((a8 >> 0 & 1) * 7 + (a8 >> 1 & 1) * 73 + (a8 >> 2 & 1) * 11 + (a8 >> 3 & 1) * 13 + (a8 >> 4 & 1) * 53 + (a8 >> 5 & 1) * 89 + (a8 >> 6 & 1) * 29 + (a8 >> 7 & 1) * 31 ^ 60) + ((a9 >> 0 & 1) * 2 + (a9 >> 1 & 1) * 3 + (a9 >> 2 & 1) * 5 + (a9 >> 3 & 1) * 37 + (a9 >> 4 & 1) * 7 + (a9 >> 5 & 1) * 43 + (a9 >> 6 & 1) * 13 + (a9 >> 7 & 1) * 61 ^ 67) + ((a10 >> 0 & 1) * 2 + (a10 >> 1 & 1) * 5 + (a10 >> 2 & 1) * 7 + (a10 >> 3 & 1) * 43 + (a10 >> 4 & 1) * 11 + (a10 >> 5 & 1) * 13 + (a10 >> 6 & 1) * 53 + (a10 >> 7 & 1) * 89 ^ 86) + ((a11 >> 0 & 1) * 5 + (a11 >> 1 & 1) * 7 + (a11 >> 2 & 1) * 73 + (a11 >> 3 & 1) * 43 + (a11 >> 4 & 1) * 11 + (a11 >> 5 & 1) * 13 + (a11 >> 6 & 1) * 59 + (a11 >> 7 & 1) * 31 ^ 95) + ((a12 >> 0 & 1) * 3 + (a12 >> 1 & 1) * 5 + (a12 >> 2 & 1) * 73 + (a12 >> 3 & 1) * 41 + (a12 >> 4 & 1) * 43 + (a12 >> 5 & 1) * 13 + (a12 >> 6 & 1) * 83 + (a12 >> 7 & 1) * 89 ^ 64) + ((a13 >> 0 & 1) * 2 + (a13 >> 1 & 1) * 7 + (a13 >> 2 & 1) * 71 + (a13 >> 3 & 1) * 11 + (a13 >> 4 & 1) * 43 + (a13 >> 5 & 1) * 13 + (a13 >> 6 & 1) * 29 + (a13 >> 7 & 1) * 61 ^ 94) + ((a14 >> 0 & 1) * 2 + (a14 >> 1 & 1) * 5 + (a14 >> 2 & 1) * 7 + (a14 >> 3 & 1) * 11 + (a14 >> 4 & 1) * 13 + (a14 >> 5 & 1) * 79 + (a14 >> 6 & 1) * 47 + (a14 >> 7 & 1) * 83 ^ 85) + ((a15 >> 0 & 1) * 3 + (a15 >> 1 & 1) * 67 + (a15 >> 2 & 1) * 37 + (a15 >> 3 & 1) * 5 + (a15 >> 4 & 1) * 73 + (a15 >> 5 & 1) * 11 + (a15 >> 6 & 1) * 13 + (a15 >> 7 & 1) * 61 ^ 66) + ((a16 >> 0 & 1) * 2 + (a16 >> 1 & 1) * 67 + (a16 >> 2 & 1) * 5 + (a16 >> 3 & 1) * 7 + (a16 >> 4 & 1) * 71 + (a16 >> 5 & 1) * 11 + (a16 >> 6 & 1) * 13 + (a16 >> 7 & 1) * 61 ^ 33) + ((a17 >> 0 & 1) * 67 + (a17 >> 1 & 1) * 3 + (a17 >> 2 & 1) * 5 + (a17 >> 3 & 1) * 37 + (a17 >> 4 & 1) * 43 + (a17 >> 5 & 1) * 11 + (a17 >> 6 & 1) * 13 + (a17 >> 7 & 1) * 61 ^ 69) + ((a18 >> 0 & 1) * 2 + (a18 >> 1 & 1) * 3 + (a18 >> 2 & 1) * 37 + (a18 >> 3 & 1) * 7 + (a18 >> 4 & 1) * 71 + (a18 >> 5 & 1) * 41 + (a18 >> 6 & 1) * 11 + (a18 >> 7 & 1) * 29 ^ 64) + ((a19 >> 0 & 1) * 3 + (a19 >> 1 & 1) * 5 + (a19 >> 2 & 1) * 41 + (a19 >> 3 & 1) * 11 + (a19 >> 4 & 1) * 43 + (a19 >> 5 & 1) * 47 + (a19 >> 6 & 1) * 53 + (a19 >> 7 & 1) * 29 ^ 98) + ((a20 >> 0 & 1) * 2 + (a20 >> 1 & 1) * 3 + (a20 >> 2 & 1) * 7 + (a20 >> 3 & 1) * 71 + (a20 >> 4 & 1) * 43 + (a20 >> 5 & 1) * 13 + (a20 >> 6 & 1) * 47 + (a20 >> 7 & 1) * 79 ^ 67) + ((a21 >> 0 & 1) * 2 + (a21 >> 1 & 1) * 3 + (a21 >> 2 & 1) * 5 + (a21 >> 3 & 1) * 37 + (a21 >> 4 & 1) * 11 + (a21 >> 5 & 1) * 43 + (a21 >> 6 & 1) * 13 + (a21 >> 7 & 1) * 79 ^ 71) + ((a22 >> 0 & 1) * 97 + (a22 >> 1 & 1) * 67 + (a22 >> 2 & 1) * 5 + (a22 >> 3 & 1) * 37 + (a22 >> 4 & 1) * 7 + (a22 >> 5 & 1) * 41 + (a22 >> 6 & 1) * 11 + (a22 >> 7 & 1) * 61 ^ 94) + ((a23 >> 0 & 1) * 3 + (a23 >> 1 & 1) * 71 + (a23 >> 2 & 1) * 7 + (a23 >> 3 & 1) * 43 + (a23 >> 4 & 1) * 11 + (a23 >> 5 & 1) * 79 + (a23 >> 6 & 1) * 53 + (a23 >> 7 & 1) * 61 ^ 93) + ((a24 >> 0 & 1) * 2 + (a24 >> 1 & 1) * 3 + (a24 >> 2 & 1) * 71 + (a24 >> 3 & 1) * 73 + (a24 >> 4 & 1) * 11 + (a24 >> 5 & 1) * 13 + (a24 >> 6 & 1) * 61 + (a24 >> 7 & 1) * 31 ^ 90) + ((a25 >> 0 & 1) * 97 + (a25 >> 1 & 1) * 2 + (a25 >> 2 & 1) * 3 + (a25 >> 3 & 1) * 67 + (a25 >> 4 & 1) * 5 + (a25 >> 5 & 1) * 11 + (a25 >> 6 & 1) * 13 + (a25 >> 7 & 1) * 83 ^ 32) + ((a26 >> 0 & 1) * 2 + (a26 >> 1 & 1) * 3 + (a26 >> 2 & 1) * 5 + (a26 >> 3 & 1) * 37 + (a26 >> 4 & 1) * 7 + (a26 >> 5 & 1) * 41 + (a26 >> 6 & 1) * 11 + (a26 >> 7 & 1) * 53 ^ 65) + ((a27 >> 0 & 1) * 2 + (a27 >> 1 & 1) * 3 + (a27 >> 2 & 1) * 73 + (a27 >> 3 & 1) * 43 + (a27 >> 4 & 1) * 11 + (a27 >> 5 & 1) * 13 + (a27 >> 6 & 1) * 53 + (a27 >> 7 & 1) * 61 ^ 82) + ((a28 >> 0 & 1) * 2 + (a28 >> 1 & 1) * 67 + (a28 >> 2 & 1) * 3 + (a28 >> 3 & 1) * 37 + (a28 >> 4 & 1) * 7 + (a28 >> 5 & 1) * 11 + (a28 >> 6 & 1) * 47 + (a28 >> 7 & 1) * 59 ^ 68) + ((a29 >> 0 & 1) * 2 + (a29 >> 1 & 1) * 37 + (a29 >> 2 & 1) * 5 + (a29 >> 3 & 1) * 73 + (a29 >> 4 & 1) * 13 + (a29 >> 5 & 1) * 47 + (a29 >> 6 & 1) * 53 + (a29 >> 7 & 1) * 59 ^ 65) + ((a30 >> 0 & 1) * 2 + (a30 >> 1 & 1) * 67 + (a30 >> 2 & 1) * 71 + (a30 >> 3 & 1) * 73 + (a30 >> 4 & 1) * 41 + (a30 >> 5 & 1) * 11 + (a30 >> 6 & 1) * 13 + (a30 >> 7 & 1) * 89 ^ 93) + ((a31 >> 0 & 1) * 2 + (a31 >> 1 & 1) * 3 + (a31 >> 2 & 1) * 67 + (a31 >> 3 & 1) * 37 + (a31 >> 4 & 1) * 73 + (a31 >> 5 & 1) * 11 + (a31 >> 6 & 1) * 43 + (a31 >> 7 & 1) * 59 ^ 96)
s1mpl3_VM_us3s_link3d_l1st_st4ck
```
Và chính xác nó là passcode cần tìm:

## try_me
Bài này chỉ có 1 file.
Chạy thử chương trình:

Đọc code trong IDA:
* Hàm start:
```c
void __fastcall __noreturn start(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v3; // rax
unsigned int v4; // esi
__int64 v5; // [rsp-8h] [rbp-8h] BYREF
void *retaddr; // [rsp+0h] [rbp+0h] BYREF
v4 = v5;
v5 = v3;
sub_80A200((__int64)sub_808ECB, v4, (__int64)&retaddr, 0LL, 0LL, a3, (__int64)&v5);
}
```
* Hàm sub_808ECB:
```c
void sub_808ECB()
{
const char *v0; // [rsp+8h] [rbp-8h]
v0 = (const char *)sub_82EDC0(34LL);
sub_812F50((__int64)"%32s", v0);
if ( (unsigned __int64)sub_401190() <= 0x1F )
{
sub_812DC0((__int64)"Try HardeR");
sub_812290(1LL);
}
__asm { jmp rax }
}
```
Sau khi đọc code trong các hàm con thì mình có được:
* ```sub_812F50``` là hàm nhập vào input của mình
* ```sub_812DC0``` là hàm print
* ```sub_812290``` là hàm exit
* ```sub_401190``` là hàm lấy độ dài của v0, nghĩa là nếu nhập vào độ dài <= 0x1F (31) thì in ra "Try HardeR" và thoát.
Đặc biệt có một đoạn ở cuối IDA của mình đã không dịch được:
```__asm { jmp rax }```
Không biết vì sao nên mình thử dùng ghidra xem có ra hàm gì không:
```c
void FUN_00808ecb(void)
{
undefined8 uVar1;
ulong uVar2;
uVar1 = FUN_0082edc0(0x22);
FUN_00812f50(&UNK_008bd004,uVar1);
uVar2 = thunk_FUN_008314f0(uVar1);
if (uVar2 < 0x20) {
FUN_00812dc0(&UNK_008bd009);
FUN_00812290(1);
}
FUN_00401ad5(uVar1);
return;
}
```
Và đó là hàm ```FUN_00401ad5``` với đầu vào là input của mình
* ```FUN_00401ad5```
Đầy là một hàm có kích thước quá lớn nên không thể decomplie được, nhưng vì chỉ có các thao tác ```ADD, XOR, SUB``` nên nếu cần thì có thể reverse được


Cuối hàm thì nó có gọi tới FUN_00808e85
```c
void FUN_00808e85(ulong param_1,undefined8 param_2,undefined8 param_3,undefined8 param_4,
int *param_5,undefined8 param_6)
{
long lVar1;
undefined8 extraout_RDX;
undefined8 uVar2;
uVar2 = 0;
lVar1 = FUN_0085f6f0(0,0,param_3,param_4,param_5,param_6);
if (lVar1 < 0) {
FUN_00808ca9(param_1,uVar2,extraout_RDX,param_4,param_5,param_6);
}
else {
FUN_00808d97(param_1,uVar2,extraout_RDX,param_4,param_5,param_6);
}
return;
}
//#################################################################
void FUN_00808ca9(ulong param_1,undefined8 param_2,undefined8 param_3,undefined8 param_4,
int *param_5,undefined8 param_6)
{
ulong uVar1;
ulong extraout_RDX;
ulong extraout_RDX_00;
ulong uVar2;
long in_FS_OFFSET;
int local_50;
int local_4c;
byte local_46 [54];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_46[0xe] = 0x18;
local_46[0xf] = 0x35;
local_46[0x10] = 0x40;
local_46[0x11] = 0x7b;
local_46[0x12] = 0xbc;
local_46[0x13] = 0xd1;
local_46[0x14] = 0xea;
local_46[0x15] = 0xcf;
local_46[0x16] = 0x33;
local_46[0x17] = 0x4e;
local_46[0x18] = 0x50;
local_46[0x19] = 0xd1;
local_46[0x1a] = 0x1b;
local_46[0x1b] = 99;
local_46[0x1c] = 0xaa;
local_46[0x1d] = 0xc2;
uVar1 = 0x49e304c7a952867;
local_46[0x1e] = 0xe4;
local_46[0x1f] = 0xda;
local_46[0x20] = 0xaa;
local_46[0x21] = 0x49;
local_46[0x22] = 0xec;
local_46[0x23] = 0xcd;
local_46[0x24] = 0xf8;
local_46[0x25] = 0x5f;
local_46[0x26] = 0x67;
local_46[0x27] = 0x28;
local_46[0x28] = 0x95;
local_46[0x29] = 0x7a;
local_46[0x2a] = 0x4c;
local_46[0x2b] = 0x30;
local_46[0x2c] = 0x9e;
local_46[0x2d] = 4;
uVar2 = param_1;
for (local_50 = 0; local_50 < 0x20; local_50 = local_50 + 1) {
uVar1 = (ulong)*(byte *)(param_1 + (long)local_50);
if (*(byte *)(param_1 + (long)local_50) != local_46[(long)local_50 + 0xe]) {
uVar2 = 1;
FUN_00812290((undefined8 *)0x1);
uVar1 = extraout_RDX;
}
}
local_46[0] = 0x70;
local_46[1] = 0x58;
local_46[2] = 0x17;
local_46[3] = 0x44;
local_46[4] = 0x42;
local_46[5] = 0x55;
local_46[6] = 0x5a;
local_46[7] = 0x5e;
local_46[8] = 0x43;
local_46[9] = 0x17;
local_46[10] = 0x43;
local_46[0xb] = 0x5f;
local_46[0xc] = 0x5e;
local_46[0xd] = 0x44;
for (local_4c = 0; local_4c < 0xe; local_4c = local_4c + 1) {
uVar2 = (ulong)(uint)(int)(char)(local_46[local_4c] ^ 0x37);
FUN_00821830(local_46[local_4c] ^ 0x37,param_2,uVar1,param_4,param_5,param_6);
uVar1 = extraout_RDX_00;
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
FUN_008618b0(uVar2,param_2,uVar1,param_4,param_5,param_6);
}
return;
}
//#################################################################
void FUN_00808d97(ulong param_1,undefined8 param_2,undefined8 param_3,undefined8 param_4,
int *param_5,undefined8 param_6)
{
ulong uVar1;
ulong extraout_RDX;
ulong extraout_RDX_00;
ulong uVar2;
long in_FS_OFFSET;
int local_50;
int local_4c;
byte local_46 [54];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
local_46[0xe] = 0x81;
local_46[0xf] = 0xb;
local_46[0x10] = 0xeb;
local_46[0x11] = 0x94;
local_46[0x12] = 0xbd;
local_46[0x13] = 0xb4;
local_46[0x14] = 0x58;
local_46[0x15] = 0x17;
local_46[0x16] = 0x50;
local_46[0x17] = 0xb5;
local_46[0x18] = 99;
local_46[0x19] = 0xd0;
local_46[0x1a] = 0xcb;
local_46[0x1b] = 0x62;
local_46[0x1c] = 0xaa;
local_46[0x1d] = 0x11;
uVar1 = 0x199e991e4f0c25c;
local_46[0x1e] = 0x77;
local_46[0x1f] = 0x87;
local_46[0x20] = 0xe9;
local_46[0x21] = 0x8e;
local_46[0x22] = 0xf;
local_46[0x23] = 0x5f;
local_46[0x24] = 0x19;
local_46[0x25] = 0xd9;
local_46[0x26] = 0x5c;
local_46[0x27] = 0xc2;
local_46[0x28] = 0xf0;
local_46[0x29] = 0xe4;
local_46[0x2a] = 0x91;
local_46[0x2b] = 0xe9;
local_46[0x2c] = 0x99;
local_46[0x2d] = 1;
uVar2 = param_1;
for (local_50 = 0; local_50 < 0x20; local_50 = local_50 + 1) {
uVar1 = (ulong)*(byte *)(param_1 + (long)local_50);
if (*(byte *)(param_1 + (long)local_50) != local_46[(long)local_50 + 0xe]) {
uVar2 = 1;
FUN_00812290((undefined8 *)0x1);
uVar1 = extraout_RDX;
}
}
local_46[0] = 0x70;
local_46[1] = 0x58;
local_46[2] = 0x17;
local_46[3] = 0x44;
local_46[4] = 0x42;
local_46[5] = 0x55;
local_46[6] = 0x5a;
local_46[7] = 0x5e;
local_46[8] = 0x43;
local_46[9] = 0x17;
local_46[10] = 0x43;
local_46[0xb] = 0x5f;
local_46[0xc] = 0x5e;
local_46[0xd] = 0x44;
for (local_4c = 0; local_4c < 0xe; local_4c = local_4c + 1) {
uVar2 = (ulong)(uint)(int)(char)(local_46[local_4c] ^ 0x37);
FUN_00821830(local_46[local_4c] ^ 0x37,param_2,uVar1,param_4,param_5,param_6);
uVar1 = extraout_RDX_00;
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
FUN_008618b0(uVar2,param_2,uVar1,param_4,param_5,param_6);
}
return;
}
```
Đến đây thì có thể đoán được chương trình sẽ đem input của mình qua một đống ADD, SUB, XOR sau đó chạy hàm này để check với một mảng hằng ở một trong hai hàm trên kia.
Chạy thử với hàm hai hàm trên mà bỏ qua đoạn check input:
```cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
int local_46[54];
void FUN_00808ca9(int input[]) {
local_46[0xe] = 0x18;
local_46[0xf] = 0x35;
local_46[0x10] = 0x40;
local_46[0x11] = 0x7b;
local_46[0x12] = 0xbc;
local_46[0x13] = 0xd1;
local_46[0x14] = 0xea;
local_46[0x15] = 0xcf;
local_46[0x16] = 0x33;
local_46[0x17] = 0x4e;
local_46[0x18] = 0x50;
local_46[0x19] = 0xd1;
local_46[0x1a] = 0x1b;
local_46[0x1b] = 99;
local_46[0x1c] = 0xaa;
local_46[0x1d] = 0xc2;
local_46[0x1e] = 0xe4;
local_46[0x1f] = 0xda;
local_46[0x20] = 0xaa;
local_46[0x21] = 0x49;
local_46[0x22] = 0xec;
local_46[0x23] = 0xcd;
local_46[0x24] = 0xf8;
local_46[0x25] = 0x5f;
local_46[0x26] = 0x67;
local_46[0x27] = 0x28;
local_46[0x28] = 0x95;
local_46[0x29] = 0x7a;
local_46[0x2a] = 0x4c;
local_46[0x2b] = 0x30;
local_46[0x2c] = 0x9e;
local_46[0x2d] = 4;
// for (int i = 0; i < 0x20; ++i) {
// if (input[i] != local_46[i + 0xe]) {
// exit(0);
// }
// }
local_46[0] = 0x70;
local_46[1] = 0x58;
local_46[2] = 0x17;
local_46[3] = 0x44;
local_46[4] = 0x42;
local_46[5] = 0x55;
local_46[6] = 0x5a;
local_46[7] = 0x5e;
local_46[8] = 0x43;
local_46[9] = 0x17;
local_46[10] = 0x43;
local_46[0xb] = 0x5f;
local_46[0xc] = 0x5e;
local_46[0xd] = 0x44;
string t = "";
for (int i = 0; i < 0xe; ++i)
t += char(local_46[i] ^ 0x37);
cout << t;
return;
}
void FUN_00808d97(int input[]) {
local_46[0xe] = 0x81;
local_46[0xf] = 0xb;
local_46[0x10] = 0xeb;
local_46[0x11] = 0x94;
local_46[0x12] = 0xbd;
local_46[0x13] = 0xb4;
local_46[0x14] = 0x58;
local_46[0x15] = 0x17;
local_46[0x16] = 0x50;
local_46[0x17] = 0xb5;
local_46[0x18] = 99;
local_46[0x19] = 0xd0;
local_46[0x1a] = 0xcb;
local_46[0x1b] = 0x62;
local_46[0x1c] = 0xaa;
local_46[0x1d] = 0x11;
local_46[0x1e] = 0x77;
local_46[0x1f] = 0x87;
local_46[0x20] = 0xe9;
local_46[0x21] = 0x8e;
local_46[0x22] = 0xf;
local_46[0x23] = 0x5f;
local_46[0x24] = 0x19;
local_46[0x25] = 0xd9;
local_46[0x26] = 0x5c;
local_46[0x27] = 0xc2;
local_46[0x28] = 0xf0;
local_46[0x29] = 0xe4;
local_46[0x2a] = 0x91;
local_46[0x2b] = 0xe9;
local_46[0x2c] = 0x99;
local_46[0x2d] = 1;
// for (int i = 0; i < 0x20; ++i) {
// if (input[i] != local_46[i + 0xe]) {
// exit(0);
// }
// }
local_46[0] = 0x70;
local_46[1] = 0x58;
local_46[2] = 0x17;
local_46[3] = 0x44;
local_46[4] = 0x42;
local_46[5] = 0x55;
local_46[6] = 0x5a;
local_46[7] = 0x5e;
local_46[8] = 0x43;
local_46[9] = 0x17;
local_46[10] = 0x43;
local_46[0xb] = 0x5f;
local_46[0xc] = 0x5e;
local_46[0xd] = 0x44;
string t = "";
for (int i = 0; i < 0xe; ++i)
t += char(local_46[i] ^ 0x37);
cout << t;
return;
}
signed main() {
int input[0x20] = {};
FUN_00808ca9(input);
cout << '\n';
FUN_00808d97(input);
getchar();
return 0;
}
```
Cả 2 hàm đều ra cùng kết quả:
```
Go submit this
Go submit this
```
Vậy mình sẽ reverse cả 2 hàm :v bằng cách lấy local_46[0xe..0x2d] đi ngược lên theo đống ADD, SUB, XOR trong hàm ```FUN_00401ad5``` để ra input.
Hàm đó thực sự quá dài (>4e6 dòng) để có thể copy ra, nên mình sẽ dùng lệnh dump ra lưu vào file:
```
dump verilog memory result.bin 0x00401adf 0x00808c9c
```
///Sai lầm nằm ở đây khi thay vì dump code assembly để cho có lệnh rõ ràng thì mình lại dump dạng hex rồi phải đi mò và thêm đống bug sau đó (vì tự nhiên có hàm chen vô giữa)
Các lệnh đều có dạng:
```
<typ> byte ptr [RAX + <pos>], <val>
```
Dựa và hexdump thì:
* mở đầu là 0x80
* nếu là loại thao tác với [RAX] (vị trí 0) thì đi theo sau là typ, val
* còn không thì theo sau là typ, pos, val
Code python:
```python
f = open("result.bin", "r").read().split()
s = [0x81, 0xb, 0xeb, 0x94, 0xbd, 0xb4, 0x58, 0x17, 0x50, 0xb5, 99, 0xd0, 0xcb, 0x62, 0xaa, 0x11, 0x77, 0x87, 0xe9, 0x8e, 0xf, 0x5f, 0x19, 0xd9, 0x5c, 0xc2, 0xf0, 0xe4, 0x91, 0xe9, 0x99, 1]
stack = []
for i in range(len(f) - 1, -1, -1):
x = int(f[i], 16)
if x == 0x80:
if len(stack) == 3:
val = stack[-3]
pos = stack[-2]
typ = stack[-1]
stack = []
elif len(stack) == 2:
val = stack[-2]
pos = 0
typ = stack[-1]
stack = []
if typ == 0x00 or typ == 0x40:
s[pos] = (s[pos] - val + 128) % 128
elif typ == 0x68 or typ == 0x28:
s[pos] = (s[pos] + val) % 128
else:
s[pos] ^= val
s[pos] %= 128
else:
stack.append(x)
for i in s:
print(chr(i), end = '')
```
Bug bắt đầu từ đây :cry:
1. Nhận ra 0x80 chưa chắc là bắt đầu của lệnh mới vì nó cúng có thể là một val
2. Lúc chạy thì bị lỗi tràn mảng, chỉ có thể là do pos tràn trong s[pos], mà sao tràn được, chắc là so sai format :v. Code in đoạn in ra địa chỉ để xem nó lỗi từ chỗ nào thì phát hiện ra ngay giữ đoạn code dài thì lại lọt vào một hàm khác (không chỉ 1 mà còn nhiều hàm khác).

cũng may các hàm này không có giá trị 0x80 :v
3. Vì có hàm chen vào giữ nên sau khi cho chạy qua hàm đó thì không thể biết được truy vấn tiếp theo là loại nào trong 2 loại trên (vì không thể xét độ dài 2 hay 3 nữa), vả lắm rồi nên phải chơi if test...
Và đây là code in được kết quả
```python
# import sys
# sys.stdout = open("output.txt", 'w')
f = open("result.bin", "r").read().split()
s = [0x81, 0xb, 0xeb, 0x94, 0xbd, 0xb4, 0x58, 0x17, 0x50, 0xb5, 99, 0xd0, 0xcb, 0x62, 0xaa, 0x11, 0x77, 0x87, 0xe9, 0x8e, 0xf, 0x5f, 0x19, 0xd9, 0x5c, 0xc2, 0xf0, 0xe4, 0x91, 0xe9, 0x99, 1]
stack = []
cnt = 0
id = 0x00808c98 + 4
for i in range(len(f) - 1, -1, -1):
x = int(f[i], 16)
id -= 1
if x == 0x80:
if len(stack) == 3:
val = stack[-3]
pos = stack[-2]
typ = stack[-1]
stack = []
elif len(stack) == 2:
val = stack[-2]
pos = 0
typ = stack[-1]
stack = []
elif len(stack) < 2: # (**1)
stack.append(x)
if len(stack) > 3:
stack = stack[1:]
continue
if id == 0x004cee81: # (**3)
val = pos
pos = 0
if typ == 0x00 or typ == 0x40:
s[pos] = (s[pos] - val + 128) % 128
print(f'{hex(id)} ADD [RAX + {hex(pos)}], {hex(val)}')
elif typ == 0x68 or typ == 0x28:
s[pos] = (s[pos] + val) % 128
print(f'{hex(id)} SUB [RAX + {hex(pos)}], {hex(val)}')
else:
s[pos] ^= val
s[pos] %= 128
print(f'{hex(id)} XOR [RAX + {hex(pos)}], {hex(val)}')
else:
stack.append(x)
if len(stack) > 3: # (**2)
stack = stack[1:]
for i in s:
print(chr(i), end = '')
```
Kết quả:

