FAT32驱动内核溢出分析

本文深入探讨了FAT32文件系统的数据结构,特别是FAT32BootSector,并分析了修改FATCount字段如何可能导致内核崩溃。通过实验,发现在向U盘写入数据时会触发异常,内核崩溃发生在nt!ExFreePoolWithTag函数。通过调试,揭示了 NumberOfFATs 的非法值导致内存溢出,进而破坏了kernel pool block。文章提供了调试过程及源码参考。
摘要由CSDN通过智能技术生成

首先来关注下FAT32的数据结构,下图展示的是FAT32BootSector的格式:

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

该段数据位于第一扇区。更多有关fat32的数据结构以及各字段含义请参阅官方文档:

http://www.ntfs.com/fat-partition-sector.htm

Icewall在博客中提到,修改10H偏移处的FATCount数值即可造成蓝屏。

我们用010Editor打开一个U盘,修改此处02为0x77:

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

不过并没有蓝屏,并且在测试中稳定运行了很长时间。

只有在向U盘中写入数据的时候,才会触发异常:

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

内核崩溃在nt!ExFreePoolWithTag函数中,很显然,这个kernelpoolblock被破坏了。

HeapOverflow

调整好verifier重新来过,捕捉到异常:

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

Windbg提示的地方位于被破坏的堆块申请的时刻.

此处的代码逻辑,如果NumberOfFATs的值大于2,那么跳入申请内存的程序分支:

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

在调试的时候观察NumberOfBytes参数

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

这是直接把FAT32BootSector的NumberOfFATs字段数值77h作为NumberOfBytes申请内存。

Kernelpool刚申请好的样子,77h对齐,长度为80h,标记Fati。

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

这中间经历了kernelpoolblock数据的填充,也是在这个时候,发生了溢出。

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

在调用nt!ExFreePoolWithTag之前,再来看一下内存:

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

此时Fati标签后面的块首已经被淹没了。

Vulnerability

对相邻的下方块首下硬件写入断点,可以追查到溢出的时刻:

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析

就在申请内存之后的位置。

实际上此处的代码逻辑可以参看github上对fat32驱动实现的源码:

MS14-063(CVE-2014-4115)FAT32驱动内核溢出分析
/*++
Copyright (c) 1989-2000 Microsoft Corporation
Module Name:
Write.c
Abstract:
This module implements the File Write routine for Write called by the
dispatch driver.
--*/
#include "FatProcs.h"
//
// The Bug check file id for this module
//
#define BugCheckFileId (FAT_BUG_CHECK_WRITE)
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_WRITE)
//
// Macros to increment the appropriate performance counters.
//
#define CollectWriteStats(VCB,OPEN_TYPE,BYTE_COUNT) { \
PFILESYSTEM_STATISTICS Stats = &(VCB)->Statistics[KeGetCurrentProcessorNumber()].Common; \
if (((OPEN_TYPE) == UserFileOpen)) { \
Stats->UserFileWrites += 1; \
Stats->UserFileWriteBytes += (ULONG)(BYTE_COUNT); \
} else if (((OPEN_TYPE) == VirtualVolumeFile || ((OPEN_TYPE) == DirectoryFile))) { \
Stats->MetaDataWrites += 1; \
Stats->MetaDataWriteBytes += (ULONG)(BYTE_COUNT); \
} \
}
BOOLEAN FatNoAsync = FALSE;
//
// Local support routines
//
VOID
FatDeferredFlushDpc (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
VOID
FatDeferredFlush (
PVOID Parameter
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FatDeferredFlush)
#pragma alloc_text(PAGE, FatCommonWrite)
#endif
NTSTATUS
FatFsdWrite (
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine implements the FSD part of the NtWriteFile API call
Arguments:
VolumeDeviceObject - Supplies the volume device object where the
file being Write exists
Irp - Supplies the Irp being processed
Return Value:
NTSTATUS - The FSD   for the IRP
--*/
{
PFCB Fcb;
NTSTATUS Status;
PIRP_CONTEXT IrpContext = NULL;
BOOLEAN ModWriter = FALSE;
BOOLEAN TopLevel;
DebugTrace(+1, Dbg, "FatFsdWrite\n", 0);
//
// Call the common Write routine, with blocking allowed if synchronous
//
FsRtlEnterFileSystem();
//
// We are first going to do a quick check for paging file IO. Since this
// is a fast path, we must replicate the check for the fsdo.
//
if (!FatDeviceIsFatFsdo( IoGetCurrentIrpStackLocation(Irp)->DeviceObject)) {
Fcb = (PFCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext);
if ((NodeType(Fcb) == FAT_NTC_FCB) &&
FlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE)) {
//
// Do the usual STATUS_PENDING things.
//
IoMarkIrpPending( Irp );
//
// Perform the actual IO, it will be completed when the io finishes.
//
FatPagingFileIo( Irp, Fcb );
FsRtlExitFileSystem();
return STATUS_PENDING;
}
}
#ifdef __ND_FAT__
do {

try {
if (IrpContext == NULL) { 
TopLevel = FatIsIrpTopLevel( Irp );
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
IrpContext->TopLevel = TopLevel;
}
//
// This is a kludge for the mod writer case. The correct state
// of recursion is set in IrpContext, however, we much with the
// actual top level Irp field to get the correct WriteThrough
// behaviour.
//
if (IoGetTopLevelIrp() == (PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP) {
ModWriter = TRUE;
IoSetTopLevelIrp( Irp );
}
//
// If this is an Mdl complete request, don't go through
// common write.
//
if (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE )) {
DebugTrace(0, Dbg, "Calling FatCompleteMdl\n", 0 );
Status = FatCompleteMdl( IrpContext, Irp );
} else {
#ifdef __ND_FAT_SECONDARY__
#if 0
{
BOOLEAN lazyWriter;
PSCB scb = (PSCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext);
DebugTrace( 0, Dbg, ("FatFsdWrite: SavedTopLevelIrp = %p, FatIsTopLevelRequest() = %d, FatIsTopLevelFat() = %d, CanWait = %d\n", 
FatGetTopLevelContext()->SavedTopLevelIrp, FatIsTopLevelRequest(IrpContext), FatIsTopLevelFat(IrpContext), FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) );
lazyWriter = ( scb->LazyWriteThread[0] == PsGetCurrentThread() || scb->LazyWriteThread[1] == PsGetCurrentThread());

if (lazyWriter) {
if (IS_SECONDARY_FILEOBJECT(IoGetCurrentIrpStackLocation(Irp)->FileObject))
ASSERT( !FatIsTopLevelRequest(IrpContext) && FatIsTopLevelFat(IrpContext) );
ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
ASSERT( FlagOn(Irp->Flags, IRP_PAGING_IO) );

if (VolumeDeviceObject->Secondary)
ASSERT( FatGetTopLevelContext()->SavedTopLevelIrp == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP );
}
if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) {
ASSERT( FatIsTopLevelRequest(IrpContext) );

} else {

ASSERT( FlagOn(Irp->Flags, IRP_NOCACHE) );

if (FatIsTopLevelRequest(IrpContext)) {

ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );

} else {

if (FatIsTopLevelFat(IrpContext)) {

if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {

if (IS_SECONDARY_FILEOBJECT(IoGetCurrentIrpStackLocation(Irp)->FileObject))
ASSERT( lazyWriter == TRUE ); // || FatGetTopLevelContext()->SavedTopLevelIrp == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP );
} else
ASSERT( FatGetTopLevelContext()->SavedTopLevelIrp == (PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP ||
FatGetTopLevelContext()->SavedTopLevelIrp == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP );
} else {
ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
if (IrpContext->TopLevelIrpContext->MajorFunction != IRP_MJ_WRITE && VolumeDeviceObject->Secondary)
DebugTrace( 0, Dbg, ("FatFsdWrite: IrpContext->TopLevelIrpContext->MajorFunction = %d\n", 
IrpContext->TopLevelIrpContext->MajorFunction) );
if (IS_SECONDARY_FILEOBJECT(IoGetCurrentIrpStackLocation(Irp)->FileObject))
ASSERT( IrpContext->TopLevelIrpContext->MajorFunction == IRP_MJ_WRITE );
}
}
}
}
#endif
if (IS_SECONDARY_FILEOBJECT(IoGetCurrentIrpStackLocation(Irp)->FileObject)) {
BOOLEAN secondaryResourceAcquired = FALSE;
BOOLEAN secondaryRecoveryResourceAcquired = FALSE;
#if 0
BOOLEAN lazyWriter;
PSCB scb = (PSCB)(IoGetCurrentIrpStackLocation(Irp)->FileObject->FsContext);
#ifdef __ND_FAT_DBG__
ASSERT( FlagOn(IrpContext->NdFatFlags, ND_FAT_IRP_CONTEXT_FLAG_SECONDARY_FILE) );
#endif
lazyWriter = ( scb->LazyWriteThread[0] == PsGetCurrentThread() || scb->LazyWriteThread[1] == PsGetCurrentThread());
#endif
if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {

secondaryResourceAcquired = 
SecondaryAcquireResourceSharedStarveExclusiveLite( IrpContext, 
&VolumeDeviceObject->Secondary->Resource, 
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
if (secondaryResourceAcquired == FALSE) {
//ASSERT( lazyWriter );
ASSERT( FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) ||
FlagOn(VolumeDeviceObject->Secondary->Flags, SECONDARY_FLAG_RECONNECTING) );

FatCompleteRequest( IrpContext, Irp, Status = STATUS_FILE_LOCK_CONFLICT );
break;
}
try {
Status = FatCommonWrite( IrpContext, Irp );

} finally {
ASSERT( ExIsResourceAcquiredSharedLite(&VolumeDeviceObject->Secondary->Resource) );
SecondaryReleaseResourceLite( NULL, &VolumeDeviceObject->Secondary->Resource );
}
break;
}
Status = STATUS_SUCCESS;
while (TRUE) {

ASSERT( secondaryRecoveryResourceAcquired == FALSE );
ASSERT( secondaryResourceAcquired == FALSE );
if (FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) || 
FlagOn(VolumeDeviceObject->Secondary->Flags, SECONDARY_FLAG_RECONNECTING)) {

if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
Status = FatFsdPostRequest( IrpContext, Irp );
break;
}
}

if (FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED)) {

secondaryRecoveryResourceAcquired 
= SecondaryAcquireResourceExclusiveLite( IrpContext, 
&VolumeDeviceObject->Secondary->RecoveryResource, 
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );

if (!FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) ) {
SecondaryReleaseResourceLite( IrpContext, &VolumeDeviceObject->Secondary->RecoveryResource );
secondaryRecoveryResourceAcquired = FALSE;
continue;
}
secondaryResourceAcquired 
= SecondaryAcquireResourceExclusiveLite( IrpContext, 
&VolumeDeviceObject->Secondary->Resource, 
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
try {

SessionRecovery( VolumeDeviceObject->Secondary, IrpContext );

} finally {
SecondaryReleaseResourceLite( IrpContext, &VolumeDeviceObject->Secondary->Resource );
secondaryResourceAcquired = FALSE;
SecondaryReleaseResourceLite( IrpContext, &VolumeDeviceObject->Secondary->RecoveryResource );
secondaryRecoveryResourceAcquired = FALSE;
}
continue;
}
secondaryResourceAcquired 
= SecondaryAcquireResourceSharedLite( IrpContext, 
&VolumeDeviceObject->Secondary->Resource, 
BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
if (secondaryResourceAcquired == FALSE) {
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
电子图书资源服务系统是一款基于 Java Swing 的 C-S 应用,旨在提供电子图书资源一站式服务,可从系统提供的图书资源中直接检索资源并进行下载。.zip优质项目,资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目。 本人系统开发经验充足,有任何使用问题欢迎随时与我联系,我会及时为你解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(若有),项目具体内容可查看下方的资源详情。 【附带帮助】: 若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步。 【本人专注计算机领域】: 有任何使用问题欢迎随时与我联系,我会及时解答,第一时间为你提供帮助,CSDN博客端可私信,为你解惑,欢迎交流。 【适合场景】: 相关项目设计中,皆可应用在项目开发、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面中 可借鉴此优质项目实现复刻,也可以基于此项目进行扩展来开发出更多功能 【无积分此资源可联系获取】 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。积分/付费仅作为资源整理辛苦费用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值