image

Credit

Ari Sohandri Putra is a software developer with over a decade of experience, specializing in low-level programming and hardware-related systems. He is also the founder of ARIMetic. Much of his work focuses on exploring how software interacts with hardware, leading him to develop tools like HardMon, HeatMon for temperature monitoring, and uBattery for analyzing the health and behavior of laptop and UPS batteries.

He is passionate about building practical tools that help users better understand and manage their devices, especially in areas like performance and hardware monitoring.

Find Ari at: CodeNomaden (Personal Blog)


uDiscMounter - Driver Architecture

1. Introduction

uDiscMounter is a virtual drive system that allows a disk image (such as ISO) to be mounted as if it were a real physical CD/DVD device.

Unlike a normal application, the core functionality relies on a kernel-mode driver, which integrates directly with the Windows storage stack. This driver makes the operating system believe that a real storage device exists, even though all data actually comes from a file.

2. High-Level Architecture

The system is split into two main layers:

[ User Mode ]
---------------------------------
uDiscMounter Application
    |
    | IOCTL (DeviceIoControl)
    v

[ Kernel Mode ]
---------------------------------
uDiscMounter Driver
    |
    v
Windows Storage Stack
(File System → Class Driver → SCSI)

Key Idea

The driver emulates a CD-ROM device, and translates:

Disk Request (LBA-based)
        ↓
File Offset (inside ISO file)

3. Windows Storage Stack Integration

When a file is accessed from the mounted drive, the request flows through several layers:

Application (Read File)
        ↓
File System (CDFS / UDF)
        ↓
Class Driver (cdrom.sys)
        ↓
SCSI Request (SRB + CDB)
        ↓
uDiscMounter Driver
        ↓
ISO File (on disk)

4. SCSI Emulation Concept

Windows communicates with storage devices using SCSI commands.

Example: READ(10) command

CDB Structure:
---------------------------------
Byte 0  : Operation Code (0x28)
Byte 2-5: LBA (Logical Block Address)
Byte 7-8: Transfer Length

Your driver receives this and must respond as if it were real hardware.

5. Core Translation Logic

This is the heart of the system:

LBA (sector index)
        ↓
Offset = LBA * SectorSize (2048 for ISO)
        ↓
Read from ISO file
        ↓
Return data to OS

6. Driver Initialization (C++)

#include <ntddk.h>

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(RegistryPath);

    DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]  = DispatchCreateClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchDeviceControl;
    DriverObject->MajorFunction[IRP_MJ_SCSI] = DispatchScsi;

    DriverObject->DriverUnload = DriverUnload;

    IoCreateDevice(
        DriverObject,
        0,
        L"\\Device\\uDiscMounter",
        FILE_DEVICE_CD_ROM,
        0,
        FALSE,
        &g_DeviceObject
    );

    return STATUS_SUCCESS;
}

7. Mounting an Image

When the user mounts an ISO:

NTSTATUS OpenImageFile(PCWSTR path)
{
    OBJECT_ATTRIBUTES attr;
    IO_STATUS_BLOCK io;
    UNICODE_STRING uPath;

    RtlInitUnicodeString(&uPath, path);

    InitializeObjectAttributes(
        &attr,
        &uPath,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        NULL
    );

    return ZwCreateFile(
        &g_ImageHandle,
        GENERIC_READ,
        &attr,
        &io,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ,
        FILE_OPEN,
        FILE_NON_DIRECTORY_FILE,
        NULL,
        0
    );
}

8. Handling SCSI Requests

This is where the driver behaves like a real device.

NTSTATUS DispatchScsi(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
    PSCSI_REQUEST_BLOCK srb = (PSCSI_REQUEST_BLOCK)stack->Parameters.Scsi.Srb;

    UCHAR op = srb->Cdb[0];

    switch (op)
    {
        case SCSIOP_READ:
        {
            ULONG lba =
                (srb->Cdb[2] << 24) |
                (srb->Cdb[3] << 16) |
                (srb->Cdb[4] << 8)  |
                (srb->Cdb[5]);

            ULONG sectors =
                (srb->Cdb[7] << 8) |
                (srb->Cdb[8]);

            LARGE_INTEGER offset;
            offset.QuadPart = (LONGLONG)lba * 2048;

            IO_STATUS_BLOCK io;

            ZwReadFile(
                g_ImageHandle,
                NULL,
                NULL,
                NULL,
                &io,
                srb->DataBuffer,
                sectors * 2048,
                &offset,
                NULL
            );

            srb->SrbStatus = SRB_STATUS_SUCCESS;
            Irp->IoStatus.Status = STATUS_SUCCESS;
            break;
        }

        default:
            srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
            Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
            break;
    }

    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

9. Low-Level (Assembly Perspective)

At the lowest level, the operation becomes:

; Convert LBA to file offset

mov     rax, rcx        ; rcx = LBA
imul    rax, 2048       ; sector size

; Call ZwReadFile using offset

mov     rcx, g_ImageHandle
call    ZwReadFile

10. Unmount Process

When the drive is removed:

- Close ISO file (ZwClose)
- Remove symbolic link
- Delete device object
- Driver releases memory

11. Key Technical Characteristics

Runs in kernel mode
Emulates a SCSI CD-ROM device
Uses file-backed storage (ISO)
Translates LBA → file offset
Typically read-only
Integrates into Windows storage stack

12. Conclusion

uDiscMounter works by bridging two worlds:

The OS expects a hardware storage device
The driver provides a software-backed illusion

By implementing SCSI command handling and translating requests into file operations, the system achieves a seamless virtual drive experience.