Главная / Кейсы / PC Control
Эксплуатация 24/7 IoT & Systems Architecture Windows 10 Enterprise LTSC (1 ПК) ESP8266 Bare-Metal C++

PC Control: внеполосное управление и разблокировка ПК без USB-донглов

Осознанный оверинжиниринг одной домашней рабочей станции: от ESP8266 и физического управления питанием до удалённой разблокировки Windows через границу Session 0.

Спортивный интерес вместо ожидания доставки: разблокировка без USB HID
ESP8266 управляет питанием и физической кнопкой, а системный агент работает ещё до входа пользователя. Telegram → реле включает ПК → Windows загружается → агент Session 0 через PsExec запускает отдельный процесс в интерактивной сессии → процесс вводит настроенный пароль на защищённом столе Winlogon → WTS подтверждает разблокировку.

Так компьютер можно включить и довести до рабочего стола без USB HID-донгла и аппаратного KVM. Пароль Windows при этом используется: аутентификация не отключается.
Время разблокировки
1.4 – 1.8 с
Полный цикл авторизации со сбросом шторки
Аппаратные USB-донглы
0 шт
Никаких эмуляторов клавиатуры и занятых портов
Окно верификации выключения
20 с
Защита от фантомных сетевых пакетов
Автоматические тесты
412 тестов
Питание, статусы, MQTT и рабочий сетап · локальный прогон: 60% строк выбранных Python-модулей; без прошивки и скрипта ввода пароля
Глубина архива метрик
30 дней
Срез активности окон и сети в SQLite WAL

Сквозная топология и протоколы передачи

Системный агент, пользовательский агент и микроконтроллер выполняют разные задачи. ESP8266 управляет питанием и не обращается напрямую к файловой системе Windows. Доступ к паролю зависит от конфигурации и прав процессов.

topology_map.txt — PC Control System Overview TLS 1.3 / QoS 1
┌────────────────────────────────────────────────────────────────────────────────────────┐
│                             КЛИЕНТСКИЙ ИНТЕРФЕЙС TELEGRAM                              │
│     • Мобильный клиент / Desktop   • Custom Emoji UI   • Интерактивные кнопки          │
└───────────────────────────────────────────┬────────────────────────────────────────────┘
                                            │ HTTPS (Bot API)
                                            ▼
┌────────────────────────────────────────────────────────────────────────────────────────┐
│                         ОБЛАЧНЫЙ КООРДИНАТОР VDS (Ubuntu Linux)                        │
│  Хостинг: api.w1bbex.ru • systemd: pc-control-bot.service (User: pccontrol, Sandboxed) │
│                                                                                        │
│  ┌────────────────────────┐  ┌────────────────────────┐  ┌──────────────────────────┐  │
│  │     power_flow.py      │  │    health_engine.py    │  │       history.py         │  │
│  │  Конечный автомат FSM  │  │  Фильтр Retain-пакетов │  │  SQLite WAL Хранилище    │  │
│  │  Атомарный State JSON  │  │  20с окно тишины сети  │  │  Серверный Matplotlib    │  │
│  └───────────┬────────────┘  └───────────┬────────────┘  └──────────────────────────┘  │
│              └─────────────────────┬─────┘                                             │
│                                    ▼                                                   │
│                      Брокер Mosquitto MQTT (Порт 8883, TLS 1.2/1.3)                    │
└────────────────────────────────────┬───────────────────────────────────────────────────┘
                                     │
             ┌───────────────────────┴────────────────────────┐
             │ MQTT TLS (Wi-Fi)                               │ MQTT TLS (Tailscale / WAN)
             ▼                                                ▼
┌─────────────────────────────────────────┐   ┌──────────────────────────────────────────┐
│      АППАРАТНЫЙ УРОВЕНЬ (ESP8266)       │   │        ЛОКАЛЬНЫЙ ХОСТ ПК (WINDOWS)       │
│                                         │   │                                          │
│  • Микроконтроллер: ESP8266 (160 МГц)   │   │ ┌──────────────────────────────────────┐ │
│  • Реле: GPIO5 (D1) на контакты PWR_SW  │   │ │ SESSION 0: boot_agent.py (SYSTEM)    │ │
│  • Кнопка кейса: GPIO14 (D5, IRAM ISR)  │   │ │ • Запуск при старте ОС (Планировщик) │ │
│  • Стейт-машина замка Морзе (EEPROM)    │   │ │ • WTS API (честные статусы экрана)   │ │
│  • BearSSL TLS 1.2 (PROGMEM X.509)      │   │ │ • Мост сенсоров LibreHardwareMonitor │ │
│  • Акустическая пасхалка: playRelayBeat │   │ │ • Вызов PsExec64 -s -x -i            │ │
└────────────────────┬────────────────────┘   │ └──────────────────┬───────────────────┘ │
                     │ Физический             │                    │ PsExec64 -s -x -i   │
                     │ импульс ATX            │                    ▼                     │
                     ▼                        │ ┌──────────────────────────────────────┐ │
┌─────────────────────────────────────────┐   │ │ ДЕСКТОП WINLOGON: type_password.py   │ │
│    КОНТАКТЫ PWR_SW МАТЕРИНСКОЙ ПЛАТЫ    │   │ │ • SetThreadDesktop(hDeskWinlogon)    │ │
│  • Короткий клик: 500 мс (включение)    │   │ │ • Сброс шторки блокировки            │ │
│  • Удержание: 5000 мс (аварийный сброс) │   │ │ • Скан-коды клавиш (VkKeyScanW)      │ │
└─────────────────────────────────────────┘   │ └──────────────────┬───────────────────┘ │
                                              │                    │ Успешный вход       │
                                              │                    ▼                     │
                                              │ ┌──────────────────────────────────────┐ │
                                              │ │ SESSION 1+: agent.py (User Desktop)  │ │
                                              │ │ • Запуск при интерактивном входе     │ │
                                              │ │ • Опрос окон (3с poll, SQLite WAL)   │ │
                                              │ │ • Извлечение иконок GDI -> TG Emoji  │ │
                                              │ │ • Бинарный JPEG-протокол скриншотов  │ │
                                              │ │ • WinRT GSMTC + PyCaw (CoreAudio)    │ │
                                              │ └──────────────────────────────────────┘ │
                                              └──────────────────────────────────────────┘
Главное меню бота PC Control (/menu) — статус сессии, датчики и кнопки действий Команда /menu в Telegram

Центральный пульт управления: дашборд и основные функции

Вместо перегруженных веб-панелей единой точкой входа выступает Telegram-бот. При вызове команды /menu формируется лаконичный статус-отчет и адаптивная клавиатура управления:

  • Мониторинг сессии Windows: честный статус через WTS API (аптайм 2 ч 37 мин 51 сек), адаптивный эмодзи ОС Windows, фиксация активного окна на рабочем столе (Telegram — PC Control) и текущий играющий трек с прогрессом воспроизведения.
  • Атомарный срез LibreHardwareMonitor: точная температура и загрузка CPU (37% • 42°C), видеокарта с отображением потребляемой мощности (35% • 39°C • 16.3 Вт), оперативная память (47.6% • 8.1 ГБ / 17.1 ГБ) и статус системного накопителя [C:] (свободно 91.9 ГБ).
  • Пульт быстрых действий: кнопки управления питанием, мгновенный скриншот всех мониторов со сжатием в JPEG (quality 72, thumbnail 1600x1000 под лимит MQTT в 950 КБ), лаунчер программ, регулировка звука и кнопка выключения.

Разбор ключевых подсистем

Детальный анализ физического железа, изоляции ядра Windows, облачного FSM и пользовательского медиа-агента.

Микроконтроллер ESP8266 на частоте 160 МГц

