• Stars
    star
    166
  • Rank 227,748 (Top 5 %)
  • Language
    C++
  • Created about 9 years ago
  • Updated almost 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Mac OS X kernel mode filter driver ( a kernel extension ) for devices, file systems and network

MacOSX-Kernel-Filter

License

The license model is a BSD Open Source License. This is a non-viral license, only asking that if you use it, you acknowledge the authors, in this case Slava Imameev.

The project uses the distorm disassembler https://github.com/gdabah/distorm which is now released under BSD license.

Features

The project contains a kernel mode driver filter for Mac OS X , aka kernel extension ( kext ).
The driver allows to intercept requests to any internal or external device ( USB, FireWire, PCI, Thunderbolt, Bluetooth, WiFi ), allow or disable requests based on user credentials and device type, perform collection of data sent to external storage devices.
The driver allows to change data read or written from/to external storages.
The driver allows to intercept network trafic, collect it in the user mode, change it and reinject in the network.
The driver can protect itself and selected user mode processes even from users with root privilege ( to a certain degree, depending on attackers skills ).
The driver doesn't require to be loaded on boot, the special technique used in the driver allows it to be loaded anytime and control devices started before it was loaded.

General Information

The driver was developed in 2009-2013 and tested on Mac OS X Snow Leopard, Mac OS X Lion, Mac OS X Mountain Lion, Mac OS X Mavericks, Mac OS X Yosemite.
The project requires XCode 5.

Design

This kernel extension (kext) consists of a number of fairly independent subsystems. Some of these subsystems are discussed in the following documents

https://github.com/slavaim/MacOSX-FileSystem-Filter/blob/master/README.md
https://github.com/slavaim/MacOSX-IOKit-Hooker/blob/master/README.md
https://github.com/slavaim/MacOSX-SparseFile-KernelMode/blob/master/README.md
https://github.com/slavaim/MacOSX-VFS-Isolation-Filter/blob/master/README.md
https://github.com/slavaim/MacOSX-Network-Sockets-Filter/blob/master/README.md

The kext uses a hooking technique to provide filtering functionality. The kext hooks C++ virtual table for IOKit classes to filter access to IOKit objects and file operation tables for VFS to filter access to file systems.

Apple I/O Kit is a set of classes to develop kernel modules for Mac OS X and iOS. Its analog in the Windows world is KMDF/UMDF framework. I/O Kit is built atop Mach and BSD subsystems like Windows KMDF is built atop WDM and kernel API.

The official way to develop a kernel module filter for Mac OS X and iOS is to inherit filter C++ class from a C++ class it filters. This requires access to a class declaration which is not always possible as some classes are Apple private or not published by a third party developers. In most cases all these private classes are inherited from classes that are in the public domain. That means they extend an existing interface/functionality and a goal to filter device I/O can be achieved by filtering only the public interface. This is nearly always true because an I/O Kit class object that is attached to this private C++ class object is usually an Apple I/O Kit class that knows nothing about the third party extended interface or is supposed to be from a module developed by third party developers and it knows only about a public interface. In both cases the attached object issues requests to a public interface.

Let's consider some imaginary private class IOPrivateInterface that inherits from some IOAppleDeviceClass which declaration is available and an attached I/O Kit object issues requests to IOAppleDeviceClass interface

class IOPrivateInterface: public IOAppleDeviceClass { };

You want to filter requests to a device managed by IOPrivateInterface, that means that you need to declare your filter like

class IOMyFilter: public IOPrivateInterface{ };

this would never compile as you do not have access to IOPrivateInterface class. You can't declare you filter as

class IOMyFilter: public IOAppleDeviceClass { };

as this will jettison all IOPrivateInterface code and the device will not function correctly.

There might be another reason to avoid standard I/O Kit filtering by inheritance. A filtering class objects replaces an original class object in the I/O Kit device stack. That means a module with a filter should be available on device discovery and initialization. In nearly all cases this means that an instance of a filter class object will be created during system startup. This puts a great responsibility on a module developer as an error might render the system unbootable without an easy solution for a customer to fix the problem.

To overcome these limitations I developed a hooking technique for I/O kit classes. I/O Kit uses virtual C++ functions so a class can be extended but its clients still be able to use a base class declaration. That means that all functions that used for I/O are placed in the array of function pointers known as vtable.

The hooking technique supports two types of hooking.

  • replacing vtable array pointer in class object
  • replacing selected functions in vtable array without changing vtable pointer

The former method allows to filter access to a particular object of a class but requires knowing a vtable size. The latter method allows to filter request without knowing vtable size but as vtable is shared by all objects of a class a filter will see requests to all objects of a particular class. To get a vtable size you need a class declaration or get the size by reverse engineering.

The hooker code can be found in DldHookerCommonClass.cpp .

The kext uses C++ templates to avoid code duplication. Though Apple documentation declares that C++ templates are not supported by I/O Kit it is true only if a template is used to declare I/O kit object. You can compile I/O kit module with C++ template classes if they are not I/O Kit classes but template parameters can be I/O Kit classes. As you probably know after instantiation a template is just an ordinary C++ class. Template classes support is not required from run time environment. You can't declare I/O Kit class as a template just because a way Apple declares them by using C style preprocessor definitions.

Below is a call stack when a hooked I/O Kit object virtual function is called by IOStorage::open

DldIOService::open at DldIOService.cpp:364  
DldHookerCommonClass::open at DldHookerCommonClass.cpp:621  
IOServiceVtableDldHookDldInheritanceDepth_0::open_hook at IOServiceDldHook.cpp:20  
IOStorage::open at IOStorage.cpp:216  
IOApplePartitionScheme::scan at IOApplePartitionScheme.cpp:258  
IOApplePartitionScheme::probe at IOApplePartitionScheme.cpp:101  
IOService::probeCandidates at IOService.cpp:2702  
IOService::doServiceMatch at IOService.cpp:3088  
_IOConfigThread::main at IOService.cpp:3350  

The more thorough hooker description can be found here https://github.com/slavaim/MacOSX-IOKit-Hooker/blob/master/README.md

Loading the module

To load the kext run the following commands, in my case the project's directory is /work/DL/GitHub/DL

 $ sudo chown -R root:wheel /work/DL/GitHub/DLDriver/DLDriver/Build/Products/Release/DLDriver.kext  
 $ sudo chown -R root:wheel /work/DL/GitHub/DLDriver/DLDriver/Build/Products/Release/DLDriverPrivate.kext  
 $ sudo kextutil -v /work/DL/GitHub/DLDriver/DLDriver/Build/Products/Release/DLDriver.kext -d /work/DL/GitHub/DLDriver/DLDriver/Build/Products/Release/DLDriverPrivate.kext  

To verify that the kext has loaded run
$ kextstat | grep SlavaImameev

the output should be like

132    1 0xffffff8030ae5400 0xd0       0xd0       SlavaImameev.devicelock.agent.privatedriver (1.0)  
133    0 0xffffff7f82265000 0xd8000    0xd8000    SlavaImameev.devicelock.agent.driver (1.0) <132 118 7 5 4 3 2 1>  

Client

The folder DldClient contains a user mode client that connects to a loaded kernel module. The client code is out of sync with the driver but nevertheless it gives some ideas how to communicate with the kernel module. The client project was composed in an ancient XCode 3.2.6 , this is a mid 2010 project.