
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.
In general, it was necessary to understand the mechanism for starting PTPCamera, and to disable this function as correctly as possible.

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 (
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.
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.
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.
So, we know that PTPCamera was launched by the launchd process, at the command of a certain application X.
Turn on launchd (
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
and inform the daemon
We analyze the resulting file
We request
Now we know that the “culprit” of the launch
The main suspect on this list is
We launch
So this is SQL!
So, the working hypothesis is that the library works with a SQLite database, most likely it is a file
And there is!
It can be seen that the
I edit the database, we turn off the launch
If necessary, return everything back, roll back the changes, or simply delete the db file (the name of the folder
Further is a matter of technology.
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 commandkillall -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.app
etc.) Live system folders /System/Library/Image Capture/Devices
and /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.conf
line to the file*.debug /var/log/debug.log
and inform the daemon
syslogd
about the need to re-read the settingssudo 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
launchd
information 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
PTPCamera
is SystemUIServer
, no less.Poking SystemUIServer
There is a suspicion that the desired functionality is not in itselfSystemUIServer
, 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 commandstrings
. 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.DeviceDiscoveryDatabase
in /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.501
user 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
PTPCamera
for 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/Caches
tells us that the OS can regenerate the contents if necessary). Further is a matter of technology.