Прошивка написана на C++ и оптимизирована для работы в условиях нестабильного Wi-Fi и жестких ограничений оперативной памяти (heap < 10 KB). Все критические обработчики кнопок выполняются в оперативной памяти контроллера.

  • Неблокирующая очередь ISR: прерывание кнопки вынесено в IRAM_ATTR и пишет события в кольцевой буфер на 16 элементов с дебаунсом 35 мс.
  • Замок Морзе: распознавание кликов (< 500мс) и удержаний (≥ 500мс). Защищен фильтром тишины: 5 секунд до ввода и 3 секунды после.
  • Анти-брутфорс в EEPROM: 3 неверных ввода блокируют кнопку с записью контрольной суммы XOR в энергонезависимую память. Сброс только по MQTT.
pc_remote_mqtt.ino
// Кольцевой буфер прерываний кнопки в IRAM
void IRAM_ATTR physicalButtonISR() {
  unsigned long now = millis();
  int pinState = digitalRead(PHYSICAL_BUTTON_PIN);
  uint8_t nextHead = (isrQueueHead + 1) % ISR_QUEUE_SIZE;
  if (nextHead != isrQueueTail) {
    isrTimeQueue[isrQueueHead] = now;
    isrStateQueue[isrQueueHead] = pinState;
    isrQueueHead = nextHead;
  }
}
Интерактивный эмулятор аппаратного замка Морзе (GPIO14)

Проверьте работу физического алгоритма: зажимайте кнопку на нужное время. Короткий клик (< 500мс) распознается как «К» (•), зажатие (≥ 500мс) — как «Д» (—). Пауза между нажатиями более 5 секунд сбрасывает ввод (порог тишины ATTEMPT_QUIET_MS).

Анти-брутфорс EEPROM:
Целевой код замка: К — Д — К — К — Д
1 К • <500мс
2 Д — ≥500мс
3 К • <500мс
4 К • <500мс
5 Д — ≥500мс
500мс
Короткий клик (<500мс) Удержание (≥500мс)
Статус контроллера GPIO14
Ожидание ввода... Зажмите кнопку для первого символа.
hardware_wiring_blueprint.txt — Физическая распиновка NodeMCU & F_PANEL Bare-Metal Wiring
┌────────────────────────────────────────────────────────────────────────────────────────┐
│                     ФИЗИЧЕСКАЯ РАСПИНОВКА И КОНТУРЫ ПОДКЛЮЧЕНИЯ                        │
│                       NodeMCU ESP8266 v3 • Gigabyte B450M S2H                          │
└────────────────────────────────────────────────────────────────────────────────────────┘

    [ Блок питания ATX ]
             │
             ├────── Фиолетовый провод (+5VSB дежурка 5В) ───────┐
             └────── Черный провод (Общий GND) ──────────────────┤
                                                                 ▼
[ Кнопка на корпусе ПК ]                                 ┌───────────────┐
             │                                           │    NodeMCU    │
             └───── 2-pin провод (кнопка кейса) ────────►│ D5   ESP8266  │
                    (IRAM ISR, Морзе, EEPROM Lock)       │               │
                                                         │ 160 МГц Turbo │
                                                         │               │
  ┌────────────────── 3-pin (VCC / GND / Сигнал D1) ─────┤ D1         D2 │
  │                                                      └───┬─────────┬─┘
  ▼                                                          │         │
┌──────────────────────┐                                     │         │
│  Модуль оптореле     │                                     │         │
│  1-канал 3.3V Songle │                                     │         │
└──────────┬───────────┘                                     │         │
           │ Сухие контакты COM + NO                         │         │
           ▼                                                 │         │
