安全地探索 Windows 内核






3.33/5 (15投票s)
本文向您展示如何使用 IDA 和 Virtual Box 深入研究 Windows 内核
引言
我们将使用来自 我们之前文章 的驱动程序,以便在内核模式下使用调试器断点。然后,我们将用我们自己的实现替换 ZwCreateFile
和 ZwClose
函数调用,以揭示内部 _OPEN_PACKET
结构的用法。
背景
你需要安装
- Virtual Box
- Virtual Box 扩展包(以便我们的 Guest OS 可以看到 USB 闪存驱动器)
- Guest OS(我安装了 Windows 8.1 x86 和 Windows 8.1 x64)
- Virtual Box Guest Additions http://download.virtualbox.org/virtualbox/5.2.6/(以便我们可以在 Host 和 Guest 之间共享文件夹)。
在 Guest OS 中,以管理员身份运行 cmd 并执行以下命令
bcdedit /copy {current} /d "Windows 8.1 Debug"
bcdedit /debug {NEW GUID} on
bcdedit /set {NEW GUID} testsigning on
// 仅适用于 Windows 的 64 位版本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);
包含我们自己的 ZwCreateFile
和 ZwClose
函数实现的源文件
#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
)函数(如 FsRtlpCleanupEcps
和 IopCloseFile
)的地址,我们需要获取导出的符号地址并添加偏移量(可以通过调试原始内核代码时观察到的地址相减来计算偏移量)。
// 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);