TryHackMe Basic Malware RE Walkthrough: Static Analysis with Ghidra
Room Info
- Platform: TryHackMe
- Room Name: Basic Malware RE
- Category: Malware Analysis / Reverse Engineering
- Room Link: https://tryhackme.com/room/basicmalwarere
- Difficulty: Easy
- ZIP Password: MalwareTech
- Learning Objective: Fundamentals of static malware analysis
- Note: These archives really do contain live malware, and Windows will delete them automatically the moment you run strings or file on them. So you may want to temporarily disable your antivirus first. If you're purely interested in reversing and not yet ready to dive into malware analysis, feel free to skip this room if you have any concerns!
Introduction
This room contains three challenges (strings1, strings2, strings3), all designed to teach static analysis techniques. Every task shares one thing in common:
- When the program runs, it computes and displays the MD5 hash of some string
- Our job is to find that original string (the flag) using reverse engineering tools, without running the program
Golden rule: 👉 Never run the executable! This is the cardinal rule of malware analysis. If you absolutely must run it, do so in a sandbox or a virtual machine to avoid infecting your machine.
Tooling
Essential Tools
- Ghidra: A free, open-source decompiler (https://ghidra-sre.org/)
- strings: Linux's built-in string extraction utility
- md5sum: For verifying answers
Environment Recommendations
- A Linux virtual machine (Kali, Ubuntu, etc.)
- Or Windows + WSL
- An isolated environment (never analyze malware samples on your main system)
Challenge 1: strings1.exe
Task Description
This executable prints an MD5 Hash on the screen when executed. Can you grab the exact flag? Note: You don't need to run the executable!
Walkthrough
Step 1: Basic Recon
First, check the file type:
file strings1.exe_
# strings1.exe_: PE32 executable (GUI) Intel 80386, for MS Windows, 6 sections
md5sum strings1.exe_
# 76b58619d2834419e82e0f6a605c8811
Step 2: String Scan (Hitting a Trap)
strings strings1.exe_ | head -50
What we find:
- ⚠️ Thousands of fake flags in
FLAG{...}format - Suspicious string:
"We've been compromised!" - API calls:
MessageBoxA,sprintf,%02x
Spotting the trap: The flood of fake flags is a classic anti-analysis technique, designed to throw off the naive strings approach.
Step 3: Ghidra Decompilation Analysis
3.1 Import and Analyze
- Open Ghidra and create a new project
- Import
strings1.exe_ - Run the automatic analysis (Analysis → Auto Analyze)
3.2 Locate the entry Function
In the Symbol Tree → Functions, find the entry function:
void entry(void)
{
char *lpText;
lpText = md5_hash(PTR_s_FLAG{CAN-I-MAKE-IT-ANYMORE-OBVIO_00432294);
MessageBoxA((HWND)0x0, lpText, "We've been compromised!", 0x30);
ExitProcess(0);
}
Program logic:
- Calls the
md5_hash()function on some string - Displays the MD5 result via a MessageBox
- The title is
"We've been compromised!"← this is not the content being hashed!
3.3 Follow the Pointer Chain (the key step)
First hop - double-click PTR_s_FLAG{CAN-I-MAKE-IT-ANYMORE-OBVIO_00432294:
PTR_s_FLAG{CAN-I-MAKE-IT-ANYMORE-OBVIO_00432294 XREF[1]: entry:004022b4(R)
00432294 28 48 42 00 addr s_FLAG{CAN-I-MAKE-IT-ANYMORE-OBVIO_00424828
= "FLAG{CAN-I-MAKE-IT-ANYMORE-OB"
→ This is a pointer, pointing to address 0x00424828
Second hop - double-click s_FLAG{CAN-I-MAKE-IT-ANYMORE-OBVIO_00424828:
s_FLAG{CAN-I-MAKE-IT-ANYMORE-OBVIO_00424828 XREF[2]: entry:004022b9(*), 00432294(*)
00424828 46 4c 41 ds "FLAG{Redacted}"
47 7b 43
41 4e 2d
🎯 Found the real flag!
Step 4: Verify the Answer
echo -n "FLAG{Redacted}" | md5sum
# 4c827c4ca62781d707cd049da13539ee
Program Behavior, Diagrammed
┌────────────────────────────────────────────┐
│ entry() │
│ │
│ 1. Read pointer 0x00432294 │
│ └→ points to 0x00424828 │
│ └→ "FLAG{CAN-I-MAKE-IT-ANYMORE- │
│ OBVIOUS}" │
│ │
│ 2. Compute MD5 │
│ └→ 4c827c4ca62781d707cd049da13539ee │
│ │
│ 3. Show in MessageBox │
│ Title: "We've been compromised!" │
│ Text: (MD5 hash) │
└────────────────────────────────────────────┘
Flag
FLAG{Redacted}
MD5: 4c827c4ca62781d707cd049da13539ee
Key Takeaways
🎯 Recognizing Anti-Analysis Techniques
- A flood of fake flags: used to overwhelm the strings tool
- Multi-level pointers: the real data hides behind a pointer to a pointer
- Misleading strings: the MessageBox title looks important but is really a smokescreen
Challenge 2: strings2.exe
Task Description
Same as strings1: find the original string whose MD5 is computed.
Walkthrough
Step 1: Ghidra Decompilation
Open strings2.exe straight in Ghidra and find the entry function:
void entry(void)
{
char local_2c [36];
char *local_8;
builtin_strncpy(local_2c, "FLAG{STACK-STRINGS-ARE-BEST-STRINGS}", 0x24);
local_8 = md5_hash(local_2c);
MessageBoxA((HWND)0x0, local_8, "We've been compromised!", 0x30);
ExitProcess(0);
}
🎯 The full flag is right there! Even easier than strings1!
Step 2: Verify
echo -n "FLAG{Redacted}" | md5sum
# e80782d8c30671eb61acc63c5dca914e
Why Is This One Actually Easier?
The original design intent.
Per MalwareTech's teaching design, strings2 was supposed to make learners:
- Look at the stack operations in the assembly
- Manually parse the hex value of each byte
- Convert the hex to ASCII characters to reconstruct the string
You were originally supposed to see something like this:
mov BYTE PTR [ebp-0x2c], 0x46 ; 'F'
mov BYTE PTR [ebp-0x2b], 0x4c ; 'L'
mov BYTE PTR [ebp-0x2a], 0x41 ; 'A'
mov BYTE PTR [ebp-0x29], 0x47 ; 'G'
; ... more byte-by-byte assignments
But Ghidra Is Too Smart!
Ghidra's decompiler recognizes this as a strncpy operation and displays the full string directly, which is exactly why this challenge turns out to be the easiest of the three!
Verifying with the strings Tool
strings strings2.exe_ | grep "STACK"
# (no results)
❌ Nothing! Because the string is built dynamically on the stack and doesn't exist in any static data section.
Technical Comparison
| Trait | strings1 | strings2 |
|---|---|---|
| Storage location | .rdata (static) |
Stack (dynamic) |
| Visible to strings | ✅ (buried among fake flags) | ❌ completely invisible |
| Ghidra difficulty | Follow 2 levels of pointers | Displayed directly |
| Hiding technique | Flood of fake flags | Stack strings |
Flag
FLAG{Redacted}
MD5: e80782d8c30671eb61acc63c5dca914e
Key Takeaways
🎯 The Stack Strings Technique
- Definition: building a string dynamically on the stack at runtime, rather than hardcoding it in a data section
- Purpose: to evade static scanning tools like
strings - Detection: can only be spotted through decompilation or disassembly
🤔 The Flag's Little Joke
"STACK-STRINGS-ARE-BEST-STRINGS" says exactly what it means — because they're so effective at slipping past simple static analysis!
Challenge 3: strings3.exe
Task Description
Same as the first two challenges: find the original string whose MD5 is computed.
Walkthrough
Step 1: Ghidra Decompilation
Open strings3.exe in Ghidra and find the entry function:
void entry(void)
{
CHAR local_4a4;
undefined1 local_4a3 [1027];
char *local_a0;
MD5 local_9c [144];
HRSRC local_c;
undefined4 local_8;
MD5::MD5(local_9c);
local_4a4 = '\0';
memset(local_4a3, 0, 0x3ff);
// Find the resource
local_c = FindResourceA((HMODULE)0x0, "rc.rc", &DAT_00000006);
// Load the string resource with String ID = 0x110 (272)
local_8 = 0x110;
LoadStringA((HINSTANCE)0x0, 0x110, &local_4a4, 0x3ff);
// Compute MD5
local_a0 = MD5::digestString(local_9c, &local_4a4);
MessageBoxA((HWND)0x0, local_a0, "We've been compromised!", 0x30);
ExitProcess(0);
}
Step 2: Understand the Program Logic
Key observation:
LoadStringA((HINSTANCE)0x0, 0x110, &local_4a4, 0x3ff);
- The program uses the Windows API
LoadStringA() - It loads the string with String ID = 0x110 (decimal 272) from the resource table
- This string is stored in the PE file's Resource Section (.rsrc)
Step 3: Locate the Resource in Ghidra
3.1 Open Program Trees
- Menu: Window → Program Trees
- Expand it and find the
.rsrcsection - Double-click into
.rsrc
3.2 Browse the String Tables
In the Listing window, you'll see a structure like this:
**************************************************************
* Rsrc_StringTable_8_409 Size of resource: 0x55c bytes *
**************************************************************
Rsrc_StringTable_9_409
00408xxx ... p_unicode u"FLAG{...}" Rsrc String ID 128
00408xxx ... p_unicode u"FLAG{...}" Rsrc String ID 129
...
3.3 Find String ID 272
Windows String Resource structure:
- Each String Table holds 16 strings
- String ID 272 = 17 × 16
- So it lives in Rsrc_StringTable_12_409 (0x11 = 17)
What we find:
Rsrc_StringTable_12_409 XREF[1]: entry:004022ff(*)
0040aef0 27 00 46 p_unicode u"FLAG{Redacted}"
00 4c 00 Rsrc String ID 272
41 00 47
🎯 Found the flag!
Step 4: Verify the Answer
echo -n "FLAG{Redacted}" | md5sum
# 1011cafbd736cdf2ae90964613c911fe
Windows PE Resource Table Basics
What Is the Resource Section?
A PE file's .rsrc section can store:
- 🖼️ Icons
- 📝 String Tables
- 🎵 Audio files
- 📋 Dialog definitions
- 🗂️ Arbitrary binary data (RCDATA)
Why Use the Resource Section?
Legitimate uses:
- Multi-language support (strings in different languages)
- Separating UI resources from program logic
- Easier resource management and updates
Flag
FLAG{Redacted}
MD5: 1011cafbd736cdf2ae90964613c911fe
Key Takeaways
🎯 The Resource Hiding Technique
- Definition: stashing sensitive data inside the PE resource table
- Purpose: to evade static scanning while supporting a modular design
- Detection: requires dedicated PE analysis tools
🤔 The Flag's Little Joke
"RESOURCES-ARE-POPULAR-FOR-MALWARE" — the flag itself is teaching you a real-world attack technique!
Lessons Learned
Technical Comparison Table
| Trait | strings1 | strings2 | strings3 |
|---|---|---|---|
| Storage location | .rdata (static data) |
Stack | .rsrc (resource table) |
strings visibility |
✅ visible (but obfuscated) | ❌ invisible | ❌ invisible |
| Ghidra difficulty | ⭐⭐ follow pointers | ⭐ displayed directly | ⭐⭐ inspect resource table |
| Anti-obfuscation technique | 4000+ fake flags | Stack strings | Resource hiding |
| Real-world case | Statically embedded config | API hashing | Multi-language / modular malware |
| Time to complete | ~15 minutes | ~5 minutes | ~10 minutes |
Member discussion