Моя борьба с PTPCamera или увлекательная история о реверсинге для самых маленьких

от автора

Почти все продвинутые зеркальные камеры, а также некоторые правильные мыльницы, позволяют управлять собой с компьютера. Программное управление камерой дает интересные возможности, например: съемка time lapse video, сопряжение камеры с микроскопом, эксперименты в области компьютерного зрения. Для управления камерой вендоры предоставляют свои проприетарные SDK, которые обычно работают исключительно под Windows и поддерживают камеры только в рамках определенной линейки (например у Canon есть аж 4 несовместимых между собой SDK). Какое счастье, что есть достойная открытая альтернатива — проект gphoto.

Прямо сейчас gphoto поддерживает 1598 моделей камер и список постоянно растет. Проект собирается под все UNIX-like ОС, включая Linux и Mac OS X. Съемкой можно управлять как при помощи command line утилиты, так и из своей собственной программы, используя библиотеку libgphoto. Доступны биндинги для разных языковых платформ, включая node.js.

В современных ОС присутствуют встроенные средства для работы с цифровыми камерами — как правило под «работой» подразумевается только выгрузка фото из камеры. Эти встроенные механизмы препятствуют работе gphoto, так как захватывают USB устройство в эксклюзивном режиме. Особенно интересно дела в этом плане обстоят в Mac OS X — ОС не предоставляет никаких штатных возможностей для отключения, но при этом система поддержки цифровых камер легко поддается реверс инжинирингу.

Скрипты для блокировки запуска PTPCamera на github.

PTPCamera

Интернет советует выполнить команду killall -9 PTPCamera (убить процесс PTPCamera) после подключение фотоаппарата для нормальной работы gphoto. Это в самом деле помогает, однако каждый раз при подключении камеры процедуру приходится повторять заново. Разумеется, программу PTPCamera можно просто удалить, но мне хотелось обойтись менее радикальным решением.

В общем, требовалось понять механизм запуска PTPCamera, и максимально корректно отключить эту функцию.

О захвате изображений в Mac OS X

Согласно имеющимся источникам [1,2], инфраструктура захвата изображений в Mac OS X устроена следующим образом.

На вершине стека расположены приложения, с которыми непосредственно взаимодействует пользователь (ex: iPhoto).

В самом низу расположены приложения для управления устройствами, к числу последних относится PTPCamera. Приложения для управления устройствами (MassStorageCamera.app, PTPCamera.app, TWAINBridge.app и тд.) живут в системных папках /System/Library/Image Capture/Devices и /Library/Image Capture/Devices.

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

Кто же запускает PTPCamera?

Попробуем определить механизм запуска PTPCamera, и для начала определим родительский процесс.

$ pgrep PTPCamera 29045  $ ps -O ppid -p 29045   PID  PPID   TT  STAT      TIME COMMAND 29045   202   ??  S      0:00.10 /System/Library/Image Capture/Devices/PTPCamer  $ ps -p 202   PID TTY           TIME CMD   202 ??         0:16.75 /sbin/launchd 

Итак, мы видим что PTPCamera запущен процессом launchd. В Mac OS X launchd — это универсальная запускалка для системных и пользовательских демонов. В системе запущено по экземпляру launchd для каждого активного пользователя. Launchd пользователя root выполняет те же функции, что init в традиционных UNIX системах.

$ ps -A -O user | grep /sbin/launchd     1 root             ??  Ss     3:06.80 /sbin/launchd   172 _windowserver    ??  Ss     0:00.06 /sbin/launchd   202 nickz            ??  Ss     0:16.76 /sbin/launchd   206 _spotlight       ??  Ss     0:00.27 /sbin/launchd 28919 _cvmsroot        ??  Ss     0:00.01 /sbin/launchd 28937 _securityagent   ??  Ss     0:00.01 /sbin/launchd

Кроме демонов, laucnhd также запускает графические приложения по команде других программ. PTPCamera — это именно последний случай, как можно убедиться по ID задачи в launchd, об этом нам говорит [префикс].

$ launchctl list | grep PTPCamera 29045	-	[0x0-0x457457].com.apple.PTPCamera

Итак, мы знаем что PTPCamera был запущен процессом launchd, по команде некого приложения X.

Включим логирование launchd (launchctl log level debug), и спровоцируем повторный запуск PTPCamera.

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

Добавляем в файл /etc/syslog.conf строку

*.debug /var/log/debug.log

и информируем демон syslogd о необходимости перечитать настройки

sudo killall -HUP syslogd

Анализируем полученный файл debug.log. Находим искомое:

([0x0-0x2d92d9].com.apple.PTPCamera[24472]): Spawned by PID 240: com.apple.SystemUIServer.agent