┌─────────────────────────────────────────────────────────┐  │         │
│ Материнская плата Gigabyte B450M S2H                    │  │         │
│                                                         │  │         │
│   [ F_PANEL ] ◄── замыкание контактов PWR_SW ───────────┘  │         │
│                                                                      │
│   [ 12V RGB ] ─── ✕ ПУСТОЙ (12В сожжет 5В диоды!)                    │
│                                                                      │
│   [ CPU_FAN ] ─── 4-pin PWM (обороты мотора) ──────────────►────┐    │
└─────────────────────────────────────────────────────────┘      │    │
                                                                 │    │ 3-pin ARGB (DIN)
                                                                 │    │ (16x WS2812B)
                                                                 ▼    ▼
                                                      ┌───────────────────────────────────┐
                                                      │  Кулер CPU: Ocypus Gamma A40 WH   │
                                                      │  • Мотор: 4-pin PWM от платы      │
                                                      │  • Подсветка: 5V ARGB от ESP (D2) │
                                                      └───────────────────────────────────┘

Низкоуровневые хуки Win32 и изоляция сессий

Системный агент запускается из-под учетной записи NT AUTHORITY\SYSTEM в Session 0. Он не имеет графического контекста, но обладает правами взаимодействовать с Service Control Manager и рабочими столами Windows.

  • Консенсус WTS API: опрашиваются WTSGetActiveConsoleSessionId, флаг WTSSessionInfoEx и дескриптор OpenInputDesktop.
  • Запуск через PsExec64: агент использует -s -x -i, чтобы запустить отдельный процесс ввода на защищённом столе интерактивной сессии.
  • Защитный аборт ввода: если за 5 секунд имя десктопа не переключилось в Winlogon, скрипт завершается без ввода символов (код 2).
type_password.py
# Привязка потока к активному десктопу ввода
hDesk = user32.OpenInputDesktop(0, False, DESKTOP_SWITCHDESKTOP)
user32.SetThreadDesktop(hDesk)

# Проверка защитного барьера Winlogon
if get_desktop_name() != "Winlogon":
    sys.exit(2)  # Экстренный выход без ввода пароля

# Трансляция скан-кодов и эмуляция ввода
for char in password:
    vk = user32.VkKeyScanW(ord(char))
    user32.keybd_event(vk & 0xFF, 0, 0, 0)

Конечный автомат FSM и фильтрация сетевых призраков

Облачный координатор на Linux VDS выступает единым источником правды. Он транслирует команды только при подтвержденных переходах FSM и страхует систему от распространенных багов брокеров сообщений.

  • Проверка актуальности статусов: health_engine.py отклоняет более старые статусы по временной метке с учётом переходов состояния и идентификатора загрузки.
  • Окно выключения 20 секунд: статус SHUTDOWN фиксируется только при непрерывном отсутствии heartbeat от обоих агентов в течение 20 секунд.
  • Проверяемый деплой: deploy_vds.py проверяет SHA-256 и импорты, перезапускает службу и при неуспешной проверке её состояния или обнаружении traceback выполняет откат.
health_engine.py
# Защита от фантомных пакетов с устаревшим timestamp
if packet_ts < component.last_valid_ts:
    logger.warning("Dropped outdated MQTT packet")
    return

# Верификация отключения хоста
if silent_duration >= 20.0 and agents_offline:
    self.state = PCState.SHUTDOWN_OBSERVED

Серверная аналитика и графики телеметрии (Matplotlib + SQLite WAL)

Все телеметрические срезы с агентов Windows непрерывно агрегируются в локальную базу SQLite на VDS. Для исключения перегрузки сети бот не передает миллионы сырых точек на телефон, а на лету генерирует темный четырехслойный дашборд через Matplotlib.

Точек в базе: 117 482
Периоды: 1ч • 6ч • 24ч • 7д • 30д
Сетевой трафик: до 125 Мбит/с
График телеметрии нагрузки CPU/GPU, температур, памяти и сети за 30 дней (117k точек) Генерация на VDS • 30 дней истории

Интерактивная сессия, WinRT GSMTC и GDI иконки

