windows 計劃任務的變遷及其非常規排查

作者:rep_Su@青藤實驗室
原文鏈接:https://mp.weixin.qq.com/s/aS5MRwnYR5pqE1PmKiH24w

之前我們分享了一篇 windows 計劃任務隱藏新姿勢分享,看到留言感興趣的是計劃任務的排查,因此又出了一篇詳細的排查教程,希望對大家有所幫助。

研究背景

在 server 2012 上使用 schtasks 創建計劃任務時,我意外的發現,當分別使用參數 /mo/ri 時,計劃任務創建的方式有所不同,具體如下圖:

我在其參數說明中也未見對此現象的具體描述



且在計劃任務管理器上發現其區別似乎只在於觸發器的不同。抱着一探究竟的想法,我花了一週左右的時間研究並整理了此文,來爲 windows 計劃任務的相關問題提供些綿薄之力。

本文中所研究的計劃任務均由 schtasks.exe 創建。

探索原因

爲了一探究竟,在 server 2012 上,我分別對兩種啓動流程進行了追蹤,在初步的瞭解之後,我發現隨着 windows 系統的變遷,計劃任務的相關進程的啓動和計劃任務的創建有舊版和新版之分,爲了更好的理解,結合研究的內容下述依次對新舊版進行大致的說明。

舊版的計劃任務

進程啓動

通過對計劃任務的監控,我發現,在 server 2012 上,計劃任務進程的啓動,主要依賴於計劃任務文件的讀取和註冊表項配置的訪問。當使用參數 /ri 時,計劃任務進程創建的堆棧如下圖所示:

我發現此時其進程創建的關鍵模塊爲 schedsvc,通過堆棧可以大致看出此類計劃任務進程的創建由 schedsvc 管理, schedsvc 會啓動回調 job,從隊列中捕獲到並啓動計劃任務的 job,進而創建計劃任務進程。

在 schedsvc.dll 中,我可以清晰的看到,計劃任務進程創建時,實際上創建的是 taskeng.exe 進程,這也解釋了爲什麼我看到此類計劃任務的父進程是 taskeng。

在這一參數創建的計劃任務進程啓動時,我發現,schedsvc.dll 與 taskeng 分工明確,schedsvc.dll 主要負責註冊表中對應計劃任務的讀取及更新,其中比較關鍵的行爲是:schedsvc 會負責從計劃任務的job隊列中啓動計劃任務,並且將計劃任務進程的執行時間寫入註冊表項 DynamicInfo中

寫入註冊表項DynamicInfo的過程如下



寫入註冊表的值如下

寫入後註冊表中對應項的值如下

通過分析,我發現,在 DynamicInfo 註冊表項中記錄的二進制偏移+c處的內容正是其計劃任務執行的 UTC 時間信息。

通過對計劃任務進程行爲的追蹤,我發現,每次計劃任務進程被創建時,DynamicInfo 註冊表項均會被更新,即通過對註冊表中的 DynamicInfo 的監控及其中時間數據的解析,我可以知道某計劃任務的進程在某時刻被執行,從而定位到對應的計劃任務。 而taskeng則主要負責 \Windows\System32\Tasks 目錄下的計劃任務文件的讀取及啓動對應的計劃任務進程









Task 文件讀取如下

計劃任務進程創建如下
當將參數更改爲 /mo 時,我發現此時的進程創建的堆棧完全改變了,如下圖



可以看到,之前的 schedsvc 模塊已經完全看不到了,取而代之的是 UBPM 和 EventAggregation,UBPM全稱是統一後臺進程管理器。它是自 Windows 7 和 Windows Server 2008 R2 引入的一種新的調度引擎,關於其更多的介紹,可以參考文末參考鏈接。而 EventAggregation,其描述爲“用戶態的 Event Aggregation 庫”(Event Aggregation User Mode Library)

從搜索引擎中,我暫未找到關於其更詳細的介紹,只知道其大致爲事件聚合相關的用戶態庫,通過對計劃任務進程行爲的分析,我可以看到,在計劃任務進程啓動的過程中,其主要扮演着對計劃任務事件的處理、通知以及信號分發的角色。


結合相關堆棧,我初步認爲 UBPM 主要負責捕獲被稱之爲 Trigger 的信號,當 Trigger 到達時,便會執行對應的 TriggerActions 啓動計劃任務進程,這一部分會在後文現代的計劃任務中進行部分說明。

在 winserver 2012,根據UBPM的代碼邏輯,計劃任務進程啓動時會在如下路徑生成對應的計劃任務ID的服務日誌文件,並將計劃任務的報告信息寫入文件中。

其中記錄的依然是計劃任務進程執行的時間信息,依舊爲 UTC 時間。

實際上,這也爲我提供了一個找到舊版 UBPM 調度引擎啓動的對應計劃任務的方法,我可以直接在 \Windows\System32\LogFiles\Scm 文件夾,找到最新的文件,再根據文件名,在註冊表中定位到計劃任務的ID,從而定位到計劃任務。



