Post

CyberXbytes CTF 2025

CyberXbytes CTF 2025

Introduction

This write-up covers my solutions to the Reverse Engineering challenges from CyberXBytes CTF 2025, held on November 15, 2025.

timmy wish

Description: its a curse… its a storm full of whispers…Fairies with shadowed grins, hacks that hunger, what did the wand steal?

First, I load the binary into Ghidra and notice that the entry function refers to this function after printing a story.

Alt

This loop:

1
2
3
4
5
local_9 = 0x42;
for (local_18 = 0; local_18 < 0xe3; local_18++) {
    DAT_00404040[i] = DAT_00402020[i] ^ local_9;
    local_9 = local_9 * 0x11 + 0x5e;
}
  • Reads encrypted bytes from DAT_00402020
  • XOR-decodes them using a rolling key
  • Stores decoded bytes into DAT_00404040

To read the decoded flag in the memory after the function execution, load the file into GDB and break the execution at the function FUN_00401126

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(gdb) break *0x0401126
Breakpoint 2 at 0x401126
(gdb) run
In the dim glow of his computer screen, young hacker Timmy tinkered with forbidden code.
One stormy night, he stumbled upon an ancient artifact: a glowing wand etched with binary runes.
"If only I had the ultimate hacking tool," Timmy whispered, waving the wand.
A puff of pink smoke filled the room, and a cheerful fairy appeared. "Your wish is my command!" she chimed.
The fairy waved her wand, and lines of enchanted code materialized, forming a virtual machine of pure magic.
Timmy ran the code, excitement bubbling. The VM hummed to life, granting his wish.
But as the fairy smiled, her eyes flickered red for a split second—devilish eyes, hiding a sinister gleam.
Timmy blinked—had he imagined it? The tool connected to a distant server, whispering secrets.
Unbeknownst to Timmy, the fairy was no benevolent sprite, but a vessel for ancient malice.
She pulled strings from the shadows, turning his hack into a C2 beacon, eyes ever watchful.
And so, the boy who dreamed of code became the cartoon we know, but the demon's whisper lingered...
The end.

Breakpoint 2, 0x0000000000401126 in ?? ()

Run the finish A command that will execute until the current function returns and then give you control back in the caller. After finish completes, dump the memory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(gdb) finish 
Run till exit from #0  0x0000000000401126 in ?? ()
0x0000000000401a25 in ?? ()
(gdb) x/300bx 0x404040
0x404040:       0x19    0x42    0x00    0x01    0x01    0x05    0x06    0x00
0x404048:       0x01    0x01    0x3b    0x05    0x06    0x01    0x01    0x01
0x404050:       0x20    0x05    0x06    0x02    0x01    0x01    0x27    0x05
0x404058:       0x06    0x03    0x01    0x01    0x30    0x05    0x06    0x04
0x404060:       0x01    0x01    0xaa    0x17    0xaa    0x18    0x2b    0x00
0x404068:       0x16    0x05    0x03    0x01    0x1a    0x05    0x06    0x05
0x404070:       0x01    0x01    0x20    0x05    0x06    0x06    0x01    0x01
0x404078:       0x3b    0x05    0x06    0x07    0x01    0x01    0x36    0x05
0x404080:       0x06    0x08    0x01    0x01    0x27    0x05    0x06    0x09
0x404088:       0x01    0x01    0xaa    0x17    0xaa    0x18    0x53    0x00
0x404090:       0x16    0x05    0x03    0x01    0x31    0x05    0x06    0x0a
0x404098:       0x01    0x01    0x39    0x05    0x06    0x0b    0x01    0x01
0x4040a0:       0x06    0x05    0x06    0x0c    0x01    0x01    0x71    0x05
0x4040a8:       0x06    0x0d    0x01    0x01    0x34    0x05    0x06    0x0e
0x4040b0:       0x01    0x01    0xaa    0x17    0xaa    0x18    0x7b    0x00
0x4040b8:       0x16    0x05    0x03    0x01    0x73    0x05    0x06    0x0f
0x4040c0:       0x01    0x01    0x2e    0x05    0x06    0x10    0x01    0x01
0x4040c8:       0x73    0x05    0x06    0x11    0x01    0x01    0x31    0x05
0x4040d0:       0x06    0x12    0x01    0x01    0x2a    0x05    0x06    0x13
0x4040d8:       0x01    0x01    0xaa    0x17    0xaa    0x18    0xa3    0x00
0x4040e0:       0x16    0x05    0x03    0x01    0x1d    0x05    0x06    0x14
0x4040e8:       0x01    0x01    0x27    0x05    0x06    0x15    0x01    0x01
0x4040f0:       0x3b    0x05    0x06    0x16    0x01    0x01    0x27    0x05
0x4040f8:       0x06    0x17    0x01    0x01    0x31    0x05    0x06    0x18
0x404100:       0x01    0x01    0xaa    0x17    0xaa    0x18    0xcb    0x00
0x404108:       0x16    0x05    0x03    0x01    0x1d    0x05    0x06    0x19
0x404110:       0x01    0x01    0x21    0x05    0x06    0x1a    0x01    0x01
0x404118:       0x70    0x05    0x06    0x1b    0x01    0x01    0x3f    0x05
0x404120:       0x06    0x1c    0x01    0x00    0x00    0x00    0x00    0x00
0x404128:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x404130:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x404138:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x404140:       0x43    0x79    0x62    0x65    0x72    0x58    0x62    0x79
0x404148:       0x74    0x65    0x73    0x7b    0x44    0x33    0x76    0x31
0x404150:       0x6c    0x31    0x73    0x68    0x5f    0x65    0x79    0x65
0x404158:       0x73    0x5f    0x63    0x32    0x7d    0x00    0x00    0x00
0x404160:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x404168:       0x00    0x00    0x00    0x00

Everything before 0x404140 looks like structured VM instruction bytes, and the decoded flag is in these three addresses

1
2
3
0x404148:       0x74    0x65    0x73    0x7b    0x44    0x33    0x76    0x31
0x404150:       0x6c    0x31    0x73    0x68    0x5f    0x65    0x79    0x65
0x404158:       0x73    0x5f    0x63    0x32    0x7d    0x00    0x00    0x00

decode the hex values

1
2
hex_bytes = "43 79 62 65 72 58 62 79 74 65 73 7b 44 33 76 31 6c 31 73 68 5f 65 79 65 73 5f 63 32 7d"
print(bytes.fromhex(hex_bytes).decode())
1
2
$ python3 decode.py
CyberXbytes{D3v1l1sh_eyes_c2}

