Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Feel Safe, Stumbling into Windows Kernel

3.33/5 (15 votes)
17 Feb 2018CPOL2 min read 12.5K  
This article shows you how to dig into windows kernel using IDA and Virtual Box

This article appears in the Third Party Products and Tools section. Articles in this section are for the members only and must not be used to promote or advertise products in any way, shape or form. Please report any spam or advertising.

Introduction

We will use driver from our previous article to break with debugger in kernel mode. Then, we will replace ZwCreateFile and ZwClose function calls with our own implementation that reveals usage of internal _OPEN_PACKET structure.

Background

You need to install:

  1. Virtual Box
  2. Virtual Box Extension Pack (so our Guest OS can see USB flash drive)
  3. Guest OS (I installed both Windows 8.1 x86 and Windows 8.1 x64)
  4. Virtual Box Guest Additions http://download.virtualbox.org/virtualbox/5.2.6/ (so we can share folders between Host and Guest).

In Guest OS, run cmd as administrator and execute the following commands:

  1. bcdedit /copy {current} /d "Windows 8.1 Debug"
  2. bcdedit /debug {NEW GUID} on
  3. bcdedit /set {NEW GUID} testsigning on // required only for 64 bit version of Windows
  4. bcdedit /dbgsettings serial debugport:1 baudrate:115200

Image 1

Now shutdown guest OS and add serial port:

Image 2

Insert USB flash drive and add USB filter for it:

Image 3Image 4

Add shared folder:

Image 5Image 6

Using the Code

We will simplify our user mode program to use service:

C++
HANDLE Handle;
ULONG Id;

Handle = CreateFileW(L"\\\\.\\DeviceInfo", 0, FILE_SHARE_READ |
         FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);

if (Handle != INVALID_HANDLE_VALUE)
{
    Id = USBDriveRef(Handle, 0);

    if (Id != INVALID_ID)
    {
        DeviceUnref(Handle, Id);

        wprintf(L"success\n");
    }
    else wprintf(L"failure\n");

    CloseHandle(Handle)
}

User mode code must be recompiled with release configuration so we can run it on Guest OS without Visual Studio installed. On the other hand, we can stay with debug configuration for our driver.

Put executables to the shared folder on Host OS (driver, program to run service, program to use service). Launch Guest OS and wait for OS selection window. Now Run IDA (32 bit version for 32 bit Guest / 64 bit version for 64 bit Guest) and go to Debugger -> Attach -> Windbg debugger

Image 7Image 8

Go to Debug options -> Set specific options and select Kernel mode debugging

Image 9

Enter the following connection string:

com:port=\\.\pipe\com_1,baud=115200,pipe

Hit OK, select process to attach to:

Image 10

And hit OK again. Now return to Guest OS and select "Windows 8.1 Debug" boot entry. IDA will break:

Image 11

At the bottom of the screen, you can enter WINDBG commands. Enter the following WINDBG command:

bu deviceowner!GetDeviceObject      // set unresolved breakpoint

Continue process and allow system to boot. Now copy our executables from shared folder (in explorer: Network -> VBOXSVR -> \\VBOXSVR\Shared) to another location and run service. Run program to use service, and our unresolved breakpoint will be hit:

Image 12

Now you can dig into ZwCreateFile function and follow the execution flow. Enter the following WINDBG command to see the layout of internal _OPEN_PACKET structure:

dt nt!_OPEN_PACKET

You can use Reactos source repository to get an idea of what should happen and to discover internal structure names.

Header file that contains definition of internal structures and internal function prototypes:

C++
typedef struct _IO_DRIVER_CREATE_CONTEXT
{
    SHORT Size;
    PVOID pExtraCreateParameter;
    PVOID pDeviceObjectHint;
    PVOID pTxnParameters;
} IO_DRIVER_CREATE_CONTEXT, *PIO_DRIVER_CREATE_CONTEXT;

