Как делались скриншоты на старых кнопочных Nokia

Технологии ушедших: как делались скриншоты на старых кнопочных Nokia

Владимир Туров
Владимир Туров Разработчик
6 апреля 2026

Разбираемся, как делали скриншоты на старых телефонах, которые не предусматривали такой опции «из коробки» для простого пользователя.

Изображение записи

Возможность делать скриншот — это базовый минимум, который есть во всех современных смартфонах. Даже у героев вечных споров, iOS и Android используются похожие комбинации «Громкость + Кнопка питания». Разве что на iOS используется «+», а в Android — «-».

Пятнадцать лет назад все было иначе. На устройствах было больше кнопок, но возможность сделать скриншот была роскошью. Задача осложнялась тем, что бюджетные телефоны на платформе S40 не имели многозадачности, так что написать универсальное приложение-скриншотер на телефон было невозможно. 

Тем не менее, решения были. В этой статье я проведу небольшой экскурс в историю программ для скриншотов с телефона, кратко расскажу про протокол Phonet и, возможно, вызову легкие приступы ностальгии.

У меня был Nokia 6303 classic — стильный телефон с металлической крышкой на платформе Nokia Series 40 (S40). Мой путь с этим телефоном разошелся, но спустя почти полтора десятка лет я решил, что хочу разобраться в технологиях, которые использовал, будучи школьником. 

Я нашел в продаже б/у Nokia 6303 classic и погрузился в тему.

Данная информация предоставляется в развлекательных целях. Отправка некорректных низкоуровневых команд может привести к необратимым изменениям и «окирпичиванию» телефона. Не выполняйте действий со своим телефоном, если не уверены в своих действиях. Автор не несет ответственности за возможный ущерб, нанесенный телефонам.

Nokia Screen Dump

Окно программы Nokia Screen Dump на Windows с кнопками управления «Dump» и «Save Dump».
Интерфейс приложения.

Nokia Screen Dump (NSD) — это программа для Windows с минимальным порогом входа, разработанная на форуме allnokia. Для работы достаточно установить Nokia Connectivity Cable Driver, подключить телефон в режиме «PC Suite» и нажать «File – Connection» в интерфейсе программы. 

Коллаж из трех изображений: (а) пустой фон системы без приложения, (б) успешный скриншот запущенного приложения, (в) системное диалоговое окно «Завершить приложение?».
Слева направо: (а) фон под J2ME-приложением, (б) удачный скриншот приложения, (в) диалоговое окно, которое помогает сделать скриншот.

После этих действий кнопка «Dump» снимает скриншот, а «Save Dump» позволяет сохранять в BMP-файл. Отдельного внимания стоит настройка «Java». Так сложилось, что нельзя сделать скриншот J2ME-приложений: на изображение попадает только интерфейс системы. 

Эта проблема решается довольно интересно: приложение эмулирует нажатие красной кнопки на телефоне, что вызывает открытие системного диалога «Завершить приложение?». Так как снятие скриншота автоматизировано, то получается попасть в момент, когда J2ME-приложение уже приостановлено, но диалоговое окно еще не отрисовано. 

Nokia Screen Dump был простой утилитой для своего времени, которая работает до сих пор. Единственная сложность в современности — найти драйвера и саму программу.

Gammu

Альтернативный кроссплатформенный (Windows, Linux и macOS) вариант — gammu. Это утилита командной строки для автоматизации взаимодействия с GSM-модемами. Много раз она упоминалась на Хабре в контексте SMS-шлюзов и оповещений. Gammu поддерживает универсальные команды для модемов, а также умеет управлять отдельными сериями и моделями телефонов. Платформа Nokia S40 поддерживается неплохо.

Как и ранее, телефон необходимо подключить по USB в режиме «PC Suite», а затем выполнить несколько действий по идентификации устройства. Сперва запустим gammu-detect, это поможет найти совместимые устройства.


      # gammu-detect
; gammu-detect сгенерировал конфигурационный файл.
; Подробнее можно узнать в Руководстве Пользователя Gammu.

[gammu]
device = /dev/ttyACM0
name = Nokia Nokia_6303_classic
connection = at

