7 min read

TryHackMe Basic Malware RE (zh-TW)

TryHackMe Basic Malware RE (zh-TW)

題目資訊

  • 平台: TryHackMe
  • 房間名稱: Basic Malware RE
  • 分類: Malware Analysis / Reverse Engineering
  • 房間連結: https://tryhackme.com/room/basicmalwarere
  • 難度: Easy
  • ZIP 密碼: MalwareTech
  • 學習目標: 靜態惡意軟體分析基礎
  • 備註: 這些壓縮檔真的都有病毒,Windows會自動刪除,不管是執行strings、file查看,所以可以先暫時把掃毒關掉,如果單純只對reverse有興趣,還沒有要往Malware Analysis,有疑慮也可以先略過這題喔!

前言

這個房間包含三個挑戰(strings1, strings2, strings3),旨在教授靜態分析技術。所有題目都有一個共同點:

  • 程式執行時會計算某個字串的 MD5 hash 並顯示
  • 我們的任務是不執行程式,用逆向工程工具找出那個原始字串(flag)

重要原則: 👉 Never run the executable! 這是惡意軟體分析的黃金法則,如果真的需要,也建議在沙盒或是虛擬機跑,避免感染電腦。

工具準備

必備工具

  • Ghidra: 免費開源的反編譯工具 (https://ghidra-sre.org/)
  • strings: Linux 內建的字串提取工具
  • md5sum: 驗證答案用

環境建議

  • Linux 虛擬機(Kali, Ubuntu 等)
  • 或 Windows + WSL
  • 隔離環境(不要在主系統分析惡意軟體樣本)

Challenge 1: strings1.exe

題目描述

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!

解題過程

Step 1: 基礎偵察

首先檢查檔案類型:

file strings1.exe_
# strings1.exe_: PE32 executable (GUI) Intel 80386, for MS Windows, 6 sections

md5sum strings1.exe_
# 76b58619d2834419e82e0f6a605c8811

Step 2: 字串掃描(遇到陷阱)

strings strings1.exe_ | head -50

發現

  • ⚠️ 千個 FLAG{...} 格式的假 flag
  • 可疑字串: "We've been compromised!"
  • API 呼叫: MessageBoxA, sprintf, %02x

陷阱識別: 大量假 flag 是典型的反分析技術,用來混淆一般的字串方法。

Step 3: Ghidra 反編譯分析

3.1 導入與分析

  1. 開啟 Ghidra,建立新專案
  2. 導入 strings1.exe_
  3. 執行自動分析(Analysis → Auto Analyze)

3.2 定位 entry 函數

Symbol TreeFunctions 中找到 entry 函數:

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);
}

程式邏輯

  1. 對某個字串呼叫 md5_hash() 函數
  2. 將 MD5 結果透過 MessageBox 顯示
  3. 標題是 "We've been compromised!"不是被 hash 的內容!

3.3 追蹤指標鏈(關鍵步驟)

第一次跳轉 - 雙擊 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"

→ 這是個指標,指向地址 0x00424828

第二次跳轉 - 雙擊 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

🎯 找到真正的 flag!

Step 4: 驗證答案

echo -n "FLAG{Redacted}" | md5sum
# 4c827c4ca62781d707cd049da13539ee

程式行為圖解

┌────────────────────────────────────────────┐
│ entry()                                    │
│                                            │
│ 1. 讀取指標 0x00432294                      │
│    └→ 指向 0x00424828                      │
│       └→ "FLAG{CAN-I-MAKE-IT-ANYMORE-      │
│           OBVIOUS}"                        │
│                                            │
│ 2. 計算 MD5                                 │
│    └→ 4c827c4ca62781d707cd049da13539ee    │
│                                            │
│ 3. 顯示在 MessageBox                        │
│    Title: "We've been compromised!"        │
│    Text: (MD5 hash)                        │
└────────────────────────────────────────────┘

Flag

FLAG{Redacted}

MD5: 4c827c4ca62781d707cd049da13539ee

學習重點

🎯 反分析技術識別

  • 大量假 flag: 用來混淆 strings 工具
  • 多層指標: 真正資料藏在指標的指標後面
  • 誤導性字串: MessageBox 標題看似重要,實際是煙霧彈

Challenge 2: strings2.exe

題目描述

與 strings1 相同,找出被計算 MD5 的原始字串。

解題過程

Step 1: Ghidra 反編譯

直接用 Ghidra 打開 strings2.exe,找到 entry 函數:

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);
}

🎯 直接看到完整 flag! 比 strings1 還簡單!

Step 2: 驗證

echo -n "FLAG{Redacted}" | md5sum
# e80782d8c30671eb61acc63c5dca914e

為什麼這題反而更簡單?

原本的設計意圖
根據 MalwareTech 的教學設計,strings2 應該讓學習者:

  1. 查看組合語言中的 stack 操作
  2. 手動解析每個 byte 的 hex 值
  3. 將 hex 轉換成 ASCII 字元重建字串

