4)、_CDB 在_SCSI_REQUEST_BLOCK结构最后是UCHAR Cdb[16]; 但是Cdb不一定是16个字节,长度由_SCSI_REQUEST_BLOCK.CdbLength决定,从上面可以看出这里是0Ah个字节。该结构是一个联合体,这里使用的是_CDB10结构。 mov byte ptr [esi+0Ah], 0Ah ; _SCSI_REQUEST_BLOCK.CdbLength = 0Ah mov cl, [ebp+IRP_ID] add cl, 11h shl cl, 1 mov [esi+30h], cl ; _CDB[0] = 28h mov cl, [esi+31h] and cl, 1Fh or cl, 80h mov [esi+31h], cl ; _CDB[1] = 80h mov ecx, eax shr ecx, 18h mov [esi+32h], cl ; if Read _CDB[2] = 0 mov ecx, eax shr ecx, 10h mov [esi+33h], cl ; if Read _CDB[3] = 0 mov ecx, eax shr ecx, 8 mov [esi+34h], cl ; if Read _CDB[4] = 0 mov [esi+35h], al ; if Read _CDB[5] = 0 mov ax, [ebp+SecNum] mov [esi+37h], ah ; if Read _CDB[7] = 0 cmp ax, bx setnz al mov [esi+38h], al ; if Read _CDB[8] = 1 从scsi.h中找到如下: // 10-byte commands #define SCSIOP_READ_FORMATTED_CAPACITY 0x23 #define SCSIOP_READ_CAPACITY 0x25 #define SCSIOP_READ 0x28 #define SCSIOP_WRITE 0x2A 可以看出,_CDB[0]为OperationCode,28h表示SCSIOP_READ,2Ah表示SCSIOP_WRITE。_CDB[1]按位描述了一些属性,可以参考scsi.h,这里将这个字节设为80h。_CDB[2]到_CDB[5]描述的是扇区位置,依次为dwSectorLowPos从高字节到低字节的值。_CDB[7]和_CDB[8]描述了要写书的扇区数。如果小于0,则设为1。 由于本人的语言组织能力较弱,写得比较乱,详细的请看idb文件。这个函数将会完成向指定扇区读写数据。
9、读取主分区表 调用My_Read_WriteSector(DiskDeviceObj, 3, pBuffer, 0, 1)来获取磁盘0号扇区的数据,大小为1个扇区的大小,即512字节。 0号扇区保存了主分区表,结构为: typedef struct _MBR_SECTOR { UCHAR BootCode[446]; PARTITION_ENTRY Partition[4]; USHORT Signature; } MBR_SECTOR, *PMBR_SECTOR; 这里主要用到了PARTITION_ENTRY结构: typedef struct _PARTITION_ENTRY { UCHAR active; // 能否启动标志 UCHAR StartHead; // 该分区起始磁头号 UCHAR StartSector; // 起始柱面号高2位:6位起始扇区号 UCHAR StartCylinder; // 起始柱面号低8位 UCHAR PartitionType; // 分区类型 UCHAR EndHead; // 该分区终止磁头号 UCHAR EndSector; // 终止柱面号高2位:6位终止扇区号 UCHAR EndCylinder; // 终止柱面号低8位 ULONG StartLBA; // 起始扇区号 ULONG TotalSector; // 分区尺寸(总扇区数) } PARTITION_ENTRY, *PPARTITION_ENTRY; 其中active为80h,表示该分区能够启动系统。 PartitionType表示分区类型,如NTFS、FAT32等等,如果为FF表示该分区为扩展分区,扩展分区会再有分区表,划分为逻辑分区。如果你有兴趣,可以查阅其它资料。 本处依次判断Partition[0]到Partition[3]是否为启动分区,如果是,则计算StartLBA,这里比较有意思的是StartLBA[n] = StartLBA[0] + ... + StartLBA[n],这个我是比较奇怪的,从《数据恢复技术》一书中,我的理解是,如果为基本分区,StartLBA直接是分区的起始逻辑扇区,如果是扩展分区,则需要加上的保留扇区数。这里是让我感到不解的,如果自己分析,要自己测试多个系统、多种分区格式的分区表,遂放弃。希望知道的兄弟能够指点迷津,抑或是机器狗的一个bug。但对于大多数系统,Partition[0]即是启动分区,Partition[0].StartLBA也就是该分区的起始扇区位置。 判断Partition[0].PartitionType如果不是PARTITION_TYPE_FAT32、PARTITION_TYPE_FAT32_LBA或PARTITION_TYPE_NTFS,则返回失败。
10、读取启动分区的第一个扇区 定位到启动分区后,调用My_Read_WriteSector(DiskDeviceObj, 3, pBuffer, StartLBA, 1)来读取启动分区的第一个扇区的内容,即分区信息。结构如下: typedef struct _BBR_SECTOR { USHORT JmpCode; // 2字节跳转指令,跳转到引导代码 UCHAR NopCode; // 1字节nop指令,填充用,保证跳转指令长3个字节 UCHAR OEMName[8]; // 8字节的OEMName // 下面开始为: BPB( BIOS Parameter Block ) USHORT BytesPerSector; // 每个扇区的字节数 (512 1024 2048 4096) UCHAR SectorsPerCluster; // 每个簇的扇区数 ( 1 2 4 8 16 32 64 128 )两者相乘不能超过32K(簇最大大小) USHORT ReservedSectors; // 从卷的第一个扇区开始的保留扇区数目,该值不能为0,对于FAT12/FAT16,该值通常为1,对于FAT32,典型值为32 UCHAR NumberOfFATs; // 卷上FAT数据结构的数目,该值通常应为2,[NTFS不使用NumberOfFATs字段,必须为0] USHORT RootEntries; // 对于FAT12/FAT16,该值表示32字节目录项的数目,对于FAT32,该值必须为0;[NTFS不使用] USHORT NumberOfSectors16; // 该卷上的扇区总数,该字段可以为0,如果该字段为0,则NumberOfSectors32不能为0;对于FAT32,该字段必须为0 [FAT32/NTFS不使用该字段] UCHAR MediaDescriptor; // 介质类型 USHORT SectorsPerFAT16; // 该字段标识一个FAT结构占有的扇区数(FAT12/FAT16),对于FAT32卷,该字段必须为0;[FAT32/NTFS不使用该字段] USHORT SectorsPerTrack; // 用于INT 0x13中断的每个磁道的扇区数 USHORT HeadsPerCylinder; // 用于INT 0x13中断的每个柱面的磁头数 ULONG HiddenSectors; // 包含该FAT卷的分区之前的隐藏扇区数 ULONG NumberOfSectors32; // 该字段包含该卷上的所有扇区数目,对于FAT32,该字段不为0;FAT12/FAT16可根据实际大小是否超过65536个扇区数决定是否采用该字段; [NTFS不使用该字段] // 下面开始为: EBPB ( Extended BIOS Parameter Block ) ULONG SectorsPerFAT32; // 对于FAT32,该字段包含一个FAT的大小,而SectorsPerFAT16字段必须为0; } BBR_SECTOR, *PBBR_SECTOR;
取得分区信息之后,获取SectorsPerCluster和ReservedSectors。 判断Partition[0].PartitionType是否是PARTITION_TYPE_FAT32或PARTITION_TYPE_FAT32_LBA,如果是,则获取SectorsPerFAT32和NumberOfFATs。
11、计算文件对应的扇区位置 Lcn_HightPart 和Lcn_LowPart由第7步获得。 1)、如果是FAT32分区,dwSectorLowPos = StartLBA + ReservedSectors + SectorsPerFAT32 * NumberOfFATs + SectorsPerCluster * Lcn_LowPart; FileSectorHighPos = Lcn_HightPart * SectorsPerCluster; 2)、如果是NTFS分区,dwSectorLowPos = StartLBA + ReservedSectors + SectorsPerCluster * Lcn_LowPart; FileSectorHighPos = Lcn_HightPart * SectorsPerCluster; FileSectorHighPos在将数据写入扇区时,并没有使用。
12、写入病毒 是由DeviceIoControl的800C004h号控制命令触发的,写入文件的数据由DeviceIoControl传入。调用My_Read_WriteSector(DiskDeviceObj, 4, pBuffer, dwSectorLowPos, dwFileSize / 512 + 1)来实现向扇区的写入。 另外DeviceIoControl的800C008h号控制命令会摘掉ntfs的AttachedDevice。
参考资料 1、《数据恢复技术》 2、http://dev.csdn.net/article/78/78564.shtm 3、http://hi.baidu.com/ptf_phoenix/blog/item/c5db4f1f319f6ecca7866994.html 4、winddk 5、msdn 还有一些网上的其它资料,不一一列出。 上一页 1 2 |