typedef struct _OPEN_PACKET
{
    SHORT Type;
    SHORT Size;
    FILE_OBJECT *pFileObject;
    NTSTATUS FinalStatus;
    ULONG_PTR Information;
    ULONG ParseCheck;
    union
    {
        FILE_OBJECT *pRelatedFileObject;
        DEVICE_OBJECT *pReferencedDeviceObject;
    };
    OBJECT_ATTRIBUTES *pOriginalAttributes;
    LARGE_INTEGER AllocationSize;
    ULONG CreateOptions;
    USHORT FileAttributes;
    USHORT ShareAccess;
    PVOID pEaBuffer;
    ULONG EaLength;
    ULONG Options;
    ULONG Disposition;
    FILE_BASIC_INFORMATION *pBasicInformation;
    FILE_NETWORK_OPEN_INFORMATION *pNetworkInformation;
    CREATE_FILE_TYPE CreateFileType;
    PVOID pMailslotOrPipeParameters;
    BOOLEAN Override;
    BOOLEAN QueryOnly;
    BOOLEAN DeleteOnly;
    BOOLEAN FullAttributes;
    PVOID pLocalFileObject;
    ULONG InternalFlags;
    KPROCESSOR_MODE AccessMode;
    IO_DRIVER_CREATE_CONTEXT DriverCreateContext;
} OPEN_PACKET, *POPEN_PACKET;

typedef BOOLEAN (__fastcall *TFsRtlpCleanupEcps)(PVOID pExtraCreateParameter);

typedef NTSTATUS (__fastcall *TIopCreateFile)(PHANDLE pHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES pObjectAttributes,
    PIO_STATUS_BLOCK pIoStatusBlock,
    PLARGE_INTEGER pAllocationSize,
    ULONG FileAttributes,
    ULONG ShareAccess,
    ULONG Disposition,
    ULONG CreateOptions,
    PVOID pEaBuffer,
    ULONG EaLength,
    CREATE_FILE_TYPE CreateFileType,
    PVOID pExtraCreateParameters,
    ULONG Options,
    ULONG Flags,
    PIO_DRIVER_CREATE_CONTEXT pIoDriverCreateContext);

typedef VOID (*TIopCloseFile)(PEPROCESS pProcess, 
PFILE_OBJECT pFileObject, ULONG HandleCount, ULONG SystemHandleCount);        // no __fastcall

extern TFsRtlpCleanupEcps FsRtlpCleanupEcps;
extern TIopCloseFile IopCloseFile;

extern "C"
{
    extern POBJECT_TYPE *IoDriverObjectType;

    NTKERNELAPI NTSTATUS ObOpenObjectByName(POBJECT_ATTRIBUTES pObjectAttributes,
        POBJECT_TYPE pObjectType,
        KPROCESSOR_MODE AccessMode,
        PACCESS_STATE pAccessState,
        ACCESS_MASK DesiredAccess,
        POPEN_PACKET pOpenPacket,
        PHANDLE pHandle);

    NTKERNELAPI NTSTATUS IoCheckEaBufferValidity(PFILE_FULL_EA_INFORMATION pEaBuffer,
        ULONG EaLength,
        PULONG pErrorOffset);
}

NTSTATUS OpenDriver(WCHAR *pDriverName, DRIVER_OBJECT **ppDriverObject);

NTSTATUS CreateFile(PHANDLE pHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES pObjectAttributes,
    PIO_STATUS_BLOCK pIoStatusBlock,
    PLARGE_INTEGER pAllocationSize,
    ULONG FileAttributes,
    ULONG ShareAccess,
    ULONG Disposition,
    ULONG CreateOptions,
    PVOID pEaBuffer,
    ULONG EaLength);

NTSTATUS OpenFile(PHANDLE pHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES pObjectAttributes,
    PIO_STATUS_BLOCK pIoStatusBlock,
    ULONG ShareAccess,
    ULONG OpenOptions);

NTSTATUS Close(HANDLE Handle);

Source file that contains our own implementation of ZwCreateFile and ZwClose functions:

C++
#include <wdm.h>
#include "Internals.h"

TFsRtlpCleanupEcps FsRtlpCleanupEcps;
TIopCloseFile IopCloseFile;

NTSTATUS OpenDriver(WCHAR *pDriverName, DRIVER_OBJECT **ppDriverObject)
{
    HANDLE Handle;
    NTSTATUS Status;
    UNICODE_STRING ObjectName;
    OBJECT_ATTRIBUTES ObjectAttributes;
    DRIVER_OBJECT *pDriverObject;

    RtlInitUnicodeString(&ObjectName, pDriverName);

    InitializeObjectAttributes(&ObjectAttributes, &ObjectName, OBJ_CASE_INSENSITIVE, NULL, NULL);

    Status = ObOpenObjectByName(&ObjectAttributes, *IoDriverObjectType, 
             KernelMode, NULL, FILE_READ_ATTRIBUTES, NULL, &Handle);

    if (NT_SUCCESS(Status))
    {
        Status = ObReferenceObjectByHandle(Handle, 0, NULL, KernelMode, (PVOID*)&pDriverObject, NULL);

        if (NT_SUCCESS(Status)) *ppDriverObject = pDriverObject;

        Close(Handle);
    }

    return Status;
}

