RE101 — Reverse Engineering Fundamentals Walkthrough
Analyze diverse file types including binaries, obfuscated scripts, and corrupted archives using reverse engineering techniques to extract hidden flags.
Overview
| Platform | CyberDefenders |
| Category | Malware Analysis |
| Difficulty | Medium |
| Focus | Binary Analysis · Script Deobfuscation · File Format Repair · Custom Encryption |
| Lab Link | RE101 |
The RE101 challenge is an introductory reverse engineering lab designed to teach fundamental analysis techniques across multiple file types. The challenge presents six distinct artifacts — each requiring a different approach:
- Base64 encoding hidden in binary
- JSFuck obfuscated JavaScript
- Brainfuck esoteric programming language
- Corrupted ZIP file repair
- Stack-string obfuscation in binaries
- Custom position-dependent XOR encryption
This challenge is not about advanced malware techniques. Instead, it emphasizes foundational skills that every reverse engineer must master: recognizing encoding schemes, understanding scripting languages, working with file format specifications, and tracing execution flow in binaries.
Objective
The goal of this lab is to extract hidden flags from six different files using:
- Static string extraction
- Script execution in safe environments
- Esoteric language recognition
- File format specification knowledge
- Disassembly and stack string reconstruction
- Encryption algorithm reversal
By the end of the investigation, we will demonstrate:
- How to find encoded data in binaries using
strings - How JSFuck uses only 6 characters to encode JavaScript
- How to recognize and execute Brainfuck code
- How to repair corrupted archive headers
- How stack strings evade static analysis
- How to reverse position-dependent XOR encryption
Tools Used
Throughout this analysis, the following tools are used:
strings,file,xxd— Basic file analysisbase64— Decodingnode— JavaScript runtimebrainfuck— Esoteric language interpreterhexedit/ HxD — Hex editing- IDA Pro / Ghidra — Disassembly and decompilation
- Python — Custom decryption scripts
Q1 — Base64 Encoding in Binary
Question
File: MALWARE000 - I’ve used this new encryption I heard about online for my warez; I bet you can’t extract the flag!
Initial Analysis
The first step with any unknown binary is simple: run strings.
1
strings malware000
This reveals interesting output buried in the binary:
1
2
ZmxhZzwwb3BzX2lfdXNlZF8xMzM3X2I2NF9lbmNyeXB0aW9uPgo=
If you see this message...
The string ending with = is immediately recognizable as Base64 padding.
Decoding the Flag
Standard Base64 decoding works perfectly:
1
base64 -d <<< "ZmxhZzwwb3BzX2lfdXNlZF8xMzM3X2I2NF9lbmNyeXB0aW9uPgo="
The decoded flag is:
1
0ops_i_used_1337_b64_encryption
Q2 — JSFuck Obfuscation
Question
File: Just some JS - Check out what I can do!
Initial Analysis
Opening the JavaScript file reveals something unusual — code composed entirely of six characters:
1
2
3
4
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]
+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])
[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!!
[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+...
This is JSFuck — an esoteric programming style that uses only these 6 characters: []()!+
Understanding JSFuck
JSFuck exploits JavaScript’s type coercion to construct any code from just six symbols:
| Expression | Result |
|---|---|
![] | false |
!![] | true |
[][[]] | undefined |
+[] | 0 |
+!+[] | 1 |
!+[]+!+[] | 2 |
From these primitives, JSFuck can construct strings, access properties, and execute arbitrary JavaScript.
Extracting the Flag
JSFuck is valid JavaScript — just execute it:
1
node just_some_js
The output reveals:
1
what_a_cheeky_language!1!
Q3 — Brainfuck Esoteric Language
Question
File: This is not JS - I’m tired of Javascript. Luckily, I found the grand-daddy of that lame last language!
Initial Analysis
The filename is deliberately misleading. Let’s check what we’re dealing with:
1
file this_is_not_js
Output:
1
ASCII text, with very long lines (320)
Viewing the content reveals:
1
2
3
4
5
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.
<----------.++++++++++++++++++.>++++++++.++++++++.<+++++++++++++++++.------
-----.------------.+.++++++++++.++++++++++++.++++++++++.>----.<----------.>
---.++.---.<++++++++.>+++.<------.>-----..----.+++++.<++++++.<++++++++++++++
++++++++++++++.
This is Brainfuck — an esoteric programming language that uses only 8 commands: +-<>[].,
Understanding Brainfuck
| Command | Description |
|---|---|
+ | Increment current cell |
- | Decrement current cell |
> | Move pointer right |
< | Move pointer left |
[ | Jump past ] if cell is zero |
] | Jump back to [ if cell is non-zero |
. | Output current cell as ASCII |
, | Read input into current cell |
Extracting the Flag
Execute the Brainfuck code:
1
brainfuck this_is_not_js
Output:
1
Now_THIS_is_programming
Q4 — ZIP File Header Repair
Question
File: Unzip Me - I zipped flag.txt and encrypted it with the password “password”, but I think the header got messed up… You can have the flag if you fix the file
Initial Analysis
Attempting to open the ZIP file fails:
1
2
3
4
5
6
file file.zip_broken
# Output: data (not recognized as ZIP)
unzip file.zip_broken
# Error: filename too long--truncating.
# flag.txtUT^I: bad extra field length (local)
The archive is corrupted. Let’s examine it with a hex dump:
1
xxd file.zip_broken | head -n 20
Output:
1
2
3
00000000: 504b 0304 0a00 0900 0000 5aab 8e50 5622 PK........Z..PV"
00000010: 6795 2000 0000 1400 0000 5858 1c00 666c g. .......XX..fl
00000020: 6167 2e74 7874 5554 0900 03dc 6296 5edc ag.txtUT....b.^.
ZIP File Structure
Understanding the Local File Header (LFH):
| Offset | Size | Description |
|---|---|---|
| 0x00 | 4 | Signature (50 4B 03 04) |
| 0x04 | 2 | Version needed |
| 0x06 | 2 | General purpose bit flag |
| 0x08 | 2 | Compression method |
| 0x0A | 4 | Last mod time/date |
| 0x0E | 4 | CRC-32 |
| 0x12 | 4 | Compressed size |
| 0x16 | 4 | Uncompressed size |
| 0x1A | 2 | Filename length ← CORRUPTED! |
| 0x1C | 2 | Extra field length |
Identifying the Bug
At offset 0x1A-0x1B, we have: 58 58
In little-endian, this equals: 0x5858 = 22,616 bytes for filename length!
But “flag.txt” is only 8 bytes: 66 6C 61 67 2E 74 78 74
Repairing the Header
Using a hex editor:
1
hexedit file.zip_broken
Navigate to offset 0x1A and change:
58 58→08 00
This sets the filename length to 8 bytes (little-endian: 0x0008).
Extracting the Flag
After repair:
1
2
3
4
5
zip -FF file.zip_broken --out file_fixed.zip
unzip file_fixed.zip
# Enter password: (empty - just press Enter)
cat flag.txt
The extracted flag is:
1
R3ad_th3_spec
Q5 — Stack String Obfuscation
Question
File: MALWARE101 - Apparently, my encryption isn’t so secure. I’ve got a new way of hiding my flags!
Initial Analysis
Running basic analysis reveals nothing useful:
1
2
3
4
5
6
7
8
file malware101
# Output: ELF 64-bit LSB executable, x86-64, dynamically linked, stripped
strings malware101
# Output: Nothing to see here....
./malware101
# Output: Nothing to see here....
Dynamic analysis with strace and ltrace also shows only the decoy message.
The Decompiler Trap
Loading into IDA and using the decompiler (F5) shows:
1
2
3
4
5
__int64 __fastcall main(int a1, char **a2, char **a3)
{
printf("Nothing to see here....\n");
return 0;
}
This looks completely innocent! But wait — the decompiler optimizes away dead code.
The flag is constructed on the stack but never used, so the decompiler removes it entirely.
The Assembly Tells the Truth
Switching to assembly view reveals what the decompiler hid:
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
main proc near
push rbp
mov rbp, rsp
sub rsp, 1C0h
mov rdi, offset format ; "Nothing to see here....\n"
mov [rbp+var_4], 0
mov [rbp+var_1A0], 67h ; 'g'
mov [rbp+var_1A9], 61h ; 'a'
mov [rbp+var_1A3], 72h ; 'r'
mov [rbp+var_1A2], 69h ; 'i'
mov [rbp+var_1A1], 6Eh ; 'n'
mov [rbp+var_1AB], 73h ; 's'
mov [rbp+var_199], 3Eh ; '>'
mov [rbp+var_198], 0
mov [rbp+var_1A7], 6Bh ; 'k'
mov [rbp+var_19F], 73h ; 's'
mov [rbp+var_1AE], 61h ; 'a'
mov [rbp+var_1AA], 54h ; 'T'
mov [rbp+var_1A6], 5Fh ; '_'
mov [rbp+var_19E], 5Fh ; '_'
mov [rbp+var_1AF], 6Ch ; 'l'
mov [rbp+var_1B0], 66h ; 'f'
mov [rbp+var_1A5], 73h ; 's'
mov [rbp+var_1A4], 74h ; 't'
mov [rbp+var_19D], 4Ch ; 'L'
mov [rbp+var_1A8], 43h ; 'C'
mov [rbp+var_19B], 41h ; 'A'
mov [rbp+var_19A], 4Fh ; 'O'
mov [rbp+var_1AD], 67h ; 'g'
mov [rbp+var_1AC], 3Ch ; '<'
mov [rbp+var_19C], 4Dh ; 'M'
mov al, 0
call _printf
main endp
The characters are written to the stack in scrambled order — an additional obfuscation layer to confuse manual analysis!
Reconstruction
Sort by stack offset and extract the ASCII values:
| Offset | Hex | Char |
|---|---|---|
| -0x1B0 | 0x66 | f |
| -0x1AF | 0x6C | l |
| -0x1AE | 0x61 | a |
| -0x1AD | 0x67 | g |
| -0x1AC | 0x3C | < |
| -0x1AB | 0x73 | s |
| -0x1AA | 0x54 | T |
| -0x1A9 | 0x61 | a |
| -0x1A8 | 0x43 | C |
| -0x1A7 | 0x6B | k |
| -0x1A6 | 0x5F | _ |
| -0x1A5 | 0x73 | s |
| -0x1A4 | 0x74 | t |
| -0x1A3 | 0x72 | r |
| -0x1A2 | 0x69 | i |
| -0x1A1 | 0x6E | n |
| -0x1A0 | 0x67 | g |
| -0x19F | 0x73 | s |
| -0x19E | 0x5F | _ |
| -0x19D | 0x4C | L |
| -0x19C | 0x4D | M |
| -0x19B | 0x41 | A |
| -0x19A | 0x4F | O |
| -0x199 | 0x3E | > |
Reconstructed flag:
1
sTaCk_strings_LMAO
Extraction Script
1
2
3
4
5
6
7
8
9
10
11
12
stack_data = [
(-0x1B0, 0x66), (-0x1AF, 0x6C), (-0x1AE, 0x61), (-0x1AD, 0x67),
(-0x1AC, 0x3C), (-0x1AB, 0x73), (-0x1AA, 0x54), (-0x1A9, 0x61),
(-0x1A8, 0x43), (-0x1A7, 0x6B), (-0x1A6, 0x5F), (-0x1A5, 0x73),
(-0x1A4, 0x74), (-0x1A3, 0x72), (-0x1A2, 0x69), (-0x1A1, 0x6E),
(-0x1A0, 0x67), (-0x19F, 0x73), (-0x19E, 0x5F), (-0x19D, 0x4C),
(-0x19C, 0x4D), (-0x19B, 0x41), (-0x19A, 0x4F), (-0x199, 0x3E),
]
stack_data.sort(key=lambda x: x[0])
flag = ''.join(chr(val) for _, val in stack_data)
print(flag) # flag<sTaCk_strings_LMAO>
Q6 — Custom Position-Dependent XOR Encryption
Question
File: MALWARE201 - Ugh… I guess I’ll just roll my own encryption. I’m not too good at math, but it looks good to me!
Initial Analysis
Running the binary provides helpful output:
1
./malware201
Output:
1
2
3
The encrypted flag is: "6d7861 6cdd7e657e476a4fccf7ca736855425 3dcd7d46becd bd2e11c6dded1c2"
encrypt("my message", 10) == "6f7d61676dd7ed676fdb617ddb7b71"
The binary shows us:
- The encrypted flag (32 bytes)
- A sample encryption of “my message”
Disassembly Analysis
Examining the encryption function in IDA reveals the algorithm:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned char* encrypt(const char* plaintext, size_t length) {
unsigned char* output = calloc(length, 1);
for (size_t i = 0; i < length; i++) {
// Position-dependent key
unsigned char key = ((i % 0xFF) | 0xA0);
// Transform plaintext: double and set LSB
unsigned char transformed = (2 * plaintext[i]) | 1;
// XOR encryption
output[i] = key ^ transformed;
}
return output;
}
Encryption Algorithm Breakdown
Formula:
1
encrypted[i] = ((i % 0xFF) | 0xA0) ^ ((2 * plaintext[i]) | 1)
Components:
- Position-dependent key:
(i % 0xFF) | 0xA0- Creates unique key per position
- OR with 0xA0 ensures bits 7 and 5 are set
- Plaintext transformation:
(2 * plaintext[i]) | 1- Doubles the byte (left shift)
- Sets LSB to 1 (makes it odd)
- XOR: Combines key and transformed plaintext
Decryption Process
To reverse the encryption:
Step 1: XOR encrypted byte with position key
1
temp = encrypted[i] ^ ((i % 0xFF) | 0xA0)
Step 2: Remove LSB and reverse doubling
1
plaintext[i] = (temp ^ 1) / 2
Encrypted Flag Data
From .rodata section (32 bytes):
1
2
6D 78 61 6C DD 7E 65 7E 47 6A 4F CC F7 CA 73 68
55 42 53 DC D7 D4 6B EC DB D2 E1 1C 6D DE D1 C2
Decryption Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
encrypted_flag = [
0x6D, 0x78, 0x61, 0x6C, 0xDD, 0x7E, 0x65, 0x7E,
0x47, 0x6A, 0x4F, 0xCC, 0xF7, 0xCA, 0x73, 0x68,
0x55, 0x42, 0x53, 0xDC, 0xD7, 0xD4, 0x6B, 0xEC,
0xDB, 0xD2, 0xE1, 0x1C, 0x6D, 0xDE, 0xD1, 0xC2
]
def decrypt_byte(encrypted_byte, index):
key = (index % 0xFF) | 0xA0
temp = key ^ encrypted_byte
plaintext = (temp ^ 1) // 2
return plaintext
flag = ''.join(chr(decrypt_byte(b, i)) for i, b in enumerate(encrypted_flag))
print(flag)
Verification
Verifying position 0 manually:
Encryption of ‘f’ (0x66) at position 0:
1
2
3
key = (0 % 0xFF) | 0xA0 = 0xA0
transformed = (2 * 0x66) | 1 = 0xCC | 1 = 0xCD
encrypted = 0xA0 ^ 0xCD = 0x6D ✓
Decryption of 0x6D at position 0:
1
2
3
key = 0xA0
temp = 0x6D ^ 0xA0 = 0xCD
plaintext = (0xCD ^ 1) / 2 = 0xCC / 2 = 0x66 = 'f' ✓
The decrypted flag is:
1
malwar3-3ncryp710n-15-Sh17
Conclusion
The RE101 lab covers foundational reverse engineering concepts that appear repeatedly in real-world analysis:
| Question | Technique | Flag |
|---|---|---|
| Q1 | Static strings + Base64 | 0ops_i_used_1337_b64_encryption |
| Q2 | JSFuck execution | what_a_cheeky_language!1! |
| Q3 | Brainfuck esoteric language | Now_THIS_is_programming |
| Q4 | ZIP header repair | R3ad_th3_spec |
| Q5 | Stack string reconstruction | sTaCk_strings_LMAO |
| Q6 | Position-dependent XOR reversal | malwar3-3ncryp710n-15-Sh17 |
Skills Demonstrated
Static Analysis:
- String extraction with
strings - Hex dump interpretation
- Disassembly reading
- File format understanding
Dynamic Analysis:
- Safe code execution
- System/library call tracing
Reverse Engineering Concepts:
- Encoding vs Encryption vs Obfuscation
- Esoteric programming languages
- Stack-based string construction
- Position-dependent encryption
- XOR properties and reversibility
These fundamentals form the building blocks for advanced malware analysis. Mastering them ensures you can handle obfuscation and encoding schemes before tackling more complex evasion techniques.
see yaa again <3