引導撰寫用於惡意軟體識別的高品質 YARA-X 偵測規則。適用於撰寫、審查或最佳化 YARA 規則。涵蓋命名規範、字串選擇、效能最佳化、從舊版 YARA 遷移,以及降低誤報率。觸發條件:YARA、YARA-X、malware detection、threat hunting、IOC、signature、crx module、dex module。
YARA-X 規則撰寫指南
撰寫能精準擷取惡意軟體、又不會讓誤報淹沒你的偵測規則。
本 Skill 專為 YARA-X 設計。YARA-X 是以 Rust 開發的舊版 YARA 繼任者,為 VirusTotal 生產系統提供支援,也是官方推薦的實作版本。若已有現成規則,請參閱 從舊版 YARA 遷移。
核心原則
-
字串必須能產生良好的 Atom — YARA 會擷取 4 個位元組的子序列以進行快速比對。包含重複位元組、常見序列或少於 4 個位元組的字串,會迫使過多檔案進入耗時的位元組碼驗證流程。
-
鎖定特定家族,而非模糊分類 —「偵測勒索軟體」這類規則等於什麼都抓又什麼都抓不到;「偵測 LockBit 3.0 組態擷取常式」才能精準命中目標。
-
部署前務必對正常檔案進行測試 — 一條會觸發 Windows 系統檔案告警的規則毫無價值。部署前請透過 VirusTotal 的正常檔案語料庫(goodware corpus)或自訂的乾淨檔案集驗證。
-
先執行低代價檢查以觸發捷徑運算 — 在執行高成本的字串搜尋或模組呼叫前,先放上
filesize < 10MB and uint16(0) == 0x5A4D這類快捷條件。 -
Metadata 即檔案說明 — 未來的你(以及你的團隊)需要知道這條規則抓到了什麼、原因為何,以及樣本來源。
適用場景
- 撰寫用於惡意軟體偵測的新 YARA-X 規則
- 審查現有規則的品質或效能問題
- 最佳化執行速度過慢的規則集
- 將 IOC 或威脅情資轉化為偵測特徵碼
- 除錯與修正誤報(False Positive)問題
- 準備將規則部署至生產環境
- 將舊版 YARA 規則遷移至 YARA-X
- 分析 Chrome 擴充功能(crx 模組)
- 分析 Android 應用程式(dex 模組)
不適用場景
- 需要反組譯的靜態分析 → 使用 Ghidra/IDA 相關 Skill
- 動態惡意軟體分析 → 使用沙盒分析相關 Skill
- 網路層級偵測 → 使用 Suricata/Snort 相關 Skill
- 使用 Volatility 進行記憶體鑑識 → 使用記憶體鑑識相關 Skill
- 純 Hash 偵測 → 直接使用 Hash 清單即可
YARA-X 概觀
YARA-X 是以 Rust 開發的舊版 YARA 繼任者:常規表示式比對速度快 5-10 倍、更友善的錯誤訊息、內建格式化工具、更嚴謹的驗證機制、全新模組(crx、dex),且具備 99% 的規則相容性。
安裝: brew install yara-x (macOS) 或 cargo install yara-x
核心命令: yr scan、yr check、yr fmt、yr dump
跨平台考量
YARA 適用於任何檔案類型。請依據目標對象調整模式:
| 平台 | Magic Bytes | 不佳的字串 | 理想的字串 |
|---|---|---|---|
| Windows PE | uint16(0) == 0x5A4D |
API 名稱、Windows 路徑 | Mutex 名稱、PDB 路徑 |
| macOS Mach-O | uint32(0) == 0xFEEDFACE (32-bit), 0xFEEDFACF (64-bit), 0xCAFEBABE (universal) |
常見的 Obj-C 方法 | 鍵盤記錄器字串、常駐(persistence)路徑 |
| JavaScript/Node | (無須設定) | require, fetch, axios |
混淆器特徵碼、eval+decode 鏈 |
| npm/pip packages | (無須設定) | postinstall, dependencies |
可疑的套件名稱、資料外洩 (exfil) URL |
| Office 文件 | uint32(0) == 0x504B0304 |
VBA 關鍵字 | 巨集自動執行、編碼過後的 Payload |
| VS Code 擴充功能 | (無須設定) | vscode.workspace |
罕見的 activationEvents、隱藏檔案存取行為 |
| Chrome 擴充功能 | 使用 crx 模組 |
常見 Chrome API | 權限濫用、manifest 異常 |
| Android Apps | 使用 dex 模組 |
標準 DEX 結構 | 混淆後的類別(class)、可疑權限 |
macOS 惡意軟體偵測
目前尚無專用的 Mach-O 模組,請結合 Magic Bytes 檢查與字串模式:
Magic bytes:
// Mach-O 32-bit
uint32(0) == 0xFEEDFACE
// Mach-O 64-bit
uint32(0) == 0xFEEDFACF
// 通用二進位檔 (Fat Binary)
uint32(0) == 0xCAFEBABE or uint32(0) == 0xBEBAFECA
macOS 惡意軟體的良好指標:
- 鍵盤記錄器跡象:
CGEventTapCreate、kCGEventKeyDown - SSH Tunnel 字串:
ssh -D、tunnel、socks - 常駐路徑:
~/Library/LaunchAgents、/Library/LaunchDaemons - 憑證竊取:
security find-generic-password、keychain
來自 Airbnb BinaryAlert 的範例模式:
rule SUSP_Mac_ProtonRAT
{
strings:
// Library indicators
$lib1 = "SRWebSocket" ascii
$lib2 = "SocketRocket" ascii
// Behavioral indicators
$behav1 = "SSH tunnel not launched" ascii
$behav2 = "Keylogger" ascii
condition:
(uint32(0) == 0xFEEDFACF or uint32(0) == 0xCAFEBABE) and
any of ($lib*) and any of ($behav*)
}
JavaScript 偵測決策樹
撰寫 JavaScript 規則?
├─ npm 套件?
│ ├─ 檢查 package.json 模式
│ ├─ 尋找 postinstall/preinstall Hook
│ └─ 鎖定資料外洩 (exfil) 模式:fetch + 環境變數存取 + 憑證路徑
├─ 瀏覽器擴充功能?
│ ├─ Chrome:使用 crx 模組
│ └─ 其他:鎖定 manifest 模式、背景腳本行為
├─ 獨立 JS 檔案?
│ ├─ 尋找混淆標記:eval+atob、fromCharCode 鏈
│ ├─ 鎖定獨特的函式/變數名稱(通常在縮減後依然保留)
│ └─ 檢查是否有打包/編碼過後的 Payload
└─ 經過縮減(Minified)/ webpack 打包檔?
├─ 鎖定打包後仍保留的獨特字串(URL、Magic Value)
└─ 避開函式名稱(會被混淆/重命名)
JavaScript 特有的良好字串:
- 以太坊函式選擇器 (Ethereum function selectors):
{ 70 a0 82 31 }(transfer) - 零寬字元(隱寫術):
{ E2 80 8B E2 80 8C } - 混淆器特徵碼:
_0x、var _0x - 特定的 C2 模式:網域名稱、webhook URL
JavaScript 特有的不佳字串:
require、fetch、axios— 太過常見Buffer、crypto— 到處都有合法的用途- 單純的
process.env— 需要搭配特定的環境變數名稱
必備工具箱
| 工具 | 用途 |
|---|---|
| yarGen | 擷取候選字串:yarGen.py -m samples/ --excludegood → 透過 yr check 驗證 |
| FLOSS | 擷取混淆字串/堆疊字串:floss sample.exe(當 yarGen 無效時使用) |
| yr CLI | 驗證:yr check;掃描:yr scan -s;檢視:yr dump -m pe |
| signature-base | 參考高品質規則範例 |
| YARA-CI | 在部署前使用正常檔案語料庫進行測試 |
掌握這五個工具即可,不要被複雜的工具清單分心。
應拒絕的合理化藉口
當你發現自己出現以下想法時,請立刻停下來重新評估。
| 合理化藉口 | 專家回應 |
|---|---|
| 「這個通用的字串已經夠獨特了」 | 先對正常檔案進行測試,你的直覺通常是錯的。 |
| 「這是 yarGen 產生的字串」 | yarGen 只是建議,你必須親自驗證。請手動檢查每一個字串。 |
| 「在我的 10 個樣本上測試過都沒問題」 | 10 個樣本不等於生產環境。請使用 VirusTotal 的正常檔案庫測試。 |
| 「寫一條規則抓出所有變種」 | 這會引發誤報風暴。請針對特定家族編寫。 |
| 「等遇到誤報再改精細一點」 | 預先撰寫嚴謹的規則。誤報會破壞使用者信任。 |
| 「這個 Hex 模式很獨特」 | 在單一樣本中獨特 ≠ 在整個惡意軟體生態系中獨特。 |
| 「效能不重要」 | 一條過慢的規則會拖慢整個規則集。請最佳化 Atom。 |
| 「PEiD 規則依然有用」 | 已經過時了。32 位元的加殼工具現在已不具參考價值。 |
| 「我以後再補更多條件」 | 部署弱規則 = 已經造成傷害。 |
| 「這只是用來做 Hunting 的」 | Hunting 規則終將成為偵測規則,應保持相同的品質標準。 |
| 「這個 API 名稱代表它有惡意」 | 合法軟體也會使用相同的 API,需要結合行為上下文判斷。 |
| 「對這些常見字串用 any of 就好」 | 常見字串搭配 any = 誤報災難。只有在字串本身即具獨特性時才用 any of。 |
| 「這個常規表示式已經夠精準了」 | /fetch.*token/ 會命中所有認證程式碼,必須加上資料外洩目的地條件。 |
| 「這個 JavaScript 看起來很乾淨」 | 攻擊者會在合法程式碼中植入注入碼。請檢查是否有 eval+decode 鏈。 |
| 「為了彈性我用 .* 好了」 | 無界限的常規表示式 = 效能災難 + 記憶體爆炸。請改用 .{0,30}。 |
| 「我到處都加上 --relaxed-re-syntax」 | 這只會掩蓋真正的 Bug。請修復常規表示式本身,而不是藏起問題。 |
決策樹
這個字串夠好嗎?
這個字串夠好嗎?
├─ 少於 4 個位元組?
│ └─ 否 — 尋找更長的字串
├─ 包含重複位元組 (0000, 9090)?
│ └─ 否 — 加上前後上下文
├─ 是 API 名稱 (VirtualAlloc, CreateRemoteThread)?
│ └─ 否 — 改用呼叫點 (call site) 的 Hex 模式
├─ 出現在 Windows 系統檔案中?
│ └─ 否 — 太過通用,尋找獨特特徵
├─ 是常見路徑 (C:\Windows\, cmd.exe)?
│ └─ 否 — 尋找惡意軟體專屬路徑
├─ 對此惡意軟體家族是獨一無二的嗎?
│ └─ 是 — 使用它
└─ 也出現在其他惡意軟體中?
└─ 也許 — 與家族專屬標記組合使用
何時使用 "all of" 與 "any of"
應該要求匹配所有字串還是允許任意字串?
├─ 字串個別對惡意軟體具備獨特性?
│ └─ any of them(單一字串即具備可疑性)
├─ 字串本身常見,但組合起來很可疑?
│ └─ all of them(需要完整模式匹配)
├─ 字串具備不同的置信度等級?
│ └─ 分組:all of ($core_*) and any of ($variant_*)
└─ 出現大量誤報?
└─ 嚴格化:將 any 改為 all,並增加所需的必要字串
來自生產環境的教訓: 規則中使用 any of ($network_*) 且字串包含 "fetch"、"axios"、"http" 時,幾乎會命中所有的 Web 應用程式。將規則修改為同時需要憑證路徑 AND 網路呼叫 AND 資料外洩目的地後,誤報完全消除。
何時該放棄目前的規則撰寫方向
在以下情況請果斷停下並轉向:
-
yarGen 只回傳 API 名稱與通用路徑 → 參閱 當字串失效時,轉向結構分析
-
找不到 3 個獨特字串 → 很可能已加殼。請針對解殼後的版本撰寫,或轉為偵測加殼器本身。
-
規則命中正常檔案(Goodware) → 字串不夠獨特。1-2 個命中 = 調查並加嚴;3-5 個命中 = 尋找其他指標;6 個以上命中 = 重頭開始。
-
即使最佳化後效能依然極差 → 架構問題。拆分為多個聚焦的規則,或加入嚴格的前置過濾條件。
-
描述很難寫 → 規則定義太模糊。如果你無法清楚說明它抓到了什麼,代表它抓到了太多不相關的東西。
誤報(False Positive)除錯流程
誤報調查流程:
│
├─ 1. 命中了哪一個字串?
│ 執行:yr scan -s rule.yar false_positive.exe
│
├─ 2. 它是否屬於合法的 Library?
│ └─ 加上:not $fp_vendor_string 排除條件
│
├─ 3. 是否為常見的開發模式?
│ └─ 尋找更具體的指標,替換該字串
│
├─ 4. 是否為多個通用字串組合在一起被匹配?
│ └─ 加嚴條件為必須匹配全部 + 加上獨特標記
│
└─ 5. 惡意軟體是否使用了常見技術?
└─ 鎖定惡意軟體專屬的實作細節,而非技術本身
Hex vs 文字 vs 常規表示式
應該使用哪種字串類型?
│
├─ 精確的 ASCII/Unicode 文字?
│ └─ TEXT: $s = "MutexName" ascii wide
│
├─ 特定的位元組序列?
│ └─ HEX: $h = { 4D 5A 90 00 }
│
├─ 帶有變化的位元組序列?
│ └─ 帶萬用字元的 HEX: { 4D 5A ?? ?? 50 45 }
│
├─ 具備結構的模式(URL、路徑)?
│ └─ 有界限的常規表示式:/https:\/\/[a-z]{5,20}\.onion/
│
└─ 未知編碼(XOR、base64)?
└─ 帶修飾符的 TEXT: $s = "config" xor(0x00-0xFF)
樣本是否已加殼?(優先檢查)
在撰寫任何基於字串的規則之前:
樣本是否已加殼?
├─ 熵值 (Entropy) > 7.0?
│ └─ 很可能已加殼 — 先尋找未加殼的層級
├─ 很少或幾乎沒有可讀字串?
│ └─ 很可能已加殼 — 改用熵值、PE 結構或加殼器特徵碼
├─ 偵測到 UPX/MPRESS/自訂加殼器?
│ └─ 鎖定解殼後的 Payload,或者直接偵測加殼器本身
└─ 有可讀的字串可用?
└─ 繼續進行基於字串的偵測
專家指引: 切勿針對加殼層撰寫規則。加殼方式會變,但 Payload 不會變。
當字串失效時,轉向結構分析
如果 yarGen 只回傳 API 名稱與通用路徑:
St