notpolite

Description: can you charm the beast, or does it devour?

1
2
3
4
5
6
$ file notpolite                   
notpolite: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=a0113d1c20566f9e4f0a65042365242e09dc7970, for GNU/Linux 3.2.0, stripped
$  ./notpolite      
The algorithm was not polite
but handsome
and yeah, I think everyone can do it—do it, do it    

This is the entry function. There is a call to the function FUN_00401126

Alt

Function FUN_00401126

Alt

The concept same as the first challenge, the program reads the encoded flag from DAT_00402020 XORs them with a pseudo-random byte, and writes the result to the memory address DAT_00404040.

Load the file into gdb and break the execution at the function FUN_00401126

1
2
3
4
5
6
7
8
9
10
11
(gdb) b *0x0401126
Breakpoint 1 at 0x401126
(gdb) run
The algorithm was not polite
but handsome
and yeah, I think everyone can do it—do it, do it

Breakpoint 1, 0x0000000000401126 in ?? ()
(gdb) finish 
Run till exit from #0  0x0000000000401126 in ?? ()
0x0000000000401400 in ?? ()

Examine 509 bytes of memory starting at the address 0x404040

1
(gdb) x/300bx 0x404040
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
0x404040:       0x01    0x43    0x06    0x00    0x08    0x01    0x79    0x06
0x404048:       0x01    0x08    0x01    0x62    0x06    0x02    0x08    0x01
0x404050:       0x65    0x06    0x03    0x08    0x01    0x72    0x06    0x04
0x404058:       0x08    0x01    0x58    0x06    0x05    0x08    0x01    0x62
0x404060:       0x06    0x06    0x08    0x01    0x79    0x06    0x07    0x08
0x404068:       0x01    0x74    0x06    0x08    0x08    0x01    0x65    0x06
0x404070:       0x09    0x08    0x01    0x73    0x06    0x0a    0x08    0x01
0x404078:       0x7b    0x06    0x0b    0x08    0x01    0x65    0x06    0x0c
0x404080:       0x08    0x01    0x61    0x06    0x0d    0x08    0x01    0x73
0x404088:       0x06    0x0e    0x08    0x01    0x79    0x06    0x0f    0x08
0x404090:       0x01    0x5f    0x06    0x10    0x08    0x01    0x66    0x06
0x404098:       0x11    0x08    0x01    0x6c    0x06    0x12    0x08    0x01
0x4040a0:       0x61    0x06    0x13    0x08    0x01    0x67    0x06    0x14
0x4040a8:       0x08    0x01    0x7d    0x06    0x15    0x08    0x01    0x43
0x4040b0:       0x06    0x00    0x09    0x01    0x79    0x06    0x01    0x09
0x4040b8:       0x01    0x62    0x06    0x02    0x09    0x01    0x65    0x06
0x4040c0:       0x03    0x09    0x01    0x72    0x06    0x04    0x09    0x01
0x4040c8:       0x58    0x06    0x05    0x09    0x01    0x62    0x06    0x06
0x4040d0:       0x09    0x01    0x79    0x06    0x07    0x09    0x01    0x74
0x4040d8:       0x06    0x08    0x09    0x01    0x65    0x06    0x09    0x09
0x4040e0:       0x01    0x73    0x06    0x0a    0x09    0x01    0x7b    0x06
0x4040e8:       0x0b    0x09    0x01    0x74    0x06    0x0c    0x09    0x01
0x4040f0:       0x6f    0x06    0x0d    0x09    0x01    0x6f    0x06    0x0e
0x4040f8:       0x09    0x01    0x5f    0x06    0x0f    0x09    0x01    0x73
0x404100:       0x06    0x10    0x09    0x01    0x69    0x06    0x11    0x09
0x404108:       0x01    0x6d    0x06    0x12    0x09    0x01    0x70    0x06
0x404110:       0x13    0x09    0x01    0x6c    0x06    0x14    0x09    0x01
0x404118:       0x65    0x06    0x15    0x09    0x01    0x7d    0x06    0x16
0x404120:       0x09    0x10    0x64    0x00    0x00    0x00    0x16    0x18
0x404128:       0xe6    0x00    0x01    0x43    0x06    0x00    0x10    0x01
0x404130:       0x79    0x06    0x01    0x10    0x01    0x62    0x06    0x02
0x404138:       0x10    0x01    0x65    0x06    0x03    0x10    0x01    0x72
0x404140:       0x06    0x04    0x10    0x01    0x58    0x06    0x05    0x10
0x404148:       0x01    0x62    0x06    0x06    0x10    0x01    0x79    0x06
0x404150:       0x07    0x10    0x01    0x74    0x06    0x08    0x10    0x01
0x404158:       0x65    0x06    0x09    0x10    0x01    0x73    0x06    0x0a
0x404160:       0x10    0x01    0x7b    0x06    0x0b    0x10    0x01    0x68
0x404168:       0x06    0x0c    0x10    0x01    0x6d    0x06    0x0d    0x10
--Type <RET> for more, q to quit, c to continue without paging--

Type C to continue dumping the memory content

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0x404170:       0x01    0x6d    0x06    0x0e    0x10    0x01    0x6d    0x06
0x404178:       0x0f    0x10    0x01    0x6d    0x06    0x10    0x10    0x01
0x404180:       0x6d    0x06    0x11    0x10    0x01    0x5f    0x06    0x12
0x404188:       0x10    0x01    0x69    0x06    0x13    0x10    0x01    0x5f
0x404190:       0x06    0x14    0x10    0x01    0x74    0x06    0x15    0x10
0x404198:       0x01    0x68    0x06    0x16    0x10    0x01    0x69    0x06
0x4041a0:       0x17    0x10    0x01    0x6e    0x06    0x18    0x10    0x01
0x4041a8:       0x6b    0x06    0x19    0x10    0x01    0x5f    0x06    0x1a
0x4041b0:       0x10    0x01    0x65    0x06    0x1b    0x10    0x01    0x76
0x4041b8:       0x06    0x1c    0x10    0x01    0x65    0x06    0x1d    0x10
0x4041c0:       0x01    0x72    0x06    0x1e    0x10    0x01    0x79    0x06
0x4041c8:       0x1f    0x10    0x01    0x31    0x06    0x20    0x10    0x01
0x4041d0:       0x5f    0x06    0x21    0x10    0x01    0x63    0x06    0x22
0x4041d8:       0x10    0x01    0x61    0x06    0x23    0x10    0x01    0x6e
0x4041e0:       0x06    0x24    0x10    0x01    0x5f    0x06    0x25    0x10
0x4041e8:       0x01    0x64    0x06    0x26    0x10    0x01    0x6f    0x06
0x4041f0:       0x27    0x10    0x01    0x69    0x06    0x28    0x10    0x01
0x4041f8:       0x74    0x06    0x29    0x10    0x01    0x64    0x06    0x2a
0x404200:       0x10    0x01    0x6f    0x06    0x2b    0x10    0x01    0x69
0x404208:       0x06    0x2c    0x10    0x01    0x74    0x06    0x2d    0x10
0x404210:       0x01    0x64    0x06    0x2e    0x10    0x01    0x6f    0x06
0x404218:       0x2f    0x10    0x01    0x69    0x06    0x30    0x10    0x01
0x404220:       0x74    0x06    0x31    0x10    0x01    0x67    0x06    0x32
0x404228:       0x10    0x01    0x6f    0x06    0x33    0x10    0x01    0x6f
0x404230:       0x06    0x34    0x10    0x01

