# 1. LLM
Với hint có sẵn từ đề bài `linked-list master` thì em cũng lò mò thử thêm về cái này
Bài này em dùng AI kha khá vì code này em đọc không hiểu cha lắm.
[Link](https://www-programiz-com.translate.goog/dsa/linked-list?_x_tr_sl=en&_x_tr_tl=vi&_x_tr_hl=vi&_x_tr_pto=tc)

Chắc cũng sẽ có gì đó liên quan mà ha.
```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;
}
```
Chương trình in thông báo nhập `key`
```c
v7 = (unsigned __int8 *)off_1400040A8;
v6 = 0;
while ( v7 )
```
Ở đoạn này, v7 trỏ tới 1 địa chỉ, sau đó nó vòng lặp và nó dừng khi là ....`NULL`, giống như ta duyệt từng phần tử bằng con trỏ, và dừng khi con trỏ = `NULL`. Giống như linked-list.

Dựa vào đoạn code này thì có thể phân tích cái node
Chương trình xin 18 ô nhớ
```
v3[0] v3[1] v3[2] v3[3] v3[4] v3[5] v3[6] v3[7]
v3[8] v3[9]
v3[10] v3[11] v3[12] v3[13] v3[14] v3[15] v3[16] v3[17]
````
ghi a1 vào v[8], a2 vào v[9]
v3[0..7] : con trỏ trước
v3[10..17]: con trỏ sau

Tiếp đến chương trình sẽ thu thập dữ liệu

Khi này thì cái số input sẽ cộng vào key. Ví dụ như node0 có sẵn key = 36, em nhập 1 thì chỗ key đó thành 37.
Lúc này em có là
```
Số tại node 0 = (36 + input 1)
Số tại node 1= (18 + input 2)
Số tại node 2= (7 + input 3)
```
Tiếp tục với
```c=
v9 = sub_1400013F0(v11, 1337);
v10 = sub_1400013F0(v11, 9999);
```
```c
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);
}
```
Tại đây v9 làm công việc là check xem nó thỏa mãn để vào hàm if hay chưa, nếu thỏa mãn thì v10 sẽ giải flag
Giờ em xem cái hàm `sub_...F0` đó nó làm gì
```c
__int64 __fastcall sub_1400013F0(__int64 a1, __int64 a2)
{
int i; // [rsp+0h] [rbp-28h]
__int64 v4; // [rsp+8h] [rbp-20h]
__int64 v5; // [rsp+10h] [rbp-18h]
v5 = 0;
while ( a1 )
{
v4 = 1;
for ( i = 0; i < *(unsigned __int8 *)(a1 + 9); ++i )
v4 *= a2;
v5 += v4 * *(unsigned __int8 *)(a1 + 8);
a1 = *(_QWORD *)(a1 + 10);
}
return v5;
}
```
Ở đây thì kiểu a1 trỏ tới cái node còn a2 sẽ là 1337 hoặc 9999
Tiếng người sẽ là
```
tong = 0
node = a1
while (node != NULL):
gia_tri = 1
lặp (s) lần:
gia_tri = gia_tri * a2
tong = tong + gia_tri * so_trong_node
node = node->next
return tong
```
v9 == 0xD427202CB4B2LL mới chịu vô hàm in flag, thì có kết quả, giờ mình tìm input


# 2. 33,550,337
Challenge cho em 1 file apk, em xử dụng `BLUESTACK` để mở nó

Ờm thì cũng hiểu được vấn đề ở đây cần làm là gì, click vào nút kia đến khi nào đủ số lượng sẽ có được flag.
Ban đầu em suy nghĩ đến việc tìm 1 phần mềm nào đó, thay đổi dữ liệu liên quan đến ClickButton, kiểu bấm vào nút đó là full điểm luôn, nhưng em mò mãi không kiếm được phần mềm nào như vậy.
Sau đó em nghĩ đến việc`Hack Game` như cách hồi đó em dùng để cheat vàng, tiền trong Angry Bird, Subway Sufer, ... đại loại là mấy game APK

Sử dụng phầm mềm này để sửa dữ liệu của game
Tham khảo thêm về cách hack trong video này :
[Cách hack game](https://www.youtube.com/watch?v=cOcKEumJX4Y)

Sử dụng nút search này để tìm kiếm những giá trị tương đương.
Em click cho đến 1 con số nào đó xấu xấu, lọc tìm giá trị cần thay đổi cho dễ


Giờ thì em sẽ chỉnh sửa nó thành giá trị em mong muốn

Và thế là em có được FLAG

`KCSC{now stay true thaw winter's rime anew woven memories your boon and like a melody sing our reverie and in our memory pen the end of our journey}`
Challenge này khá lạ mà cũng khá tuổi thơ, mấy anh không spoil trước là nó dễ thì chắc em cũng chã dám tải về mà thử luôn í
→ Qua bài này, có thể cách giải của em không phải là cách mà author muốn hướng đến, em mong được 1 hint về phần mềm có thể giúp em sửa code từ game này để `1 click and done`
# 3. Just_EzReversing
Hàm main :
```c
__int64 sub_140002934()
{
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(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, 0x200u) )
{
sub_140018900("Correct!\n");
return 1;
}
else
{
sub_140018900("Incorrect!\n");
return 0;
}
}
else
{
sub_140018900("Incorrect length!\n");
return 0;
}
}
```
Chương trình húp input
Check độ dài `if ( v2 == sub_140019F60(6, 2) )`

6 mũ 2 là 6 * 6 = ~~36~~

Tiếp tục tại đây, v4 = độ dài của chuỗi mình nhập là 36
<details>
<summary> str = sub__1400027A8 này nọ </summary>
__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)) & 0x1F];
}
}
if ( v9 > 0 )
{
v4 = v11++;
*(_BYTE *)(v7 + v4) = aAbcdefghijklmn[(v10 << (5 - v9)) & 0x1F];
}
if ( a3 )
{
while ( (v11 & 7) != 0 )
{
v5 = v11++;
*(_BYTE *)(v7 + v5) = 61;
}
}
*(_BYTE *)(v7 + v11) = 0;
return v7;
}
</details>
Hàm này thì nó là encode base32, nhận diện qua `& 0x1F` ,`while ( (v11 & 7) != 0 )` và `gán = 61 (ký tự =)`
Khi này strlen(Str) = 64 (36 chia 5 = 7.2, làm tròn thành 8 , và mỗi 5 kí tự được encode thành 8 kí tự nên ở đây có Str sẽ có 64 kí tự bao gồm padding `=`)
Vòng lặp for tiếp theo thì sẽ lấy các giá trị i là 0,2,4,5 ... đến 64 tổng cặp 32 lần lặp.

