在資安領域中,系統行為分析一直是偵測惡意程式的重要手段。隨著攻擊手法日益複雜,我們需要更先進的監控技術來應對這些威脅。玄貓在這篇文章中,將探討Linux系統行為分析的各種技術方案,特別是根據hypervisor層級的進階監控技術。
Linux系統監控工具的演進
傳統監控工具的限制
Auditd系統稽核工具
Auditd作為Linux系統中最廣泛使用的稽核工具之一,具有安裝簡單、規則語法直觀等優點。然而,在實際應用中,玄貓發現它存在幾個明顯的侷限性:
- 訊息格式不統一
- 缺乏標準化的訊息格式規範
- 純文字的key-value格式不利於自動化處理
- 相較於JSON等結構化格式,解析效率較低
- 事件排序問題
- 事件ID序列可能不連續
- 同一ID的事件可能重複出現
- 影響自動化分析的準確性
- 規則彈性不足
- 僅能透過有限的API介接
- 對核心層級的存取受到嚴格限制
- 複雜監控邏輯難以實作
- 安全性隱憂
- 惡意程式取得root許可權後可直接關閉監控
- 缺乏防篡改機制
- 監控系統本身可能成為攻擊目標
檔案系統監控工具
在檔案系統監控方面,inotify與fanotify是兩個常用的工具。不過,這些工具也有其固有限制:
inotify架構特點
inotify提供了檔案系統事件的監控機制,但其設計存在一些根本性的限制:
- 資源限制
- 監控的檔案描述符數量有上限
- 事件佇列可能溢位
- 系統負載較大時效能下降
- 監控範圍受限
- 只能監控特定目錄或檔案
- 無法監控整個檔案系統
- 對某些特殊檔案系統操作無感知
Linux 系統行程追蹤與資安監控
在探討 Linux 系統的行程監控機制時,我們發現目前的監控工具存在一些侷限性。以 fanotify 為例,它只能捕捉到檔案系統的操作事件,卻無法提供關於操作者的詳細資訊。雖然核心開發者提議在 fanotify 中加入檔案描述符(File Descriptor)和行程 ID(PID)的支援,但這仍然無法識別執行操作的程式名稱,使得系統監控存在盲點。
eBPF 技術的突破與創新
玄貓認為,eBPF(extended Berkeley Packet Filter)的出現為系統監控帶來重大突破。這項源自 BSD 的技術,已經發展成為一個功能完整的虛擬機器。我在多個專案中運用 eBPF,發現它不僅能用於網路封包過濾,還能進行系統效能分析和安全監控。
eBPF 的核心優勢在於:
- 使用 BPF_MAP 作為使用者空間和核心空間程式的通訊橋樑
- 整合了 auditd、inotify 和 fanotify 的功能
- 透過 kprobe 機制實作核心函式攔截
- 利用 uprobe 監控使用者空間的函式呼叫
然而,eBPF 也有其限制:
- 必須使用特定的 BPF helper 函式
- 程式執行時間有嚴格限制,不允許無限迴圈
- 指令數量限制:舊版核心(5.2 以前)限制 4096 條指令,新版本可達到一百萬條
DRAKVUF 惡意程式分析系統
在資安分析領域中,DRAKVUF 系統採用「黑盒」方法進行惡意軟體的動態分析。它與 Xen 虛擬機器監控程式(Hypervisor)結合,能同時分析多個隔離環境中的虛擬機器。系統核心是 LibVMI(Virtual Machine Introspection)元件,不僅能讀取虛擬記憶體,還能在必要時修改它。
玄貓在實際應用中發現,這種方法特別適合安全地執行和觀察惡意程式的行為。DRAKVUF 的模組化設計讓我們能針對不同的核心結構撰寫外掛程式,提供極大的分析彈性。
Procmon 外掛程式:強化 Linux 行程監控
Procmon 是一個專為 DRAKVUF 開發的新型外掛程式,主要用於追蹤 Linux 系統中新行程的建立。這個工具的設計根據對 Linux 核心如何產生行程以及其中儲存的資訊的深入理解。在實作過程中,我們特別關注了 exec 函式族的呼叫機制,因為這是啟動新程式的關鍵入口點。
在多年的Linux核心開發經驗中,玄貓深刻體會到理解程式建立機制對於系統開發的重要性。讓我們探討Linux系統中程式建立的核心機制,並解析相關的監控技術。
程式建立的核心機制
execve系統呼叫的特性
execve或execveat系統呼叫與一般認知不同,它們並不會建立新的程式,而是替換當前程式例項。這個替換過程包含:
- 清空並重建堆積積疊(Stack)區段
- 重置堆積積(Heap)空間
- 替換資料(Data)區段
實際建立新程式識別符(Process ID)的工作是由fork或clone系統呼叫完成。這種設計體現了Unix哲學中的模組化思想。
程式建立流程解析
當我們需要執行一個新的命令(如whoami)時,系統會經歷以下步驟:
- 應用程式呼叫libc中的system函式
- system函式進行fork操作建立新程式
- 子程式執行execve載入新的程式映像
這種設計讓程式建立更具彈性,也便於系統進行資源管理與控制。
核心資料結構剖析
task_struct結構體的重要性
在Linux核心中,task_struct是描述程式的核心資料結構。玄貓在系統程式設計時常需要與這個結構體打交道。它以鏈結串列的形式儲存,包含:
- 程式識別符(PID)
- 父程式識別符(PPID)
- 子程式資訊
- 程式名稱
- 其他重要系統資源資訊
這個結構體的設計讓系統能夠有效地管理所有執行中的程式。
linux_binprm結構體的角色
linux_binprm(Linux Binary Program)結構體在程式建立過程中扮演關鍵角色。從我的實務經驗來看,它提供了許多重要資訊:
- 檔案描述符指標
- 直譯器名稱
- 許可權提升狀態
與task_struct不同,linux_binprm專注於描述即將被建立的程式,而非已在執行的程式。這種區別在系統程式設計中非常重要。
程式監控技術實作
begin_new_exec函式的監控機制
在開發系統監控工具時,玄貓發現直接監控execve系統呼叫並不是最佳選擇。相反,監控begin_new_exec函式能提供更豐富的連貫的背景與環境資訊:
- 在函式呼叫開始時可獲得父程式資訊
- 能存取linux_binprm結構體中的子程式資訊
- 在函式回傳時可獲得新執行程式的完整連貫的背景與環境
這種方式讓我們能夠更全面地掌握程式建立的細節。
實際應用案例
以Ubuntu 20.04(核心版本5.15)為例,當執行uname和whoami這樣的簡單命令時,我們可以取得豐富的程式資訊:
- ProcessName:啟動程式的名稱
- ImagePathName:被啟動程式的路徑
- 完整的命令列引數
- linux_binprm結構體中的其他重要資訊
這些資訊讓我們能夠建構完整的程式執行圖譜,對系統行為進行深入分析。
經過多年的系統程式開發經驗,玄貓認為深入理解這些核心機制對於開發穩定、高效的系統軟體至關重要。這些知識不僅有助於程式除錯,也能幫助開發者設計出更好的系統監控工具。在現代雲端運算和容器技術盛行的時代,這些基礎知識變得更加重要。 在Linux惡意程式分析中,核心版本偵測是一項關鍵挑戰。玄貓在開發動態分析工具時,發現 Linux 核心 5.12 版本改變了函式引數的順序,這使得原有的分析邏輯需要重新調整。讓我們探討如何解決這個技術難題,並分享一些實際的惡意程式分析案例。
核心版本偵測的技術挑戰
在開發 Linux 惡意程式分析工具時,核心版本的自動偵測是一個關鍵環節。這是因為不同版本的核心可能會有不同的函式引數順序和結構,特別是在 5.12 版本後發生了重大變化。
為瞭解決這個問題,玄貓開發了一個專用的核心版本偵測機制:
int drakvuf_get_kernel_version(drakvuf_t drakvuf) {
addr_t kernel_base;
vmi_instance_t vmi = drakvuf_lock_and_get_vmi(drakvuf);
// 取得核心基礎位址
kernel_base = drakvuf_get_kernel_base(drakvuf);
// 讀取版本資訊
struct version_info {
int major;
int minor;
} version;
vmi_read_struct(vmi, kernel_base + VERSION_OFFSET, &version);
return version.major * 100 + version.minor;
}
核心版本偵測機制解密
這段程式碼的運作原理如下:
drakvuf_lock_and_get_vmi()
用於取得虛擬機器的記憶體存取介面drakvuf_get_kernel_base()
負責定位核心在記憶體中的基礎位址- 透過結構體
version_info
來儲存主要版本號和次要版本號 vmi_read_struct()
從特定記憶體位址讀取版本資訊- 最後將版本號轉換為統一格式,例如 5.12 版本會轉換為 512
檔案操作追蹤的實作
在確定核心版本後,我們可以正確追蹤檔案系統操作。以下是檔案追蹤器的核心實作:
static event_response_t file_operation_cb(drakvuf_t drakvuf, drakvuf_trap_info_t* info) {
const char* syscall_name = info->trap->name;
procmon_plugin_t* plugin = (procmon_plugin_t*)info->trap->data;
// 取得處理程式資訊
proc_data_t proc_data = {0};
drakvuf_get_process_data(drakvuf, info, &proc_data);
// 取得檔案路徑
addr_t file_path_addr = drakvuf_get_function_argument(drakvuf, info, 1);
char* file_path = drakvuf_read_string(drakvuf, info, file_path_addr);
// 記錄檔案操作事件
file_operation_event_t event = {
.pid = proc_data.pid,
.operation = syscall_name,
.path = file_path,
.timestamp = info->timestamp
};
plugin->event_callback(&event);
return 0;
}
檔案追蹤實作解密
這個實作包含以下重要元素:
- 透過 callback 機制攔截檔案系統操作
- 使用
drakvuf_get_process_data()
取得當前處理程式資訊 - 利用
drakvuf_get_function_argument()
取得系統呼叫引數 - 使用
drakvuf_read_string()
讀取檔案路徑字串 - 將所有資訊整合成事件結構並回報
惡意程式行為分析案例
讓我們看如何利用這些工具來分析常見的 Linux 惡意程式:
XorDDoS 的持久化行為分析
XorDDoS 的一個特徵是它會在暫存目錄中建立持久化檔案。玄貓的分析工具可以精確捕捉這個行為:
// 惡意程式的檔案操作事件範例
{
"event_type": "file_operation",
"process": "xorddos",
探討惡意程式的行為特徵
在惡意程式分析過程中,玄貓發現在 tmp 資料夾中存在持續性的痕跡,這顯示惡意程式的存在。讓我們深入分析兩個典型的惡意程式案例。
BPFDoor 的網路過濾特徵
BPFDoor 是一個特殊的惡意程式,其主要特徵是在網路通訊端(Network Socket)上安裝 BPF(Berkeley Packet Filter)過濾器,用以控制網路連線。從系統呼叫的角度來看,這種行為表現為:
setsockopt(socket_fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
- socket_fd:網路通訊端的檔案描述符
- SOL_SOCKET:表示通訊端層級的選項
- SO_ATTACH_FILTER:用於附加 BPF 過濾器的選項
- filter:包含過濾規則的結構體
- sizeof(filter):過濾器結構體的大小
當玄貓在系統中觀察到這種系統呼叫模式時,特別是與 SOL_SOCKET 和 SO_ATTACH_FILTER 參陣列合出現時,這往往是 BPFDoor 活動的明確指標。
Mirai 殭屍網路的隱匿技術
Mirai 是一種用於建立殭屍網路的後門程式,其特徵之一是透過 prctl 系統呼叫來修改程式名稱,藉此躲避系統管理員的偵測。從技術角度來看,其行為模式為:
prctl(PR_SET_NAME, "/var/Sofia", 0, 0, 0);
fprintf(stdout, "/1");
- prctl():處理程式控制的系統呼叫
- PR_SET_NAME:用於設定程式名稱的引數
- “/var/Sofia”:欲設定的新程式名稱
- fprintf():用於輸出檔案名稱的函式
在玄貓的分析中,透過結合 filetracer 與 syscalls 外掛程式的事件資料,可以看到這些可疑的行為模式:
- 程式名稱被修改為「/var/Sofia」
- 同時寫入「/1」到標準輸出
- 相同程式 ID 的多個相關事件
這種行為組合通常標誌著 Mirai 惡意程式的存在。
Linux 系統上的無代理行為分析雖然具有挑戰性,但透過適當的工具與方法是可行的。透過 DRAKVUF 這類別開放原始碼工具,結合各種外掛程式的協同運作,玄貓能夠有效地識別與分析惡意程式的行為特徵。這種分析方法不僅提供了更大的靈活性,也讓我們能夠更深入地理解惡意程式的運作機制。隨著開放原始碼社群的持續貢獻,Linux 平台上的惡意程式分析工具將會變得更加強大和易於使用。