RE101 — Reverse Engineering Fundamentals Walkthrough
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.
strings malware000
This reveals interesting output buried in the binary:
ZmxhZzwwb3BzX2lfdXNlZF8xMzM3X2I2NF9lbmNyeXB0aW9uPgo=
If you see this message...
The string ending with = is immediately recognizable as Base64 padding.
Decoding the Flag#
Standard Base64 decoding works perfectly:
base64 -d <<< "ZmxhZzwwb3BzX2lfdXNlZF8xMzM3X2I2NF9lbmNyeXB0aW9uPgo="
The decoded flag is:
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:
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]
+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])
[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!!
[]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+...
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:
node just_some_js
The output reveals:
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:
file this_is_not_js
Output:
ASCII text, with very long lines (320)
Viewing the content reveals:
++++++++++[>+>+++>+++++++>++++++++++<<<<-]>>>>++.++++++.-----------.++++++.
<----------.++++++++++++++++++.>++++++++.++++++++.<+++++++++++++++++.------
-----.------------.+.++++++++++.++++++++++++.++++++++++.>----.<----------.>
---.++.---.<++++++++.>+++.<------.>-----..----.+++++.<++++++.<++++++++++++++
++++++++++++++.
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:
brainfuck this_is_not_js
Output:
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:
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:
xxd file.zip_broken | head -n 20
Output:
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:
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:
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:
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:
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:
__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:
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:
sTaCk_strings_LMAO
Extraction Script#
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:
./malware201
Output:
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:
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:
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
temp = encrypted[i] ^ ((i % 0xFF) | 0xA0)
Step 2: Remove LSB and reverse doubling
plaintext[i] = (temp ^ 1) / 2
Encrypted Flag Data#
From .rodata section (32 bytes):
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#
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:
key = (0 % 0xFF) | 0xA0 = 0xA0
transformed = (2 * 0x66) | 1 = 0xCC | 1 = 0xCD
encrypted = 0xA0 ^ 0xCD = 0x6D ✓
Decryption of 0x6D at position 0:
key = 0xA0
temp = 0x6D ^ 0xA0 = 0xCD
plaintext = (0xCD ^ 1) / 2 = 0xCC / 2 = 0x66 = 'f' ✓
The decrypted flag is:
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