計劃任務的創建

對於上述提到的 /ri/MO 的這兩個參數,計劃任務創建的過程大致相同,在 server 2012 上,負責計劃任務創建的關鍵模塊爲 schedsvc,在計劃任務創建的過程中其會執行一系列操作,這裏只簡要對其中的關鍵行爲進行說明,暫不做進一步挖掘。在後文中的現代的計劃任務中也會做部分補充。
獲取task文件夾的安全描述符進行權限檢查
讀取對應註冊表項的中的關鍵項信息




創建計劃任務文件

設置對應計劃任務在註冊表中各子項的值





即在計劃任務創建的過程中,schedsvc 主要負責獲取相關計劃任務的安全權限並對其進行檢查,隨後會對關鍵的註冊表項 TaskCache\Tasks\{ID} 進行讀取並創建計劃任務文件,然後根據對關鍵註冊表項的讀取結果,再將註冊表的各對應子項寫入計劃任務的相關內容。

現代的計劃任務

然而,當我把目光聚焦到較新的操作系統時,我發現變化已然發生。

進程啓動

在較新的windows版本中(此測試版本爲win10 19042.685),我發現在相關的 schedsvc 模塊中舊版中的相關進程啓動函數邏輯已經找不到了,之前的 taskeng 的執行邏輯也不復存在了。
取而代之的是,無論採用是 /MO 還是 /RI 參數創建計劃任務,計劃任務的執行流程都統一由 UBPM 管理,其執行流程也和 server 2012 上的略有不同,其堆棧情況如下

我可以看到 UBPM 依然在 Trigger 到達後,會對其進程處理,但是實際上多了一層封裝

經過分析,此處,handle 函數的參數 a3 是一個 UBPM_TRIGGER_CONSUMER_BLOCK 結構體,此結構體隨後會被作爲參數傳遞到 UbpmpPerformTriggerActions函數。a3+0x18 偏移處是一個 UBPM_INPUT_ACTION_PARAMS 結構體,此值會被傳入 UbpmpLaunchExeAction 函數的第一個參數用來啓動對應的計劃任務 action。最終 UBPM_INPUT_ACTION_PARAMS 結構體會在 UbpmpLaunchExeAction 函數中進行解析後作爲參數傳遞給 UbpmpLaunchOneTask 用來啓動計劃任務進程。

在 UBPM_INPUT_ACTION_PARAMS 結構體中記錄了計劃任務名稱,計劃任務內容等相關信息如下:
但是由於這些相關的結構體均未文檔化,想要對其結構體進行進一步更詳細的逆向分析需要耗費較長的時間,這也不是本文的目的,因此此處不做過多的展開。
通過對進程創建過程中的行爲的跟蹤發現,現代的計劃任務進程啓動的過程中,更多的依賴於註冊表內容的讀取而非計劃任務文件的讀取,單純靠計劃任務文件的檢測已經很難有所效果。
此外,server 2012 中提到UBPM模式下的 scm 路徑下的日誌文件也不復存在,取而代之的是報告信息被寫入到註冊表 DynamicInfo 項中。與taskeng 中的啓動流程中的操作極爲相似,其中依然包含計劃任務進程的時間信息,在偏移+C處爲其時間信息,依然採用的 UTC 時間。













這也意味着,在現代的 UBPM 調度引擎啓動的計劃任務中,對計劃任務的檢測方向已經轉移到了註冊表中。我需要對註冊表 DynamicInfo 項中的數據進行解析,從而來確定某ID對應的計劃任務進程在某一時刻曾被啓動。

計劃任務的創建

在現代的計劃任務創建的流程中,我發現對計劃任務創建進行管理的關鍵模塊依然是 schedsvc.dll,但與 server 2012 上的也有所不同,由於 schedsvc 模塊功能多樣且複雜,此處重點對 SchRpcEnumTasks 和 SchRpcRegisterTask 做一下補充說明。

在計劃任務創建的過程中,schedsvc的SchRpcEnumTasks,SchRpcRegisterTask 函數起到關鍵作用。SchRpcEnumTasks 函數會對計劃任務的關鍵註冊表進行檢索。 SchRpcEnumTasks主要負責對TaskCache\\Tree註冊表下各項計劃任務的的SD(由於較新的系統上,註冊表項中已引入 SD 子項,此處不再通過文件獲取SD而是直接讀取註冊表下的讀取),ID,Index等內容進行檢索。

SchRpcEnumTasks 實際調用的是 RpcServer::EnumFolder 函數,在 EnumFolder 函數中首先會調用 RegTreeEntryOpen 來打開目標計劃任務在 tree 中的對應註冊表。

其後,在 RpcServer::EnumFolder 函數中,其會調用 JobStore::RegJobSecurityQuery 對 SD 進行檢索,調用 JobStore::RegGetTreeInfo 對 ID 和 Index 進行檢索,同時還會做一些權限的獲取行爲,調用 FolderEnumerator::FindNext 進行循環遍歷。




