# 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

và đây là các node

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
```

## 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 (=)
```

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


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

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ý

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

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`

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