在數位鑑識領域,分析 Windows 登入檔對於還原系統活動至關重要。本文將示範如何運用 Python 擷取作業系統資訊、服務組態等關鍵鑑識資料。我們會使用 python-registry 函式庫解析登入檔結構,並探討 Winreg 和 Winregistry 模組的進階應用,結合 Python 的日誌記錄功能,讓鑑識流程更有效率且易於追蹤。這些技術能幫助鑑識人員快速取得目標系統的關鍵資訊,例如產品名稱、版本、服務狀態等,以便更精確地重構事件過程。

從Windows登入檔擷取數位鑑識資訊的Python工具

在數位鑑識分析中,Windows登入檔是一個重要的資訊來源。透過分析登入檔,可以獲得有關作業系統、已安裝軟體、服務組態等寶貴資訊。本文將介紹如何使用Python來擷取和分析Windows登入檔中的資訊。

登入檔分析基礎

Windows登入檔是一個階層式資料函式庫,儲存了作業系統和應用程式的組態資訊。數位鑑識分析師可以透過分析登入檔來瞭解系統的組態、已安裝的軟體、執行的服務等。

使用python-registry函式庫

python-registry是一個用於解析Windows登入檔的Python函式庫。它提供了一個簡單的API來存取和解析登入檔。

安裝python-registry

首先,需要安裝python-registry函式庫。可以使用pip來安裝:

pip install python-registry

擷取作業系統資訊

以下是一個範例程式碼,示範如何使用python-registry來擷取作業系統的相關資訊:

#!/usr/bin/python3
import sys
from Registry import Registry

def get_os_info(reg_path):
    reg = Registry.Registry(reg_path)
    print("正在分析Windows登入檔中的SOFTWARE...")
    try:
        key = reg.open("Microsoft\\Windows NT\\CurrentVersion")
        print("\t產品名稱: " + key.value("ProductName").value())
        print("\t目前版本: " + key.value("CurrentVersion").value())
        print("\tService Pack: " + key.value("CSDVersion").value())
        print("\t產品ID: " + key.value("ProductId").value() + "\n")
    except Registry.RegistryKeyNotFoundException as exception:
        print("例外:", exception)

if __name__ == "__main__":
    get_os_info(sys.argv[1])

程式碼解密:

  1. 載入登入檔:使用Registry.Registry(sys.argv[1])載入指定的登入檔。
  2. 開啟登入檔鍵:使用reg.open("Microsoft\\Windows NT\\CurrentVersion")開啟指定的登入檔鍵。
  3. 擷取資訊:透過key.value("ProductName").value()等方法擷取作業系統的相關資訊。
  4. 錯誤處理:使用try-except區塊處理可能發生的Registry.RegistryKeyNotFoundException例外。

執行上述指令碼時,需要傳遞一個引數指定SOFTWARE登入檔的路徑。範例輸出如下:

$ python3 get_os_info.py samples/Win7/SOFTWARE
正在分析Windows登入檔中的SOFTWARE...
    產品名稱: Windows 7 Enterprise
    目前版本: 6.1
    Service Pack: Service Pack 1
    產品ID: 00392-972-8000024-85767

擷取服務組態資訊

以下是一個範例程式碼,示範如何使用python-registry來擷取服務的組態資訊:

#!/usr/bin/python3
from Registry import Registry
import sys

def get_current_control_set(registry):
    try:
        key = registry.open("Select")
        for value in key.values():
            if value.name() == "Current":
                return value.value()
    except Registry.RegistryKeyNotFoundException as exception:
        print("無法找到SYSTEM\Select鍵", exception)

