本文目錄
資安事件調查時,DNS 查詢紀錄常常是最關鍵的線索:哪台電腦查了可疑的網域、什麼時間開始連到中繼站,DNS 記錄裡都看得到。但 Windows DNS Server 預設不會記錄查詢內容,就算開了偵錯記錄,也只是一個巨大的純文字檔,很難搜尋與統計。
這篇整理把 Windows DNS Server 的查詢記錄透過 NXLog 送進 Graylog、再拆解成欄位的完整做法。
架構
Windows DNS Server(偵錯記錄檔) → NXLog(讀檔、過濾、轉碼) → GELF UDP → Graylog(擷取器拆欄位)
啟用 DNS 偵錯記錄
在 DNS 管理員中,對伺服器按右鍵選「內容」,切到「偵錯記錄」頁籤:

建議的勾選:
| 項目 | 建議 | 說明 |
|---|---|---|
| 記錄封包供偵錯之用 | 勾選 | 總開關 |
| 封包方向 | 連入,需要時再加連出 | 連入是用戶端送來的查詢 |
| 傳輸通訊協定 | UDP,需要時再加 TCP | 一般查詢都是 UDP |
| 封包內容 | 查詢/傳輸 | 只記查詢就夠用 |
| 封包類型 | 要求,需要時再加回應 | 要求就有查詢的網域與類型 |
| 詳細資料 | 不勾 | 勾了會多出很多行,NXLog 也要另外處理 |
| 檔案路徑和名稱 | 例如 D:\temp\dns\dns.log | 一定要填,否則預設寫在系統目錄 |
| 最大容量 | 例如 500 MB | 達到上限時會覆寫最舊的內容 |
勾越多,記錄量越大,DNS 伺服器的負擔也越重。我的建議是先從「連入、UDP、查詢、要求」開始,確認記錄量可以接受,再視需要增加。
注意:偵錯記錄只有一個檔案,達到最大容量時會覆寫最舊的內容,不會切成多個檔案。NXLog 要讀的就是這一個檔案,記錄的保存交給 Graylog。
設定 NXLog
在 DNS 伺服器上安裝 NXLog,在設定檔加入:
<Extension gelf>
Module xm_gelf
</Extension>
<Extension charconv>
Module xm_charconv
</Extension>
# 空白行
define DNS_EMPTY_REGEX /^(?:\s*)$/
# 註解行(# 或 ; 開頭)
define DNS_COMMENT_REGEX /^\s*[#;]/
# 不是日期開頭的行(YYYY/M/D 加空白)
define DNS_NOT_DATE_START_REGEX /^(?!\d{4}\/\d{1,2}\/\d{1,2}\s)/
<Input in_dnslog>
Module im_file
File "D:\\temp\\dns\\dns.log"
InputType LineBased
SavePos TRUE
Exec $raw_event = convert($raw_event, "cp950", "utf-8");
<Exec>
# 1) 丟掉空白行
if $raw_event =~ %DNS_EMPTY_REGEX% { drop(); }
# 2) 丟掉註解行
if $raw_event =~ %DNS_COMMENT_REGEX% { drop(); }
# 3) 丟掉不是日期開頭的行(檔頭說明、詳細資料的延續行)
if $raw_event =~ %DNS_NOT_DATE_START_REGEX% { drop(); }
# 整行放在 dns_message,交給 Graylog 拆解
$dns_message = $raw_event;
# 摘要用行尾的查詢網域
if $raw_event =~ /\s([^\s]+)$/ {
$short_message = $1;
} else {
$short_message = $raw_event;
}
delete($full_message);
delete($raw_event);
$SourceName = "windows-dns-debug";
$SeverityValue = 6;
</Exec>
</Input>
<Output out_dnslog>
Module om_udp
Host 192.168.1.10:12201
OutputType GELF
</Output>
<Route dnslog>
Path in_dnslog => out_dnslog
</Route>
幾個重點:
File:要和 DNS 偵錯記錄設定的路徑一致convert(..., "cp950", "utf-8"):繁體中文版 Windows 寫出的記錄是 Big5(cp950),要轉成 UTF-8,否則時間欄位裡的「上午」「下午」會變亂碼- 三條過濾規則:偵錯記錄檔開頭有一大段說明文字,這些都不是查詢記錄,先在 NXLog 端丟掉,不要送進 Graylog
Host:改成你的 Graylog 主機與 GELF UDP Input 的通訊埠
注意:第三條規則「日期開頭」是依繁體中文版 Windows 的日期格式(2025/8/19 下午 03:26:43)寫的。英文版 Windows 的格式是 8/19/2025 3:26:43 PM,這條規則會把所有記錄都丟掉,要改成對應的格式。先打開記錄檔看一下實際的格式再調整。
重新啟動 NXLog 服務後,檢查 NXLog 自己的記錄檔,看到成功連線就代表設定沒問題:
INFO [om_udp|out_dnslog] connecting to 192.168.1.10:12201
INFO [om_udp|out_dnslog] successfully connected to 192.168.1.10:12201
Graylog 接收
Graylog 要先有一個 GELF UDP 的 Input,通訊埠和 NXLog 設定的一致。收到記錄後,在搜尋頁面展開任一筆,至少會看到這些欄位:
| 欄位 | 內容 |
|---|---|
dns_message | 完整的一行 DNS 記錄 |
message | 查詢的網域(原始格式) |
SourceName | windows-dns-debug |
SourceModuleName、SourceModuleType | NXLog 的 Input 名稱與模組 |
EventReceivedTime | NXLog 讀到這一行的時間 |
左側的「Received by」會顯示是哪個 Input 收到的,下一步要用到它。
用擷取器拆解欄位
到「System → Inputs」,找到剛才的 Input,按「Manage extractors」:

右上角「Actions → Import extractors」:

貼上以下 JSON,按「Add extractors to input」:
{
"extractors": [
{
"title": "msdns_1",
"extractor_type": "grok",
"converters": [],
"order": 0,
"cursor_strategy": "copy",
"source_field": "dns_message",
"target_field": "",
"extractor_config": {
"grok_pattern": "%{DATA:QueryDateTime}\\s+%{WORD:thread_id}\\s+%{WORD:context}\\s+%{BASE16NUM:internal_id}\\s+%{WORD:protocol}\\s+%{WORD:direction}\\s+%{IPV4:remote_ip}\\s+%{BASE16NUM:xid}\\s+(?:(?<qr>R)\\s+)?%{WORD:opcode}\\s+\\[%{BASE16NUM:flags_hex}\\s+%{WORD:flags_codes}\\s+%{WORD:response_code}\\]\\s+%{WORD:querytype}\\s+%{GREEDYDATA:queryname_s}"
},
"condition_type": "none",
"condition_value": ""
},
{
"title": "msdns_queryname_s",
"extractor_type": "regex_replace",
"converters": [],
"order": 1,
"cursor_strategy": "copy",
"source_field": "queryname_s",
"target_field": "queryname",
"extractor_config": {
"regex": "\\(\\d+\\)",
"replacement": ".",
"replace_all": true
},
"condition_type": "none",
"condition_value": ""
},
{
"title": "msdns_queryname_2",
"extractor_type": "regex_replace",
"converters": [],
"order": 2,
"cursor_strategy": "copy",
"source_field": "queryname",
"target_field": "queryname",
"extractor_config": {
"regex": "^\\.+|\\.+$",
"replacement": "",
"replace_all": true
},
"condition_type": "none",
"condition_value": ""
}
],
"version": "6.0.3"
}

三個擷取器的作用:
| 順序 | 名稱 | 作用 |
|---|---|---|
| 1 | msdns_1 | 用 Grok 把整行拆成時間、方向、來源 IP、查詢類型、回應碼等欄位 |
| 2 | msdns_queryname_s | 把 DNS 記錄的網域格式 (7)master5(10)teamviewer(3)com(0) 裡的 (數字) 換成 . |
| 3 | msdns_queryname_2 | 去掉頭尾多出來的 .,得到 master5.teamviewer.com |
DNS 偵錯記錄裡的網域是以「(長度)標籤」的格式記錄的,所以要經過第 2、3 步才會變成一般看得懂的網域名稱。第 3 步的取代字元我設成空字串,這樣網域頭尾才不會殘留空白。
確認順序
擷取器一定要依序執行,順序錯了欄位就拆不出來。確認清單由上到下是 msdns_1、msdns_queryname_s、msdns_queryname_2,不對的話按「Sort extractors」調整:

驗證
新進來的記錄就會帶有拆好的欄位:

分析時最常用的欄位:
| 欄位 | 用途 |
|---|---|
queryname | 查詢的網域,找可疑網域、統計熱門網域 |
remote_ip | 發出查詢的用戶端 IP,找出是哪台電腦 |
querytype | 查詢類型(A、AAAA、MX、TXT 等),大量 TXT 查詢可能是 DNS 通道 |
response_code | 回應碼,大量 NXDOMAIN 可能是惡意程式在試網域 |
direction | Rcv 是收到的查詢,Snd 是送出的回應 |
例如找出查詢某個可疑網域的電腦:
queryname:*example-bad-domain.com
再用 remote_ip 做統計,就知道是哪幾台電腦。
注意事項
- 擷取器只對新進來的記錄生效,之前收到的記錄不會重新拆解
- 記錄量:DNS 查詢量大的環境,一天可能有上百萬筆,Graylog 的儲存空間與 index 保留期要先估算
- 隱私:DNS 查詢紀錄可以看出每個人瀏覽了哪些網站,屬於敏感資料,存取權限要控管,並在資訊安全政策中說明
- Windows Server 2016 以上也可以改用 DNS 的分析事件記錄(Analytic log,透過 ETW),效能影響比偵錯記錄小,但要搭配支援 ETW 的收集工具
結論
Windows DNS 偵錯記錄本身很難用,但送進 Graylog 拆成欄位之後,就能直接搜尋「誰查了什麼網域」,資安事件調查時非常有用。關鍵是 NXLog 端先轉碼、過濾掉非記錄的行,Graylog 端的擷取器順序要對。
參考資料
- Microsoft Learn:DNS logging and diagnostics
https://learn.microsoft.com/zh-tw/windows-server/networking/dns/dns-logging-and-diagnostics?tabs=desktop-experience - NXLog 以記錄時間覆寫 timestamp
https://blog.jason.tools/2026/01/nxlog-logeventtime.html - NXLog CE 記憶體暴增案例
https://blog.jason.tools/2026/01/nxlog-oversized-string.html



