大数跨境
0
0

一种底层磁盘数据截获方法(附源码)

一种底层磁盘数据截获方法(附源码) Coco跨境电商
2025-07-24
0
导读:看雪论坛作者ID:TurkeybraNC

写这篇文章的灵感来于对Windows存储设备栈的分析,截获了SCSI命令。



1


分析思路

Windows存储设备栈布局

我们先看一张图(画的比例不好请见谅)

R3与ntdll.dll

在3环下,一个程序发起文件读写请求,被传递给ntdll.dll,ntdll.dll对请求进行包装,成为了IRP(I/O Request Package),IRP通过KiFastCallEntry进入内核,这些都很熟悉了。我们着重分析一下内核层。


R0下的各个模块

IO Manger


IO管理器接收来自用户态(ntdll.dll)的IRP,进行合法性校验和分发。也就是通过它的DriverDispatch向下传递IRP包,同时构建标准化的IRP结构,包含操作类型(如文件读写)、缓冲区指针等元数据。


MiniFilter


过滤驱动层位于文件系统驱动(FSD)之上,主动拦截所有IRP。这也是最常见的过滤驱动地点。可检测文件读写、进程创建等敏感操作。也可数据加密/解密,在IRP传递过程中透明处理数据(如BitLocker)。


ntfs.sys


文件系统驱动解析IRP中的文件路径,定位磁盘物理位置(簇映射)。管理元数据:NTFS主文件表($MFT)、权限、日志等。实现缓存机制:通过内存管理器(Mm)减少磁盘访问。


这之后,才是我们需要关注的设备


Storage Driver


存储类驱动(Storage Driver)通过SCSI端口接口控制特定类型设备(如CD-ROM统一驱动、磁盘统一驱动),将IRP转换为SRB+CDB(命令描述块),成为为SCSI设备请求。


SRB/CDB是什么?


SRB


SRB:SCSI请求块(协议传输载体),可以跨层通信,是在存储类驱动 → 端口驱动 → 微端口驱动间传递命令/数据的标准化容器。


举个简化示例:SRB的结构


typedef struct _SCSI_REQUEST_BLOCK {
    USHORT    Length;             // SRB结构体长度
    UCHAR     Function;           // 操作类型(如SRB_FUNCTION_EXECUTE_SCSI)
    UCHAR     SrbStatus;          // 状态码(如SCSI_STATUS_SUCCESS)
    PVOID     DataBuffer;         // 数据缓冲区指针(读写目标地址)
    ULONG     DataTransferLength; // 传输数据长度
    ULONG     TimeOutValue;       // 超时时间(毫秒)
    PVOID     OriginalRequest;    // 关联的原始IRP指针
    PVOID     NextSrb;            // 队列中下一个SRB(支持链表)

// --- SCSI专属字段 ---
    UCHAR     PathId;             // SCSI路径ID(多通道HBA)
    UCHAR     TargetId;           // 目标设备ID
    UCHAR     Lun;                // 逻辑单元号(LUN)
    UCHAR     CdbLength;          // CDB长度(6/10/12/16字节)
    UCHAR     SenseInfoBuffer[18];// 感知数据缓冲区(错误详情)
    UCHAR     Cdb[16];            // 内嵌CDB命令块
} SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK;


CDB


命令描述块,内嵌在SRB中,封装SCSI标准操作指令(如读/写/查询),为变长指令。


举个简化示例:CDB的结构


typedef struct _CDB {
    UCHAR OperationCode;  // 操作码(决定命令类型和CDB长度)
    UCHAR Flags;          // 控制标志(如强制单元访问FUA)
    ULONG LBA;            // 逻辑区块地址(Little-Endian)
    UCHAR TransferLength; // 传输区块数
    UCHAR Control;        // 附加控制位
} CDB, *PCDB;


典型CDB命令示例


READ(10)	0x28	28 00 AA AA AA AA 00 00 BB BB	读取LBA=0xAAAAAA, 长度0xBBBB区块  
WRITE(12) 0xAA AA 00 00 00 LL LL LL LL 00 00 NN NN 写入LBA=0xLLLLLLLL, 长度0xNNNN区块  
INQUIRY    0x12 12 00 00 00 LL 00 查询设备信息(返回LL字节)


现代CDB支持扩展操作码(如READ(16)的0x88),应对大容量设备。


SRB+CDB 协作流程




图说得很清楚了,我就不赘述了。


Storage Port Driver