Đến đoạn này rối rối ròi nè anh ơi
kiểu kiểu cái vòng lặp này , đoạn strncpy sẽ là copy 2 byte liên tiếp từ `str` xong dán vào địa chỉ của destination. Lần lượt vị trí 0-1, 2-3,4-5... trong mỗi vòng lặp.
Tiếp tục là `sub_140002753`
```c=
__int64 __fastcall sub_140002753(const char *a1, __int64 a2)
{
size_t v2; // rax
_BYTE v4[96]; // [rsp+20h] [rbp-60h] BYREF
sub_140002420(v4);
v2 = strlen(a1);
sub_14000246E(v4, a1, v2);
return sub_14000250B(v4, a2);
}
```
Nó có thêm 2 hàm con nữa
<details>
<summary> 420 </summary>
__int64 __fastcall sub_140002420(__int64 a1)
{
__int64 result; // rax
*(_DWORD *)(a1 + 64) = 0;
*(_QWORD *)(a1 + 72) = 0;
*(_DWORD *)(a1 + 80) = 1732584193;
*(_DWORD *)(a1 + 84) = -271733879;
*(_DWORD *)(a1 + 88) = -1732584194;
result = a1;
*(_DWORD *)(a1 + 92) = 271733878;
return result;
}
</details>
Nó khởi tạo các hằng số gì đó đó