В моем случае имя и путь устройства был обнаружен верно, а способ подключения — нет. Если кто-то уже пытался использовать gammu с вашим телефоном, то можно поискать об этом запись в базе данных. Я нашел, способ подключения — dku2phonet. Заполняем файл конфигурации ~/.gammu.


      [gammu]
device = /dev/ttyACM0
name = Nokia Nokia_6303_classic
connection = dku2phonet

Затем проверяем, что устройство отвечает на команды.


      # gammu identify           
Устройство : /dev/ttyACM0
Производитель : Nokia
Модель         : 6303c (RM-443)
Прошивка     : 06.21 I (03-04-09)
Оборудование : 1000
IMEI                 : 35319903XXXXXXX
Код продукта : 0580772

Теперь можно сделать скриншот.


      # gammu screenshot /tmp/2
Информация: File saved as /tmp/2.bmp

Результат не отличается от того, что делал Nokia Screen Dump. Единственное исключение — для скриншотов J2ME-приложений нужно приложить дополнительные усилия: выполнять команду и нажимать на кнопку сброса вызова одновременно. 

У gammu есть подпрограмма presskeysequence, которая выполняет эмуляцию нажатий физических кнопок, однако эта она не реализована для моего телефона и способа подключения.


      # gammu presskeysequence R
Функция ещё не написана. Будем рады помощи.

Разные программы дают одинаковый результат потому что работа программ заключается в отправке команд и обработке ответа. Скриншот делает прошивка телефона.

Протокол Phonet

В файле конфигурации gammu я указывал тип подключения — dku2phonet. Имя подключения состоит из имени кабеля (dku2) и имени протокола (phonet). Сперва разберемся с кабелями.

Внешний вид кабеля Nokia DKU-2 с разъемами USB-A и фирменным POP-Port.
Кабель Nokia DKU-2. Источник.

Есть два очень похожих кабеля — DKU-2 и DKU-5. С одной стороны это USB-A, с другой — POP-Port. Отличительная особенность DKU-5 — наличие микросхем для эмуляции COM-порта. Это было сделано для старых телефонов, в которых не было USB-стека. А еще телефоны общались с микросхемой кабеля и проверяли оригинальность аксессуара. 🙂

DKU-2 — это обычный USB с нестандартным интерфейсом со стороны телефона. На более новых устройствах, таких как мой 6303, POP-Port превратился в Micro-USB, но идея «кабель — это просто провода» сохранилась. Телефон все еще принимает команды по COM-порту в режиме «PC Suite», но теперь конвертер USB-COM находится в телефоне.
Phonet — это протокол взаимодействия компонентов телефона между собой, разработанный Nokia. Описание протокола можно обнаружить в ядре Linux. Заголовок пакета Phonet содержит восемь байт информации.


      struct phonethdr {
  uint8_t  pn_media;  /* Тип протокола */
  uint8_t  pn_rdev;   /* Идентификатор приёмника */
  uint8_t  pn_sdev;   /* Идентификатор отправителя */
  uint8_t  pn_res;    /* Идентификатор ресурса или функции */
  uint16_t pn_length; /* Размер тела сообщения big-endian (минус шесть байт) */
  uint8_t  pn_robj;   /* Object ID приёмнка */
  uint8_t  pn_sobj;   /* Object ID отправителя */
};

В описании протокола используется только одна константа: модем всегда имеет идентификатор 0x00.

До этого момента мы опирались на факты. С этого момента нам придется довольствоваться догадками, реализованными в ПО. Первая догадка состоит в том, что для связи с телефоном используется заголовок длиной 6 байт, без Object ID. Первый байт — это тип протокола, в проекте gammu их известно три: 

  • 0x14 — пакет по ИК-порту;
  • 0x19 — пакет по Bluetooth;
  • 0x1b — пакет по COM-порту.

Второй и третий байты —  идентификаторы устройств, которые ожидает телефон. В gammu прописано три константы:

  • 0x00 — телефон.
  • 0x0C — компьютер при связи по проводу.
  • 0x10 — компьютер при связи по Bluetooth.

Однако в тестах я случайно использовал идентификатор 0x10 при связи по проводу и телефон это принял. Nokia Screen Dump также использует идентификатор 0x10.