原本應該看到類似這樣:

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'
; ... 更多逐字元賦值

但 Ghidra 太聰明了!

Ghidra 的反編譯器識別出這是 strncpy 操作,直接顯示完整字串,所以這題變成最簡單的!

strings 工具驗證

strings strings2.exe_ | grep "STACK"
# (沒有結果)

❌ 找不到!因為字串是動態建構在堆疊上,不存在於靜態資料區段。

技術對比

特性 strings1 strings2
儲存位置 .rdata (靜態) Stack (動態)
strings 可見 ✅ (混在假 flag 中) ❌ 完全不可見
Ghidra 難度 需追蹤 2 層指標 直接顯示
隱藏技術 大量假 flag Stack strings

Flag

FLAG{Redacted}

MD5: e80782d8c30671eb61acc63c5dca914e

學習重點

🎯 Stack Strings 技術

  • 定義: 在執行時動態在堆疊上建構字串,而非硬編碼在資料區段
  • 目的: 躲避 strings 等靜態掃描工具
  • 偵測: 必須透過反編譯或反組譯才能發現

🤔 Flag 的幽默

"STACK-STRINGS-ARE-BEST-STRINGS" 意思是「堆疊字串是最好的字串」——因為它們能有效躲避簡單的靜態分析!

Challenge 3: strings3.exe

題目描述

與前兩題相同,找出被計算 MD5 的原始字串。

解題過程

Step 1: Ghidra 反編譯

用 Ghidra 打開 strings3.exe,找到 entry 函數:

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);
  
  // 尋找資源
  local_c = FindResourceA((HMODULE)0x0, "rc.rc", &DAT_00000006);
  
  // 載入 String ID = 0x110 (272) 的字串資源
  local_8 = 0x110;
  LoadStringA((HINSTANCE)0x0, 0x110, &local_4a4, 0x3ff);
  
  // 計算 MD5
  local_a0 = MD5::digestString(local_9c, &local_4a4);
  MessageBoxA((HWND)0x0, local_a0, "We've been compromised!", 0x30);
  ExitProcess(0);
}

Step 2: 理解程式邏輯

關鍵觀察

LoadStringA((HINSTANCE)0x0, 0x110, &local_4a4, 0x3ff);
  • 程式使用 Windows API LoadStringA()
  • 從資源表載入 String ID = 0x110 (十進制 272) 的字串
  • 這個字串被存儲在 PE 檔案的 Resource Section (.rsrc)

Step 3: 在 Ghidra 中定位資源

3.1 開啟 Program Trees

  1. 選單:WindowProgram Trees
  2. 展開,找到 .rsrc section
  3. 雙擊進入 .rsrc

3.2 瀏覽 String Tables

在 Listing 視窗中,你會看到類似這樣的結構:

**************************************************************
* 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 找到 String ID 272

Windows String Resource 結構

  • 每個 String Table 包含 16 個字串
  • String ID 272 = 17 × 16
  • 所以在 Rsrc_StringTable_12_409 (0x11 = 17)

找到的內容:

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 

🎯 找到 flag!

Step 4: 驗證答案

echo -n "FLAG{Redacted}" | md5sum
# 1011cafbd736cdf2ae90964613c911fe

Windows PE 資源表基礎

什麼是 Resource Section?

PE 檔案的 .rsrc 區段可以儲存:

  • 🖼️ 圖示 (Icons)
  • 📝 字串表 (String Tables)
  • 🎵 音效檔
  • 📋 對話框定義
  • 🗂️ 任意二進制資料 (RCDATA)

為什麼要用 Resource Section?

正常用途

  • 多語言支援(不同語言的字串)
  • 分離 UI 資源與程式邏輯
  • 方便資源管理與更新

Flag

FLAG{Redacted}

MD5: 1011cafbd736cdf2ae90964613c911fe

學習重點

🎯 Resource Hiding 技術

  • 定義: 將敏感資料存放在 PE 資源表中
  • 目的: 躲避靜態掃描,支援模組化設計
  • 偵測: 需要專門的 PE 分析工具

🤔 Flag 的幽默

"RESOURCES-ARE-POPULAR-FOR-MALWARE" 意思是「資源表在惡意軟體中很受歡迎」——這句話本身就在教你真實的攻擊技術!

經驗分享

技術對比表

特性 strings1 strings2 strings3
儲存位置 .rdata (靜態資料) Stack (堆疊) .rsrc (資源表)
strings 可見性 ✅ 可見(但有混淆) ❌ 不可見 ❌ 不可見
Ghidra 難度 ⭐⭐ 需追蹤指標 ⭐ 直接顯示 ⭐⭐ 需查資源表
反混淆技術 4000+ 假 flag Stack strings Resource hiding
真實案例 靜態嵌入配置 API hashing 多語言/模組化惡意軟體
完成時間 ~15 分鐘 ~5 分鐘 ~10 分鐘