SchRpcRegisterTask負責管理具體計劃任務的創建註冊行爲。 SchRpcRegisterTask 實際調用的是 RpcServer::RegisterTask 函數,在其中經過一系列的判斷後,會調用 RegTaskEntryCreate 函數進行計劃任務註冊表項的實際創建和值的設置或調用 UpdateTaskEntry 進行更新。

其關鍵函數調用及對應註冊表項與函數的創建關係大致如下圖,其中箭頭代表 API 調用,圓圈代表設置的註冊表項,從上到下爲大致的執行流程邏輯,通過這些函數關係,我可以進一步探索對應註冊表項裏數據的更多含義,由於關係較爲複雜,限於篇幅和時間,此處僅將其重要流程圖做以展示,不做更多的展開。
當大部分註冊表項相關值設置完畢後,schedsvc 會調用 JobStore::XmlSaveTaskFile 函數將計劃任務的內容寫入到對應的 tasks 文件夾中的文件中,如 \Windows\System32\Tasks\test






至此計劃任務基本創建完畢。

計劃任務的檢測與排查

在前文中,我斷斷續續的提到了部分關於計劃任務檢測與排查的思路,在這裏,我做下簡單的總結,結合前文內容,我可以將計劃任務分爲兩大類。

計劃任務ID的獲取

對比 server 2012 與 win10 中的計劃任務的創建與啓動情況,我發現,在現代的計劃任務中,微軟對註冊表的青睞顯然更高一些,通過對計劃任務進程創建的追蹤我發現,在傳統的以taskeng進程啓動的計劃任務與現代的UBPM方式啓動的計劃任務的這一類方式中,在計劃任務進程啓動時,關於計劃任務的執行時間信息均會被即時地記錄在 TaskCache\Tasks\{ID}\DynamicInfo 註冊表項中。
對於此類計劃任務,我可以通過 sysmon 這類工具進行主動檢測。設置簡單的規則對 DynamicInfo 註冊表進行檢測,隨後通過 sysmon 的日誌進行確認排查
當系統中有計劃任務啓動時,命中的 sysmon 規則效果如下圖:

此外,結合上述分析,我也可以自己寫代碼對對應註冊表項進行遍歷解析,獲取計劃任務的執行時間。核心代碼如下:

代碼獲取到計劃任務內容如下:







舊版的UBPM啓動的這一類計劃任務中,由於微軟貼心地爲我提供了對應的計劃任務日誌文件,這使得我可以直接通過文件的修改時間信息對計劃任務進行定位,對應的文件路徑通常爲 \Windows\System32\Tasks\\{ID},如下:

定位註冊表中的計劃任務

通過前文的方式獲取到計劃任務的ID後,我就可以直接通過 ID 在註冊表中搜索或直接在 \Schedule\TaskCache\Tasks\{ID} 註冊表位置找到對應註冊表項,如下:

在其中我可以看到關於計劃任務的具體信息,在一定的情況下,只借助註冊表中的計劃任務信息,計劃任務也可以被正常啓動。因此,當排查到惡意的計劃任務項時,我需要同時清理 C:\Windows\System32\Tasks\ 目錄下的計劃任務文件和上圖中的對應 ID 的計劃任務註冊表內容。

通過windows系統日誌排查計劃任務

此外,通過windows系統自帶的事件查看器,我也能對計劃任務進行定位和排查啓動的計劃任務的相關事件,再通過對應的計劃任務事件名在註冊表 Schedule\TaskCache\Tree{name}或C:\Windows\System32\Tasks\ 目錄下找到對應計劃任務獲取到對應的計劃任務 ID

獲取到 ID 後,再按照前文所述內容進行進一步的定位操作即可。

小結

從傳統的 taskeng 及舊版的 UBPM 的混合使用到現代的UBPM,我可以看到,微軟似乎更傾向於從普通文件的記錄轉換到註冊表中的統一管理,實際上,在現代的計劃任務中,單純的通過檢測計劃任務的文件,對於計劃任務的實時檢測來說是收益甚微的,因此,這也在提醒我在計劃任務的檢測中,我的檢測重心也應該向註冊表傾斜。實際上,關於計劃任務相應註冊表的各項含義還可以做進一步的挖掘來對計劃任務做更深的瞭解,但由於篇幅和時間所限,本次研究在一些地方也只是淺嘗輒止。

本文僅從 schtasks 創建的計劃任務出發,對舊版的計劃任務和現代的計劃任務的相關過程和關鍵行爲做了簡單的分析,並結合分析情況對計劃任務的檢測提出了一些建議以作拋磚引玉之用,文中若有不恰之處也歡迎討論指正。

參考

https://techcommunity.microsoft.com/t5/ask-the-performance-team/windows-7-windows-server-2008-r2-unified-background-process/ba-p/374206


Paper 本文由 Seebug Paper 發佈,如需轉載請註明來源。本文地址:https://paper.seebug.org/1479/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章