When I tried to hex decode them, it resulted in garbled text for the last part

1
2
3
4
5
6
7
hex_val = """
01430600080179060108016206020801650603080172060408015806050801620606080179060708017406080801650609080173060A08017B060B080165060C080161060D080173060E080179060F08015F0610080166061108016C06120801610613080167061408017D06150801430600090179060109016206020901650603090172060409015806050901620606090179060709017406080901650609090173060A09017B060B090174060C09016F060D09016F060E09015F060F0901730610090169061109016D0612090170061309016C0614090165061509017D061609016D060E10016D060F10016D061010016D061110015F0612100169061310015F061410017406151001680616100169061710016E061810016B061910015F061A100165061B100176061C100165061D100172061E100179061F100131062010015F06211001630622100161062310016E062410015F0625100164062610016F062710016906281001740629100164062A10016F062B100169062C100174062D100164062E10016F062F10016906301001740631100167063210016F063310016F0634100101
"""
byte_data = bytes.fromhex(hex_val)

ascii_string = "".join(chr(b) for b in byte_data if 32 <= b <= 126)
print(ascii_string)
1
CyberXbytes{easy_flag}CyberXbytes{too_simple}mmmm_i_think_every1 _!c"a#n$_%d&o'i(t)d*o+i,t-d.o/i0t1g2o3o4

The reason we have two decoy flags and the last part results in random strings. It’s because the program writes the decoded strings into three memory regions using the VM instructions to write bytecodes into the target memory address, which is not as obvious as the first challenge. From the memory dump, we note that each 5-byte block encodes one character and its position and a group.

1
[ opcode | value | opcode2 | index | program_id ]

Example: Memory address 0x404040

1
0x01    0x43    0x06    0x00    0x08
  • The first byte (0x01) is always the same → VM instruction type.
  • The second byte (0x43) is an ASCII value (0x43 = ‘C’).
  • The third byte (0x06) is constant → separator or type.
  • The fourth byte (00, 01, 02, …) increases → index in memory.
  • The fifth byte (10, 09, 08) changes in groups.

The two flags were clear because the bytecode was sorted, but not for the last one

  • First flag ends at address: 0x4040a8 group id 0x08
  • Second flag: starts after the first flag and ends at address 0x404120 group id 0x09
  • As for the last part, this one needs to be sorted.

Before sorting the last part, we need to continue dumping the memory after 0x404230

1
2
3
4
5
(gdb) x/300bx 0x404230
0x404230:       0x06    0x34    0x10    0x01    0x64    0x06    0x35    0x10
0x404238:       0x01    0x7d    0x06    0x36    0x10    0x00    0x00    0x00
0x404240:       0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
.....

The last part ends at 0x404238 the rest are zero. Now we need to write a Python code to sort the bytecodes of the last part based on the program ID (groups: 10, 9 , 8) and the index of the ASCII character.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/usr/bin/env python3

