Search code examples
macosiokitkernel-extensionfacetime

Trying to disable device access through OS X kext with a higher probe score


I need to disable access to certain devices (the built-in FaceTime Camera, for example), and have been trying to do so by providing a simple kext with a higher probe score than what is currently attached.

My kext matches and attaches to the camera as both IOProviderClass IOUSBInterface and IOProviderClass IOUSBDevice, but all of the original kexts still attach to it as well. This is how it looks in IORegistryExplorer:

FaceTime HD Camera (Built-in)@14700000   # USB device
+-- FaceTime HD Camera (Built-in)@0      # USB interface
|   +-- FaceTimeDisabler                 # my kext matching as IOUSBInterface
|   +-- IOUSBInterfaceUserClientV3       # original
+-- IOUSBDeviceUserClientV2              # original
+-- IOUSBInterface@1                     # original
|   +-- IOUSBInterfaceUserClientV3       # original
+-- IOUSBInterface@2                     # original
+-- FaceTimeDisabler                     # my kext matching as IOUSBDevice

I thought the point of device and driver matching was to select a single kext to attach to the device, so I expected that if my kext matched with the highest probe score, it would gain exclusive control of the device, but that clearly isn't the case.

My questions are these:

  1. How can I ensure that my kext gains exclusive access to a device?
  2. If there is a better approach to disable access to a device, what is it?

Solution

  • If you want to claim exclusive access to a USB device or interface, you will need to open() the nub object, specifying the kUSBOptionBitOpenExclusivelyMask bit in the options argument.

    So in your FaceTimeDisabler's start() method, do something like:

    IOUSBNub* usb_provider = OSDynamicCast(IOUSBNub, provider);
    if (usb_provider == nullptr)
    {
      IOLog("FaceTimeDisabler: Provider is not USB nub\n");
      return false;
    }
    
    bool got_exclusive_access = usb_provider->open(this, kUSBOptionBitOpenExclusivelyMask);
    this->opened = got_exclusive_access;
    if (!got_exclusive_access)
    {
      IOLog("FaceTimeDisabler: failed to acquire exclusive access to camera USB device/interface.\n");
      return false;
    }
    

    And then in your stop() method, make sure you make the corresponding close() call if and only if the open() succeeded (this->opened is true in the above example).

    Note that the kUSBOptionBitOpenExclusivelyMask flag only works for the USB family, even though open() exists everywhere. On IOPCIDevice objects, exclusivity is implicit in the open(), so don't pass the USB exclusive bit when you're trying to block access to PCI-based FaceTime cameras, but do call open/close. (My 2015 13" rMBP's camera is PCI based - vendor ID 0x14e4, device ID 0x1570, class code 04 80 00)