Запрашиваем у launchd информацию о com.apple.SystemUIServer.agent:

$ launchctl list com.apple.SystemUIServer.agent { 	"Program" = "/System/Library/CoreServices/SystemUIServer.app/Contents/MacOS/SystemUIServer"; };

Теперь мы знаем, что «виновник» запуска PTPCamera — это SystemUIServer, ни много ни мало.

Ковыряем SystemUIServer

Имеется подозрение, что искомый функционал находится не в самом SystemUIServer, а в одном из прилинкованных фреймворков:

$ otool -L /System/Library/CoreServices/SystemUIServer.app/Contents/MacOS/SystemUIServer /System/Library/CoreServices/SystemUIServer.app/Contents/MacOS/SystemUIServer: 	/System/Library/PrivateFrameworks/CoreUI.framework/Versions/A/CoreUI 	/System/Library/PrivateFrameworks/Admin.framework/Versions/A/Admin 	/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon 	/System/Library/PrivateFrameworks/SystemUIPlugin.framework/Versions/A/SystemUIPlugin 	/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit 	/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration 	/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit 	/System/Library/Frameworks/Security.framework/Versions/A/Security 	/System/Library/PrivateFrameworks/ICANotifications.framework/Versions/A/ICANotifications 	/System/Library/PrivateFrameworks/iPod.framework/Versions/A/iPod 	... 

В этом списке главный подозреваемый — ICANotifications.framework. ICA — это сокращение от image capture, тот же акроним используется в публичных фреймворках для захвата изображений.

Изучаем ICANotifications.framework

Лирическое отступление. Исполняемый файл состоит из кода и неизменяемых статических данных (разные константы, таблицы, и тп.) Особый интерес представляют строковые константы. Извлечь их можно при помощи команды strings.

Запускаем strings /System/Library/PrivateFrameworks/ICANotifications.framework/Versions/A/ICANotifications, и наслаждаемся результатами:

... /Library/Caches/com.apple.ImageCaptureNotifications.DeviceDiscoveryDatabase.%d ... CREATE TABLE DBVersion (ID integer primary key not null, typeID integer, value integer) CREATE TABLE SourceFile (ID integer primary key not null, typeID integer, bundleID varchar(256), bundleVersion integer, bundlePath varchar(256), deviceDiscoveryPath varchar(256), deviceDiscoveryModDate varchar(20), readDate varchar(20), iTWAINDS integer) CREATE TABLE IOUSBDevice (ID integer primary key not null, typeID integer, idVendor integer, idProduct integer) CREATE TABLE IOUSBInterface (ID integer primary key not null, typeID integer, bInterfaceClass integer, bInterfaceSubClass integer, bInterfaceProtocol integer) ... 

Дык это ж SQL!

Итак, рабочая гипотеза — библиотека работает с SQLite базой данных, наиболее вероятно, что это файл com.apple.ImageCaptureNotifications.DeviceDiscoveryDatabase в /Library/Caches.

$ ls /Library/Caches/com.apple.ImageCaptureNotifications.DeviceDiscoveryDatabase.* /Library/Caches/com.apple.ImageCaptureNotifications.DeviceDiscoveryDatabase.501  $ sqlite3 /Library/Caches/com.apple.ImageCaptureNotifications.DeviceDiscoveryDatabase.501 sqlite> .schema CREATE TABLE DBVersion (ID integer primary key not null, typeID integer, value integer); CREATE TABLE IOUSBDevice (ID integer primary key not null, typeID integer, idVendor integer, idProduct integer); CREATE TABLE IOUSBInterface (ID integer primary key not null, typeID integer, bInterfaceClass integer, bInterfaceSubClass integer, bInterfaceProtocol integer); ...

Так и есть!

Видно, что в конец имени (com.apple.ImageCaptureNotifications.DeviceDiscoveryDatabase.501) дописывается ID пользователя, для каждого пользователя поддерживается собственный файл. Из содержимого понятно, что база задает список соответствий класса устройств и управляющей программы, которую нужно запускать при обнаружении устройства.

Редактирую базу данных, мы отключаем запуск PTPCamera для любого выбранного пользователя, это однозначный успех!

При необходимости вернуть все обратно, откатываем изменения, или просто удаляем файл бд (название папки /Library/Caches сообщает нам, что ОС при необходимости может перегенерировать содержимое).

Дальнейшее — дело техники.

Факультативные материалы

  1. Amit Singh Mac OS X Internals: A Systems Approach [amazon]
  2. Репозиторий проекта

ссылка на оригинал статьи http://habrahabr.ru/post/173323/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *