> ## Content Index
> Fetch the complete content index at: https://taiwanding.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# TryHackMe Basic Malware RE Walkthrough: Static Analysis with Ghidra
- URL: https://taiwanding.com/en/tryhackme-basic-malware-re-writeup/
- Published: 2025-10-14T08:00:09.000Z
- Updated: 2026-07-15T02:49:58.000Z
- Author: Kevin Chen
- Tags: #en, #en-ctf

## Room Info

- **Platform**: TryHackMe
- **Room Name**: Basic Malware RE
- **Category**: Malware Analysis / Reverse Engineering
- **Room Link**: [https://tryhackme.com/room/basicmalwarere](https://tryhackme.com/room/basicmalwarere?ref=taiwanding.com)
- **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:

```bash
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)

```bash
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

1. Open Ghidra and create a new project
2. Import `strings1.exe_`
3. Run the automatic analysis (Analysis → Auto Analyze)

#### 3.2 Locate the entry Function

In the **Symbol Tree** → **Functions**, find the `entry` function:

```c
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**:

1. Calls the `md5_hash()` function on some string
2. Displays the MD5 result via a MessageBox
3. 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

```bash
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:

```c
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

```bash
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:

1. Look at the stack operations in the **assembly**
2. Manually parse the hex value of each byte
3. Convert the hex to ASCII characters to reconstruct the string

You were originally supposed to see something like this:

```assembly
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

```bash
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:

```c
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**:

```c
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

1. Menu: **Window** → **Program Trees**
2. Expand it and find the `.rsrc` section
3. 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

```bash
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                     |