Develop a userspace read-only disk driver for macOS - macos

I have a special disk image format (compressed and with extra information such as bad blocks) and would like to make that available as a block device (e.g. under /dev/rdisk) in macOS.
I am looking for pointers on how to write a read-only block level userspace driver.
I believe I need to do this with IOKit, but the docs are quite sparse in that area, and provide no sample code for this as far as I can tell.
I've had a look at Amit Singh's Mac OS Internals, but that only goes about filerting existing blocks that get routed through the added driver. But I need to read the data from a separate file, i.e. I need to use the file system, and I also want to do this in a userspace app if possible because making a kext is both hard to debug and is getting deprecated now, too.
Ideally, this should work on current macOS versions, including 10.15, but I'd also be happy with a solution that only works on older macOS versions, starting at 10.6.
Maybe I am still misunderstanding some things here. I was under the impression that it was possible even before 10.15 to write userspace IOKit drivers. But maybe that's not possible with Block Storage devices? Any clarifications would be welcome. I feel quite lost.
Update June 22, 2020
I also asked the question on the OSXFUSE support forum, and got a negative reply there.

Maybe I am still misunderstanding some things here. I was under the impression that it was possible even before 10.15 to write userspace IOKit drivers.
This is a true statement, but it does not mean it's possible to write all types of drivers in userspace, even with 10.15. The confusion likely stems from the fact that "IOKit" is used to mean subtly different things in different contexts:
Fundamentally, it is the OOP-based driver stack technology which is built into the XNU kernel (macOS, iOS, etc.), written in C++. Its core data structure is the I/O Kit registry, which is a directed graph of objects representing devices, device (sub-)functions, virtual device, service, drivers, user space client connections, etc. (most of it has properties of it, but nodes can technically have multiple "parent" nodes, and some actually do so on your typical Mac system)
The IOKit user space libraries. (a.k.a. IOKit.framework a.k.a. IOKitLib + various IOCFPlugins) These provide a view of the kernel's I/O Registry to user space programs, and allow them to perform certain interactions with IOKit objects. This is essentially limited to:
Iterating over the registry graph and matching services based on matching rules. Both return handles to specific I/O Registry entries to the user space program.
Getting properties on I/O Registry entries, and if the object explicitly supports it, setting (some) properties.
Creating a user client connection. This is the only way to create an entry in the I/O Registry from regular user space, and these are always IOUserClient subclasses, and only if the parent object specfically permits it.
The IOKit view available to DriverKit based System Extension processes (new in 10.15), which allows instantiation of entirely user space backed registry entry objects, for subclassing a (bounded) selection of super class types.
To answer the question of your specific case:
To implement a driver for a block storage device which will appear to the system as any other, you need to subclass IOStorage or one of its subclasses (typically IOBlockStorageDevice).
IOKit.framework allows you to create user space drivers which will only be used by that user space process or other user space processes. It is therefore not possible to use it to create drivers that will be used by some part of the kernel. For example, it is not possible to create a block device driver which will be used by the kernel's VFS system.
DriverKit currently (as of macOS 10.15.4 SDK) does not allow dext-backed subclasses of any of the storage stack's classes. So a dext will not solve the problem.
The only way to do what you're trying to do, as far as I'm aware, is to have a 2-part driver: a kext implementing an IOBlockStorageDevice subclass, as well as a service which offers a user client interface for some user space process to connect to for creating instances of block devices, and which will handle the I/O requests for the device. This is a sort of analog to the FUSE mechanism which works at the VFS layer instead of the block layer.This is exactly how macOS's built-in disk image mounting system works. Unfortunately, neither the kext nor user space parts of this are open source, documented, or a public API. So unless you reverse engineer it and find a boundary which doesn't enforce the requirement of an Apple code signature, you basically have to reimplement this.
I'm not currently aware of an existing open source system that does this, but I haven't looked very hard. Especially for read-only access, it shouldn't be terribly hard to do if you know what you're doing, but it comes with the usual downsides of developing and distributing kexts.
Note that you could also use FUSE to open your disk image if the block device it represents hosts a file system you wish to mount, and a FUSE implementation for that file system exists.

Related

How did Wine64 manage to handle macOS?