Chắc là MD5.
<details>
<summary> 50B </summary>
_BYTE *__fastcall sub_14000250B(__int64 a1, __int64 a2)
{
unsigned __int64 v2; // rax
unsigned __int64 v3; // rax
_BYTE *result; // rax
__int64 v5; // [rsp+28h] [rbp-8h]
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
unsigned __int64 v7; // [rsp+28h] [rbp-8h]
unsigned __int64 i; // [rsp+28h] [rbp-8h]
v5 = *(unsigned int *)(a1 + 64);
if ( *(_DWORD *)(a1 + 64) > 0x37u )
{
v7 = v5 + 1;
*(_BYTE *)(a1 + *(unsigned int *)(a1 + 64)) = 0x80;
while ( v7 <= 0x3F )
{
v3 = v7++;
*(_BYTE *)(a1 + v3) = 0;
}
sub_1400016D0(a1, a1);
memset((void *)a1, 0, 0x38u);
}
else
{
v6 = v5 + 1;
*(_BYTE *)(a1 + *(unsigned int *)(a1 + 64)) = 0x80;
while ( v6 <= 0x37 )
{
v2 = v6++;
*(_BYTE *)(a1 + v2) = 0;
}
}
*(_QWORD *)(a1 + 72) += (unsigned int)(8 * *(_DWORD *)(a1 + 64));
*(_WORD *)(a1 + 56) = *(_QWORD *)(a1 + 72);
*(_BYTE *)(a1 + 58) = BYTE2(*(_QWORD *)(a1 + 72));
*(_BYTE *)(a1 + 59) = BYTE3(*(_QWORD *)(a1 + 72));
*(_WORD *)(a1 + 60) = WORD2(*(_QWORD *)(a1 + 72));
*(_BYTE *)(a1 + 62) = BYTE6(*(_QWORD *)(a1 + 72));
*(_BYTE *)(a1 + 63) = HIBYTE(*(_QWORD *)(a1 + 72));
result = (_BYTE *)sub_1400016D0(a1, a1);
for ( i = 0; i <= 3; ++i )
{
*(_BYTE *)(a2 + i) = *(_DWORD *)(a1 + 80) >> (8 * i);
*(_BYTE *)(i + 4 + a2) = *(_DWORD *)(a1 + 84) >> (8 * i);
*(_BYTE *)(i + 8 + a2) = *(_DWORD *)(a1 + 88) >> (8 * i);
result = (_BYTE *)(i + 12 + a2);
*result = *(_DWORD *)(a1 + 92) >> (8 * i);
}
return result;
}
</details>
Em dùng chatGPT đọc hộ những hàm sub đó kèm theo là kết nối logic những việc làm phía trên, thì em thu được kết quả là `nhập flag → mã hóa base32 → chia 2 ký tự → MD5 → ghép lại`
Tìm hiểu thêm thì MD5 sẽ băm 1 dữ liệu bất kì thành chuỗi 16 byte
Cuối cùng là thực hiện phép tính so sánh với mảnh cố định `unk_14001B040`
Với 32 vòng lặp thì băm MD5 được 32*16 = 512 byte
Mảng cố định gồm :

Dù đa phần đoạn MD5 rồi destination... này em tra AI kha khá, nhưng đến bước này em hiểu sơ lược rằng mỗi 16 byte liên tiếp là MD5 của 2 ký tự Base32 .
Em có hướng là với 16 byte `D9 B2 52 86 6A 25 98 6F D8 EC 8A 6C 9B DE D2 75` sẽ là str[0] + str[1]

nhăm nhăm, kiểu kiểu vậy đó, giờ em copy hết 512 byte đó bỏ vô cái tool này nha mấy anh

Thủ công vậy thoai, chứ hỏng biết code...