raw = [
0x01,0x43,0x06,0x00,0x08,0x01,0x79,0x06,0x01,0x08,0x01,0x62,0x06,0x02,0x08,
0x01,0x65,0x06,0x03,0x08,0x01,0x72,0x06,0x04,0x08,0x01,0x58,0x06,0x05,0x08,
0x01,0x62,0x06,0x06,0x08,0x01,0x79,0x06,0x07,0x08,0x01,0x74,0x06,0x08,0x08,
0x01,0x65,0x06,0x09,0x08,0x01,0x73,0x06,0x0a,0x08,0x01,0x7b,0x06,0x0b,0x08,
0x01,0x65,0x06,0x0c,0x08,0x01,0x61,0x06,0x0d,0x08,0x01,0x73,0x06,0x0e,0x08,
0x01,0x79,0x06,0x0f,0x08,0x01,0x5f,0x06,0x10,0x08,0x01,0x66,0x06,0x11,0x08,
0x01,0x6c,0x06,0x12,0x08,0x01,0x61,0x06,0x13,0x08,0x01,0x67,0x06,0x14,0x08,
0x01,0x7d,0x06,0x15,0x08,

0x01,0x43,0x06,0x00,0x09,0x01,0x79,0x06,0x01,0x09,0x01,0x62,0x06,0x02,0x09,
0x01,0x65,0x06,0x03,0x09,0x01,0x72,0x06,0x04,0x09,0x01,0x58,0x06,0x05,0x09,
0x01,0x62,0x06,0x06,0x09,0x01,0x79,0x06,0x07,0x09,0x01,0x74,0x06,0x08,0x09,
0x01,0x65,0x06,0x09,0x09,0x01,0x73,0x06,0x0a,0x09,0x01,0x7b,0x06,0x0b,0x09,
0x01,0x74,0x06,0x0c,0x09,0x01,0x6f,0x06,0x0d,0x09,0x01,0x6f,0x06,0x0e,0x09,
0x01,0x5f,0x06,0x0f,0x09,0x01,0x73,0x06,0x10,0x09,0x01,0x69,0x06,0x11,0x09,
0x01,0x6d,0x06,0x12,0x09,0x01,0x70,0x06,0x13,0x09,0x01,0x6c,0x06,0x14,0x09,
0x01,0x65,0x06,0x15,0x09,0x01,0x7d,0x06,0x16,0x09,

0x01,0x43,0x06,0x00,0x10,0x01,0x79,0x06,0x01,0x10,0x01,0x62,0x06,0x02,0x10,
0x01,0x65,0x06,0x03,0x10,0x01,0x72,0x06,0x04,0x10,0x01,0x58,0x06,0x05,0x10,
0x01,0x62,0x06,0x06,0x10,0x01,0x79,0x06,0x07,0x10,0x01,0x74,0x06,0x08,0x10,
0x01,0x65,0x06,0x09,0x10,0x01,0x73,0x06,0x0a,0x10,0x01,0x7b,0x06,0x0b,0x10,
0x01,0x68,0x06,0x0c,0x10,0x01,0x6d,0x06,0x0d,0x10,0x01,0x6d,0x06,0x0e,0x10,
0x01,0x6d,0x06,0x0f,0x10,0x01,0x6d,0x06,0x10,0x10,0x01,0x6d,0x06,0x11,0x10,
0x01,0x5f,0x06,0x12,0x10,0x01,0x69,0x06,0x13,0x10,0x01,0x5f,0x06,0x14,0x10,
0x01,0x74,0x06,0x15,0x10,0x01,0x68,0x06,0x16,0x10,0x01,0x69,0x06,0x17,0x10,
0x01,0x6e,0x06,0x18,0x10,0x01,0x6b,0x06,0x19,0x10,0x01,0x5f,0x06,0x1a,0x10,
0x01,0x65,0x06,0x1b,0x10,0x01,0x76,0x06,0x1c,0x10,0x01,0x65,0x06,0x1d,0x10,
0x01,0x72,0x06,0x1e,0x10,0x01,0x79,0x06,0x1f,0x10,0x01,0x31,0x06,0x20,0x10,
0x01,0x5f,0x06,0x21,0x10,0x01,0x63,0x06,0x22,0x10,0x01,0x61,0x06,0x23,0x10,
0x01,0x6e,0x06,0x24,0x10,0x01,0x5f,0x06,0x25,0x10,0x01,0x64,0x06,0x26,0x10,
0x01,0x6f,0x06,0x27,0x10,0x01,0x69,0x06,0x28,0x10,0x01,0x74,0x06,0x29,0x10,
0x01,0x64,0x06,0x2a,0x10,0x01,0x6f,0x06,0x2b,0x10,0x01,0x69,0x06,0x2c,0x10,
0x01,0x74,0x06,0x2d,0x10,0x01,0x64,0x06,0x2e,0x10,0x01,0x6f,0x06,0x2f,0x10,
0x01,0x69,0x06,0x30,0x10,0x01,0x74,0x06,0x31,0x10,0x01,0x67,0x06,0x32,0x10,
0x01,0x6f,0x06,0x33,0x10,0x01,0x6f,0x06,0x34,0x10,0x01,0x64,0x06,0x35,0x10,
0x01,0x7d,0x06,0x36,0x10
]

groups = {}
i = 0
while i < len(raw):
    if raw[i] == 1:
        ch = chr(raw[i+1])
        pos = raw[i+3]
        group = raw[i+4]
        groups.setdefault(group, {})[pos] = ch
        i += 5
    else:
        i += 1

for g in sorted(groups):
    s = "".join(groups[g][i] for i in sorted(groups[g]))
    print(f"Group {hex(g)}:", s)
1
2
3
4
python3 emulater2.py
Group 0x8: CyberXbytes{easy_flag}
Group 0x9: CyberXbytes{too_simple}
Group 0x10: CyberXbytes{hmmmmm_i_think_every1_can_doitdoitdoitgood}

The real flag is the one in the group 0x10

VeryPolite

Description: consider one thing. it’s very polite challenge.

1
2
file verypolite                   
verypolite: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=97c30421475a8c8879f8a3730996e8e1da643027, for GNU/Linux 3.2.0, stripped

The program does not print anything and enters a loop state if no arguments are passed.

Here is the entry function, which calls FUN_00404140 with a function pointer to FUN_004015c0

Alt

This is the function FUN_00401910that calculates the flag. I added comments and changed the code to make it more readable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// ------------------------------------------------------------
// Decrypts 4 blocks of encoded data into a continuous output buffer.
// Each block has:
//   - A size (number of 4-byte integers)
//   - A starting XOR key
//   - A pointer to encoded uint32 values
//
// Output is written sequentially to param_out and null-terminated.
// ------------------------------------------------------------
void decode_flag(uint8_t *out_buffer)
{
    int block_index = 0;
    int total_bytes_written = 0;
    uint8_t *write_ptr = out_buffer;

    // Iterate over 4 encrypted blocks
    do {
        // Number of uint32 values in this block (85 per dump)
        int block_len = DAT_004790f0[block_index];

        // Starting XOR key for this block (0x11, 0x22, 0x33, 0x44)
        uint8_t xor_key = DAT_004790ec[block_index];

        // Pointer to the encoded uint32 block
        uint32_t *encoded_ptr = (uint32_t *)PTR_DAT_004a1e40[block_index];

        if (block_len > 0)
        {
            uint8_t *cur_out = write_ptr;

            // Process block_len 32-bit integers
            for (int i = 0; i < block_len; i++)
            {
                uint32_t value = encoded_ptr[i];

                // Extract each byte of the 32-bit value
                for (int byte_index = 0; byte_index < 4; byte_index++)
                {
                    uint8_t extracted_byte = (value >> (byte_index * 8)) & 0xFF;

                    // XOR with current key
                    uint8_t decoded = extracted_byte ^ xor_key;

                    // Write decoded byte to output
                    cur_out[byte_index] = decoded;

                    // Increment key for next byte
                    xor_key++;
                }

                // Move output pointer forward by 4 bytes
                cur_out += 4;
            }

            // Move the main write pointer forward by total bytes processed
            write_ptr += block_len * 4;
        }

        // Track total bytes written, for adding final '\0'
        total_bytes_written += block_len * 4;
        block_index++;

    } while (block_index != 4);

    // Null-terminate output string
    out_buffer[total_bytes_written] = '\0';
}

From the code, the structure of the algorithm is as follows: The program iterates over 4 encrypted blocks, each block contains:

  • DAT_004790f0 contain the size of each block
  • DAT_004790ec contain starting xor key
  • PTR_DAT_004a1e40 contains a pointer to the actual encrypted blocks.

We need to read these three memory dumps to calculate the final flag. Load the binary into gdb and extract:

  • DAT_004790f0 (4 integers)
  • DAT_004790ec (4 bytes)
  • PTR_DAT_004a1e40 (4 pointers) each pointer points to iVar1 * 4 bytes of encrypted data