Идентификатор функции и тело сообщения — это самая большая загадка. В gammu все значения захардкожены без объяснений. Сперва идет общий заголовок для тела — 0x00, 0x01, 0x00. Второй байт, предположительно, идентификатор транзакции, задаваемый отправителем. Так можно отличать ответ на «свой» запрос от других ответов. Скриншот состоит из двух команд.

  1. Запрос информации об экране — высоты и ширины.
  2. Запрос изображения.

Рассмотрим первый запрос.

Скриншот окна Nokia Screen Dump, демонстрирующий HEX-коды запроса информации о разрешении дисплея.
Запрос информации о дисплее из Nokia Screen Dump.

Как и полагается недокументированным протоколам, данные разнятся. В gammu используется заголовок ноль-единица-ноль, в NSD — какие-то другие значения. Даже в теле команды второй байт отличается, в gammu указан 0x01. Однако на ответ это не влияет.

Ответ не разобран полностью, но два важных параметра известны: ширина и высота экрана. 0x00F0 = 240, 0x0140 = 320. Разрешение экрана Nokia 6303 classic — 240×320. Все сходится.

Запрос изображения изменяет первый байт тела команды с 0x06 на 0x07 и в ответ приходят 615 сообщений по 516 байт. Из них первые 6 байт — заголовок phonet, 10 байт — метаинформация, включающая в себя номер сообщения и размер полезной нагрузки. Остальные 500 байт — изображение в формате 32 бита на пиксель. Проверяем математику: 240*320*4 = 307200 байт информации. 307200 / 500 = 614.4 ≈ 615 пакетов. 

В gammu вопрос записи «сырых» данных в файл решают просто: дополняют заголовком BMP и получается файл, который может читать компьютер. Так как телефон отправляет 4 байта на пиксель, то выравнивание по границе 32 бит, необходимое BMP, уже сделано.

С одной стороны нет магии в том, чтобы вытащить данные, подготовленные телефоном. С другой стороны, все эти форматы не были документированы, а реверс-инжиниринг — это весьма трудоемкая задача.

Бонус: взаимодействие с телефоном

Как отмечалось ранее, Nokia Screen Dump для Windows требует драйвер Nokia Connectivity Cable Driver, который пока что доступен в интернете и совместим с текущими версиями операционных систем. А gammu — это универсальное решение с множеством абстракций, понять которые может быть не просто. В качестве бонуса оставлю минимальный пример взаимодействия с телефоном на ОС Ubuntu.

При подключении телефон появляется в системе как символьное устройство /dev/ttyACM0. Это устройство, похоже, принимает АТ-команды. Однако нам нужен интерфейс, который принимает phonet. В lsusb можно найти четвертый дескриптор, совместимый с USB CDC ACM — эмуляцией последовательного порта через USB.


      # lsusb -v -d 0421:01b0

Bus 001 Device 028: ID 0421:01b0 Nokia Mobile Phones 6303 classic Phone (PC Suite mode)
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            2 Communications
  bDeviceSubClass         0 [unknown]
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x0421 Nokia Mobile Phones
  idProduct          0x01b0 6303 classic Phone (PC Suite mode)
  bcdDevice            6.21
  iManufacturer           1 Nokia
  iProduct                2 Nokia 6303 classic
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    [УДАЛЕНО ДЛЯ КРАТКОСТИ]
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        4
      bAlternateSetting       1
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 [unknown]
      bInterfaceProtocol      0 
      iInterface              0 
      ** UNRECOGNIZED:  04 24 fd 00
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x03  EP 3 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               0
    [УДАЛЕНО ДЛЯ КРАТКОСТИ]

Нас интересует то, что этот дескриптор имеет класс CDC Data. Запоминаем его номер (bInterfaceNumber = 4), используемый эндпоинт (EP 3) и адреса для чтения и записи (0x83 IN, 0x03 OUT). Максимальный размер пакета, кстати, считывается как 64 байта. Но, как показала практика, телефон отдает до 543 байт.
Используем эти значения в прототипе на Python, который запрашивает разрешение экрана. Модуль usb доступен в пакете PyUSB.


      import usb.core