Cần cù bù thông minh với
```
d9b252866a25986fd8ec8a6c9bded275
c79bda0e66f07bee662a6840b907bf39
8d36b361a4794422c70681586a957350
182be0c5cdcd5072bb1864cdee4d3d6e
accb66f0ecd826aac89065990e1da97f
569b3ced3555bfae63cadb6558a6fcbe
33d2a08eee4c17811940fefbc4e09778
6ba89ab41823af9b65ea5d233031b9f8
e4a59df8b97206109eb4b7f2fe528a4d
ad44ac8c02285f6b9164a08492a78f92
0a5b65725f6a3ba95821cec61dbb46d3
2ff2d45e983c58e96c9ba33d69721ce8
51f581937765890f2a706c77ea8af3cc
a36c71eb239a0231ba0c6e7a4590f062
42adc777f3c2c97005150cb7b86165b6
c23fa9996925b610710d93e28c59a3e2
d5c44258d51659f96279c470ce8185dc
6e029fbfa817dfc458598a5d38a6755f
7c78ebb4c223c96f8e6ccc29f73cc28e
76aa96369abbba52e621bfa83da8e64f
ba7d0834d23b91504bed685901dc3ac5
38f99abcc1d339c277c0669e7bc373c0
0a5b65725f6a3ba95821cec61dbb46d3
853bc55c1041f24b9392069331e65336
f214a7d42e0de5875d55189e01e2e187
7b7cd24ea6f08b711cf4053beac43cc5
64f3bd1741ab8d6ba545a1ae09bb8728
8a6b178d3af0a5a9b2744ca31921d5e2
bfd2343910378a1b537c3c8036a5c07e
2063849717d32dd19e534b77cabac517
2063849717d32dd19e534b77cabac517
2063849717d32dd19e534b77cabac517
```
thì em có được chuỗi base32 là : `JNBVGQ33GN5F6YTSOU3TGX3NMQ2V6NDOMRPWIM3DN5SDGX3CGRZTGMZSPU======`

→ Thì qua bài này, em học được thêm về việc băm MD5 cũng như nhận dạng đặc trưng base32, dù em vẫn chưa nắm rõ được cách băm như nào và cũng phụ thuộc ChatGPT để giúp em hiểu code đang nói gì.
# 4. Medium Antidebug
Ở bài này, em không làm gì cả

Em thử connect IDA với Claude và chạy thử nó ra flag, em vẫn chưa vững việc debug cũng như phải làm gì với các chương trình antidebug. Em vẫn chưa có ý tưởng hay gì về bài này . Sorry anh `Robertowen` vì solved 1 cách hư hỏng như này
---
---
---
---
---
---
---
# 5. flag in wonderland
Bài này cho em file .gb , là 1 file rom game boy.
Tìm hiểu sơ sơ thì file rom game boy nó có tính chất như sau :
+ Toàn bộ code + data là ROM thô
+ Không có section
+ Relocation cũng không có luôn
+ CPU đọc dữ liệu trực tiếp từ ROM
+ Không có `Có .text, .data, .bss` nên khả năng là sẽ không mảng `FLAG` trong code
Em sử dụng `Ghira 10.4` + `Plugin Gameboy` để xem mã giả + mã máy trong file .gb này
Tìm `string` = flag , ref xem hàm nào gọi đó và em tìm được hàm main trong này

```c
void FUN_0391(void)
{
byte bVar1;
undefined2 uVar2;
undefined uVar3;
undefined uVar4;
undefined *puVar5;
undefined *puVar6;
char *pcStack_1a3;
undefined auStack_178 [5];
undefined auStack_173 [7];
undefined auStack_16c [7];
undefined auStack_165 [320];
undefined auStack_25 [2];
undefined auStack_23 [30];
undefined local_5;
pcStack_1a3 = "Enter flag:";
FUN_0200(0x402);
FUN_0b79();
FUN_0f5d();
for (bVar1 = 0; bVar1 < 0x17; bVar1 = bVar1 + 1) {
(&DAT_c0f6)[bVar1] = (&DAT_c0f6)[bVar1] ^ 0xcc;
}
bVar1 = 0;
while( true ) {
if (0x1f < bVar1) break;
auStack_23[bVar1] = *(undefined *)((int)&pcStack_1a3 + (uint)bVar1);
bVar1 = bVar1 + 1;
}
FUN_021e(0x17,&DAT_c0f6);
local_5 = 0x17;
puVar6 = auStack_165;
FUN_02bc(auStack_25,puVar6,0x20);
FUN_021e(0x17,&DAT_c0f6,puVar6,puVar6);
puVar5 = auStack_16c;
FUN_02bc(auStack_16c,0x20,puVar6);
uVar3 = SUB21(puVar5,0);
uVar4 = (undefined)((uint)puVar5 >> 8);
FUN_021e(0x17,&DAT_c0f6,puVar5);
FUN_02bc(auStack_173,CONCAT11(uVar4,uVar3),auStack_173,0x20,0,0x37);
uVar2 = FUN_10ac(auStack_178,&DAT_c0d6,0x20);
if ((byte)((byte)((uint)uVar2 >> 8) | (byte)uVar2) == 0) {
FUN_0200(2,0x405,"Correct!");
}
else {
FUN_0200(2,0x405,&DAT_04d2);
}
do {
FUN_01bd();
} while( true );
}
```
GPT đọc cái `FUN_021e` thì nó là mã hóa RC4
Hiểu sơ là :
>In enter flag
> Đọc input (32 byte)
>Giải mã RC4 key ( Key trong rom (DAT_c0f6) = key thật XOR 0xCC), key này dùng cho RC4
```c
for (bVar1 = 0; bVar1 < 0x17; bVar1 = bVar1 + 1) {
(&DAT_c0f6)[bVar1] = (&DAT_c0f6)[bVar1] ^ 0xcc;}
```
> auStack_23 chứa input , 32 byte
> Phần sau là mã hóa, xong check với mảng DAT_c0d6 rồi in Correct
Hướng đi bây giờ của em là
- tìm mảng DAT_c0d6 : ciphertex 32 byte
- tìm mảng DAT_c0f6 : key RC4 23 byte

