65.9K
CodeProject 正在变化。 阅读更多。
Home

安全地探索 Windows 内核

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.33/5 (15投票s)

2018年2月14日

CPOL

2分钟阅读

viewsIcon

12847

本文向您展示如何使用 IDA 和 Virtual Box 深入研究 Windows 内核

引言

我们将使用来自 我们之前文章 的驱动程序,以便在内核模式下使用调试器断点。然后,我们将用我们自己的实现替换 ZwCreateFileZwClose 函数调用,以揭示内部 _OPEN_PACKET 结构的用法。

背景

你需要安装

  1. Virtual Box
  2. Virtual Box 扩展包(以便我们的 Guest OS 可以看到 USB 闪存驱动器)
  3. Guest OS(我安装了 Windows 8.1 x86 和 Windows 8.1 x64)
  4. Virtual Box Guest Additions http://download.virtualbox.org/virtualbox/5.2.6/(以便我们可以在 Host 和 Guest 之间共享文件夹)。

在 Guest OS 中,以管理员身份运行 cmd 并执行以下命令

  1. bcdedit /copy {current} /d "Windows 8.1 Debug"
  2. bcdedit /debug {NEW GUID} on
  3. bcdedit /set {NEW GUID} testsigning on // 仅适用于 Windows 的 64 位版本
  4. bcdedit /dbgsettings serial debugport:1 baudrate:115200

现在关闭 Guest OS 并添加串口

插入 USB 闪存驱动器并为其添加 USB 过滤器

添加共享文件夹

Using the Code

我们将简化我们的用户模式程序以使用服务

    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)
    }

用户模式代码必须使用 release 配置重新编译,以便我们可以在 Guest OS 上运行它,而无需安装 Visual Studio。另一方面,我们可以对我们的驱动程序使用 debug 配置。

将可执行文件放入 Host OS 上的共享文件夹中(驱动程序、运行服务的程序、使用服务的程序)。启动 Guest OS 并等待 OS 选择窗口。现在运行 IDA(32 位版本用于 32 位 Guest / 64 位版本用于 64 位 Guest),然后转到 Debugger -> Attach -> Windbg debugger

转到 Debug options -> Set specific options 并选择 Kernel mode debugging

输入以下连接字符串

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

点击 OK,选择要附加到的进程

再次点击 OK。现在返回 Guest OS 并选择“Windows 8.1 Debug”启动项。IDA 将断点

在屏幕底部,你可以输入 WINDBG 命令。输入以下 WINDBG 命令

bu deviceowner!GetDeviceObject      // set unresolved breakpoint

继续进程并允许系统启动。现在从共享文件夹(在资源管理器中:Network -> VBOXSVR -> \\VBOXSVR\Shared)将我们的可执行文件复制到另一个位置并运行服务。运行使用服务的程序,我们的未解析断点将被命中

现在你可以深入研究 ZwCreateFile 函数并跟踪执行流程。输入以下 WINDBG 命令以查看内部 _OPEN_PACKET 结构的布局

dt nt!_OPEN_PACKET

你可以使用 Reactos 源代码仓库来了解应该发生什么以及发现内部结构名称。

包含内部结构定义和内部函数原型的头文件

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);

包含我们自己的 ZwCreateFileZwClose 函数实现的源文件

#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);
}

要获取未导出的(private)函数(如 FsRtlpCleanupEcpsIopCloseFile)的地址,我们需要获取导出的符号地址并添加偏移量(可以通过调试原始内核代码时观察到的地址相减来计算偏移量)。

// 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);
© . All rights reserved.