1
2
3
4
5
6
7
(gdb) x/4wd 0x004790f0
0x4790f0:      55   55  55      55
(gdb) x/4bx 0x004790ec
0x4790ec:      0x11 0x22        0x33    0x44
(gdb) x/4gx 0x004a1e40
0x4a1e40:      0x00000000004793a0       0x00000000004792c0
0x4a1e50:      0x00000000004791e0       0x0000000000479100

Extract the data pointed by each pointer (Each block has 55(size)* 4 = 220 bytes.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
(gdb) x/220bx 0x4793a0 # block 0
0x4793a0:      0x3a 0x39        0x38    0x3f 0x3e  0x3d 0x3c    0x33
0x4793a8:      0x32 0x31        0x30    0x37 0x36  0x35 0x34    0x0b
0x4793b0:      0x0a 0x09        0x08    0x0f 0x0e  0x0d 0x0c    0x03
0x4793b8:      0x02 0x01        0x00    0x07 0x06  0x05 0x04    0x1b
0x4793c0:      0x1a 0x19        0x18    0x1f 0x1e  0x1d 0x1c    0x13
0x4793c8:      0x12 0x11        0x10    0x17 0x16  0x15 0x14    0x6b
0x4793d0:      0x6a 0x69        0x68    0x6f 0x6e  0x6d 0x6c    0x63
0x4793d8:      0x62 0x61        0x60    0x67 0x66  0x65 0x64    0x7b
0x4793e0:      0x7a 0x79        0x78    0x7f 0x7e  0x7d 0x7c    0x73
0x4793e8:      0x72 0x71        0x70    0x77 0x76  0x75 0x74    0x4b
0x4793f0:      0x4a 0x49        0x48    0x4f 0x4e  0x4d 0x4c    0x43
0x4793f8:      0x42 0x41        0x40    0x47 0x46  0x45 0x44    0x5b
0x479400:      0x5a 0x59        0x58    0x5f 0x5e  0x5d 0x5c    0x53
0x479408:      0x52 0x51        0x50    0x57 0x56  0x55 0x54    0xab
0x479410:      0xaa 0xa9        0xa8    0xaf 0xab  0xab 0xaa    0xa5
0x479418:      0xa4 0xa7        0xa5    0xa1 0xa0  0xa3 0xa1    0xbd
0x479420:      0xbc 0xbf        0xbe    0xb9 0xb8  0xbb 0xba    0xb6
0x479428:      0xb4 0xb7        0xb6    0xb1 0xb0  0xb0 0xb2    0x8d
0x479430:      0x8c 0x8f        0x8e    0x89 0x88  0x8b 0x8a    0x85
0x479438:      0x87 0x81        0x80    0x87 0x86  0x85 0x84    0x9b
0x479440:      0x9a 0x99        0x98    0x9a 0x98  0x9b 0x9a    0x95
0x479448:      0x94 0x97        0x96    0x91 0x90  0x93 0x92    0xed
0x479450:      0xec 0xef        0xee    0xe9 0xe8  0xeb 0xea    0xe5
0x479458:      0xe4 0xe7        0xe6    0xe1 0xe0  0xe3 0xe2    0xfd
0x479460:      0xfc 0xff        0xfe    0xf9 0xf8  0xfb 0xfa    0xf5
0x479468:      0xf4 0xf7        0xf6    0xf1 0xf0  0xf3 0xf2    0xcd
0x479470:      0xcc 0xcf        0xcd    0xcf 0xce  0xcd 0xcc    0xc3
0x479478:      0xc2 0xc1        0xc0    0xc7
(gdb) x/220bx 0x4792c0 # block 1
0x4792c0:      0x09 0x08        0x0f    0x0e 0x0d  0x0c 0x03    0x02
0x4792c8:      0x01 0x00        0x07    0x06 0x05  0x04 0x1b    0x1a
0x4792d0:      0x19 0x18        0x1f    0x1e 0x1d  0x1c 0x13    0x12
0x4792d8:      0x11 0x10        0x17    0x16 0x15  0x14 0x6b    0x6a
0x4792e0:      0x69 0x68        0x6f    0x6e 0x6d  0x6c 0x63    0x62
0x4792e8:      0x61 0x60        0x67    0x66 0x65  0x64 0x7b    0x7a
0x4792f0:      0x79 0x78        0x7f    0x7e 0x7d  0x7c 0x73    0x72
0x4792f8:      0x71 0x70        0x72    0x70 0x73  0x72 0x4d    0x4c
0x479300:      0x4f 0x4e        0x49    0x48 0x4b  0x4a 0x45    0x44
0x479308:      0x47 0x46        0x41    0x40 0x43  0x42 0x5d    0x5c
0x479310:      0x5f 0x5e        0x59    0x58 0x5b  0x5a 0x55    0x54
0x479318:      0x57 0x56        0x51    0x50 0x53  0x52 0xad    0xac
0x479320:      0xaf 0xae        0xa9    0xa8 0xab  0xaa 0xa5    0xa4
0x479328:      0xa7 0xa6        0xa1    0xa0 0xa3  0xa2 0xbd    0xbc
0x479330:      0xbf 0xbe        0xb9    0xb8 0xbb  0xba 0xb5    0xb4
0x479338:      0xb7 0xb6        0xb2    0xb6 0xb5  0xb4 0x8b    0x8a
0x479340:      0x89 0x88        0x8f    0x8e 0x8d  0x8c 0x83    0x82
0x479348:      0x81 0x80        0x87    0x86 0x85  0x84 0x9b    0x9a
0x479350:      0x99 0x98        0x9f    0x9e 0x9d  0x9c 0x93    0x92
0x479358:      0x91 0x90        0x97    0x96 0x95  0x94 0xeb    0xea
0x479360:      0xe9 0xe8        0xef    0xee 0xed  0xe9 0xe3    0xe2
0x479368:      0xe1 0xe0        0xe7    0xe6 0xe0  0xe4 0xfb    0xfa
0x479370:      0xf9 0xf8        0xff    0xfe 0xfd  0xfc 0xf3    0xf2
0x479378:      0xf1 0xf0        0xf7    0xf6 0xf5  0xf4 0xcb    0xca
0x479380:      0xc9 0xc8        0xca    0xc8 0xcb  0xca 0xc5    0xc4
0x479388:      0xc7 0xc6        0xc1    0xc0 0xc3  0xc2 0xdd    0xdc
0x479390:      0xdf 0xde        0xd9    0xd8 0xdb  0xda 0xd5    0xd4
0x479398:      0xd7 0xd6        0xd1    0xd0
(gdb) x/220bx 0x4791e0 # block 2
0x4791e0:      0x1e 0x19        0x1b    0x1b 0x1a  0x15 0x14    0x17
0x4791e8:      0x16 0x11        0x10    0x13 0x12  0x6d 0x6c    0x6f
0x4791f0:      0x6e 0x69        0x68    0x6b 0x6a  0x65 0x64    0x67
0x4791f8:      0x66 0x61        0x60    0x63 0x62  0x7d 0x7c    0x7f
0x479200:      0x7e 0x79        0x78    0x7b 0x7a  0x75 0x74    0x77
0x479208:      0x76 0x71        0x70    0x73 0x72  0x4d 0x4c    0x4f
0x479210:      0x4d 0x4f        0x4e    0x4d 0x4c  0x43 0x42    0x41
0x479218:      0x40 0x47        0x46    0x45 0x44  0x5b 0x5a    0x59
0x479220:      0x58 0x5f        0x5e    0x5d 0x5c  0x53 0x52    0x51
0x479228:      0x50 0x57        0x56    0x55 0x54  0xab 0xaa    0xa9
0x479230:      0xa8 0xaf        0xae    0xad 0xac  0xa3 0xa2    0xa1
0x479238:      0xa0 0xa7        0xa6    0xa5 0xa4  0xbb 0xba    0xb9
0x479240:      0xb8 0xba        0xb8    0xbb 0xba  0xb6 0xb2    0xb1
0x479248:      0xb0 0xb7        0xb6    0xb5 0xb4  0x8b 0x8a    0x89
0x479250:      0x88 0x8f        0x8e    0x8d 0x8c  0x83 0x82    0x84
0x479258:      0x86 0x81        0x80    0x83 0x82  0x9d 0x9c    0x9f
0x479260:      0x9e 0x99        0x98    0x9b 0x9a  0x95 0x94    0x97
0x479268:      0x96 0x91        0x90    0x93 0x92  0xed 0xec    0xef
0x479270:      0xee 0xe9        0xe8    0xeb 0xea  0xe5 0xe4    0xe7
0x479278:      0xe6 0xe1        0xe0    0xe3 0xe2  0xfd 0xfc    0xff
0x479280:      0xfe 0xf9        0xf8    0xfb 0xfa  0xf5 0xf4    0xf7
0x479288:      0xf6 0xf1        0xf0    0xf3 0xf2  0xcd 0xcc    0xcf
0x479290:      0xce 0xc9        0xc8    0xcb 0xca  0xc5 0xc4    0xc7
0x479298:      0xc5 0xc7        0xc6    0xc5 0xc4  0xdb 0xda    0xd9
0x4792a0:      0xd8 0xdf        0xde    0xdd 0xdc  0xd3 0xd2    0xd1
0x4792a8:      0xd0 0xd7        0xd6    0xd5 0xd4  0x2b 0x2a    0x29
0x4792b0:      0x28 0x2f        0x2e    0x2d 0x2c  0x23 0x22    0x21
0x4792b8:      0x20 0x27        0x26    0x25
(gdb) x/220bx 0x479100 # block 3
0x479100:      0x6f 0x6e        0x6d    0x6c 0x63  0x62 0x61    0x60
0x479108:      0x67 0x66        0x65    0x64 0x7b  0x7a 0x79    0x78
0x479110:      0x7f 0x7e        0x7d    0x7c 0x73  0x72 0x71    0x70
0x479118:      0x77 0x73        0x73    0x72 0x4d  0x4c 0x4f    0x4e
0x479120:      0x49 0x48        0x4b    0x4a 0x45  0x44 0x47    0x46
0x479128:      0x41 0x40        0x43    0x42 0x5d  0x5c 0x5f    0x5e
0x479130:      0x59 0x58        0x5b    0x5a 0x55  0x54 0x57    0x56
0x479138:      0x51 0x50        0x53    0x52 0xad  0xac 0xaf    0xae
0x479140:      0xa9 0xa8        0xab    0xaa 0xa5  0xa4 0xa7    0xa6
0x479148:      0xa1 0xa0        0xa3    0xa2 0xbd  0xbc 0xbf    0xbe
0x479150:      0xb9 0xb8        0xbb    0xba 0xb5  0xb7 0xb1    0xb0
0x479158:      0xb7 0xb6        0xb5    0xb4 0x8b  0x8a 0x89    0x88
0x479160:      0x8f 0x8e        0x8d    0x8c 0x83  0x82 0x81    0x80
0x479168:      0x87 0x86        0x85    0x84 0x9b  0x9a 0x99    0x98
0x479170:      0x9f 0x9e        0x9d    0x9c 0x93  0x92 0x91    0x90
0x479178:      0x97 0x96        0x95    0x94 0xeb  0xea 0xe9    0xe8
0x479180:      0xef 0xee        0xed    0xec 0xe3  0xe2 0xe1    0xe0
0x479188:      0xe7 0xe6        0xe5    0xe4 0xfb  0xfa 0xf9    0xf8
0x479190:      0xff 0xfe        0xfd    0xfc 0xf3  0xf2 0xf1    0xf0
0x479198:      0xf7 0xf3        0xf3    0xf2 0xcd  0xcc 0xcf    0xce
0x4791a0:      0xc9 0xc8        0xcb    0xca 0xc5  0xc4 0xc7    0xc6
0x4791a8:      0xc1 0xc0        0xc3    0xc2 0xdd  0xdc 0xdf    0xde
0x4791b0:      0xd9 0xd8        0xdb    0xda 0xd5  0xd4 0xd7    0xd6
0x4791b8:      0xd1 0xd0        0xd3    0xd2 0x2d  0x2c 0x2f    0x2e
0x4791c0:      0x29 0x28        0x2b    0x2a 0x25  0x24 0x27    0x26
0x4791c8:      0x21 0x20        0x23    0x22 0x3d  0x3c 0x3f    0x3e
0x4791d0:      0x39 0x38        0x3b    0x3a 0x35  0x34 0x37    0x36
0x4791d8:      0x31 0x30        0x33    0x31

Now we can build the decoder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#!/usr/bin/env python3
# Reproduce FUN_00401910 decoding (blocks from 0x4793a0,0x4792c0,0x4791e0,0x479100)
# Keys: 0x11, 0x22, 0x33, 0x44
# Each block length: 55 words (55 * 4 = 220 bytes)

keys = [0x11, 0x22, 0x33, 0x44]
words_count = 55  # each block has 55 uint32 words

# Block bytes 
block0 = [
0x3a,0x39,0x38,0x3f,0x3e,0x3d,0x3c,0x33,0x32,0x31,0x30,0x37,0x36,0x35,0x34,0x0b,
0x0a,0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x1b,
0x1a,0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x6b,
0x6a,0x69,0x68,0x6f,0x6e,0x6d,0x6c,0x63,0x62,0x61,0x60,0x67,0x66,0x65,0x64,0x7b,
0x7a,0x79,0x78,0x7f,0x7e,0x7d,0x7c,0x73,0x72,0x71,0x70,0x77,0x76,0x75,0x74,0x4b,
0x4a,0x49,0x48,0x4f,0x4e,0x4d,0x4c,0x43,0x42,0x41,0x40,0x47,0x46,0x45,0x44,0x5b,
0x5a,0x59,0x58,0x5f,0x5e,0x5d,0x5c,0x53,0x52,0x51,0x50,0x57,0x56,0x55,0x54,0xab,
0xaa,0xa9,0xa8,0xaf,0xab,0xab,0xaa,0xa5,0xa4,0xa7,0xa5,0xa1,0xa0,0xa3,0xa1,0xbd,
0xbc,0xbf,0xbe,0xb9,0xb8,0xbb,0xba,0xb6,0xb4,0xb7,0xb6,0xb1,0xb0,0xb0,0xb2,0x8d,
0x8c,0x8f,0x8e,0x89,0x88,0x8b,0x8a,0x85,0x87,0x81,0x80,0x87,0x86,0x85,0x84,0x9b,
0x9a,0x99,0x98,0x9a,0x98,0x9b,0x9a,0x95,0x94,0x97,0x96,0x91,0x90,0x93,0x92,0xed,
0xec,0xef,0xee,0xe9,0xe8,0xeb,0xea,0xe5,0xe4,0xe7,0xe6,0xe1,0xe0,0xe3,0xe2,0xfd,
0xfc,0xff,0xfe,0xf9,0xf8,0xfb,0xfa,0xf5,0xf4,0xf7,0xf6,0xf1,0xf0,0xf3,0xf2,0xcd,
0xcc,0xcf,0xcd,0xcf,0xce,0xcd,0xcc,0xc3,0xc2,0xc1,0xc0,0xc7
]

block1 = [
0x09,0x08,0x0f,0x0e,0x0d,0x0c,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x1b,0x1a,
0x19,0x18,0x1f,0x1e,0x1d,0x1c,0x13,0x12,0x11,0x10,0x17,0x16,0x15,0x14,0x6b,0x6a,
0x69,0x68,0x6f,0x6e,0x6d,0x6c,0x63,0x62,0x61,0x60,0x67,0x66,0x65,0x64,0x7b,0x7a,
0x79,0x78,0x7f,0x7e,0x7d,0x7c,0x73,0x72,0x71,0x70,0x72,0x70,0x73,0x72,0x4d,0x4c,
0x4f,0x4e,0x49,0x48,0x4b,0x4a,0x45,0x44,0x47,0x46,0x41,0x40,0x43,0x42,0x5d,0x5c,
0x5f,0x5e,0x59,0x58,0x5b,0x5a,0x55,0x54,0x57,0x56,0x51,0x50,0x53,0x52,0xad,0xac,
0xaf,0xae,0xa9,0xa8,0xab,0xaa,0xa5,0xa4,0xa7,0xa6,0xa1,0xa0,0xa3,0xa2,0xbd,0xbc,
0xbf,0xbe,0xb9,0xb8,0xbb,0xba,0xb5,0xb4,0xb7,0xb6,0xb2,0xb6,0xb5,0xb4,0x8b,0x8a,
0x89,0x88,0x8f,0x8e,0x8d,0x8c,0x83,0x82,0x81,0x80,0x87,0x86,0x85,0x84,0x9b,0x9a,
0x99,0x98,0x9f,0x9e,0x9d,0x9c,0x93,0x92,0x91,0x90,0x97,0x96,0x95,0x94,0xeb,0xea,
0xe9,0xe8,0xef,0xee,0xed,0xe9,0xe3,0xe2,0xe1,0xe0,0xe7,0xe6,0xe0,0xe4,0xfb,0xfa,
0xf9,0xf8,0xff,0xfe,0xfd,0xfc,0xf3,0xf2,0xf1,0xf0,0xf7,0xf6,0xf5,0xf4,0xcb,0xca,
0xc9,0xc8,0xca,0xc8,0xcb,0xca,0xc5,0xc4,0xc7,0xc6,0xc1,0xc0,0xc3,0xc2,0xdd,0xdc,
0xdf,0xde,0xd9,0xd8,0xdb,0xda,0xd5,0xd4,0xd7,0xd6,0xd1,0xd0
]

block2 = [
0x1e,0x19,0x1b,0x1b,0x1a,0x15,0x14,0x17,0x16,0x11,0x10,0x13,0x12,0x6d,0x6c,0x6f,
0x6e,0x69,0x68,0x6b,0x6a,0x65,0x64,0x67,0x66,0x61,0x60,0x63,0x62,0x7d,0x7c,0x7f,
0x7e,0x79,0x78,0x7b,0x7a,0x75,0x74,0x77,0x76,0x71,0x70,0x73,0x72,0x4d,0x4c,0x4f,
0x4d,0x4f,0x4e,0x4d,0x4c,0x43,0x42,0x41,0x40,0x47,0x46,0x45,0x44,0x5b,0x5a,0x59,
0x58,0x5f,0x5e,0x5d,0x5c,0x53,0x52,0x51,0x50,0x57,0x56,0x55,0x54,0xab,0xaa,0xa9,
0xa8,0xaf,0xae,0xad,0xac,0xa3,0xa2,0xa1,0xa0,0xa7,0xa6,0xa5,0xa4,0xbb,0xba,0xb9,
0xb8,0xba,0xb8,0xbb,0xba,0xb6,0xb2,0xb1,0xb0,0xb7,0xb6,0xb5,0xb4,0x8b,0x8a,0x89,
0x88,0x8f,0x8e,0x8d,0x8c,0x83,0x82,0x84,0x86,0x81,0x80,0x83,0x82,0x9d,0x9c,0x9f,
0x9e,0x99,0x98,0x9b,0x9a,0x95,0x94,0x97,0x96,0x91,0x90,0x93,0x92,0xed,0xec,0xef,
0xee,0xe9,0xe8,0xeb,0xea,0xe5,0xe4,0xe7,0xe6,0xe1,0xe0,0xe3,0xe2,0xfd,0xfc,0xff,
0xfe,0xf9,0xf8,0xfb,0xfa,0xf5,0xf4,0xf7,0xf6,0xf1,0xf0,0xf3,0xf2,0xcd,0xcc,0xcf,
0xce,0xc9,0xc8,0xcb,0xca,0xc5,0xc4,0xc7,0xc5,0xc7,0xc6,0xc5,0xc4,0xdb,0xda,0xd9,
0xd8,0xdf,0xde,0xdd,0xdc,0xd3,0xd2,0xd1,0xd0,0xd7,0xd6,0xd5,0xd4,0x2b,0x2a,0x29,
0x28,0x2f,0x2e,0x2d,0x2c,0x23,0x22,0x21,0x20,0x27,0x26,0x25
]

block3 = [
0x6f,0x6e,0x6d,0x6c,0x63,0x62,0x61,0x60,0x67,0x66,0x65,0x64,0x7b,0x7a,0x79,0x78,
0x7f,0x7e,0x7d,0x7c,0x73,0x72,0x71,0x70,0x77,0x73,0x73,0x72,0x4d,0x4c,0x4f,0x4e,
0x49,0x48,0x4b,0x4a,0x45,0x44,0x47,0x46,0x41,0x40,0x43,0x42,0x5d,0x5c,0x5f,0x5e,
0x59,0x58,0x5b,0x5a,0x55,0x54,0x57,0x56,0x51,0x50,0x53,0x52,0xad,0xac,0xaf,0xae,
0xa9,0xa8,0xab,0xaa,0xa5,0xa4,0xa7,0xa6,0xa1,0xa0,0xa3,0xa2,0xbd,0xbc,0xbf,0xbe,
0xb9,0xb8,0xbb,0xba,0xb5,0xb7,0xb1,0xb0,0xb7,0xb6,0xb5,0xb4,0x8b,0x8a,0x89,0x88,
0x8f,0x8e,0x8d,0x8c,0x83,0x82,0x81,0x80,0x87,0x86,0x85,0x84,0x9b,0x9a,0x99,0x98,
0x9f,0x9e,0x9d,0x9c,0x93,0x92,0x91,0x90,0x97,0x96,0x95,0x94,0xeb,0xea,0xe9,0xe8,
0xef,0xee,0xed,0xec,0xe3,0xe2,0xe1,0xe0,0xe7,0xe6,0xe5,0xe4,0xfb,0xfa,0xf9,0xf8,
0xff,0xfe,0xfd,0xfc,0xf3,0xf2,0xf1,0xf0,0xf7,0xf3,0xf3,0xf2,0xcd,0xcc,0xcf,0xce,
0xc9,0xc8,0xcb,0xca,0xc5,0xc4,0xc7,0xc6,0xc1,0xc0,0xc3,0xc2,0xdd,0xdc,0xdf,0xde,
0xd9,0xd8,0xdb,0xda,0xd5,0xd4,0xd7,0xd6,0xd1,0xd0,0xd3,0xd2,0x2d,0x2c,0x2f,0x2e,
0x29,0x28,0x2b,0x2a,0x25,0x24,0x27,0x26,0x21,0x20,0x23,0x22,0x3d,0x3c,0x3f,0x3e,
0x39,0x38,0x3b,0x3a,0x35,0x34,0x37,0x36,0x31,0x30,0x33,0x31
]

blocks = [block0, block1, block2, block3]

# check the block size. must be 55*4 = 220 bytes
for b in blocks:
    if len(b) != words_count*4:
        print("Warning: block length", len(b), "expected", words_count*4)

decoded = bytearray()

for bi, blk in enumerate(blocks):
    key = keys[bi]
    # iterate 4-byte words (little-endian)
    for w in range(words_count):
        base = w*4
        # extract bytes in little-endian order
        for j in range(4):
            byte = blk[base + j]
            decoded.append(byte ^ key)
            key = (key + 1) & 0xff

# stop at first null if present (FUN_00401910 null-terminates)
if 0 in decoded:
    decoded = decoded[:decoded.index(0)]

print("Decoded (raw bytes):")
print(decoded)

The result:

1
2
Decoded (raw bytes):
bytearray(b'++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-----.---.--------.-----.----------.++++++++++.----------------------------------------------.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------.++++++++++++++++++++++++++++++++++++++++++.++++++.+++++++++++++++++++++.---------------------------.---------------------------------------------.++++++++++++++++++++++++++++++++++++++++++++++++.---.+++++++++++++++++.----------------------------------------------------------------.++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-----------------------------------------------------------.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-----------------------------------------------------------------.')

The characters extracted (+ - .) are Brainfuck instructions. See Basics of brainfuck for more info about BF instructions. Here is the Python BF decoder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def run_bf(code):
    tape = [0] * 30000
    ptr = 0
    pc = 0
    out = []
    loop_stack = []
    code_len = len(code)

    while pc < code_len:
        cmd = code[pc]

        if cmd == '+':
            tape[ptr] = (tape[ptr] + 1) & 0xFF
        elif cmd == '-':
            tape[ptr] = (tape[ptr] - 1) & 0xFF
        elif cmd == '>':
            ptr += 1
        elif cmd == '<':
            ptr -= 1
        elif cmd == '.':
            out.append(chr(tape[ptr]))
        elif cmd == '[':
            if tape[ptr] == 0:
                # skip loop
                open_brackets = 1
                while open_brackets:
                    pc += 1
                    if code[pc] == '[':
                        open_brackets += 1
                    elif code[pc] == ']':
                        open_brackets -= 1
            else:
                loop_stack.append(pc)
        elif cmd == ']':
            if tape[ptr] != 0:
                pc = loop_stack[-1]
            else:
                loop_stack.pop()

        pc += 1

    return "".join(out)

bf_code = """
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-----.---.--------.-----.----------.++++++++++.----------------------------------------------.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.---------------------------------------------------------------.++++++++++++++++++++++++++++++++++++++++++.++++++.+++++++++++++++++++++.---------------------------.---------------------------------------------.++++++++++++++++++++++++++++++++++++++++++++++++.---.+++++++++++++++++.----------------------------------------------------------------.++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-----------------------------------------------------------.+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.-----------------------------------------------------------------.
"""

decoded = run_bf(bf_code)
print("Output:")
print(decoded)
1
2
Output:
told_U_1t5_ez_2b_p0l1t3

Finally, we get the flag.

This post is licensed under CC BY 4.0 by the author.