Em thấy là nó trống trơn, thì sau 1 hồi suy ngẫm lại, dữ liệu nằm trong ROM gì đó. Nên em nghĩ đến việc dump dữ liệu
Em sử dụng BGB

Em bấm G để tìm từ khóa C0F6 , sau đó đặt `breakpoint`. Sau đó thì nhập kí tự bất kì, enter để chạy đến đoạn đặt `breakpoint` rồi xem dữ liệu của ROM


Tại vị trí em đặt breakpoint thì C0F6 đã được XOR với 0xCC rồi, nên 23 byte tại đây sẽ là key
`74306A3174345F68333472745F6E315F66757233743431` → `t0j1t4_h34rt_n1_fur3t41`
Đoạn enc trong này là C0D6 :`ED4E69BE8449C676D7B70CA3061B4BE2115B4151D737D8B82B3F047D9664A11F`
Từ đây copy main gửi chatGPT + mớ dữ liệu trên
```c
def custom_rc4_decrypt(ciphertext, key, rounds):
# --- KSA: Khởi tạo S-Box y hệt FUN_021e ---
s = list(range(256))
j = 0
for i in range(256):
k_val = key[i % len(key)]
j = (j + s[i] + k_val) & 0xff
cVar3 = s[j]
swap_idx = (k_val + cVar3 + j) & 0xff
s[j] = s[swap_idx]
s[swap_idx] = cVar3
# --- PRGA: Giải mã ngược XOR Chaining ---
plaintext = []
idx_i = 0
idx_j = 0
prev_c = s[0] # Khởi tạo local_c = *param_2
for char_code in ciphertext:
keystream_byte = 0
# Thực hiện rounds lần hoán vị S-box
for _ in range(rounds):
idx_i = (idx_i + 1) & 0xff
idx_j = (idx_j + s[idx_i]) & 0xff
s[idx_i], s[idx_j] = s[idx_j], s[idx_i]
keystream_byte = s[(s[idx_i] + s[idx_j]) & 0xff]
# Công thức giải mã ngược của XOR Chaining:
# P = C_now ^ C_prev ^ Keystream
p_byte = char_code ^ prev_c ^ keystream_byte
plaintext.append(p_byte)
prev_c = char_code # Feedback là byte ciphertext vừa nhận
return bytes(plaintext)
def solve():
key = b"t0j1t4_h34rt_n1_fur3t41"
target = bytes.fromhex("ED4E69BE8449C676D7B70CA3061B4BE2115B4151D737D8B82B3F047D9664A11F")
# Giải mã ngược qua 3 tầng
# Tầng 3 (0x37 rounds)
step3 = custom_rc4_decrypt(target, key, 0x37)
# Tầng 2 (0x20 rounds)
# Lưu ý: Cần reset lại S-Box ban đầu cho mỗi tầng vì FUN_0391 gọi lại FUN_021e
step2 = custom_rc4_decrypt(step3, key, 0x20)
# Tầng 1 (0x17 rounds)
final_flag = custom_rc4_decrypt(step2, key, 0x17)
print("--- DECODED FLAG ---")
print(final_flag.decode(errors='ignore'))
solve()
```
nần ná na na