NTSTATUS __fastcall IopCreateFile(PHANDLE pHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES pObjectAttributes,
    PIO_STATUS_BLOCK pIoStatusBlock,
    PLARGE_INTEGER pAllocationSize,
    ULONG FileAttributes,
    ULONG ShareAccess,
    ULONG Disposition,
    ULONG CreateOptions,
    PVOID pEaBuffer,
    ULONG EaLength,
    CREATE_FILE_TYPE CreateFileType,
    PVOID pExtraCreateParameters,
    ULONG Options,
    ULONG Flags,
    PVOID pIoDriverCreateContext)
{
    BOOLEAN b;
    HANDLE Handle;
    NTSTATUS Status;
    PVOID pEaBuffer2;
    ULONG ErrorOffset;
    POPEN_PACKET pOpenPacket;

    pOpenPacket = (POPEN_PACKET)ExAllocatePool(NonPagedPool, sizeof(OPEN_PACKET));

    if (pOpenPacket)
    {
        pOpenPacket->Type = IO_TYPE_OPEN_PACKET;
        pOpenPacket->Size = sizeof(OPEN_PACKET);
        pOpenPacket->pFileObject = NULL;
        pOpenPacket->FinalStatus = 0;
        pOpenPacket->Information = 0;
        pOpenPacket->ParseCheck = 0;
        pOpenPacket->pRelatedFileObject = NULL;
        pOpenPacket->pOriginalAttributes = pObjectAttributes;

        if (pAllocationSize)
        {
            pOpenPacket->AllocationSize.LowPart = pAllocationSize->LowPart;
            pOpenPacket->AllocationSize.HighPart = pAllocationSize->HighPart;
        }
        else
        {
            pOpenPacket->AllocationSize.LowPart = 0;
            pOpenPacket->AllocationSize.HighPart = 0;
        }

        pOpenPacket->CreateOptions = CreateOptions;
        pOpenPacket->FileAttributes = FileAttributes;
        pOpenPacket->ShareAccess = ShareAccess;

        if (pEaBuffer && EaLength)
        {
            pEaBuffer2 = ExAllocatePool(NonPagedPool, EaLength);

            if (pEaBuffer2)
            {
                memcpy(pEaBuffer2, pEaBuffer, EaLength);

                Status = IoCheckEaBufferValidity((PFILE_FULL_EA_INFORMATION)pEaBuffer2, 
                         EaLength, &ErrorOffset);

                if (NT_SUCCESS(Status))
                {
                    pOpenPacket->pEaBuffer = pEaBuffer2;
                    pOpenPacket->EaLength = EaLength;
                }
                else
                {
                    ExFreePool(pEaBuffer2);
                    goto cleanup;
                }
            }
            else
            {
                Status = STATUS_INSUFFICIENT_RESOURCES;
                goto cleanup;
            }
        }
        else
        {
            pOpenPacket->pEaBuffer = NULL;
            pOpenPacket->EaLength = 0;
        }

        pOpenPacket->Options = Options;
        pOpenPacket->Disposition = Disposition;
        pOpenPacket->pBasicInformation = NULL;
        pOpenPacket->pNetworkInformation = NULL;
        pOpenPacket->CreateFileType = CreateFileType;
        pOpenPacket->pMailslotOrPipeParameters = pExtraCreateParameters;
        pOpenPacket->Override = FALSE;
        pOpenPacket->QueryOnly = FALSE;
        pOpenPacket->DeleteOnly = FALSE;
        pOpenPacket->FullAttributes = FALSE;
        pOpenPacket->pLocalFileObject = NULL;
        pOpenPacket->InternalFlags = Flags;
        pOpenPacket->AccessMode = KernelMode;

        if (pIoDriverCreateContext)
        {
            memcpy(&pOpenPacket->DriverCreateContext, pIoDriverCreateContext, 
                             sizeof(IO_DRIVER_CREATE_CONTEXT));
        }
        else
        {
            memset(&pOpenPacket->DriverCreateContext, 0, sizeof(IO_DRIVER_CREATE_CONTEXT));
            pOpenPacket->DriverCreateContext.Size = sizeof(IO_DRIVER_CREATE_CONTEXT);
        }

        Status = ObOpenObjectByName(pObjectAttributes, *IoFileObjectType, 
                 KernelMode, NULL, DesiredAccess, pOpenPacket, &Handle);

        if (pOpenPacket->DriverCreateContext.pExtraCreateParameter)
        {
            b = FsRtlpCleanupEcps(pOpenPacket->DriverCreateContext.pExtraCreateParameter);

            if (b) pOpenPacket->DriverCreateContext.pExtraCreateParameter = NULL;
        }

        if (pOpenPacket->ParseCheck == 0xBEAA0251) b = TRUE;
        else b = FALSE;

        if (NT_SUCCESS(Status) && b)
        {
            pOpenPacket->pFileObject->Flags |= FO_HANDLE_CREATED;
            pOpenPacket->pFileObject->Flags &= ~FO_DISALLOW_EXCLUSIVE;

            pIoStatusBlock->Status = pOpenPacket->FinalStatus;
            pIoStatusBlock->Information = pOpenPacket->Information;

            *pHandle = Handle;
            ObDereferenceObject(pOpenPacket->pFileObject);

            Status = pOpenPacket->FinalStatus;
        }
        else
        {
            if (NT_SUCCESS(Status))
            {
                ObCloseHandle(Handle, KernelMode);
                Status = STATUS_OBJECT_TYPE_MISMATCH;
            }

            if (NT_SUCCESS(pOpenPacket->FinalStatus))
            {
                if (pOpenPacket->pFileObject)
                {
                    if (!b)
                    {
                        if (pOpenPacket->pFileObject->FileName.Length) 
                            ExFreePoolWithTag(pOpenPacket->pFileObject->FileName.Buffer, 0);

                        pOpenPacket->pFileObject->DeviceObject = NULL;
                    }
                    else
                    {
                        if (!(pOpenPacket->pFileObject->Flags & FO_HANDLE_CREATED)) 
                              IopCloseFile(NULL, pOpenPacket->pFileObject, 1, 1);
                    }
                }
            }

            if (pOpenPacket->pFileObject) ObDereferenceObject(pOpenPacket->pFileObject);
        }
    cleanup:
        ExFreePool(pOpenPacket);
    }
    else Status = STATUS_INSUFFICIENT_RESOURCES;

    return Status;
}