It has been a major obstacle for decade. It was reported as impossible. Forum talks referred to issues related to setting and restoring GS. Wine HQ FAQ still refers to ABI incompatibility page which is not a live wiki page, but news archive link.
Wine 2.0 announced macOS 64-bit support. But… how? Isn't it something that all macOS hackers should know? Maybe some elegant (or dirty) trick interesting on its own for any x86-64 hacker.
The primary obstacle is a conflict over the GS segment base address (GS.base) maintained by the CPU under the control of the OS.
On 64-bit Windows, GS.base is used to hold the address of the Thread Environment Block (TEB) structure for each thread. Windows apps expect to access the TEB using %gs-relative addresses. This is hard-coded into the app code rather than being behind an API function.
On macOS, GS.base is used to hold the base of the thread-local storage area of the thread's struct _pthread, an internal implementation detail of the Pthreads implementation. It's less common for Mac apps to have hard-coded %gs-relative accesses baked into them, but some do and so do the system libraries.
On Linux, GS.base is available for 64-bit apps to use for their own purpose. So, there, Wine simply sets it using the OS-provided mechanism. Wine can't do that on macOS. Not only does the OS not provide any mechanism to do it but, if Wine could, it would break the system libaries. (It would also pose potential problems for the kernel on context switches and/or the kernel might fail to restore any value Wine might have set.)
The solution we figured out is only a partial solution. The most commonly accessed fields of the TEB structure are the "self" field (%gs:0x30) and a field for the thread-local storage implementation (%gs:0x58). Often, if apps need to access other fields, they first read the self field and then reference off of that.
On macOS, %gs:0x30 and %gs:0x58 correspond to particular slots of the thread-local storage area. They are in a part that's reserved to Apple (rather than, say, application uses). We found that one of those slots was unused. The other was used for the ttyname() function in the C library. As it happens, Wine never calls that function and there's little reason to expect any of the system libraries that it uses to do so, either.
So, Wine simply pokes the appropriate values at those %gs-relative locations. Therefore, when 64-bit Windows app code reads them, it gets what it needs. The actual TEB that Wine has allocated is located elsewhere (in heap-allocated memory), but apps find the address of the TEB in the place they expect to be the TEB self field, so they find it that way.
Apple has since graciously permanently reserved both of those slots for uses like Wine's. ttyname() now uses a different slot.
That said, as mentioned above, this solution is only partial. Some apps access other fields of the TEB directly using %gs-relative addresses at offsets other than 0x30 or 0x58. When they do so, they get junk values and/or overwrite values used by other parts of the system. So, Wine's support for 64-bit Windows apps is not complete on macOS. Some such apps will crash or otherwise misbehave. Luckily, it happens infrequently enough that it's not much of a problem in practice.
For reference, here are the commits that implement this solution:
http://source.winehq.org/git/wine.git/?a=commit;h=7501942008f91a9a137fe598ce5ce7cb47de5522
http://source.winehq.org/git/wine.git/?a=commit;h=3d8efb238808a519902e047d8673237debb0f0a2

How do you get a struct device for a Linux character device