Пользовательский агент в Session 1+ обслуживает мультимедиа и интерфейс Telegram. Все тяжелые вызовы дедуплицированы и оптимизированы для нулевой нагрузки на процессор.

  • Локальный якорь времени GSMTC: расчет позиции трека через _anchor + (time - anchor_time) исключает ежесекундные COM-запросы к Explorer.
  • GDI экстрактор иконок: извлечение 256x256 иконок через PrivateExtractIconsW, исправление альфа-канала и заливка в Telegram Custom Emoji.
  • Бинарный протокол скриншотов: 4 байта длины uint32 BE + JSON заголовок + сжатый JPEG с валидацией сигнатур 0xFF 0xD8.
media_manager.py
# Локальный субсекундный расчет времени трека (0% CPU)
def get_current_position(self):
    if not self.is_playing:
        return self._anchor_pos
    elapsed = time.time() - self._anchor_time
    return min(self.duration, self._anchor_pos + elapsed)
Пульт управления музыкой в Telegram — таймлайн, навигация и Моя Волна WinRT GSMTC • Пульт плеера

Полноценный пульт музыки в боте

В большинстве DIY-систем мультимедиа игнорируется, но здесь реализован полноценный медиа-пульт прямо в чате Telegram. Через Windows Runtime GSMTC бот перехватывает метаданные любого системного плеера (Яндекс Музыка, Spotify, VK, браузер).

  • Интерактивный таймлайн: отображение прогресса трека (01:04 / 02:15), автор и название композиции (sekairotten, pas de vie).
  • Полный контроль трека: пауза, перемотка на ±10 секунд, переключение треков («Пред» / «След») и добавление в избранное.
  • «Моя Волна»: отдельная выделенная кнопка для быстрого запуска персонального бесконечного радиопотока в Яндекс Музыке.
Кастомный эмодзи-пак иконок приложений Windows (PC Control App Icons) GDI Win32 • Custom Emoji Pack

Генератор кастомных эмодзи для лаунчера

Модули app_emoji_manager.py и icon_extractor.py связывают файловую систему Windows с интерфейсом Telegram. Бот автоматически сканирует установленные программы на хосте.

  • GDI PrivateExtractIconsW: извлечение нативных иконок высокого разрешения 256x256 без артефактов.
  • Автоматический пак: сборка и загрузка пакета «PC Control App Icons» в Telegram через Bot API.
  • Наглядный запуск: кнопки программ (Steam, Zed, VS Code, GTA V, Torrent, OBS, Chrome) снабжены нативными иконками.

Разбор инцидентов и краевых случаев

Неочевидные проблемы, выявленные на стыке ядра Windows, сетевого стека Paho MQTT и аппаратных интерфейсов.

Краш Paho MQTT при засыпании Windows (WinError 10054 / 1236)
Критический сбой
В библиотеке paho-mqtt для межпоточных уведомлений на платформе Windows создается локальная сокет-пара. При сетевом разрыве или переходе сетевой карты в энергосберегающий режим сокет закрывался системой, вызывая неперехватываемое исключение и аварийное падение процесса агента.
Решение: внедрен низкоуровневый monkey-patch методов Client._loop и _packet_queue. Ошибки сокет-пары 10054/1236 перехватываются без падения с мягким перезапуском сетевого цикла.
Устаревшие статусы после переподключения MQTT
Сетевая аномалия
После переподключения сохранённые или задержанные сообщения могут создавать неверную картину состояния компонентов.
Защита: health_engine.py сравнивает временные метки статусов и учитывает смену загрузки; это не описание защиты команд от повторного выполнения.
Защита от случайной блокировки учетной записи Windows
Защита от локаута
При удаленном вводе пароля смена раскладки или отсутствие маппинга символа в скан-код могли привести к отправке искаженного пароля и исчерпанию попыток входа в Windows.
Защита: скрипт пытается включить английскую раскладку, отключает Caps Lock и проверяет символы через VkKeyScanW по мере ввода. Неподдерживаемый символ прерывает процедуру до отправки Enter.