# KCSC_rec 2026 ## LLM llmao Chạy thử trước chương trình thì mình thấy nó bắt 3 key (bằng số) để lấy được flag. Đọc description của challenge thì em thấy nó là linked list của DSA (đã học đâu~) Vào hàm main: ```c int __fastcall main(int argc, const char **argv, const char **envp) { __int64 v3; // rdx __int64 v4; // r8 int v6; // [rsp+20h] [rbp-78h] unsigned __int8 *v7; // [rsp+28h] [rbp-70h] _BYTE *Block; // [rsp+30h] [rbp-68h] __int64 v9; // [rsp+48h] [rbp-50h] __int64 v10; // [rsp+50h] [rbp-48h] __int64 v11; // [rsp+58h] [rbp-40h] BYREF __int64 v12; // [rsp+60h] [rbp-38h] BYREF __int64 v13; // [rsp+68h] [rbp-30h] BYREF __int64 v14; // [rsp+70h] [rbp-28h] BYREF unsigned __int8 v15; // [rsp+78h] [rbp-20h] BYREF v12 = 0; v13 = 0; v11 = 0; v14 = 0; sub_140001070(aEnterKeyToGetF, argv, envp); v7 = (unsigned __int8 *)off_1400040A8; v6 = 0; while ( v7 ) { sub_140001070("Key%d : ", v6); sub_140001140("%d", (unsigned int)&v15); sub_140001230(&v12, &v13, v15, v7[9]); v7 = *(unsigned __int8 **)(v7 + 10); ++v6; } sub_1400012C0(off_1400040A8, v12, &v11, &v14); v9 = sub_1400013F0(v11, 1337); v10 = sub_1400013F0(v11, 9999); if ( v9 == 0xD427202CB4B2LL ) { sub_140001070(aCorrect, v3, v4); Block = malloc(0x31u); sub_140001490(&unk_140004078, 48, v10, Block); Block[48] = 0; sub_140001070("Flag: %s\n", Block); free(Block); } else { sub_140001070(aWrong, v3, v4); } return 0; } ``` non na thì nó sẽ kiểm tra kết quả bằng cách tính giá trị x=1337, nếu đúng sử dụng giá trị x=9999 để giải mã flag cấu trúc của linked listed ![image](https://hackmd.io/_uploads/r17NcWnMZl.png) và đây là các node ![image](https://hackmd.io/_uploads/S1TLv8nzbx.png) mỗi node có cấu trúc 18 byte ``` Offset 0x0-0x7 (8 byte): Con trỏ prev (trỏ đến node trước) Offset 0x8 (1 byte): Hệ số (coefficient) Offset 0x9 (1 byte): Số mũ (exponent) Offset 0xA-0x11 (8 byte): Con trỏ next (trỏ đến node sau) -gpt go ``` tính từng node ra: ``` unk_140003288 node 0 = 0 hệ số = 0x24 = 36 số mũ = 0x04 = 4 trỏ node sau = 0x140003270 vậy = 36x^4 ``` cứ như vậy tính 2 node sau: ``` node 1 = 0x140003288 hệ số = 0x12 = 18 số mũ = 0x02 = 2 trỏ node sau = 0x1400032A0 vậy = 18x^2 ``` ``` node 2= 0x140003270 hệ số= 0x07 = 7 số mũ= 0x00 = 0 trỏ node sau= 0 vậy = 7x^0 ``` kết hợp 3 node lại có công thức P(x) = (**36** + key0)x^4 + (**18** + key1)x^2 + (7 + key2)` ``` v7 = sub_1400013F0(v9, 1337); v8 = sub_1400013F0(v9, 9999); if ( v7 == 0xD427202CB4B2LL ) ```` tức P(1337) = 0xD427202CB4B2LL tính key ``` P(1337) = 36 × 1337^4 + 18 × 1337^2 + 7 = 36 × 3,195,402,929,761 + 18 × 1,787,569 + 7 = 115,034,537,647,645 0xD427202CB4B2LL= 233,264,508,613,810 độ chênh lệch = 233,264,508,613,810 - 115,034,537,647,645 = 118,229,970,966,165 => key0 × 1337^4 + key1 × 1337^2 + key2 = 118,229,970,966,165 giải pt key0 = 118,229,970,966,165 / 3,195,402,929,761 = 37 key1 = 62,565,008 / 1,787,569 = 35 key2 = 93 ``` ![image](https://hackmd.io/_uploads/S1L5gM3Mbe.png) ## Just_Ez_Rev Khi mở bằng IDA lên thì em thấy rằng không thấy main đâu cả, vậy nên em sẽ tìm string để xem có chữ nào liên quan đến main không. Dùng shift f12, và có vẻ như đây là main của chúng ta: __int64 sub_140002934() ```c { FILE *v0; // rax signed __int64 v1; // rax double v2; // xmm6_8 double v3; // xmm0_8 size_t v4; // rax _QWORD *v5; // rcx __int64 v6; // rdx char *Str; // [rsp+20h] [rbp-10h] int i; // [rsp+2Ch] [rbp-4h] sub_14000E1F7(); sub_140018900("%s", aEnterTheFlag); v0 = __acrt_iob_func(0); fgets(Buffer, 100, v0); Buffer[strcspn(Buffer, "\n")] = 0; v1 = strlen(Buffer); if ( v1 < 0 ) { v3 = (double)(int)(v1 & 1 | ((unsigned __int64)v1 >> 1)); v2 = v3 + v3; } else { v2 = (double)(int)v1; } if ( v2 == sub_140019F60(6, 2) ) { v4 = strlen(Buffer); Str = (char *)sub_1400027A8((__int64)Buffer, v4, 1); for ( i = 0; i < strlen(Str); i += 2 ) { strncpy(Destination, &Str[i], 2u); sub_140002753(Destination, &qword_140026120); v5 = (_QWORD *)((char *)&unk_1400261A0 + dword_1400263A0); v6 = qword_140026128; *v5 = qword_140026120; v5[1] = v6; dword_1400263A0 += 16; } if ( !memcmp(&unk_1400261A0, &unk_14001B040, 512u) ) { sub_140018900("Correct!\n"); return 1; } else { sub_140018900("Incorrect!\n"); return 0; } } else { sub_140018900("Incorrect length!\n"); return 0; } } ``` Hàm này là nó kiểm tra chiều dài ```if ( v2 == sub_140019F60(6, 2) )``` 6^2=64 nếu ko đúng sẽ ra output "Incorrect length!" Sau đó em trace theo sub_1400027A8: ```c __int64 __fastcall sub_1400027A8(__int64 a1, unsigned __int64 a2, char a3) { __int64 v3; // rax __int64 v4; // rax __int64 v5; // rax __int64 v7; // [rsp+28h] [rbp-28h] unsigned __int64 i; // [rsp+30h] [rbp-20h] int v9; // [rsp+38h] [rbp-18h] unsigned int v10; // [rsp+3Ch] [rbp-14h] __int64 v11; // [rsp+40h] [rbp-10h] unsigned __int64 v12; // [rsp+48h] [rbp-8h] v12 = 8 * ((a2 + 4) / 5); if ( a3 != 1 ) v12 = (8 * a2 + 4) / 5; v7 = sub_140019FC0(v12 + 1); v11 = 0; v10 = 0; v9 = 0; for ( i = 0; i < a2; ++i ) { v10 = (v10 << 8) | *(unsigned __int8 *)(a1 + i); for ( v9 += 8; v9 > 4; v9 -= 5 ) { v3 = v11++; *(_BYTE *)(v7 + v3) = aAbcdefghijklmn[(v10 >> (v9 - 5)) & 31]; } } if ( v9 > 0 ) { v4 = v11++; *(_BYTE *)(v7 + v4) = aAbcdefghijklmn[(v10 << (5 - v9)) & 31]; } if ( a3 ) { while ( (v11 & 7) != 0 ) { v5 = v11++; *(_BYTE *)(v7 + v5) = 61; } } *(_BYTE *)(v7 + v11) = 0; return v7; } ``` em nhận thấy rằng nó đang mã hóa theo base32 `ABCDEFGHIJKLMNOPQRSTUVWXYZ234567` và với input là 64 byte chúng ta có công thức sau ``` 64 × 8 = 512 bit 512 ÷ 5 = 102.4, làm tròn lên = 103 ký tự 103 không chia hết cho 8 → cần 104 tức thêm 1 padding (=) ``` ![image](https://hackmd.io/_uploads/H1QtcehGWl.png) truy vào unk_14001B040 thì em thấy có rất nhiều byte và sau khi feed cho thầy thì em mới biết đó là hash MD5, em hỏi thầy nữa thì mới biết MD5 hash là việc dùng hàm băm MD5 để biến dữ liệu thành một chuỗi cố định 32 ký tự hex. Sau đó "em" đã băm nó ra :3 ```python hex = """D9 B2 52 86 6A 25 98 6F D8 EC 8A 6C 9B DE D2 75 C7 9B DA 0E 66 F0 7B EE 66 2A 68 40 B9 07 BF 39 8D 36 B3 61 A4 79 44 22 C7 06 81 58 6A 95 73 50 18 2B E0 C5 CD CD 50 72 BB 18 64 CD EE 4D 3D 6E AC CB 66 F0 EC D8 26 AA C8 90 65 99 0E 1D A9 7F 56 9B 3C ED 35 55 BF AE 63 CA DB 65 58 A6 FC BE 33 D2 A0 8E EE 4C 17 81 19 40 FE FB C4 E0 97 78 6B A8 9A B4 18 23 AF 9B 65 EA 5D 23 30 31 B9 F8 E4 A5 9D F8 B9 72 06 10 9E B4 B7 F2 FE 52 8A 4D AD 44 AC 8C 02 28 5F 6B 91 64 A0 84 92 A7 8F 92 0A 5B 65 72 5F 6A 3B A9 58 21 CE C6 1D BB 46 D3 2F F2 D4 5E 98 3C 58 E9 6C 9B A3 3D 69 72 1C E8 51 F5 81 93 77 65 89 0F 2A 70 6C 77 EA 8A F3 CC A3 6C 71 EB 23 9A 02 31 BA 0C 6E 7A 45 90 F0 62 42 AD C7 77 F3 C2 C9 70 05 15 0C B7 B8 61 65 B6 C2 3F A9 99 69 25 B6 10 71 0D 93 E2 8C 59 A3 E2 D5 C4 42 58 D5 16 59 F9 62 79 C4 70 CE 81 85 DC 6E 02 9F BF A8 17 DF C4 58 59 8A 5D 38 A6 75 5F 7C 78 EB B4 C2 23 C9 6F 8E 6C CC 29 F7 3C C2 8E 76 AA 96 36 9A BB BA 52 E6 21 BF A8 3D A8 E6 4F BA 7D 08 34 D2 3B 91 50 4B ED 68 59 01 DC 3A C5 38 F9 9A BB C1 D3 39 C2 77 C0 66 9E 7B C3 73 C0 0A 5B 65 72 5F 6A 3B A9 58 21 CE C6 1D BB 46 D3 85 3B C5 5C 10 41 F2 4B 93 92 06 93 31 E6 53 36 F2 14 A7 D4 2E 0D E5 87 5D 55 18 9E 01 E2 E1 87 7B 7C D2 4E A6 F0 8B 71 1C F4 05 3B EA C4 3C C5 64 F3 BD 17 41 AB 8D 6B A5 45 A1 AE 09 BB 87 28 8A 6B 17 8D 3A F0 A5 A9 B2 74 4C A3 19 21 D5 E2 BF D2 34 39 10 37 8A 1B 53 7C 3C 80 36 A5 C0 7E 20 63 84 97 17 D3 2D D1 9E 53 4B 77 CA BA C5 17 20 63 84 97 17 D3 2D D1 9E 53 4B 77 CA BA C5 17 20 63 84 97 17 D3 2D D1 9E 53 4B 77 CA BA C5 17""" hex = hex.replace(" ", "").lower() hashes = [hex[i:i+32] for i in range(0, len(hex), 32)] for h in hashes: print(f'"{h}",') ``` Output: ``` "d9b252866a25986fd8ec8a6c9bded275", "c79bda0e66f07bee662a6840b907bf39", "8d36b361a4794422c70681586a957350", "182be0c5cdcd5072bb1864cdee4d3d6e", "accb66f0ecd826aac89065990e1da97f", "569b3ced3555bfae63cadb6558a6fcbe", "33d2a08eee4c17811940fefbc4e09778", "6ba89ab41823af9b65ea5d233031b9f8", "e4a59df8b97206109eb4b7f2fe528a4d", "ad44ac8c02285f6b9164a08492a78f92", "0a5b65725f6a3ba95821cec61dbb46d3", "2ff2d45e983c58e96c9ba33d69721ce8", "51f581937765890f2a706c77ea8af3cc", "a36c71eb239a0231ba0c6e7a4590f062", "42adc777f3c2c97005150cb7b86165b6", "c23fa9996925b610710d93e28c59a3e2", "d5c44258d51659f96279c470ce8185dc", "6e029fbfa817dfc458598a5d38a6755f", "7c78ebb4c223c96f8e6ccc29f73cc28e", "76aa96369abbba52e621bfa83da8e64f", "ba7d0834d23b91504bed685901dc3ac5", "38f99abbc1d339c277c0669e7bc373c0", "0a5b65725f6a3ba95821cec61dbb46d3", "853bc55c1041f24b9392069331e65336", "f214a7d42e0de5875d55189e01e2e187", "7b7cd24ea6f08b711cf4053beac43cc5", "64f3bd1741ab8d6ba545a1ae09bb8728", "8a6b178d3af0a5a9b2744ca31921d5e2", "bfd2343910378a1b537c3c8036a5c07e", "2063849717d32dd19e534b77cabac517", "2063849717d32dd19e534b77cabac517", "2063849717d32dd19e534b77cabac517", ``` 1 MD5 hash chỉ = 2 ký tự trong base32 gốc thế thì mình bruteforce ngược lại md5 base32 sau đó ghép base32 lại rồi decode ```py import hashlib import base64 base32_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" expected_hashes = [ "d9b252866a25986fd8ec8a6c9bded275", "c79bda0e66f07bee662a6840b907bf39", "8d36b361a4794422c70681586a957350", "182be0c5cdcd5072bb1864cdee4d3d6e", "accb66f0ecd826aac89065990e1da97f", "569b3ced3555bfae63cadb6558a6fcbe", "33d2a08eee4c17811940fefbc4e09778", "6ba89ab41823af9b65ea5d233031b9f8", "e4a59df8b97206109eb4b7f2fe528a4d", "ad44ac8c02285f6b9164a08492a78f92", "0a5b65725f6a3ba95821cec61dbb46d3", "2ff2d45e983c58e96c9ba33d69721ce8", "51f581937765890f2a706c77ea8af3cc", "a36c71eb239a0231ba0c6e7a4590f062", "42adc777f3c2c97005150cb7b86165b6", "c23fa9996925b610710d93e28c59a3e2", "d5c44258d51659f96279c470ce8185dc", "6e029fbfa817dfc458598a5d38a6755f", "7c78ebb4c223c96f8e6ccc29f73cc28e", "76aa96369abbba52e621bfa83da8e64f", "ba7d0834d23b91504bed685901dc3ac5", "38f99abbc1d339c277c0669e7bc373c0", "0a5b65725f6a3ba95821cec61dbb46d3", "853bc55c1041f24b9392069331e65336", "f214a7d42e0de5875d55189e01e2e187", "7b7cd24ea6f08b711cf4053beac43cc5", "64f3bd1741ab8d6ba545a1ae09bb8728", "8a6b178d3af0a5a9b2744ca31921d5e2", "bfd2343910378a1b537c3c8036a5c07e", "2063849717d32dd19e534b77cabac517", "2063849717d32dd19e534b77cabac517", "2063849717d32dd19e534b77cabac517", ] hash_to_pair = {} for c1 in base32_alphabet: for c2 in base32_alphabet: pair = c1 + c2 md5_hash = hashlib.md5(pair.encode()).hexdigest() hash_to_pair[md5_hash] = pair hash_to_pair["2063849717d32dd19e534b77cabac517"] = "==" base32_string = "" for i, expected_hash in enumerate(expected_hashes): pair = hash_to_pair[expected_hash] base32_string += pair flag = base64.b32decode(base32_string).decode('ascii') print(f"\nFLAG: {flag}") #cam on thay ``` Output: ` FLAG: KCSC{3z_bru73_md5_4nd_d3cod3_b4s332} ` ## 33,550,337th amphoreus' 33,550,337th loop bài này cho mình một file APK lúc đầu em cứ mở ida lên xem mãi mới biết rằng mình bị lùa gà một lúc sau em tải ldplayer và mở file APK thì nhận thấy nó cần chúng ta click 33550337 lần để lấy flag sau đó thì em nhớ tới cheat engine. Nhưng CE thì đâu có đổi giá trị trên emulator được nên em lên mạng xem cách hack apk và em theo [video](https://www.youtube.com/watch?v=cOcKEumJX4Y) này, tải game guardian, cơ chế hoạt động của game guardian giống cheat engine điền string rồi đi scan từng address, trúng address xong e modify nó lên max là ra flag ![image](https://hackmd.io/_uploads/By06AfhzWx.png) ![image](https://hackmd.io/_uploads/rJMRAMnMZe.png) PS: em làm ra từ hồi 3-4h sáng mà buồn ngủ quá quên submit flag T_T ## anti-debugger Nhà có 5 anti-debugger load IDA vào main: ```c int __cdecl main(int argc, const char **argv, const char **envp) { unsigned int v3; // eax int v4; // edi unsigned int v5; // edx unsigned int v6; // ecx unsigned int v7; // eax int v9; // [esp-44h] [ebp-48h] int v10; // [esp-30h] [ebp-34h] int v11; // [esp-1Ch] [ebp-20h] __debugbreak(); dword_4043D8 = IsDebuggerPresent(); __debugbreak(); dword_4043D0 = IsDebuggerPresent(); __debugbreak(); v11 = dword_4043D8; IsDebuggerPresent(); __debugbreak(); v10 = dword_4043D8; IsDebuggerPresent(); __debugbreak(); v9 = dword_4043D0; IsDebuggerPresent(); v3 = dword_4043E0; if ( dword_4043E0 ) { if ( byte_40449F[dword_4043E0] == 10 ) v3 = --dword_4043E0; if ( v3 && byte_40449F[v3] == 13 ) dword_4043E0 = --v3; } if ( v3 >= 0x40 ) __report_rangecheckfailure( v9, byte_4044A0, 64, &dword_4043E0, 0, v10, "Enter flag: ", 12, &unk_4043DC, 0, v11, "Welcome to KCSC!\n", 17, &unk_4043DC, 0, -10, -11); byte_4044A0[v3] = 0; __debugbreak(); IsDebuggerPresent(); __debugbreak(); IsDebuggerPresent(); __debugbreak(); IsDebuggerPresent(); dword_4043E8 = (dword_4043E0 + 7) & 0xFFFFFFF8; __debugbreak(); IsDebuggerPresent(); __debugbreak(); IsDebuggerPresent(); __debugbreak(); IsDebuggerPresent(); dword_4043CC = dword_4043E8; if ( dword_4043E8 == 40 ) { v4 = 1; dword_4043EC = 0; v5 = 0; v6 = 0; v7 = 0; while ( byte_4044E0[v7] == byte_403184[v6] ) { v6 = ++v5; dword_4043EC = v5; v7 = v5; if ( v5 >= 0x28 ) goto LABEL_14; } v4 = 0; LABEL_14: if ( v4 ) { __debugbreak(); IsDebuggerPresent(); __debugbreak(); IsDebuggerPresent(); } } __debugbreak(); IsDebuggerPresent(); __debugbreak(); IsDebuggerPresent(); return 0; } ``` đập vào mắt là một dãy winapi IsDebuggerPresent lần đầu nhìn vào em liền lao đầu vào debugger mà patch, patch một hồi không được thì em đi tìm hiểu các functions khác. em phát hiện ra function mà chưa từng thấy bao giờ là `TlsCallback_0` và `Handler`, search trên google thì em thấy rằng khi có function tlscallback thì dù cho mình đặt breakpoint ở entry rồi chạy debugger thì nó cũng detect được và không dừng ở breakpoint của mình. [source](https://hex-rays.com/blog/tls-callbacks) đây là hàm tlscallback ```c void __stdcall TlsCallback_0(int a1, int a2, int a3) { if (a2 == 1) // DLL_PROCESS_ATTACH { if (Handle) RemoveVectoredExceptionHandler(Handle); Handle = AddVectoredExceptionHandler(1, Handler); } } ``` chúng ta có thể nhận thấy nó cũng liên quan tới function handle, `AddVectoredExceptionHandler` đăng ký một exception handler. Handler này sẽ bắt tất cả các exception xảy ra trong chương trình Quay lại main chúng ta có thể thấy có rất nhiều lệnh như thế này ![image](https://hackmd.io/_uploads/By8Sa4hzWe.png) thì... debugbreak() tạo lệnh INT 3, INT 3 là software break khi chạy bình thường, INT 3 gây ra exception và exception được xử lý bởi handler đã đăng ký ![image](https://hackmd.io/_uploads/H1_9AE2fZe.png) nó cứ tới `VectoredExceptionHandle` vậy VEH là gì [VEH](https://learn.microsoft.com/en-us/windows/win32/debug/vectored-exception-handling) là cơ chế xử lý exception trong win tại hàm handler ```c LONG __stdcall Handler(struct _EXCEPTION_POINTERS *ExceptionInfo) { PCONTEXT ContextRecord; // esi PVOID v2; // eax if ( ExceptionInfo->ExceptionRecord->ExceptionCode != -2147483645 ) return 0; ContextRecord = ExceptionInfo->ContextRecord; sub_4012F0(); if ( dword_4043E4 < 15 ) { sub_401240(off_4031B4[dword_4043E4]); ++dword_4043E4; } if ( Handle ) RemoveVectoredExceptionHandler(Handle); v2 = AddVectoredExceptionHandler(1u, sub_4013F0); ++ContextRecord->Eip; Handle = v2; return -1; } ``` kiểm tra từ trên xuống thì nó đang kiểm tra exception code vào sub_4012F0 ngoài ra còn có `EIP` nó là thanh ghi CPU x86 giữ địa chỉ lệnh tiếp theo sẽ được thực thi. ở đây nó tăng `EIP` vậy Bỏ qua lệnh INT 3 tiếp theo nó đăng ký handler tiếp theo cho lần INT 3 sau ```c // write access to const memory has been detected, the output may be wrong! void sub_4012F0() { HANDLE CurrentProcess; // eax DWORD flOldProtect; // [esp+0h] [ebp-8h] BYREF if ( !dword_404058 ) { VirtualProtect(IsDebuggerPresent, 5u, 0x40u, &flOldProtect); IsDebuggerPresent = (BOOL (__stdcall *)())dword_404480; *((_BYTE *)&IsDebuggerPresent + 4) = byte_404484; VirtualProtect(IsDebuggerPresent, 5u, flOldProtect, &flOldProtect); CurrentProcess = GetCurrentProcess(); FlushInstructionCache(CurrentProcess, IsDebuggerPresent, 5u); } } ``` em trace một hồi thì vào hàm này, có quá nhiều cái lạ mà em có search cũng không hiểu `` `` `` `` `` `` đến đây em quá nản và không có ý tưởng nên đưa vài cái prompt cho claude... xin lỗi a robert ```py def rc4_init(key): """Khởi tạo RC4 state với key""" S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)]) % 256 S[i], S[j] = S[j], S[i] return S def rc4_crypt(S, data): """giải mã RC4""" S = S.copy() i = 0 j = 0 result = [] for byte in data: i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] k = S[(S[i] + S[j]) % 256] result.append(byte ^ k) return bytes(result) def tea_decrypt_block(v0, v1, key): """TEA """ k0, k1, k2, k3 = key delta = 0x9E3779B9 sum_val = (delta * 32) & 0xFFFFFFFF for _ in range(32): v1 = (v1 - (((v0 << 4) + k2) ^ (v0 + sum_val) ^ ((v0 >> 5) + k3))) & 0xFFFFFFFF v0 = (v0 - (((v1 << 4) + k0) ^ (v1 + sum_val) ^ ((v1 >> 5) + k1))) & 0xFFFFFFFF sum_val = (sum_val - delta) & 0xFFFFFFFF return v0, v1 def tea_decrypt(data, key): """TEA""" # Chuyển key bytes thành 4 giá trị u32 (little-endian) key_u32 = [] for i in range(0, 16, 4): val = int.from_bytes(key[i:i+4], 'little') key_u32.append(val) result = [] # Xử lý từng for i in range(0, len(data), 8): v0 = int.from_bytes(data[i:i+4], 'little') v1 = int.from_bytes(data[i+4:i+8], 'little') v0_dec, v1_dec = tea_decrypt_block(v0, v1, key_u32) result.extend(v0_dec.to_bytes(4, 'little')) result.extend(v1_dec.to_bytes(4, 'little')) return bytes(result) def unpad_pkcs7(data): """Xóa PKCS#7 padding""" if not data: return data pad_len = data[-1] if pad_len > 0 and pad_len <= 8: if all(b == pad_len for b in data[-pad_len:]): return data[:-pad_len] return data # Keys rc4_key1 = bytes([0x1a, 0x89, 0x14, 0x92, 0x22, 0x5d, 0x4f]) tea_key = bytes([0xef, 0xbe, 0xad, 0xde, 0xbe, 0xba, 0xfe, 0xca, 0x37, 0x13, 0x37, 0x13, 0x0d, 0xf0, 0xad, 0xba]) rc4_key2 = bytes([0x36, 0xbd, 0xd1, 0x3b, 0x9a, 0xa8, 0xef, 0x29, 0xff]) # encrypted result encrypted = bytes([ 0x7b, 0x6f, 0xb4, 0xf7, 0x81, 0x3d, 0xef, 0xee, 0xb2, 0x1e, 0x7e, 0x8d, 0x0c, 0x9d, 0x67, 0x41, 0xcb, 0x20, 0x53, 0xfd, 0x47, 0x71, 0xad, 0xc6, 0xbc, 0x0e, 0xf2, 0xa2, 0x69, 0x04, 0xa3, 0xb2, 0x5e, 0xd7, 0xd0, 0x6e, 0x63, 0x6f, 0x72, 0x9e ]) print("=" * 70) print("GIẢI MÃ") print("=" * 70) print(f"\n[*] Dữ liệu mã hóa ({len(encrypted)} bytes):") print(f" {encrypted.hex()}") print("\n[*] Bước 1: Giải mã RC4 với key 2 (9 bytes)...") print(f" Key: {rc4_key2.hex()}") S2 = rc4_init(rc4_key2) after_rc4_2 = rc4_crypt(S2, encrypted) print(f" Kết quả: {after_rc4_2.hex()}") print("\n[*] Bước 2: Giải mã TEA với key 128-bit...") print(f" Key: {tea_key.hex()}") after_tea = tea_decrypt(after_rc4_2, tea_key) print(f" Kết quả: {after_tea.hex()}") print("\n[*] Bước 3: Giải mã RC4 với key 1 (7 bytes)...") print(f" Key: {rc4_key1.hex()}") S1 = rc4_init(rc4_key1) flag_padded = rc4_crypt(S1, after_tea) print("\n[*] Bước 3: Giải mã RC4 với key 1 (7 bytes)...") print(f" Key: {rc4_key1.hex()}") S1 = rc4_init(rc4_key1) flag_padded = rc4_crypt(S1, after_tea) print(f" Kết quả (có padding): {flag_padded.hex()}") print("\n[*] Bước 4: Loại bỏ PKCS#7 padding...") flag = unpad_pkcs7(flag_padded) print("\n" + "=" * 70) print("[+] FLAG THU ĐƯỢC:") try: print(flag.decode()) except UnicodeDecodeError: print(flag) print("=" * 70) ``` ``` Output: [*] Dữ liệu mã hóa (40 bytes): 7b6fb4f7813defeeb21e7e8d0c9d6741cb2053fd4771adc6bc0ef2a26904a3b25ed7d06e636f729e [*] Bước 1: Giải mã RC4 với key 2 (9 bytes)... Key: 36bdd13b9aa8ef29ff Kết quả: 8dcb81f25bb9fc18d730ad183e054a6a20010ba1c659341c3c02a9743bcc5eaae4290f52465d5c20 [*] Bước 2: Giải mã TEA với key 128-bit... Key: efbeaddebebafeca371337130df0adba Kết quả: 37412f325ea184f0519ed74f969ec28b8f4971cee2820e17b018140a71062f74a21a795c78030303 [*] Bước 3: Giải mã RC4 với key 1 (7 bytes)... Key: 1a891492225d4f [*] Bước 3: Giải mã RC4 với key 1 (7 bytes)... Key: 1a891492225d4f Kết quả (có padding): 4b4353437b6e6f775f796f755f6b6e6f775f686f775f746f5f686f6f6b5f57494e4150497d41f2be [*] Bước 4: Loại bỏ PKCS#7 padding... [+] FLAG THU ĐƯỢC: b'KCSC{now_you_know_how_to_hook_WINAPI}A\xf2\xbe' ``` ## keygenme Đầu tiên em chạy thử chương trình thì thấy challenge nó check cả username lẫn serial key. Đúng cả 2 thì mới ra flag đúng. Mở bằng IDA thì em thấy toàn là functions nên tìm string, vì không thấy string nào liên quan đến chương trình nên em mò từng function từ trên xuống thì em thấy 2 function này `sub_1400017A6` ```c int __fastcall sub_1400017A6(HWND hWndParent, LPCWSTR lpText, int a3) { HWND Window; // rcx int v7; // edi int v8; // ebp int SystemMetrics; // ebx int v10; // eax const WCHAR *v11; // r8 UINT v12; // r9d tagMSG Msg; // [rsp+60h] [rbp-D8h] BYREF tagRECT Rect; // [rsp+90h] [rbp-A8h] BYREF WNDCLASSW WndClass; // [rsp+A0h] [rbp-98h] BYREF WCHAR ClassName[36]; // [rsp+F0h] [rbp-48h] BYREF dword_140020EE0 = a3; wcscpy_s(&Destination, 0x800u, lpText); wcscpy(ClassName, L"KCSCFlgDialog"); *(_QWORD *)&WndClass.style = 0; *(_QWORD *)&WndClass.cbClsExtra = 0; WndClass.hIcon = 0; *(_OWORD *)&WndClass.hbrBackground = 0; WndClass.lpfnWndProc = (WNDPROC)sub_140001B4E; WndClass.hInstance = (HINSTANCE)GetWindowLongPtrW(hWndParent, -6); WndClass.lpszClassName = ClassName; WndClass.hCursor = LoadCursorW(0, (LPCWSTR)0x7F00); WndClass.hbrBackground = 0; if ( !a3 ) { WndClass.hIcon = LoadIconW(0, (LPCWSTR)0x7F01); RegisterClassW(&WndClass); Window = CreateWindowExW( 9u, ClassName, L"Activation Failed", 0x90C80000, 0, 0, 650, 360, hWndParent, 0, WndClass.hInstance, 0); hWnd = Window; if ( Window ) goto LABEL_3; v11 = L"Activation Failed"; v12 = 16; return MessageBoxW(hWndParent, lpText, v11, v12); } WndClass.hIcon = LoadIconW(0, (LPCWSTR)0x7F04); RegisterClassW(&WndClass); Window = CreateWindowExW( 9u, ClassName, L"Activation Successful", 0x90C80000, 0, 0, 650, 360, hWndParent, 0, WndClass.hInstance, 0); hWnd = Window; if ( !Window ) { v11 = L"Activation Successful"; v12 = 64; return MessageBoxW(hWndParent, lpText, v11, v12); } LABEL_3: GetWindowRect(Window, &Rect); v7 = Rect.right - Rect.left; v8 = Rect.bottom - Rect.top; SystemMetrics = GetSystemMetrics(0); v10 = GetSystemMetrics(1); SetWindowPos(hWnd, HWND_MESSAGE|0x2LL, (SystemMetrics - v7) / 2, (v10 - v8) / 2, 0, 0, 1u); EnableWindow(hWndParent, 0); while ( GetMessageW(&Msg, 0, 0, 0) > 0 && IsWindow(hWnd) ) { TranslateMessage(&Msg); DispatchMessageW(&Msg); } EnableWindow(hWndParent, 1); SetForegroundWindow(hWndParent); SetFocus(hWndParent); return UnregisterClassW(ClassName, WndClass.hInstance); } ``` đây là một function checker sang bên main function là `sub_1400022F2` ```c __int64 __fastcall sub_1400022F2(HWND hWndParent, UINT a2, WPARAM a3, LPARAM a4) { HINSTANCE WindowLongPtrW; // rsi HWND Window; // rax HWND v9; // rax HWND v10; // rax HWND v11; // rbx HFONT FontW; // rax __int64 result; // rax HDC v14; // rbx HBRUSH v15; // r8 COLORREF v16; // r8d HPEN Pen; // rbp HGDIOBJ v18; // r12 HGDIOBJ StockObject; // rax HGDIOBJ v20; // rdi unsigned int v21; // eax CHAR String[256]; // [rsp+70h] [rbp-448h] BYREF __m128i Str; // [rsp+170h] [rbp-348h] BYREF _BYTE chText[280]; // [rsp+270h] [rbp-248h] BYREF char v25; // [rsp+388h] [rbp-130h] if ( a2 == 43 ) { result = 0; if ( *(_DWORD *)(a4 + 4) == 3 ) { v14 = *(HDC *)(a4 + 32); Str = _mm_loadu_si128((const __m128i *)(a4 + 40)); v15 = (HBRUSH)qword_140020F18; if ( dword_140020F08 ) v15 = hbr; FillRect(v14, (const RECT *)&Str, v15); v16 = 12156969; if ( dword_140020F08 ) v16 = 14391348; Pen = CreatePen(0, 1, v16); v18 = SelectObject(v14, Pen); StockObject = GetStockObject(5); SelectObject(v14, StockObject); RoundRect(v14, Str.m128i_i32[0], Str.m128i_i32[1], Str.m128i_i32[2], Str.m128i_i32[3], 5, 5); SelectObject(v14, v18); DeleteObject(Pen); GetWindowTextW(*(HWND *)(a4 + 24), (LPWSTR)chText, 256); SetBkMode(v14, 1); SetTextColor(v14, 0xFFFFFFu); v20 = SelectObject(v14, (HGDIOBJ)h); DrawTextW(v14, (LPCWSTR)chText, -1, (LPRECT)&Str, 0x25u); SelectObject(v14, v20); return 1; } } else if ( a2 > 0x2B ) { if ( a2 == 307 ) { SetTextColor((HDC)a3, 0x503E2Cu); SetBkColor((HDC)a3, 0xFFFFFFu); return (__int64)GetStockObject(0); } if ( a2 == 312 ) { SetTextColor((HDC)a3, 0x503E2Cu); SetBkColor((HDC)a3, 0xF5F0F0u); return (__int64)qword_140020F20; } if ( a2 != 273 ) return DefWindowProcW(hWndParent, a2, a3, a4); result = 0; if ( (_WORD)a3 == 3 && (a3 & 0xFFFF0000) == 0 ) { GetWindowTextA(qword_140020F50, String, 256); GetWindowTextA(qword_140020F48, Str.m128i_i8, 256); if ( String[0] && Str.m128i_i8[0] ) { if ( lpAddress && (unsigned int)((__int64 (__fastcall *)(CHAR *, __m128i *, void *))lpAddress)(String, &Str, &unk_140016520) == 1 ) { qmemcpy(chText, &unk_140016020, sizeof(chText)); v21 = strlen(Str.m128i_i8); sub_1400021BD(&Str, v21, chText, 280); v25 = 0; sub_1400153B0(Buffer, 0x400u, (wchar_t *)Format); sub_1400017A6(hWndParent, Buffer, 1); } else { sub_140002242(&unk_1400161A0, &unk_140016140); sub_1400153B0(Buffer, 0x400u, (wchar_t *)Format); sub_1400017A6(hWndParent, Buffer, 0); } return 0; } else { MessageBoxW(hWndParent, L"Please enter both Username and Serial Key!", "I", 0x30u); return 0; } } } else { if ( a2 != 2 ) { if ( a2 == 20 ) { GetClientRect(hWndParent, (LPRECT)chText); FillRect((HDC)a3, (const RECT *)chText, (HBRUSH)qword_140020F20); return 1; } if ( a2 == 1 ) { WindowLongPtrW = (HINSTANCE)GetWindowLongPtrW(hWndParent, -6); h = (WPARAM)CreateFontW(20, 0, 0, 0, 400, 0, 0, 0, 1u, 0, 0, 5u, 0, "S"); wParam = (WPARAM)CreateFontW(22, 0, 0, 0, 700, 0, 0, 0, 1u, 0, 0, 5u, 0, "S"); qword_140020F20 = CreateSolidBrush(0xF5F0F0u); qword_140020F18 = CreateSolidBrush(0xB98029u); hbr = CreateSolidBrush(0xDB9834u); Window = CreateWindowExW( 0, "S", L"KCSC Software Activation", 0x50000001u, 0, 15, 500, 30, hWndParent, 0, WindowLongPtrW, 0); SendMessageW(Window, 0x30u, wParam, 1); CreateWindowExW(0, "S", &word_140017222, 0x50000010u, 20, 55, 460, 2, hWndParent, 0, WindowLongPtrW, 0); v9 = CreateWindowExW(0, "S", L"Username:", 0x50000000u, 30, 80, 100, 20, hWndParent, 0, WindowLongPtrW, 0); SendMessageW(v9, 0x30u, h, 1); qword_140020F50 = CreateWindowExW( 0x200u, L"EDIT", &word_140017222, 0x50000080u, 140, 75, 320, 30, hWndParent, (HMENU)1, WindowLongPtrW, 0); SendMessageW(qword_140020F50, 0x30u, h, 1); v10 = CreateWindowExW(0, "S", L"Serial Key:", 0x50000000u, 30, 125, 100, 20, hWndParent, 0, WindowLongPtrW, 0); SendMessageW(v10, 0x30u, h, 1); qword_140020F48 = CreateWindowExW( 0x200u, L"EDIT", &word_140017222, 0x50000080u, 140, 120, 320, 30, hWndParent, (HMENU)2, WindowLongPtrW, 0); SendMessageW(qword_140020F48, 0x30u, h, 1); qword_140020F40 = CreateWindowExW( 0, L"BUTTON", L"Activate License", 0x5000000Bu, 175, 170, 150, 40, hWndParent, (HMENU)3, WindowLongPtrW, 0); SendMessageW(qword_140020F40, 0x30u, h, 1); SetWindowSubclass(qword_140020F40, pfnSubclass, 0, 0); v11 = CreateWindowExW( 0, "S", L"Enter your username and serial key to activate", 0x50000001u, 0, 230, 500, 20, hWndParent, 0, WindowLongPtrW, 0); FontW = CreateFontW(16, 0, 0, 0, 400, 0, 0, 0, 1u, 0, 0, 5u, 0, "S"); SendMessageW(v11, 0x30u, (WPARAM)FontW, 1); return 0; } return DefWindowProcW(hWndParent, a2, a3, a4); } if ( lpAddress ) VirtualFree(lpAddress, 0, 0x8000u); if ( h ) DeleteObject((HGDIOBJ)h); if ( wParam ) DeleteObject((HGDIOBJ)wParam); if ( qword_140020F20 ) DeleteObject(qword_140020F20); if ( qword_140020F18 ) DeleteObject(qword_140020F18); if ( hbr ) DeleteObject(hbr); PostQuitMessage(0); return 0; } return result; } ``` ```c if ( (_WORD)a3 == 3 && (a3 & 0xFFFF0000) == 0 ) { GetWindowTextA(qword_140020F50, String, 256); GetWindowTextA(qword_140020F48, Str.m128i_i8, 256); if ( String[0] && Str.m128i_i8[0] ) { if ( lpAddress && (unsigned int)((__int64 (__fastcall *)(CHAR *, __m128i *, void *))lpAddress)(String, &Str, &unk_140016520) == 1 ) ``` và ở khúc này đây là nó đang gọi username và key sau đó gọi lpAddress để check, và `unk_140016520` chứa một data nào đó mà cứ 2 byte lại được cách bởi 2 byte trống nên để anh lgbt giải thích thì thực ra nó là `uint32_t`, có 32-bit cái này em thấy nó sú sú nên cứ để đó đã xong em sẽ đi tìm cái lpAddress nó được store ở đâu, may thay ngay hàm dưới `sub_140002C82` ```c v6 = VirtualAlloc(0, 0x110u, 0x3000u, 0x40u); lpAddress = v6; if ( v6 ) { *v6 = qword_140016600; v6[33] = qword_140016708; qmemcpy( (void *)((unsigned __int64)(v6 + 1) & 0xFFFFFFFFFFFFFFF8uLL), (const void *)((char *)&qword_140016600 - ((char *)v6 - ((unsigned __int64)(v6 + 1) & 0xFFFFFFFFFFFFFFF8uLL))), 8LL * (((unsigned int)v6 - (((_DWORD)v6 + 8) & 0xFFFFFFF8) + 272) >> 3)); ``` trong qword_140016600 thì em được mã hex như thế này: ```85 C0 57 56 53 48 89 D3 0F 94 C2 08 D0 0F 85 ED 00 00 00 31 C0 48 85 C9 0F 84 E2 00 00 00 80 3C 01 00 74 0B 48 FF C0 48 3D 00 01 00 00 75 EF 31 D2 80 3C 13 00 74 14 48 FF C2 48 81 FA 00 01 00 00 75 EE 41 BB 00 01 00 00 EB 03 41 89 D3 45 85 DB 0F 84 A9 00 00 00 83 F8 0C 0F 85 A0 00 00 00 31 C0 80 39 6B 0F 85 97 00 00 00 80 79 02 61 0F 85 8D 00 00 00 80 79 04 33 0F 85 83 00 00 00 80 79 06 33 75 7D 80 79 08 35 75 77 80 79 0A 6E 75 71 80 79 01 6D 75 6B 80 79 03 72 75 65 80 79 05 76 75 5F 80 79 07 72 75 59 80 79 09 21 75 53 80 79 0B 39 75 4D 45 31 D2 41 B9 37 13 00 00 BE 0C 00 00 00 44 89 D0 42 0F BE 2C 13 99 F7 FE 44 89 C8 44 31 CD 48 63 D2 0F B6 3C 11 31 D2 F7 F6 31 EF 89 D2 44 0F B6 0C 11 41 01 F9 47 39 0C 90 75 0F 49 FF C2 45 39 D3 7F CA B8 01 00 00 00 EB 02``` sau đó em dùng ảo thuật đen, bấm c chuyển nó sang code ![image](https://hackmd.io/_uploads/B1tyK4azWe.png) em nhận thấy là `ptr [rcx]` nó đang bị rối từ 0 lên 2 lên 4 lên 6,.... nên em sắp xếp lại. Từ đó ra được như thế này: `kmar3v3r5!n9` vậy là có được user sau đó thì em chả biết làm gì tiếp nên e dùng AI, đưa nó tất cả những gì ở trên rồi nó suy ra code ```py USERNAME = "kmar3v3r5!n9" def generate_serial(username, expected): if username != USERNAME: raise ValueError("Username must be exactly 'kmar3v3r5!n9'") h = 0x1337 serial = [] for i, target in enumerate(expected): a, b = i % 12, h % 12 ch = ((target - ord(username[b])) ^ h ^ ord(username[a])) & 0xFF if not 32 <= ch <= 126: for c in range(32, 127): if ((h ^ c ^ ord(username[a])) + ord(username[b])) & 0xFFFFFFFF == target: ch = c break serial.append(chr(ch)) h = ((h ^ ch ^ ord(username[a])) + ord(username[b])) & 0xFFFFFFFF return "".join(serial) expected_hashes = [ 0x1350, 0x13E9, 0x13FC, 0x1400, 0x1453, 0x1489, 0x14F7, 0x1529, 0x15A0, 0x15DF, 0x166D, 0x1677, 0x169B, 0x1733, 0x17B8, 0x1803, 0x1875, 0x1858, 0x188D, 0x18F3, 0x1905, 0x199D, 0x1A39, 0x1AC8, 0x1AC1, 0x1AB9, 0x1B23, 0x1B6E, 0x1B9A, 0x1C2F, 0x1CBB, 0x1D36, 0x1DA8, 0x1E02, 0x1EA2, 0x1EFF, 0x1F27, 0x1F90, 0x1FEC, 0x201E, 0x20D9, 0x20BB, 0x212D, 0x212E, 0x21A4, 0x2200, 0x228D, 0x2366, 0x239F ] print("Serial: ", generate_serial(USERNAME, expected_hashes)) ``` `Output : KCSC-2026-JU57-R341-UX0R-4ndd-U4DD-W!TH-U53R-N4M3` ![image](https://hackmd.io/_uploads/Hk66tr6Gbe.png) xong decode bên cyberchef em ra được flag `KCSC{C0n9r4tu14t!0n5_Y0u_H4v3_Succ355fu11y_4ct!v4t4t3d_7h3_L!c3n53_S0ftw4r3_W!th_RC4_4nd_D3c0d3_Base64_3_T!m3s__=)))}` ## after word mấy anh ra đề rất ác, nhưng mà đề nào em thấy cũng rất hay, tuy nhiên e đã dùng AI rất nhiều và chưa tự giải cũng như là tự tìm hiểu, hi vọng mấy anh cho em biết thêm kĩ lăng debug