I have a Linux kernel module that implements a character device driver. I've read through Linux Device Drivers and followed several tutorials. At this point, I have a simple module that provides open, release, and write file operations.
I'm trying to use the Generic DMA Layer to create a streaming DMA mapping. I'm confused by the following excerpt from LDD:
Many of the functions below require a struct device. This structure
is the low-level representation of a device within the Linux device
model. It is not something that drivers often have to work with
directly, but you do need ot when using the generic DMA layer.
Usually, you can find this structure buried inside the bus specific
that describes your device. For example, it can be found as the dev
field in struct pci_device or struct usb_device.
I read further into the Linux device model, and encountered the following:
At the lowest level, every device in a Linux system is represented by
an instance of struct device.
How can I get the struct device for my character device? Is there one being created for me behind the scenes, or do I need to create it?
I tried manually creating a class with class_create() and then using that to create a device with device_create(), but when I used that device to set up DMA mappings I think I just got a bogus address. Is this the correct approach?
For a little bit more information about my platform, I'm working on the Altera SoCFPGA platform (ARM), so my device isn't a true hardware device like a USB or PCI device, but rather logic implemented in an FPGA.
I found a lot of info in Chapter 14 of LDD that I think may be relevant (buses, devices, drivers, etc.), but I'm just not sure when or how to use it. To me, it seems like that chapter is discussing a lot of data structures that all devices and drivers use, but I'm confused because I haven't had to make use of any of it.
I ended up creating a platform driver and platform device. The platform device struct has it's own struct device associated with the platform bus, which is a 'pseudo-bus' designed exactly for things like this. The official documentation for platform drivers was helpful here.
In the end, my module ended up implementing both a platform driver and a character device driver. The part that gave me the most trouble was creating a platform device and associating it with my platform driver. I started by just manually creating the device (at module install time) with platform_device_alloc and platform_device_register. Once I had this working I ended up removing the manual device creation and instead relying on a device tree entry to create my device.
It depends. In one cases you may get device created by core, in other you have to do that. (I think you are at first group)
Device pointer you are using for DMA should represent the device which does actual DMA in hardware. So, your approach is wrong there.
It might be good to pre-order LDD4.
I ran into exactly this as well. This is hardly a complete answer, but it seems that the underlying assumption in most of these docs is that basic character devices (that aren't instantiated by some higher-level bus driver etc.) won't need access to DMA. I found another shortcut to get what I needed without a struct device, but it looked like you could create your own device using device_register() or similar.
It is maybe a six years old the answer, but because you are doing your own driver, you could later use the function device_create from Linux header include/linux/device.h to create dynamically not only your device in /dev/ folder, but it would also return a pointer to a struct device that you can use later on in the creation of a DMA mapping as the book suggest.
I found particular useful this video in case that the information of the book is not enough to get the variables that you need for the device_create function. How to create a Linux Character Driver

How do I create a virtual gamepad?

How would I go about creating a "gamepad" which appears to DirectInput applications as a normal game controller but the state of its controls is actually defined by software?
Write a device driver to pretend to be one.
Specifically, Windows device drivers handle what are called Interrupt Requests via the Interrupt Request Protocol - which boils down to a wrapped up structure and a set of buffers internally in the driver.
Now the next thing you need to know is that many drivers are actually layered, or stacked, or whichever name you want to use. So for example to write a disk driver, you might interface with the driver above it (as a disk class) but use a driver below it (scsi port, for example) to actually send commands to your devices.
That's how real devices work. Fake devices need to conform to the top level interface requirements, e.g. a disk, or a controller, or a mouse, or whatever it is. However, underneath they can do anything they like - return whatever values they like.
This opens up the possibility of controlling a driver via a user-mode application and pretending to "be" a device. To send a driver messages, you can DeviceIoControl to it; then to actually get those messages you can either:
Stuff them in the Irp that makes up that DeviceIoControl.
Have the driver read them out of your process' memory space.
Drivers can also access \\Registry\\Machine and various other, non-user-specific non-explorer registry areas, so it is possible to communicate that way.
Finally, there's no saying you can't filter existing IO, rather than make it all up via a new device. There are a great many options and ways you can go about doing this.
If you're going to do this, you'll need:
VirtualKD or an expensive debugger cable and two PCs.
You probably also want to start with the references on this blog post. You'll find that there are essentially a bazillion different names for driver code, so I'll interpret some of them:
WDM = Windows Driver Model, basically the NT driver model mixed with (some of) Windows 9x.
KMDF = Kernel mode driver framework - drivers of the above type use this, plus additionally WDF (Windows Driver Foundation) which is a set of libraries on top of WDM to make it quicker to use.
UMDF = User mode driver framework - write a driver without the danger of kernel mode. If you can, use this, as kernel mode drivers that go wrong will bluescreen (in driver parlance, bugcheck) your system.
Edit: I'm not massively knowledgeable on DirectInput - there may be a way to override the various API controls in use via DLL redirection and the like, which may be simpler than the way I've described.
There is vJoy opensource project: http://sourceforge.net/projects/vjoystick/ - can be worth looking at.
The easiest solution may be to emulate an XInput device (Xbox 360 and One). These are supported in most modern games and the set up is very simple. Here is a C++ project here that provides this without any installed drivers or external dependencies: https://github.com/shauleiz/vXboxInterface/
I know it is an old question but for anyone which is interested in this topic it is also worth looking at this project called ViGEm.
You can emulate some well known gamepads like Microsoft Xbox 360 Controller, Sony DualShock 4 Controller and Microsoft Xbox One Controller. The project offers also some API to interact with these virtual controllers. E.g. the C# API can be found here
The simplest solution I found was using vJoy and its C# wrapper.
You need to download the vJoy driver from here.
You can use the vJoy SDK for implementing a feeder program: https://github.com/njz3/vJoy/tree/master/SDK/c%23
Use the C# starter project for this, or simply add the two .dll-s to your existing project as references from the x86 or x64 folder.
You can find instructions on how to use the api in the readme.odt file.

Differences in kernel mode and drivers

I am just trying to understand the differences to patching into the kernel and writing a driver.
It is my understanding that a kernel mode driver can do anything the kernel can do, and is similar in some ways to a linux module.
Why then, were AV makers so upset when Microsoft stopped them from patching into the Windows kernel?
What kind of stuff can you do through kernel patching that you can't do through a driver?
In this context patching the kernel means modifying its (undocumented?) internal structures in order to achieve some functionality, typically hooking various functions (e.g. opening a file). You are not supposed to go messing around with internal kernel structures that do not belong to you. In the past Microsoft did not provide official hooks for some things, so security companies reverse engineered the internals and hooked the kernel directly. Recently Microsoft has provided official hooks for some things, so the need to hook the kernel directly is not as strong.
It's true that a kernel-mode driver can do anything the kernel can do - after all, they both run in ring 0. The key question here is: how difficult is it? Patching things relies on internal details that may change between different kernel releases. For example, the system call number of NtTerminateProcess will change between versions, so a driver which hooks the SSDT will break between versions (although the system call number can be obtained through other means). Reading or modifying fields of internal structures such as EPROCESS or ETHREAD is risky as well, because again, these structures change between versions. None of this is impossible for a driver to do, but it's hard.
If an official interface is provided for hooking, Microsoft can guarantee compatibility between versions as well as being able to control who can do what (e.g. only signed drivers can use the object manager callbacks). However, Microsoft can't do this for everything, because some things are just implementation details that drivers shouldn't know about.

Device driver without the device?

I'm creating an application that needs to use some kernel level modules, for which I've divided the app into 2: one user-level program and one kernel level program.
After reading about device drivers and walking through some tutorials, I'm a little confused.
Can there be a device driver without any specific device associated with it? Is there anything other than the device driver (kernel code or something) which works in kernel mode?
How do anti-virus programs and other such applications work in kernel mode? Is device driver the correct way or am I missing something?
Yes, device drivers can work without an actual piece of hardware (i.e. the device) attached to the machine. Just think of the different programs that emulate a connected SCSI drive (CD-ROM, whatever) for mounting ISO images. Or think about TrueCrypt, which emulates (removable) drives using containers, which are nothing more than encrypted files on your hard drive.
A word of warning, though: Driver development requires much more thought and has to be done more carefully, no shortcuts, good testing and in general expects you to know quite a good deal about the Windows driver model. Remember that faulty and poor drivers put the whole system's stability in jeopardy.
Honestly, I don't think reading a tutorial is sufficient here. You might want to at least invest in a decent book on that subject. Just my 2 cents, though.
Sorry, but the Windows Internals book is more of a general reading for the curious. I cannot recommend it if you want to engage in driver development - or at most as prerequisite reading to understand the architecture. There are plenty of other books around, although most of them are a bit older.
Depending on your goal, you may get away with one of the simpler driver models. That is not to say that driver development is trivial - in fact I second all aspects of the warning above and would even go further - but it means that you can save some of the more tedious work, if instead of writing a legacy file system filter you'd write one based on the filter manager. However, Windows XP before SP2 did not have it installed by default and Windows 2000 would require SP4+SRP+patch if I remember correctly. WDF (Windows Driver Foundation) makes writing drivers even easier, but it is not suitable for all needs.
The term device is somewhat of bad choice here. Device has a meaning in drivers as well, and it does not necessarily refer to the hardware device (as pointed out). Roughly there is a distinction between PDOs (physical device objects) and CDOs (control device objects). The latter are usually what you get to see in user mode and what can be accessed by means of CreateFile, ReadFile, WriteFile, DeviceIoControl and friends. CDOs are usually made visible to the Win32 realm by means of symbolic links (not to be confused with the file system entities of the same name). Drive letter assignments like C: are actually symbolic links to an underlying device. It depends on the driver whether that'd be a CDO or PDO. The distinction is more of a conceptual one taught as such in classes.
And that's what I would actually recommend. Take a class about Windows driver development. Having attended two seminars from OSR myself, I can highly recommend it. Those folks know what they're talking about. Oh, and sign up to their mailing lists over at OSR Online.
Use Sysinternals' WinObj to find out more about the device and driver objects and symlinks.
As for the question about AVs, yes they use file system filter drivers (briefly mentioned above). The only alternative to a full-fledged legacy FSFD is a mini-filter.
It is possible to load a special kind of DLL in kernel mode, too. But in general a driver is the way into the kernel mode and well documented as such.
Books you may want to consider (by ISBN): Most importantly "Programming the Windows Driver Model" (0735618038), "Windows NT Device Driver Development" (1578700582), "Windows NT File System Internals" (0976717514 (OSR's new edition)), "Undocumented Windows NT" (0764545698) and "Undocumented Windows 2000 Secrets" (0201721872) - and of course "Windows NT/2000 Native API Reference" (9781578701995) (classic). Although the last three more or less give you a better insight and are not strictly needed as reading for driver developers.
Anti-virus (and system recovery) software generally make use of file-system filter drivers. A device can have multiple filter drivers arranged like a stack, and any event/operation on this device has to pass through all the stacked up drivers. For example, anti-viruses install a filter driver for disk device so that they can intercept and scan all file system (read/write) operation.
As mentioned in above post, going through a good book would be a nice way to start. Also, install DDK/WDK and refer the bundled examples.

Resources