def get_service_info(dictionary):
    service_type = {
        1: "核心裝置驅動程式",
        2: "檔案系統驅動程式",
        4: "配接器引數",
        8: "檔案系統驅動程式直譯器",
        16: "獨立行程",
        32: "共用行程",
        272: "獨立互動式程式",
        288: "共用互動式程式"
    }
    print(" 服務名稱: %s" % dictionary["SERVICE_NAME"])
    if "DisplayName" in dictionary:
        print(" 顯示名稱: %s" % "".join(dictionary["DisplayName"]).encode('utf8'))
    if "ImagePath" in dictionary:
        print(" ImagePath: %s" % dictionary["ImagePath"])
    if "Type" in dictionary:
        print(" 型別: %s" % service_type[dictionary["Type"]])
    if "Group" in dictionary:
        print(" 群組: %s" % dictionary["Group"])
    print("
---
-
---
-
---
-
---
-
---
-
---
---
")

def service_params(subkey):
    service = {}
    service["SERVICE_NAME"] = subkey.name()
    service["ModifiedTime"] = subkey.timestamp()
    for value in subkey.values():
        service[value.name()] = value.value()
    get_service_info(service)

def services_key(registry, controlset):
    services_key = "ControlSet00%d\\Services" % controlset
    try:
        key = registry.open(services_key)
    except Registry.RegistryKeyNotFoundException as exception:
        print("無法找到Services鍵", exception)
    for subkey in key.subkeys():
        service_params(subkey)

if __name__ == "__main__":
    registry = Registry.Registry(sys.argv[1])
    controlset = get_current_control_set(registry)
    services_key(registry, controlset)

程式碼解密:

  1. 取得目前的ControlSet:使用get_current_control_set(registry)函式取得目前使用的ControlSet。
  2. 定義服務資訊擷取函式get_service_info(dictionary)函式用於列印服務的相關資訊。
  3. 解析服務引數service_params(subkey)函式用於擷取每個服務的子鍵值。
  4. 存取服務鍵services_key(registry, controlset)函式用於存取並列印指定ControlSet下的服務資訊。

執行上述指令碼時,需要傳遞一個引數指定SYSTEM登入檔的路徑。範例輸出如下:

$ python3 get_services_info.py samples/Vista/SYSTEM
服務名稱: .NET CLR Data
---
-
---
-
---
-
---
-
---
-
---
---
服務名稱: .NET CLR Networking
---
-
---
-
---
-
---
-
---
-
---
---
服務名稱: ACPI
顯示名稱: b'Microsoft ACPI Driver'
ImagePath: system32\drivers\acpi.sys
型別: 核心裝置驅動程式
群組: Boot Bus Extender
---
-
---
-
---
-
---
-
---
-
---
---

Python 工具在數位鑑識分析中的應用

在 Windows 登入檔的互動中,除了之前提到的工具,還可以使用以下模組來進行更深入的分析:

這些模組能夠幫助我們取得 Windows 登入檔鍵值中的所有資料。例如,winreg 模組提供了多種方法來遍歷登入檔鍵值及其內容:

  • winreg.ConnectRegistry(computer_name, key):建立與登入檔控制程式碼的連線。
  • winreg.EnumKey(key, index):取得特定登入檔鍵值的子鍵,第一個引數代表鍵值名稱,第二個引數代表要檢索的鍵值索引。
  • winreg.EnumValue(key, index):傳回特定登入檔鍵值的所有值。

這些 Python 模組的使用簡便,能夠有效地協助我們在數位鑑識過程中檢閱特定的登入檔鍵值與記錄。

Python 中的日誌記錄

當我們編寫從命令列執行的指令碼時,訊息通常會直接顯示在執行的終端機中。為了改善這一點,我們可以引入某種訊息記錄機制,將訊息儲存到檔案或資料函式庫中。

Python 提供了 Logging 模組 (https://docs.python.org/3/library/logging.html) 作為標準函式庫的一部分。Python 中的日誌記錄是根據日誌例項的層級結構。這個模組的主要用途包括:

  • 偵錯(Debugging):檢查原始碼以尋找錯誤和缺陷。
  • 數位鑑識分析(IT Forensic Analysis):用於識別安全事件(如駭客攻擊)的原因,可能需要檢視日誌檔案。
  • 資訊科技稽核(IT Audit):日誌稽核可以幫助確定使用者的操作是否符合預期,並確保資料的安全性和完整性。

日誌記錄等級

Python 的 Logging 模組提供了五種不同的嚴重性等級,分別是:

  • Debug:提供有關錯誤或缺陷的詳細資訊。
  • Info:確認指令碼正在按預期工作。
  • Warning:提示某些意外情況或可能在未來變得更嚴重的問題(例如「磁碟空間不足」)。
  • Error:指示應用程式在某些條件下發生了錯誤,但並非嚴重到影回應用程式的運作。
  • Critical:表示發生了致命錯誤,導致程式無法在當前條件下執行。

Logging 模組元件

Logging 模組的主要元件包括:

  • Logger:記錄程式執行期間的操作,使用 logging.getLogger(logger_name) 函式建立。
  • Handler:決定處理器例項的介面行為,需要根據目的地選擇合適的處理器型別。例如,StreamHandler 將資料傳送到串流,而 FileHandler 將資料寫入檔案。同一個 Logger 可以使用多個 Handler,將訊息傳送到不同的目的地。
  • Formatter:用於定義日誌訊息的輸出格式,可以直接在應用程式碼中使用。

使用範例

import logging

# 設定日誌記錄等級為 DEBUG
logging.basicConfig(level=logging.DEBUG)

# 簡單的日誌記錄範例
logging.warning("警告訊息")

# 在日誌訊息中加入日期和時間
logging.basicConfig(format='%(asctime)s %(message)s')
logging.warning('這是訊息出現的日期和時間')

# 自定義日期和時間格式
logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning('這是自定義格式的日期和時間')

# 將日誌輸出到檔案
logging.basicConfig(filename='fileHandler.log', level=logging.DEBUG)
logging.debug('偵錯訊息')
logging.info('資訊訊息')
logging.warning('警告訊息')

內容解密:

  1. logging.basicConfig(level=logging.DEBUG):設定 Logging 模組的日誌記錄等級為 DEBUG,意味著所有 DEBUG 及以上等級的訊息都會被記錄。
  2. logging.warning("警告訊息"):輸出一個警告訊息,演示了最簡單的日誌記錄用法。
  3. logging.basicConfig(format='%(asctime)s %(message)s'):設定日誌訊息的格式,加入了日期和時間,%(asctime)s 代表日期和時間,%(message)s 代表實際的訊息內容。
  4. datefmt='%m/%d/%Y %I:%M:%S %p':自定義日期和時間的顯示格式,例如「月/日/年 時:分:秒 上午/下午」。
  5. filename='fileHandler.log':將日誌輸出到名為 fileHandler.log 的檔案中,而不是輸出到控制檯。

Python中的日誌記錄

在Python中,日誌記錄是一個非常重要的功能,能夠幫助開發者追蹤和除錯程式的執行過程。本篇文章將介紹Python中的日誌記錄模組(logging module)以及如何使用它來記錄日誌。

使用日誌記錄模組

Python的日誌記錄模組提供了一套完整的機制來記錄日誌。它允許開發者定義不同的日誌級別,例如DEBUG、INFO、WARNING、ERROR和CRITICAL,並將日誌輸出到不同的目的地,例如檔案或控制檯。

基本用法

以下是一個簡單的例子,展示瞭如何使用日誌記錄模組來記錄日誌:

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

fileHandler = logging.FileHandler('debug.log')
fileHandler.setLevel(logging.DEBUG)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fileHandler.setFormatter(formatter)

logger.addHandler(fileHandler)

logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')

在這個例子中,我們建立了一個名為logger的日誌記錄器,並將其級別設為DEBUG。然後,我們建立了一個檔案處理器(FileHandler),並將其級別設為DEBUG。最後,我們定義了一個格式器(Formatter),並將其應用於檔案處理器。

內容解密:

  1. logger = logging.getLogger(__name__):取得一個名為__name__的日誌記錄器。
  2. logger.setLevel(logging.DEBUG):將日誌記錄器的級別設為DEBUG。
  3. fileHandler = logging.FileHandler('debug.log'):建立一個檔案處理器,將日誌輸出到debug.log檔案中。
  4. fileHandler.setLevel(logging.DEBUG):將檔案處理器的級別設為DEBUG。
  5. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'):定義一個格式器,指定日誌輸出的格式。
  6. fileHandler.setFormatter(formatter):將格式器應用於檔案處理器。
  7. logger.addHandler(fileHandler):將檔案處理器新增到日誌記錄器中。

使用組態檔案

除了在程式碼中定義日誌記錄的組態外,我們還可以使用組態檔案來定義日誌記錄的組態。以下是一個例子,展示瞭如何使用組態檔案來定義日誌記錄的組態:

[loggers]
keys=root

[handlers]
keys=FileHandler,consoleHandler,rotatingFileHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=FileHandler,consoleHandler,rotatingFileHandler

[handler_FileHandler]
class=FileHandler
level=DEBUG
formatter=simpleFormatter
args=("fileHandler.log",)

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=simpleFormatter
args=(sys.stdout,)

[handler_rotatingFileHandler]
class=handlers.TimedRotatingFileHandler
level=INFO
formatter=simpleFormatter
args=("rotatingFileHandler.log",)
maxBytes=1024

[formatter_simpleFormatter]
format=%(message)s
datefmt=

在這個例子中,我們定義了一個名為logging.config的組態檔案,其中包含了日誌記錄的組態。

使用JSON組態檔案

我們也可以使用JSON檔案來定義日誌記錄的組態。以下是一個例子,展示瞭如何使用JSON檔案來定義日誌記錄的組態:

{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
        "simple": {
            "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        }
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "level": "DEBUG",
            "formatter": "simple",
            "stream": "ext://sys.stdout"
        },
        "rotating_file_handler": {
            "class": "logging.handlers.RotatingFileHandler",
            "level": "INFO",
            "formatter": "simple",
            "filename": "rotatingFileHandler.log",
            "maxBytes": 10485760,
            "backupCount": 20,
            "encoding": "utf8"
        }
    },
    "loggers": {
        "my_module": {
            "level": "DEBUG",
            "handlers": ["console"],
            "propagate": false
        }
    },
    "root": {
        "level": "DEBUG",
        "handlers": ["console", "rotating_file_handler"]
    }
}

在這個例子中,我們定義了一個名為logging.json的JSON檔案,其中包含了日誌記錄的組態。

載入組態檔案

要使用組態檔案或JSON檔案中的組態,我們需要使用fileConfig()dictConfig()方法來載入組態。以下是一個例子,展示瞭如何載入組態檔案:

import logging.config

logging.config.fileConfig('logging.config')

logger = logging.getLogger('root')

logger.debug("FileHandler message")
logger.info("message for both handlers")

同樣地,以下是一個例子,展示瞭如何載入JSON檔案:

import json
import logging.config

with open('logging.json', 'rt') as f:
    config = json.load(f)

logging.config.dictConfig(config)

logger = logging.getLogger('root')

logger.debug("FileHandler message")
logger.info("message for both handlers")

記錄異常資訊

在程式執行過程中,如果發生異常,我們可以使用日誌記錄模組來記錄異常資訊。以下是一個例子,展示瞭如何記錄異常資訊:

import logging

try:
    open('/path/to/does/not/exist', 'rb')
except Exception as exception:
    logging.error('Failed to open file', exc_info=True)
    logging.exception('Failed to open file')

在這個例子中,我們使用logging.error()方法來記錄異常資訊,並將exc_info引數設為True。或者,我們也可以使用logging.exception()方法來記錄異常資訊。

內容解密:

  1. try-except塊:捕捉異常。
  2. logging.error('Failed to open file', exc_info=True):記錄異常資訊,並將異常的堆積疊資訊包含在內。
  3. logging.exception('Failed to open file'):記錄異常資訊,並自動包含異常的堆積疊資訊。