NTSTATUS CreateFile(PHANDLE pHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES pObjectAttributes,
    PIO_STATUS_BLOCK pIoStatusBlock,
    PLARGE_INTEGER pAllocationSize,
    ULONG FileAttributes,
    ULONG ShareAccess,
    ULONG Disposition,
    ULONG CreateOptions,
    PVOID pEaBuffer,
    ULONG EaLength)
{
    return IopCreateFile(pHandle,
        DesiredAccess,
        pObjectAttributes,
        pIoStatusBlock,
        pAllocationSize,
        FileAttributes,
        ShareAccess,
        Disposition,
        CreateOptions,
        pEaBuffer,
        EaLength,
        CreateFileTypeNone,
        NULL,
        0,
        0x20,
        NULL);
}

NTSTATUS OpenFile(PHANDLE pHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES pObjectAttributes,
    PIO_STATUS_BLOCK pIoStatusBlock,
    ULONG ShareAccess,
    ULONG OpenOptions)
{
    return CreateFile(pHandle, DesiredAccess, pObjectAttributes, 
             pIoStatusBlock, NULL, 0, ShareAccess, FILE_OPEN, OpenOptions, NULL, 0);
}

NTSTATUS Close(HANDLE Handle)
{
    return ObCloseHandle(Handle, KernelMode);
}

To get the address of not exported (private) functions like FsRtlpCleanupEcps and IopCloseFile, we need to obtain address of exported symbol and add offset (offset can be calculated by subtracting addresses that can be observed when we debug the original kernel code).

C++
// Win 8.1 x86
FsRtlpCleanupEcps = (TFsRtlpCleanupEcps)((UCHAR*)ObOpenObjectByName - 0x1BCB4);
IopCloseFile = (TIopCloseFile)((UCHAR*)ObOpenObjectByName - 0x402C);

// Win 8.1 x64
FsRtlpCleanupEcps = (TFsRtlpCleanupEcps)((UCHAR*)ObOpenObjectByName + 0x79530);
IopCloseFile = (TIopCloseFile)((UCHAR*)ObOpenObjectByName + 0x5599C);

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)