存储端口驱动为所有Windows存储类驱动(如磁盘、磁带、DVD驱动)定义统一接口,提供硬件抽象,隔离Storage Driver与HBA(主机总线适配器)的硬件特性差异,同步管理同一HBA上所有设备的访问。


协议转换:


对SCSI设备:将SRB(SCSI请求块)直接传递至Storport微端口驱动(硬件专属)
对传统IDE/ATAPI/IEEE 1394总线:将SRB转换为适配器所需格式(如重组CDB/协议包)。


挂钩的设置

这是Windows存储设备栈的底部,也是我们拦截的地方。




我们想在Storage Port Driver上挂钩,设置Filter Driver。


(这里画的有点问题,Capture Driver应该在Storage Driver和Storage Port Driver之间,Capture还打错了。


看一下windbg的输出:




storahci.sys便是Storage Port Driver,也是我们挂钩的位置。


那么如何挂钩呢


答案在DriverObject->MajorFunction中




IRP_MJ_INTERNAL_DEVICE_CONTROL


Storage Driver和Storage Port Driver之间通过DriverDispatchXxx联系,所以只要修改MajorFunction的地址我们就可以截获SRB请求。


经过调试得知,二者通过IRP_MJ_INTERNAL_DEVICE_CONTROL联系,所以只需修改MajorFunction 的IRP_MJ_INTERNAL_DEVICE_CONTROL即可。


WinDbg调试

首先打个断点看一下调用上下文,这是Call到MajorFunction的IRP_MJ_INTERNAL_DEVICE_CONTROL的上下文:





NTSTATUS __fastcall DispatchXxx(
PDEVICE_OBJECT DeviceObject,
PIRP           Irp

{
RCX:DeviceObject
RDX:Irp
}


根据fastcall调用协议,rdx是指向IRP的指针,所以我们解析IRP即可。


根据Windows文档,SRB块的指针位于CurrentStackLoaction中,而CurrentStackLoaction在DeviceObject中,具体见下(也可看后面的代码):


2:kd> dt _IRP @RDX
nt!_IRP
+0x000 Type             :0n6
+0x002 Size             :0x1f0
+0x004 AllocationProcessorNumber :0
+0x006 Reserved         :0
+0x008 MdlAddress       :0xffffa68d`f25f9340 _MDL
+0x010 Flags            :0
+0x018 AssociatedIrp    :<anonymous-tag>
+0x020 ThreadListEntry  :_LIST_ENTRY [ 0xffffa68d`f2b28e00 -0xffffa68d`f2b28e00 ]
+0x030 IoStatus         :_IO_STATUS_BLOCK
+0x040 RequestorMode    :0 ''
+0x041 PendingReturned  :0 ''
+0x042 StackCount       :3 ''
+0x043 CurrentLocation  :3 ''
+0x044 Cancel           :0 ''
+0x045 CancelIrql       :0 ''
+0x046 ApcEnvironment   :0 ''
+0x047 AllocationFlags  :0x44 'D'
+0x048 UserIosb         :(null) 
+0x050 UserEvent        :(null) 
+0x058 Overlay          :<anonymous-tag>
+0x068 CancelRoutine    :(null) 
+0x070 UserBuffer       :(null) 
+0x078 Tail             :<anonymous-tag>
2:kd> dx -id 0,0,ffffa68df247f0c0 -r1 (*((ntkrnlmp!_IRP *)0xffffa68df2b28de0)).Tail
(*((ntkrnlmp!_IRP *)0xffffa68df2b28de0)).Tail                 [Type:<anonymous-tag>]
    [+0x000Overlay          [Type:<anonymous-tag>]
    [+0x000Apc              [Type:_KAPC]
    [+0x000CompletionKey    :0x0 [Type:void *]
2:kd> dx -r1 (*((ntkrnlmp!_IRP *)0xffffa68df2b28de0)).Tail.Overlay
(*((ntkrnlmp!_IRP *)0xffffa68df2b28de0)).Tail.Overlay                 [Type:<anonymous-tag>]
    [+0x000DeviceQueueEntry [Type:_KDEVICE_QUEUE_ENTRY]
    [+0x000DriverContext    [Type:void * [4]]
    [+0x020Thread           :0x0 [Type:_ETHREAD *]
    [+0x028AuxiliaryBuffer  :0x0 [Type:char *]
    [+0x030ListEntry        [Type:_LIST_ENTRY]
    [+0x040CurrentStackLocation : 0xffffa68df2b28f40 :IRP_MJ_INTERNAL_DEVICE_CONTROL / 0x0 for Device for "\Driver\storahci" [Type:_IO_STACK_LOCATION *]
    [+0x040PacketType       :0xf2b28f40 [Type:unsigned long]
    [+0x048OriginalFileObject :0x0 [Type:_FILE_OBJECT *]
    [+0x050IrpExtension     :0xffffa68df4ec6aa0 [Type:void *]
2:kd> dx -r1 ((ntkrnlmp!_IO_STACK_LOCATION *)0xffffa68df2b28f40)
((ntkrnlmp!_IO_STACK_LOCATION *)0xffffa68df2b28f40)                 : 0xffffa68df2b28f40 :IRP_MJ_INTERNAL_DEVICE_CONTROL / 0x0 for Device for "\Driver\storahci" [Type:_IO_STACK_LOCATION *]
    [<Raw View>]     [Type:_IO_STACK_LOCATION]
Device           : 0xffffa68debde6050 :Device for "\Driver\storahci" [Type:_DEVICE_OBJECT *]
File             :0x0 [Type:_FILE_OBJECT *]
CompletionRoutine : 0xfffff8047af260f0 :0xfffff8047af260f0 [Type:long (__cdecl*)(_DEVICE_OBJECT *,_IRP *,void *)]
2:kd> dx -r1 -nv (*((ntkrnlmp!_IO_STACK_LOCATION *)0xffffa68df2b28f40))
(*((ntkrnlmp!_IO_STACK_LOCATION *)0xffffa68df2b28f40))                 : IRP_MJ_INTERNAL_DEVICE_CONTROL / 0x0 for Device for "\Driver\storahci" [Type:_IO_STACK_LOCATION]
    [+0x000MajorFunction    :0xf [Type:unsigned char]
    [+0x001MinorFunction    :0x0 [Type:unsigned char]
    [+0x002Flags            :0x10 [Type:unsigned char]
    [+0x003Control          :0xe0 [Type:unsigned char]
    [+0x008Parameters       [Type:<anonymous-tag>]
    [+0x028DeviceObject     : 0xffffa68debde6050 :Device for "\Driver\storahci" [Type:_DEVICE_OBJECT *]
    [+0x030FileObject       :0x0 [Type:_FILE_OBJECT *]
    [+0x038CompletionRoutine : 0xfffff8047af260f0 :0xfffff8047af260f0 [Type:long (__cdecl*)(_DEVICE_OBJECT *,_IRP *,void *)]
    [+0x040Context          :0xffffa68df5406b90 [Type:void *]
2:kd> dx -r1 (*((ntkrnlmp!_IO_STACK_LOCATION *)0xffffa68df2b28f40)).Parameters
(*((ntkrnlmp!_IO_STACK_LOCATION *)0xffffa68df2b28f40)).Parameters                 [Type:<anonymous-tag>]
    [+0x000Create           [Type:<anonymous-tag>]
    [+0x000CreatePipe       [Type:<anonymous-tag>]
    [+0x000CreateMailslot   [Type:<anonymous-tag>]
    [+0x000Read             [Type:<anonymous-tag>]
    [+0x000Write            [Type:<anonymous-tag>]
    [+0x000QueryDirectory   [Type:<anonymous-tag>]
    [+0x000NotifyDirectory  [Type:<anonymous-tag>]
    [+0x000NotifyDirectoryEx [Type:<anonymous-tag>]
    [+0x000QueryFile        [Type:<anonymous-tag>]
    [+0x000SetFile          [Type:<anonymous-tag>]
    [+0x000QueryEa          [Type:<anonymous-tag>]
    [+0x000SetEa            [Type:<anonymous-tag>]
    [+0x000QueryVolume      [Type:<anonymous-tag>]
    [+0x000SetVolume        [Type:<anonymous-tag>]
    [+0x000FileSystemControl [Type:<anonymous-tag>]
    [+0x000LockControl      [Type:<anonymous-tag>]
    [+0x000DeviceIoControl  [Type:<anonymous-tag>]
    [+0x000QuerySecurity    [Type:<anonymous-tag>]
    [+0x000SetSecurity      [Type:<anonymous-tag>]
    [+0x000MountVolume      [Type:<anonymous-tag>]
    [+0x000VerifyVolume     [Type:<anonymous-tag>]
    [+0x000Scsi             [Type:<anonymous-tag>]
    [+0x000QueryQuota       [Type:<anonymous-tag>]
    [+0x000SetQuota         [Type:<anonymous-tag>]
    [+0x000QueryDeviceRelations [Type:<anonymous-tag>]
    [+0x000QueryInterface   [Type:<anonymous-tag>]
    [+0x000DeviceCapabilities [Type:<anonymous-tag>]
    [+0x000FilterResourceRequirements [Type:<anonymous-tag>]
    [+0x000ReadWriteConfig  [Type:<anonymous-tag>]
    [+0x000SetLock          [Type:<anonymous-tag>]
    [+0x000QueryId          [Type:<anonymous-tag>]
    [+0x000QueryDeviceText  [Type:<anonymous-tag>]
    [+0x000UsageNotification [Type:<anonymous-tag>]
    [+0x000WaitWake         [Type:<anonymous-tag>]
    [+0x000PowerSequence    [Type:<anonymous-tag>]
    [+0x000Power            [Type:<anonymous-tag>]
    [+0x000StartDevice      [Type:<anonymous-tag>]
    [+0x000WMI              [Type:<anonymous-tag>]
    [+0x000Others           [Type:<anonymous-tag>]
2:kd> dx -r1 (*((ntkrnlmp!_IO_STACK_LOCATION *)0xffffa68df2b28f40)).Parameters.Others
(*((ntkrnlmp!_IO_STACK_LOCATION *)0xffffa68df2b28f40)).Parameters.Others                 [Type:<anonymous-tag>]
    [+0x000Argument1        :0xffffa68df2c556b0 [Type:void *]
    [+0x008Argument2        :0x0 [Type:void *]
    [+0x010Argument3        :0x0 [Type:void *]
    [+0x018Argument4        :0x0 [Type:void *]


这里的Argument1便是指向SRB的指针,srb的结构定义在windbg 中可以看到,CDB的位置在偏移的0x048处:


2:kd> dt storport!_SCSI_REQUEST_BLOCK
+0x000 Length           :Uint2B
+0x002 Function         :UChar
+0x003 SrbStatus        :UChar
+0x004 ScsiStatus       :UChar
+0x005 PathId           :UChar
+0x006 TargetId         :UChar
+0x007 Lun              :UChar
+0x008 QueueTag         :UChar
+0x009 QueueAction      :UChar
+0x00a CdbLength        :UChar
+0x00b SenseInfoBufferLength :UChar
+0x00c SrbFlags         :Uint4B
+0x010 DataTransferLength :Uint4B
+0x014 TimeOutValue     :Uint4B
+0x018 DataBuffer       :Ptr64 Void
+0x020 SenseInfoBuffer  :Ptr64 Void
+0x028 NextSrb          :Ptr64 _SCSI_REQUEST_BLOCK
+0x030 OriginalRequest  :Ptr64 Void
+0x038 SrbExtension     :Ptr64 Void
+0x040 InternalStatus   :Uint4B
+0x040 QueueSortKey     :Uint4B
+0x040 LinkTimeoutValue :Uint4B
+0x044 Reserved         :Uint4B
+0x048 Cdb              : [16UChar


于是乎,可以这样解析得到CDB地址:


CurrentStackLocation=DeviceObject->Tail.OverLay.CurrentStackLocation
SRB=CurrentStackLocation->Parameters.Others.Argument1
CDB=SRB+0x048


现在,在Windbg里看一下CDB:




解析一下这条CDB命令:


28 00 03 55 67 ba 00 00 40 00 00 00 00 00 00 00 90 99 fc e6 8e e1 
28 00 03 55 67 ba 00 00 40 00  <---有效部分


Byte 0:0x28 表示 READ(10) 命令。用于从指定逻辑块地址 (LBA) 开始读取数据块。


Byte 1:这是一个位掩码字段,包含控制和保护标志。值 0x00 表示所有标志未设置。


Bytes 2-5 (Logical Block Address ): LBA 是一个 32 位无符号整数,表示读取起始地址(块号)。


计算:0x035567BA(大端序)。从逻辑块地址 55928762 开始读取数据。


Byte 6 (Group Number): 组号字段,通常保留,必须为 0。无特殊功能。


Bytes 7-8 :传输长度是一个 16 位无符号整数,表示要读取的数据块数量。


计算:0x0040(大端序)。含义:读取 64 个连续数据块。每个块大小由设备定义(通常为 512 字节或 4K 字节),需结合设备参数确定总字节数。


Byte 9 (Control): 控制字段,通常用于链接、队列或供应商特定选项。值 0x00 表示:


成功!我们解析了CDB!




2


编写驱动


最重要的DriverDispatch函数

首先,这个DriverDispatch应该有两个功能,一是分析SRB,二是调用源函数。


微软文档解释道这个级别的驱动程序不应调用系统例程(特别是文件系统例程,Io开头的),这个在我看来可能是死锁问题。


例如,我在DriverDispatch调用了NtWriteFile,而NtWriteFile又会调用我的DriverDispatch,造成死锁。


但是DbgPrint应该没什么事。


直接看代码解释吧,在DriveEntry中完成了对原函数地址有效性的检验,所以这里不用检验地址。


在这个示例中,不需对传入的参数进行检验,因为这是被修改的原函数内部完成的, 我们自己校验可能还会画蛇添足。


无论处理结果如何,都要将SRB传递。


NTSTATUS DispatchSCSI(
    PDEVICE_OBJECT pDeviceObject,
    PIRP pIrp
){
    PIO_STACK_LOCATION pStackLocation = NULL;
    PVOID pSrb = NULL;
    NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST;

// CDB is in the CurrentStackLocation
// We need to transform Irp first
    pStackLocation = pIrp->Tail.Overlay.CurrentStackLocation;
if (pStackLocation != NULL) {
//CDB is here
        pSrb = pStackLocation->Parameters.Others.Argument1;
if (pSrb != NULL) {
ReadCdb(pSrb);
        }
    }

// the function is defined as __fastcall convention
    status = DispatchRoutine(pDeviceObject, pIrp, gFuctionAddress);
return status;
}
}


DispatchRoutine是用汇编写的


DispatchRoutine proc

    mov rax, r8
    mov r8,  0H
    jmp rax       
    ret
DispatchRoutine endp


辅助函数

首先看修改MajorFunction的函数,在多核系统上还是挂一个锁比较好:


BOOLEAN WriteDriverMajorFunction(
    _In_    PDRIVER_OBJECT TargetDriver,
    _In_    PDRIVER_OBJECT SourceDriver,
    _Out_   PVOID* pOriginalFunctionAddr  
)
{
    DbgBreakPoint(); 
if (TargetDriver == NULL ||
        SourceDriver == NULL)
    {
        DbgPrint("Invalid parameters\n");
return FALSE;
    }

// Check whether the function is valid
if (TargetDriver->MajorFunction == NULL ||
        SourceDriver->MajorFunction == NULL)
    {
        DbgPrint("Driver has no MajorFunction table\n");
return FALSE;
    }

// Read the pointer
    PDRIVER_DISPATCH pTargetFn = TargetDriver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL];
    PDRIVER_DISPATCH pSourceFn = SourceDriver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL];

if (pTargetFn == NULL || pSourceFn == NULL)
    {
        DbgPrint("IRP_MJ_INTERNAL_DEVICE_CONTROL handler missing\n"
"Target: %p, Source: %p\n",
            pTargetFn, pSourceFn);
return FALSE;
    }

// Acquire a lock to exchage pointer 
    PDRIVER_DISPATCH pOriginal = (PDRIVER_DISPATCH)InterlockedExchangePointer(
        (PVOID volatile*)&TargetDriver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL],
        pSourceFn
    );

    *pOriginalFunctionAddr = (PVOID)pOriginal;
return TRUE;
}


那么,如何遍历设备栈获得底层设备呢?


我们可以利用DeviceObject的AttachTo指针遍历:


BOOLEAN GetLowestStackDevice(
    _In_  PDEVICE_OBJECT  CurrentDevice,
    _Out_ PDEVICE_OBJECT* LowestStackDevice,
    _Out_ PDRIVER_OBJECT* LowestStackDriver
)
{
//DbgBreakPoint();
    BOOLEAN bstatus = FALSE;

if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
DbgPrint("Must be called at PASSIVE_LEVEL\n");
return bstatus;
    }

if (CurrentDevice == NULL) {
DbgPrint("Empty CurrentDevice!,check it\n");
return bstatus;
    }

    PDEVICE_OBJECT currentDevice = CurrentDevice;

do {
if (!currentDevice->DeviceObjectExtension ||
            !currentDevice->DeviceObjectExtension->AttachedTo)
        {
break
        }
        currentDevice = currentDevice->DeviceObjectExtension->AttachedTo;
    } while (1);

if (!currentDevice->DriverObject) {
DbgPrint("Lowest device has no DriverObject\n");
return FALSE;
    }

    *LowestStackDevice = currentDevice;
    *LowestStackDriver = currentDevice->DriverObject;
ObReferenceObject(currentDevice);             
ObReferenceObject(currentDevice->DriverObject);
    bstatus = TRUE;
return bstatus;
}


遍历的起点是disk.sys,因为这个起点在绝大多数Windows上适用:


通过ObReferenceObjectByName打开disk.sys


BOOLEAN OpenDeviceObjectByDriverName(
    _In_  PUNICODE_STRING DriverName,
    _Out_ PDEVICE_OBJECT* DeviceObject 
)
{
    UNICODE_STRING driverName;
    PDEVICE_OBJECT pDeviceObject = NULL;
    PDRIVER_OBJECT pDriverObject = NULL;
    NTSTATUS status = 0;
    BOOLEAN bstatus = FALSE;

    driverName = *DriverName;
    status = ObReferenceObjectByName(
        &driverName,
        OBJ_CASE_INSENSITIVE,
NULL,
        FILE_ALL_ACCESS,
        *IoDriverObjectType,
        KernelMode,
NULL,
        (PVOID*)&pDriverObject
    );
if (!NT_SUCCESS(status)) {
        DbgPrint("Failed in ObReferenceObjectByName,status: 0x%X\n", status);
return bstatus;
    }

    pDeviceObject = pDriverObject->DeviceObject;
if (pDeviceObject != NULL
    {
        ObReferenceObject(pDeviceObject);
        *DeviceObject = pDeviceObject;
        bstatus = TRUE;
    }
else {
        DbgPrint("Driver has no associated device object\n");
    }


    ObDereferenceObject(pDriverObject);
return bstatus;
}


使用ObReferenceObjectByName应注意


extern POBJECT_TYPE* IoDriverObjectType;

NTKERNELAPI 
NTSTATUS
ObReferenceObjectByName(
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN PACCESS_STATE PassedAccessState OPTIONAL,
IN ACCESS_MASK DesiredAccess OPTIONAL,
IN POBJECT_TYPE ObjectType,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
    OUT PVOID* Object
);


把以上补充在头文件中,然后是解析CDB中的LBA


BOOLEAN ReadCdb(
    _In_ PVOID SrbAddress
)
{
PUCHARcdb = (PUCHAR)SrbAddress + CDB_OFFSET_IN_SRB;
UCHARopcode = cdb[0];
ULONGLONGlba =0;

// As a demonstration, this only solve READ_12 Command
// You can add others 
if (opcode != 0x28) {  // READ_12
return FALSE;
    }

    lba = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5]; 
    DbgPrint("LBA: 0x%08X\n", (ULONG_PTR)lba);
return TRUE;
}


作为示例,这里只解析了READ_12命令,你也可以添加对其他命令的处理,如对Inquiry的分析。


效果



写到这里,突然想到一些ARK不是支持通过SCSI命令解析磁盘信息吗,这个理论上可以拦截,但也没试过。




3


总结


为什么只解析CDB而非源文件

从CDB逆推文件极为困难,首先是一个磁盘可以有多个卷,文件系统各不一样,即使只有一个文件系统,以NTFS为例,MFT到LCN再到LBA可以完成,但是LBA逆推到MFT就涉及所有文件的映射,这个映射的大小堪比页表,甚至远大于页表,而且需要实时更新,所以这种方法对内存和CPU的开销都很大。


所以呢,这个项目的示例意义大于实际意义,是一个实验性质的项目。


关于附的代码(点击“阅读原文”查看/下载)

这份代码的健壮性是很差的,其只考虑SATA硬盘等典型情况,而对NVMe硬盘和PnP设备完全未考虑,正因如此,这份代码只作为示例实现,不可直接应用于生产环境。


同时,对系统底层的修改会带来极大的不稳定性,应该谨慎使用。至于PG,我进行了三次5min的测试,均未蓝屏。这份代码仅作为研究使用。





看雪ID:TurkeybraNC

https://bbs.kanxue.com/user-home-982720.htm

*本文为看雪论坛优秀文章,由 TurkeybraNC 原创,转载请注明来自看雪社区

议题征集中!看雪·第九届安全开发者峰会(SDC 2025)

# 往期推荐

安卓旧系统 OTA 包分析与漏洞提权适配

XCTF L3HCTF 2025 pwn 方向解题思路

Pwn题解析|L3CTF 2025 heack & heack_revenge

OLLVM-BR间接混淆去除

House of Einherjar

图片

球分享

球点赞

球在看


点击阅读原文查看更多

【声明】内容源于网络
0
0
Coco跨境电商
跨境分享所 | 持续提供优质干货
内容 192965
粉丝 3
Coco跨境电商 跨境分享所 | 持续提供优质干货
总阅读458.6k
粉丝3
内容193.0k