## Lavie Bài này có 2 file: ![image](https://hackmd.io/_uploads/Hk9hcS4Syg.png) Thử chạy trên cmd thì thấy chương trình yêu cầu nhập passcode: ![image](https://hackmd.io/_uploads/BJFWBIVSke.png) 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: ![image](https://hackmd.io/_uploads/ryouidUBJx.png) Để đế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: ![image](https://hackmd.io/_uploads/SytmC8_r1x.png) ## try_me Bài này chỉ có 1 file. Chạy thử chương trình: ![image](https://hackmd.io/_uploads/Sy0SBvurJg.png) Đọ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 ![image](https://hackmd.io/_uploads/S1-tXtqHJg.png) ![image](https://hackmd.io/_uploads/rJVw4Y9Syl.png) 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). ![image](https://hackmd.io/_uploads/H1xv9a9r1e.png) 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ả: ![image](https://hackmd.io/_uploads/rklZd09SJg.png) ![image](https://hackmd.io/_uploads/Ski6b0qHyl.png)