import usb.util
import struct

# Хардкод, таков путь
# 0421:01b0 Nokia Mobile Phones 6303 classic Phone (PC Suite mode)
VID = 0x0421
PID = 0x01b0

dev = usb.core.find(idVendor=VID, idProduct=PID)
if dev is None:
    raise RuntimeError("device not found")

# В системе появляется первый интерфейс, 
# представленный как ttyACM0, и принимающий АТ-команды
# Для Phonet нужно обращаться к интерфейсу 4
# Освобождаем нужный интерфейс, если его держит ядро
ifd = 4
try:
    if dev.is_kernel_driver_active(ifd):
        dev.detach_kernel_driver(ifd)
        print(f"detached kernel driver from interface {ifd}")
except (NotImplementedError, usb.core.USBError) as e:
    print(f"detach check failed for interface {ifd}: {e}")

# Захватываем интерфейс
usb.util.claim_interface(dev, ifd)

# Активируем эндпоинты на интерфейсе
dev.set_interface_altsetting(interface=ifd, alternate_setting=1)

# Адреса дескриптора
EP_OUT = 0x03
EP_IN  = 0x83

MAX_SIZE = 64

def print_phonet(header: bytes):
    transport, receiver, sender, resource, length = struct.unpack(">BBBBH", header)
    print(f"Transport: 0x{transport:02X}")
    print(f"Receiver: 0x{receiver:02X}")
    print(f"Sender: 0x{sender:02X}")
    print(f"Resource: 0x{resource:02X}")
    print(f"Length: {length}")

def communicate(payload: bytes) -> bytes:
    n = dev.write(EP_OUT, payload, timeout=1000)
    print(f"written: ({n}) {payload.hex(' ')}")

    try:
        data = bytes(dev.read(EP_IN, MAX_SIZE, timeout=1000))
        phonet_header = data[0:6]
        phonet_data = data[6:]
        print("recv:", data.hex(" "))
        print_phonet(phonet_header)
        return data
    except usb.core.USBTimeoutError:
        print("read timeout")

payload_get_resolution = b"\x1b\x00\x10\x0e\x00\x06\x00\x0d\x0e\x06\x00\x00"
data = communicate(payload_get_resolution)
width = int.from_bytes(data[10:12], "big")
height = int.from_bytes(data[12:14], "big")
print(f"{width}x{height}")

# Освобождаем ресурсы
usb.util.release_interface(dev, ifd)
usb.util.dispose_resources(dev)

Запускаем и надеемся, что это не сломает телефон.


      # python3 test.py        
written: (12) 1b 00 10 0e 00 06 00 0d 0e 06 00 00
recv: 1b 10 00 0e 00 0a 0d 53 0e 30 00 f0 01 40 0c 01
Transport: 0x1B
Receiver: 0x10
Sender: 0x00
Resource: 0x0E
Length: 10
240x320

Сработало. 

Ни в коем случае не предлагаю разрабатывать собственное решение, это минимальный прототип, позволяющий увидеть основную идею без множества абстракций, используемых в gammu. Для автоматизаций используйте gammu.

Заключение

Может показаться, что сделать скриншот — это относительно простая задача. Но на примере этой статьи становится понятно, что это технология, которая совершенствовалась годами.

Возможность делать скриншоты на Nokia S40 — это скорее техническая «фишка», которая использовалась разработчиками в своих целях, но осталась в публичных версиях прошивок. 

В Nokia S60 (Symbian 9.x) многозадачность и особенности операционной системы позволяли написать утилиты для создания скриншотов прямо на смартфоне. Есть даже официальное приложение от команды Nokia.

Мощные смартфоны с Android или iOS почти сразу умели в скриншоты. Телефоны Apple — с iOS 2 официально, а с джейлбрейком — с самой первой. Найти примеры скриншотов с первых андроидов мне не удалось.

Если раньше скриншот нужно было добывать с боем, то теперь это дело двух секунд. А полученное изображение можно сразу отредактировать, выделить важное и отправить в мессенджер. В большинстве современных ОС есть даже запись с экрана встроенными средствами. Так что прогресс не стоит на месте.