My fight with PTPCamera or a fascinating story about reversing for the smallest

  • Tutorial
Almost all advanced SLR cameras, as well as some correct soap boxes, allow you to control yourself from a computer. Software control of the camera provides interesting opportunities, for example: shooting time lapse video , pairing the camera with a microscope, experiments in the field of computer vision. To control the camera, vendors provide their proprietary SDKs, which usually work exclusively under Windows and support cameras only within a certain line (for example, Canon has as many as 4 incompatible SDKs). What a blessing that there is a decent open alternative - the gphoto project .

Gphoto currently supports 1598 camera models and the list is constantly growing. The project is built for all UNIX-like operating systems, including Linux and Mac OS X. Filming can be controlled both using the command line utility and from your own program using the libgphoto library. Bindings are available for different language platforms, including node.js .

In modern operating systems, there are built-in tools for working with digital cameras - as a rule, “work” means only uploading photos from the camera. These built-in mechanisms hinder the operation of gphoto, as they capture the USB device in exclusive mode. Especially interesting things are in this regard in Mac OS X - the OS does not provide any regular options for disabling, but the support system for digital cameras is easily amenable to reverse engineering.

Scripts to block the launch of PTPCamera on github.

PTPCamera

The Internet advises to execute the command killall -9 PTPCamera(kill the PTPCamera process) after connecting the camera for normal gphoto operation. This really helps, but every time you connect the camera, you have to repeat the procedure again. Of course, the PTPCamera program can simply be removed, but I wanted to get by with a less radical solution.

In general, it was necessary to understand the mechanism for starting PTPCamera, and to disable this function as correctly as possible.

About capturing images in Mac OS X

According to available sources [ 1 , 2 ], the image capture infrastructure in Mac OS X is organized as follows.



At the top of the stack are applications that the user interacts directly with (ex: iPhoto).

At the very bottom are applications for managing devices, the latter include PTPCamera. Applications for control devices ( MassStorageCamera.app, PTPCamera.app, TWAINBridge.appetc.) Live system folders /System/Library/Image Capture/Devicesand /Library/Image Capture/Devices.

In the middle is a communication layer that organizes the connection between the upper and lower levels. It is curious that several user applications can share the same device, and transparent work with devices on the local network is also possible.

Who launches PTPCamera?

Let's try to determine the mechanism for starting PTPCamera, and first we define the parent process.
$ 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

So, we see that PTPCamera is launched by the launchd process. On Mac OS X, launchd is a universal launcher for system and user daemons. The system runs an instance of launchd for each active user. The root launchd performs the same functions as init on traditional UNIX systems.

$ 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

In addition to daemons, laucnhd also launches graphical applications on the command of other programs. PTPCamera is just the last case, as you can see from the task ID in launchd, the [prefix] tells us this.

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

So, we know that PTPCamera was launched by the launchd process, at the command of a certain application X.

Turn on launchd ( launchctl log level debug) logging , and provoke the restart of PTPCamera.

By default, a quota of 500 log entries per second is allocated for each process in the system. Messages that are not within the limit are discarded. Set up a separate assembly of debug messages to bypass the limit.

Add a /etc/syslog.confline to the file
*.debug /var/log/debug.log

and inform the daemon syslogdabout the need to re-read the settings
sudo killall -HUP syslogd

We analyze the resulting file debug.log. We find the desired:
([0x0-0x2d92d9].com.apple.PTPCamera[24472]): Spawned by PID 240: com.apple.SystemUIServer.agent

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

Now we know that the “culprit” of the launch PTPCamerais SystemUIServer, no less.

Poking SystemUIServer

There is a suspicion that the desired functionality is not in itself SystemUIServer, but in one of the linked frameworks:
$ 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
	...

The main suspect on this list is ICANotifications.framework. ICA- This is an abbreviation for image capture , the same acronym is used in public frameworks for image capture.

Learning ICANotifications.framework

Lyrical digression. The executable file consists of code and immutable static data (different constants, tables, etc.) String constants are of particular interest. You can retrieve them using the command strings.

We launch strings /System/Library/PrivateFrameworks/ICANotifications.framework/Versions/A/ICANotifications, and enjoy the results:
...
/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)
...

So this is SQL!

So, the working hypothesis is that the library works with a SQLite database, most likely it is a file com.apple.ImageCaptureNotifications.DeviceDiscoveryDatabasein /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);
...

And there is!

It can be seen that the com.apple.ImageCaptureNotifications.DeviceDiscoveryDatabase.501user ID is appended to the end of the name ( ), each user has its own file. From the contents it is clear that the base sets the list of correspondences of the device class and the control program that must be run when the device is detected.

I edit the database, we turn off the launch PTPCamerafor any selected user, this is a definite success !

If necessary, return everything back, roll back the changes, or simply delete the db file (the name of the folder /Library/Cachestells us that the OS can regenerate the contents if necessary).

Further is a matter of technology.

Optional materials

  1. Amit Singh Mac OS X Internals: A Systems Approach [ amazon ]
  2. Project repository

Also popular now: