Hida病毒分析与防御
样本信息Hida_x64.exe
MD5: A8FDD696A912D5A339872E9529652AAB
SHA-1: 48D6FA13E10BF9681CCD49935916584151C16C4C
SHA-256: 5863025DA90C803987C4EC48AC09FDDDBD99FD7B644677475865591BF6CA6851
Hida_x86.exe
MD5: 6C1B11A4920E7294CB730614046B9CDF
SHA-1: 76C35D19190EAB42A632EC57BC126B99E043EFAF
SHA-256: 5367DA4B60BEA56BDC8EC14C01D66E7C3E6253B4122A69B34FE718E7CABA90C3
HidaSys_x64.sys
MD5: 453C61CB9DA5172BFE882CE0DD89A348
SHA-1: 52D52FD025B6B25F7338D9415E365E6F249F2240
SHA-256: 72182FF1C777CF42BC2BF5F17372A6F21B9EAF89AC7C0D2BE46747FCE246BB51
HidaSys_x86.sys
MD5: DE7DA23325C997539F3954691AB6F600
SHA-1: DDD57B118FBC300D174D360BC0DBE34AFBD63D80
SHA-256: 567EB7241090FF727220350D043191BC3F0DE6E0A44732F129339ADA5AAD3D9D
注意:样本的数字签名已经失效并被杀毒软件拉黑,所以此样本并无太大危害。本文只分析其穿透影子系统的技术及防御方法、硬盘逻辑锁原理及防御方法(只对64位的程序和驱动进行分析)
Hida_x64.exe
这就是一个驱动加载器,主要负责加载HidaSys_64.sys并发送控制码,下面是用IDA反编译的代码
LOWORD(BytesReturned) = 0;
sub_1400077A0(BytesReturned, L".\\HidaSys_x64.sys"); // wcscpy
LOWORD(lpConsoleTitle) = 0;
sub_1400077A0(lpConsoleTitle, L"Hida_x64"); // wcscpy
v35 = sub_140011280(lpServiceName, lpConsoleTitle, (LPCWSTR)BytesReturned);// 加载驱动
LOWORD(lpConsoleTitle) = 0;
if... // 错误处理
FileW = CreateFileW(L"\\\\.\\Hida", 0xC0000000, 3u, 0i64, 3u, 0, 0i64);// 打开驱动
if... // 错误处理
BytesReturned = 0;
if ( !DeviceIoControl(FileW, 0x227014u, 0i64, 0, 0i64, 0, BytesReturned, 0i64) )// 发送驱动控制码
{
// 错误处理
goto LABEL_85;
}
// 下面的都是显示中毒提示
HidaSys_x64.sys
病毒的主体就是这部分,使用IDA反编译,符号文件位置 H:\CodeSpace\Hida\x64\Release\HidaSys.pdb
发现有大量调试输出,根据调试输出重命名函数。由于病毒是通过发送控制码启动的,所以先找到ControlDispatch,继续从里面找到DestroyAll函数,流程为:
1. 通过名称获取硬盘驱动对象
2. 遍历硬盘驱动对象保存的设备对象,对每个硬盘设备进行破坏
3. 破坏硬盘设备时先解析分区表,再对每个分区进行破坏
4. 破坏分区时先破坏MFT(Master File Table,主文件表),再破坏PBR(Partition Boot Record,分区引导记录)
5. 修改MBR进行硬盘逻辑锁
所以感染此病毒后,无法进入Windows PE(逻辑锁),无法使用DiskGenius搜到分区表(PBR丢失),无法恢复文件(MFT丢失)
硬盘逻辑锁分析
感染此病毒后,MBR被修改
0000 EA 05 7C 00 00 FA 31 C08E D0 BC F0 7B FB BB 00 ..|...1.....{...
0010 10 8E C3 B8 08 02 B9 0200 B6 00 31 DB CD 13 06 ...........1....
0020 1F B8 00 20 8E C0 31 C089 C3 89 C1 89 C2 89 C7 ... ..1.........
0030 89 C6 AC 81 FE 1E 0B 732F 3C 80 73 02 EB 0F 24 .......s/<.s...$
0040 7F 88 C1 AC AA FE C9 80F9 FF 75 F7 EB E4 88 C4 ..........u.....
0050 AC 89 C3 AC 1E 68 00 201F 89 F2 89 DE 88 C1 AC .....h. ........
0060 AA E2 FC 89 D6 1F EB CAB8 00 20 8E D8 8E C0 EA .......... .....
0070 00 00 00 20 00 00 00 0000 00 00 00 00 00 00 00 ... ............
0080 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0090 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00A0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00B0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00C0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00D0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00E0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00F0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0100 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0110 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0120 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0130 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0140 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0150 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0160 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0170 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0180 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0190 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
01A0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
01B0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 FE ................
01C0 FF FF 0F FE FF FF 1A 0200 00 7D 3E 00 00 00 00 ..........}>....
01D0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
01E0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
01F0 00 00 00 00 00 00 00 0000 00 00 00 00 00 55 AA ..............U.
前面的引导代码是一段彩虹猫5.0的MBR代码,主分区表里只有一个扩展分区表,位于538扇区
扩展分区表一共有两个分区,第一个分区的起始扇区为12773,分区类型标记为07 NTFS(但实际是空的),第二个分区起始扇区为0,分区类型标记为05 Extended Partition,又是扩展分区表!(参考https://thestarman.pcministry.com/asm/mbr/PartTypes.htm)
0000 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0010 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0020 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0030 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0040 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0050 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0060 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0070 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0080 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0090 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00A0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00B0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00C0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00D0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00E0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
00F0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0100 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0110 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0120 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0130 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0140 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0150 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0160 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0170 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0180 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
0190 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
01A0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
01B0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 FE ................
01C0 FF FF 07 FE FF FF E5 3100 00 99 6E 00 00 00 FE .......1...n....
01D0 FF FF 05 FE FF FF 00 0000 00 00 F0 25 00 CB A9 ............%...
01E0 00 00 00 00 00 00 00 0000 00 00 00 00 00 00 00 ................
01F0 00 00 00 00 00 00 00 0000 00 00 00 00 00 55 AA ..............U.
所以,在Windows启动的时候读取分区表会陷入死循环,无法启动
但是DiskGenius能发现并修复逻辑锁
Windows为什么会被逻辑锁
首先查看Win2k3 disk.sys的读取分区表的源代码(位于NT\drivers\storage\disk\part.c),发现经过缓存后直接调用了IoReadPartitionInfoEx,查看IoReadPartitionInfoEx的源代码(位于NT\base\ntos\fstub\ex.c),发现判断分区表类型后又调用了IoReadPartition。
下面分析IoReadPartitionInfo(源代码位于NT\base\ntos\fstub\drivesup.c),对BUG的解释就在第599行NTSTATUS
FASTCALL
IoReadPartitionTable(
IN PDEVICE_OBJECT DeviceObject,
IN ULONG SectorSize,
IN BOOLEAN ReturnRecognizedPartitions,
OUT struct _DRIVE_LAYOUT_INFORMATION **PartitionBuffer
)
/*++
Routine Description:
This routine walks the disk reading the partition tables and creates
an entry in the partition list buffer for each partition.
The algorithm used by this routine is two-fold:
1)Read each partition table and for each valid, recognized
partition found, to build a descriptor in a partition list.
Extended partitions are located in order to find other
partition tables, but no descriptors are built for these.
The partition list is built in nonpaged pool that is allocated
by this routine.It is the caller's responsibility to free
this pool after it has gathered the appropriate information
from the list.
2)Read each partition table and for each and every entry, build
a descriptor in the partition list.Extended partitions are
located to find each partition table on the disk, and entries
are built for these as well.The partition list is build in
nonpaged pool that is allocated by this routine.It is the
caller's responsibility to free this pool after it has copied
the information back to its caller.
The first algorithm is used when the ReturnRecognizedPartitions flag
is set.This is used to determine how many partition device objects
the device driver is to create, and where each lives on the drive.
The second algorithm is used when the ReturnRecognizedPartitions flag
is clear.This is used to find all of the partition tables and their
entries for a utility such as fdisk, that would like to revamp where
the partitions live.
Arguments:
DeviceObject - Pointer to device object for this disk.
SectorSize - Sector size on the device.
ReturnRecognizedPartitions - A flag indicated whether only recognized
partition descriptors are to be returned, or whether all partition
entries are to be returned.
PartitionBuffer - Pointer to the pointer of the buffer in which the list
of partition will be stored.
Return Value:
The functional value is STATUS_SUCCESS if at least one sector table was
read.
Notes:
It is the responsibility of the caller to deallocate the partition list
buffer allocated by this routine.
--*/
{
ULONG partitionBufferSize = PARTITION_BUFFER_SIZE;
PDRIVE_LAYOUT_INFORMATION newPartitionBuffer = NULL;
LONG partitionTableCounter = -1;
DISK_GEOMETRY diskGeometry;
ULONGLONG endSector;
ULONGLONG maxSector;
ULONGLONG maxOffset;
LARGE_INTEGER partitionTableOffset;
LARGE_INTEGER volumeStartOffset;
LARGE_INTEGER tempInt;
BOOLEAN primaryPartitionTable;
LONG partitionNumber;
PUCHAR readBuffer = (PUCHAR) NULL;
KEVENT event;
IO_STATUS_BLOCK ioStatus;
PIRP irp;
PPARTITION_DESCRIPTOR partitionTableEntry;
CCHAR partitionEntry;
NTSTATUS status = STATUS_SUCCESS;
ULONG readSize;
PPARTITION_INFORMATION partitionInfo;
BOOLEAN foundEZHooker = FALSE;
BOOLEAN mbrSignatureFound = FALSE;
BOOLEAN emptyPartitionTable = TRUE;
PAGED_CODE();
//
// Create the buffer that will be passed back to the driver containing
// the list of partitions on the disk.
//
*PartitionBuffer = ExAllocatePoolWithTag( NonPagedPool,
partitionBufferSize,
'btsF' );
if (*PartitionBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Determine the size of a read operation to ensure that at least 512
// bytes are read.This will guarantee that enough data is read to
// include an entire partition table.Note that this code assumes that
// the actual sector size of the disk (if less than 512 bytes) is a
// multiple of 2, a fairly reasonable assumption.
//
if (SectorSize >= 512) {
readSize = SectorSize;
} else {
readSize = 512;
}
//
// Look to see if this is an EZDrive Disk.If it is then get the
// real parititon table at 1.
//
{
PVOID buff;
HalExamineMBR(
DeviceObject,
readSize,
(ULONG)0x55,
&buff
);
if (buff) {
foundEZHooker = TRUE;
ExFreePool(buff);
partitionTableOffset.QuadPart = 512;
} else {
partitionTableOffset.QuadPart = 0;
}
}
//
// Get the drive size so we can verify that the partition table is
// correct.
//
status = HalpGetFullGeometry(DeviceObject,
&diskGeometry,
&maxOffset);
if(!NT_SUCCESS(status)) {
ExFreePool(*PartitionBuffer);
*PartitionBuffer = NULL;
return status;
}
//
// Partition offsets need to fit on the disk or we're not going to
// expose them.Partition ends are generally very very sloppy so we
// need to allow some slop.Adding in a cylinders worth isn't enough
// so now we'll assume that all partitions end within 2x of the real end
// of the disk.
//
endSector = maxOffset;
maxSector = maxOffset * 2;
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: MaxOffset = %#I64x, maxSector = %#I64x\n",
maxOffset,
maxSector));
//
// Indicate that the primary partition table is being read and
// processed.
//
primaryPartitionTable = TRUE;
//
// The partitions in this volume have their start sector as 0.
//
volumeStartOffset.QuadPart = 0;
//
// Initialize the number of partitions in the list.
//
partitionNumber = -1;
//
// Allocate a buffer that will hold the reads.
//
readBuffer = ExAllocatePoolWithTag( NonPagedPoolCacheAligned,
PAGE_SIZE,
'btsF' );
if (readBuffer == NULL) {
ExFreePool( *PartitionBuffer );
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Read each partition table, create an object for the partition(s)
// it represents, and then if there is a link entry to another
// partition table, repeat.
//
// 这里已经写出算法了:读取每个分区表,为其所表示的分区创建一个对象,如果存在指向另一个分区表的链接条目,则重复此过程
do {
BOOLEAN tableIsValid;
ULONG containerPartitionCount;
tableIsValid = TRUE;
//
// Read record containing partition table.
//
// Create a notification event object to be used while waiting for
// the read request to complete.
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
//
// Zero out the buffer we're reading into.In case we get back
// STATUS_NO_DATA_DETECTED we'll be prepared.
//
RtlZeroMemory(readBuffer, readSize);
irp = IoBuildSynchronousFsdRequest( IRP_MJ_READ,
DeviceObject,
readBuffer,
readSize,
&partitionTableOffset,
&event,
&ioStatus );
if (!irp) {
status = STATUS_INSUFFICIENT_RESOURCES;
break;
} else {
PIO_STACK_LOCATION irpStack;
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
}
status = IoCallDriver( DeviceObject, irp );
if (status == STATUS_PENDING) {
(VOID) KeWaitForSingleObject( &event,
Executive,
KernelMode,
FALSE,
(PLARGE_INTEGER) NULL);
status = ioStatus.Status;
}
//
// Special case - if we got a blank-check reading the sector then
// pretend it was just successful so we can deal with superfloppies
// where noone bothered to write anything to the non-filesystem sectors
//
if(status == STATUS_NO_DATA_DETECTED) {
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS( status )) {
break;
}
//
// If EZDrive is hooking the MBR then we found the first partition table
// in sector 1 rather than 0.However that partition table is relative
// to sector zero.So, Even though we got it from one, reset the partition
// offset to 0.
//
if (foundEZHooker && (partitionTableOffset.QuadPart == 512)) {
partitionTableOffset.QuadPart = 0;
}
//
// Check for Boot Record signature.
//
if (((PUSHORT) readBuffer) != BOOT_RECORD_SIGNATURE) {
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_WARNING_LEVEL,
"FSTUB: (IoReadPartitionTable) No 0xaa55 found in partition table %d\n",
partitionTableCounter + 1));
break;
} else {
mbrSignatureFound = TRUE;
}
//
// Copy NTFT disk signature to buffer
//
if (partitionTableOffset.QuadPart == 0) {
(*PartitionBuffer)->Signature =((PULONG) readBuffer);
}
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) readBuffer));
//
// Keep count of partition tables in case we have an extended partition;
//
partitionTableCounter++;
//
// First create the objects corresponding to the entries in this
// table that are not link entries or are unused.
//
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: Partition Table %d:\n",
partitionTableCounter));
for (partitionEntry = 1, containerPartitionCount = 0;
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
partitionEntry++, partitionTableEntry++) {
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"Partition Entry %d,%d: type %#x %s\n",
partitionTableCounter,
partitionEntry,
partitionTableEntry->PartitionType,
(partitionTableEntry->ActiveFlag) ? "Active" : ""));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"\tOffset %#08lx for %#08lx Sectors\n",
GET_STARTING_SECTOR(partitionTableEntry),
GET_PARTITION_LENGTH(partitionTableEntry)));
if (partitionTableEntry->PartitionType == 0xEE) {
FstubFixupEfiPartition (partitionTableEntry,
maxOffset);
}
//
// Do a quick pass over the entry to see if this table is valid.
// It's only fatal if the master partition table is invalid.
//
if((HalpIsValidPartitionEntry(partitionTableEntry,
maxOffset,
maxSector) == FALSE) &&
(partitionTableCounter == 0)) {
tableIsValid = FALSE;
break;
}
//
// Only one container partition is allowed per table - any more
// and it's invalid.
//
if(IsContainerPartition(partitionTableEntry->PartitionType)) {
containerPartitionCount++;
if(containerPartitionCount != 1) {
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_ERROR_LEVEL,
"FSTUB: Multiple container partitions found in "
"partition table %d\n - table is invalid\n",
partitionTableCounter));
tableIsValid = FALSE;
break;
}
}
if(emptyPartitionTable) {
if((GET_STARTING_SECTOR(partitionTableEntry) != 0) ||
(GET_PARTITION_LENGTH(partitionTableEntry) != 0)) {
//
// There's a valid, non-empty partition here. The table
// is not empty.
//
emptyPartitionTable = FALSE;
}
}
//
// If the partition entry is not used or not recognized, skip
// it.Note that this is only done if the caller wanted only
// recognized partition descriptors returned.
//
if (ReturnRecognizedPartitions) {
//
// Check if partition type is 0 (unused) or 5/f (extended).
// The definition of recognized partitions has broadened
// to include any partition type other than 0 or 5/f.
//
if ((partitionTableEntry->PartitionType == PARTITION_ENTRY_UNUSED) ||
IsContainerPartition(partitionTableEntry->PartitionType)) {
continue;
}
}
//
// Bump up to the next partition entry.
//
partitionNumber++;
if (((partitionNumber * sizeof( PARTITION_INFORMATION )) +
sizeof( DRIVE_LAYOUT_INFORMATION )) >
(ULONG) partitionBufferSize) {
//
// The partition list is too small to contain all of the
// entries, so create a buffer that is twice as large to
// store the partition list and copy the old buffer into
// the new one.
//
newPartitionBuffer = ExAllocatePoolWithTag( NonPagedPool,
partitionBufferSize << 1,
'btsF' );
if (newPartitionBuffer == NULL) {
--partitionNumber;
status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
RtlCopyMemory( newPartitionBuffer,
*PartitionBuffer,
partitionBufferSize );
ExFreePool( *PartitionBuffer );
//
// Reassign the new buffer to the return parameter and
// reset the size of the buffer.
//
*PartitionBuffer = newPartitionBuffer;
partitionBufferSize <<= 1;
}
//
// Describe this partition table entry in the partition list
// entry being built for the driver.This includes writing
// the partition type, starting offset of the partition, and
// the length of the partition.
//
partitionInfo = &(*PartitionBuffer)->PartitionEntry;
partitionInfo->PartitionType = partitionTableEntry->PartitionType;
partitionInfo->RewritePartition = FALSE;
if (partitionTableEntry->PartitionType != PARTITION_ENTRY_UNUSED) {
LONGLONG startOffset;
partitionInfo->BootIndicator =
partitionTableEntry->ActiveFlag & PARTITION_ACTIVE_FLAG ?
(BOOLEAN) TRUE : (BOOLEAN) FALSE;
if (IsContainerPartition(partitionTableEntry->PartitionType)) {
partitionInfo->RecognizedPartition = FALSE;
startOffset = volumeStartOffset.QuadPart;
} else {
partitionInfo->RecognizedPartition = TRUE;
startOffset = partitionTableOffset.QuadPart;
}
partitionInfo->StartingOffset.QuadPart = startOffset +
UInt32x32To64(GET_STARTING_SECTOR(partitionTableEntry),
SectorSize);
tempInt.QuadPart = (partitionInfo->StartingOffset.QuadPart -
startOffset) / SectorSize;
partitionInfo->HiddenSectors = tempInt.LowPart;
partitionInfo->PartitionLength.QuadPart =
UInt32x32To64(GET_PARTITION_LENGTH(partitionTableEntry),
SectorSize);
} else {
//
// Partitions that are not used do not describe any part
// of the disk.These types are recorded in the partition
// list buffer when the caller requested all of the entries
// be returned.Simply zero out the remaining fields in
// the entry.
//
partitionInfo->BootIndicator = FALSE;
partitionInfo->RecognizedPartition = FALSE;
partitionInfo->StartingOffset.QuadPart = 0;
partitionInfo->PartitionLength.QuadPart = 0;
partitionInfo->HiddenSectors = 0;
}
}
KdPrintEx((DPFLTR_FSTUB_ID, DPFLTR_TRACE_LEVEL, "\n"));
//
// If an error occurred, leave the routine now.
//
if (!NT_SUCCESS( status )) {
break;
}
if(tableIsValid == FALSE) {
//
// Invalidate this partition table and stop looking for new ones.
// we'll build the partition list based on the ones we found
// previously.
//
partitionTableCounter--;
break;
}
//
// Now check to see if there are any link entries in this table,
// and if so, set up the sector address of the next partition table.
// There can only be one link entry in each partition table, and it
// will point to the next table.
//
partitionTableEntry = (PPARTITION_DESCRIPTOR) &(((PUSHORT) readBuffer));
//
// Assume that the link entry is empty.
//
partitionTableOffset.QuadPart = 0;
for (partitionEntry = 1;
partitionEntry <= NUM_PARTITION_TABLE_ENTRIES;
partitionEntry++, partitionTableEntry++) {
if (IsContainerPartition(partitionTableEntry->PartitionType)) {
//
// Obtain the address of the next partition table on the
// disk.This is the number of hidden sectors added to
// the beginning of the extended partition (in the case of
// logical drives), since all logical drives are relative
// to the extended partition.The VolumeStartSector will
// be zero if this is the primary parition table.
//
// BUG就在这里,既没有判断同样的扇区是否被访问过,又没有判断新扇区是否是前面的扇区,就卡在这个循环里了,直到最后因为无法分配内存而退出
partitionTableOffset.QuadPart = volumeStartOffset.QuadPart +
UInt32x32To64(GET_STARTING_SECTOR(partitionTableEntry),
SectorSize);
//
// Set the VolumeStartSector to be the begining of the
// second partition (extended partition) because all of
// the offsets to the partition tables of the logical drives
// are relative to this extended partition.
//
if (primaryPartitionTable) {
volumeStartOffset = partitionTableOffset;
}
//
// Update the maximum sector to be the end of the container
// partition.
//
maxSector = GET_PARTITION_LENGTH(partitionTableEntry);
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: MaxSector now = %#08lx\n",
maxSector));
//
// There is only ever one link entry per partition table,
// exit the loop once it has been found.
//
break;
}
}
//
// All the other partitions will be logical drives.
//
primaryPartitionTable = FALSE;
} while (partitionTableOffset.HighPart | partitionTableOffset.LowPart);
//
// Detect super-floppy media attempt #1.
// If the media is removable and has an 0xaa55 signature on it and
// is empty then check to see if we can recognize the BPB.If we recognize
// a jump-byte at the beginning of the media then it's a super floppy.If
// we don't then it's an unpartitioned disk.
//
if((diskGeometry.MediaType == RemovableMedia) &&
(partitionTableCounter == 0) &&
(mbrSignatureFound == TRUE) &&
(emptyPartitionTable == TRUE)) {
PBOOT_SECTOR_INFO bootSector = (PBOOT_SECTOR_INFO) readBuffer;
if((bootSector->JumpByte == 0xeb) ||
(bootSector->JumpByte == 0xe9)) {
//
// We've got a superfloppy of some sort.
//
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: Jump byte %#x found "
"along with empty partition table - disk is a "
"super floppy and has no valid MBR\n",
bootSector->JumpByte));
partitionTableCounter = -1;
}
}
//
// If the partition table count is still -1 then we didn't find any
// valid partition records.In this case we'll build a partition list
// that contiains one partition spanning the entire disk.
//
if(partitionTableCounter == -1) {
if((mbrSignatureFound == TRUE) ||
(diskGeometry.MediaType == RemovableMedia)) {
//
// Either we found a signature but the partition layout was
// invalid (for all disks) or we didn't find a signature but this
// is a removable disk.Either of these two cases makes a
// superfloppy.
//
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: Drive %#p has no valid MBR. "
"Make it into a super-floppy\n", DeviceObject));
KdPrintEx((DPFLTR_FSTUB_ID,
DPFLTR_TRACE_LEVEL,
"FSTUB: Drive has %#08lx sectors "
"and is %#016I64x bytes large\n",
endSector,
endSector * diskGeometry.BytesPerSector));
if (endSector > 0) {
partitionInfo = &(*PartitionBuffer)->PartitionEntry;
partitionInfo->RewritePartition = FALSE;
partitionInfo->RecognizedPartition = TRUE;
partitionInfo->PartitionType = PARTITION_FAT_16;
partitionInfo->BootIndicator = FALSE;
partitionInfo->HiddenSectors = 0;
partitionInfo->StartingOffset.QuadPart = 0;
partitionInfo->PartitionLength.QuadPart =
(endSector * diskGeometry.BytesPerSector);
(*PartitionBuffer)->Signature = 1;
partitionNumber = 0;
}
} else {
//
// We found no partitions.Make sure the partition count is -1
// so that we setup a zeroed-out partition table below.
//
partitionNumber = -1;
}
}
//
// Fill in the first field in the PartitionBuffer. This field indicates how
// many partition entries there are in the PartitionBuffer.
//
(*PartitionBuffer)->PartitionCount = ++partitionNumber;
if (!partitionNumber) {
//
// Zero out disk signature.
//
(*PartitionBuffer)->Signature = 0;
}
//
// Deallocate read buffer if it was allocated it.
//
if (readBuffer != NULL) {
ExFreePool( readBuffer );
}
if (!NT_SUCCESS(status)) {
ExFreePool(*PartitionBuffer);
*PartitionBuffer = NULL;
}
#if DBG
if (NT_SUCCESS(status)) {
FstubDbgPrintDriveLayout(*PartitionBuffer);
}
#endif
return status;
}
原因已经很清楚了,就是Windows内核在找分区的时候没有进行更多的判断,导致了死循环,但是这是Win2k3的代码,到Win11这个BUG还没有修复。
修复方法
首先需要解除硬盘逻辑锁。
如果是BIOS启动中了逻辑锁,可以用DiskGenius DOS版(或其他非微软系列的操作系统,如FreeDOS,linux)删除分区表。
如果是UEFI中了逻辑锁,就比较麻烦(有的电脑连UEFI设置都进不去),可以考虑把硬盘拆下并接到linux电脑上删除分区表。
再进入PE,如果不想恢复数据就可以重新分区重装系统了,如果想恢复数据可以找专业恢复数据的地方恢复,下面是手动恢复方法(实验性的,不一定能恢复成功,建议先备份)
1. 打开WinHex,选择工具->打开磁盘->物理驱动器->你的硬盘
2. 选择搜索->查找文本,输入NTLDR,编码选择Unicode,记下搜索到的扇区编号
3. 如果有此分区的PBR备份直接恢复PBR备份,这个分区就恢复完了(由于病毒没有破坏MFTMirr,所以MFT不存在时会自动使用MFTMirr里的备份)
如果没有此分区的PBR备份,那么就需要恢复PBR和MFT
1. 在刚刚搜索到的扇区的前一个扇区填入NTFS PBR,每个簇对应的扇区数填8,隐藏扇区数填当前的扇区编号,总扇区数填你大概估计的这个分区的扇区数(分区大小转成字节除以512,不确定就填硬盘的总扇区数,等恢复下一个分区的时候再计算),$MFT起始位置和$MFTMirr起始位置都填2(临时使用MFTMirr中存储的数据)
2. 用DiskGenius搜索分区,应该能搜到这个分区,选择文件恢复就能搜到文件了
3. 如果想要修复分区,使用WinHex搜索$LogFile(Unicode),搜到后注意辨别这个扇区的开头是不是FILE0。MFT所在簇号=(扇区号-隐藏扇区数-4)/每个簇对应的扇区数。填入PBR。
最后再用DiskGenius搜索分区,保存搜到的分区之后修复引导
防御方法
1. 禁止加载因为数字签名拉黑的驱动
2. 备份MBR,PBR,MFT
3. 遇到要加载驱动/写MBR的软件,先在虚拟机里试用
谢谢分享。 你真是个大佬,这太复杂了了啊
https://wwij.lanzout.com/b00ya59cif
密码:b6om
大佬方便在给打包一下新版本的吗,上次悬赏采纳后我又重新弄了一下,看能否打包成功,重新悬赏也可以 fandazong 发表于 2025-4-13 19:19
https://wwij.lanzout.com/b00ya59cif
密码:b6om
大佬方便在给打包一下新版本的吗,上次悬赏采纳后 ...
你这个新的程序也是一个示例程序,而且有很多bug
1. numpy库版本冲突
The conflict is caused by:
The user requested numpy==1.26.0
pandas 2.1.3 depends on numpy<2 and >=1.22.4; python_version < "3.11"
scikit-learn 1.3.2 depends on numpy<2.0 and >=1.17.3
numba 0.58.0 depends on numpy<1.26 and >=1.21
2. 使用了弃用的功能,在代码的第39行:
id: str = Field(..., min_length=2, max_length=20, regex=r'^\w+$')
报错:
pydantic.errors.PydanticUserError: `regex` is removed. use `pattern` instead
3. 改了之后还是报错:
运行错误: 计算失败: Delayed objects of unspecified length are not iterable
抱歉,这个我真不会改了,我对python的dask库不熟悉,请开新的帖子再找人弄吧。还有,不要再在此帖下回复,下面写着 警告:本版块禁止『灌水』或回复与主题无关内容,违者重罚! 本帖最后由 YWFhYmJi 于 2025-4-13 21:29 编辑
针对此病毒已经制作出专杀工具,可以搜索到被此病毒删除的NTFS分区。运行后会生成一些文件PBRSector_xxxxxx.bin,将文件恢复到硬盘第xxxxxx个扇区后再用DiskGenius搜索丢失分区就可以找到了。
#include <stdio.h>
#include <conio.h>
#include <Windows.h>
BOOL ReadFileAlign(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead)
{
DWORD dwRead = 0;
DWORD nAlignedSize = (nNumberOfBytesToRead / 512 + (nNumberOfBytesToRead % 512 ? 1 : 0)) * 512;
PUCHAR buffer = (PUCHAR)malloc(nAlignedSize);
if (!ReadFile(hFile, buffer, nAlignedSize, &dwRead, NULL))
{
*lpNumberOfBytesRead = 0;
return FALSE;
}
memcpy(lpBuffer, buffer, nNumberOfBytesToRead);
*lpNumberOfBytesRead = nNumberOfBytesToRead;
free(buffer);
return TRUE;
}
BOOL IsEmptySector(LPVOID Buffer)
{
for (UINT i = 0; i < 512; i++)
{
if (*((PUCHAR)Buffer + i))
{
return FALSE;
}
}
return TRUE;
}
#pragma pack(push, 1)
typedef struct _MFT_SEGMENT_REFERENCE {
ULONGSegmentNumberLowPart;
USHORT SegmentNumberHighPart;
USHORT SequenceNumber;
} MFT_SEGMENT_REFERENCE, *PMFT_SEGMENT_REFERENCE;
typedef MFT_SEGMENT_REFERENCE FILE_REFERENCE, *PFILE_REFERENCE;
typedef struct _MULTI_SECTOR_HEADER {
UCHARSignature;
USHORT UpdateSequenceArrayOffset;
USHORT UpdateSequenceArraySize;
} MULTI_SECTOR_HEADER, *PMULTI_SECTOR_HEADER;
typedef struct _FILE_RECORD_SEGMENT_HEADER {
MULTI_SECTOR_HEADER MultiSectorHeader;
ULONGLONG Reserved1;
USHORT SequenceNumber;
USHORT Reserved2;
USHORT FirstAttributeOffset;
USHORT Flags;
ULONG Reserved3;
FILE_REFERENCE BaseFileRecordSegment;
USHORT Reserved4;
//UPDATE_SEQUENCE_ARRAY UpdateSequenceArray;
} FILE_RECORD_SEGMENT_HEADER, *PFILE_RECORD_SEGMENT_HEADER;
typedef enum _ATTRIBUTE_TYPE_CODE {
ATTR_STANDARD_INFORMATION = 0x10,
ATTR_ATTRIBUTE_LIST = 0x20,
ATTR_FILE_NAME = 0x30,
ATTR_OBJECT_ID = 0x40,
ATTR_VOLUME_NAME = 0x60,
ATTR_VOLUME_INFORMATION = 0x70,
ATTR_DATA = 0x80,
ATTR_INDEX_ROOT = 0x90,
ATTR_INDEX_ALLOCATION = 0xA0,
ATTR_BITMAP = 0xB0,
ATTR_REPARSE_POINT = 0xC0,
ATTR_END = 0xFFFFFFFF
} ATTRIBUTE_TYPE_CODE;
#define RESIDENT_FORM 0x00
#define NONRESIDENT_FORM 0x01
typedef ULONGLONG VCN;
#define FILE_NAME_INDEX_PRESENT 0x10000000
typedef struct _ATTRIBUTE_RECORD_HEADER {
ATTRIBUTE_TYPE_CODE TypeCode;
ULONG RecordLength;
UCHAR FormCode;
UCHAR NameLength;
USHORT NameOffset;
USHORT Flags;
USHORT Instance;
union {
struct {
ULONGValueLength;
USHORT ValueOffset;
UCHARReserved;
} Resident;
struct {
VCN LowestVcn;
VCN HighestVcn;
USHORT MappingPairsOffset;
UCHAR Reserved;
LONGLONG AllocatedLength;
LONGLONG FileSize;
LONGLONG ValidDataLength;
LONGLONG TotalAllocated;
} Nonresident;
} Form;
} ATTRIBUTE_RECORD_HEADER, *PATTRIBUTE_RECORD_HEADER;
typedef struct _FILE_NAME_ATTRIBUTE {
FILE_REFERENCE ParentDirectory;
UCHAR Reserved;
ULONG FileAttributes;
ULONG AlignmentOrReserved;
UCHAR FileNameLength;
UCHAR Flags;
WCHAR FileName;
} FILE_NAME_ATTRIBUTE, *PFILE_NAME_ATTRIBUTE;
typedef struct _BIOS_PARAMETER_BLOCK
{
/*+0x0B*/ UINT16BytesPerSector;
/*+0x0D*/ UCHAR SectorsPerCluster;
/*+0x0E*/ UINT16ReservedSectors;
/*+0x0F*/ UCHAR Fats;
/*+0x11*/ UINT16RootEntries;
/*+0x13*/ UINT16Sectors;
/*+0x15*/ UCHAR Media;
/*+0x16*/ UINT16SectorsPerFat;
/*+0x18*/ UINT16SectorsPerTrack;
/*+0x1A*/ UINT16Heads;
/*+0x1C*/ UINT32HiddenSectors;
/*+0x20*/ UINT32LargeSectors;
} BIOS_PARAMETER_BLOCK, *PBIOS_PARAMETER_BLOCK;
typedef struct _NTFS_BOOT_SECTOR
{
/*+0x00*/UCHAR JmpCode;
/*+0x03*/char OemID;
/*+0x0B*/BIOS_PARAMETER_BLOCK PackedBpb;
/*+0x24*/UCHAR Unused;
/*+0x28*/UINT64 NumberSectors;
/*+0x30*/UINT64 MftStartLcn;
/*+0x38*/UINT64 Mft2StartLcn;
/*+0x40*/UCHAR ClustersPerFileRecordSegment;
/*+0x41*/UCHAR Reserved0;
/*+0x44*/UCHAR DefaultClustersPerIndexAllocationBuffer;
/*+0x45*/UCHAR Reserved1;
/*+0x48*/UINT64 SerialNumber;
/*+0x50*/UINT32 Checksum;
/*+0x54*/UCHAR BootStrap;
/*+0x1FE*/ UINT16 RecordEndSign;
} NTFS_BOOT_SECTOR, *PNTFS_BOOT_SECTOR;
#pragma pack(pop)
unsigned char BootmgrCode = {
0xFA, 0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C, 0xFB, 0x68, 0xC0, 0x07, 0x1F, 0x1E, 0x68, 0x66,
0x00, 0xCB, 0x88, 0x16, 0x0E, 0x00, 0x66, 0x81, 0x3E, 0x03, 0x00, 0x4E, 0x54, 0x46, 0x53, 0x75,
0x15, 0xB4, 0x41, 0xBB, 0xAA, 0x55, 0xCD, 0x13, 0x72, 0x0C, 0x81, 0xFB, 0x55, 0xAA, 0x75, 0x06,
0xF7, 0xC1, 0x01, 0x00, 0x75, 0x03, 0xE9, 0xDD, 0x00, 0x1E, 0x83, 0xEC, 0x18, 0x68, 0x1A, 0x00,
0xB4, 0x48, 0x8A, 0x16, 0x0E, 0x00, 0x8B, 0xF4, 0x16, 0x1F, 0xCD, 0x13, 0x9F, 0x83, 0xC4, 0x18,
0x9E, 0x58, 0x1F, 0x72, 0xE1, 0x3B, 0x06, 0x0B, 0x00, 0x75, 0xDB, 0xA3, 0x0F, 0x00, 0xC1, 0x2E,
0x0F, 0x00, 0x04, 0x1E, 0x5A, 0x33, 0xDB, 0xB9, 0x00, 0x20, 0x2B, 0xC8, 0x66, 0xFF, 0x06, 0x11,
0x00, 0x03, 0x16, 0x0F, 0x00, 0x8E, 0xC2, 0xFF, 0x06, 0x16, 0x00, 0xE8, 0x4B, 0x00, 0x2B, 0xC8,
0x77, 0xEF, 0xB8, 0x00, 0xBB, 0xCD, 0x1A, 0x66, 0x23, 0xC0, 0x75, 0x2D, 0x66, 0x81, 0xFB, 0x54,
0x43, 0x50, 0x41, 0x75, 0x24, 0x81, 0xF9, 0x02, 0x01, 0x72, 0x1E, 0x16, 0x68, 0x07, 0xBB, 0x16,
0x68, 0x52, 0x11, 0x16, 0x68, 0x09, 0x00, 0x66, 0x53, 0x66, 0x53, 0x66, 0x55, 0x16, 0x16, 0x16,
0x68, 0xB8, 0x01, 0x66, 0x61, 0x0E, 0x07, 0xCD, 0x1A, 0x33, 0xC0, 0xBF, 0x0A, 0x13, 0xB9, 0xF6,
0x0C, 0xFC, 0xF3, 0xAA, 0xE9, 0xFE, 0x01, 0x90, 0x90, 0x66, 0x60, 0x1E, 0x06, 0x66, 0xA1, 0x11,
0x00, 0x66, 0x03, 0x06, 0x1C, 0x00, 0x1E, 0x66, 0x68, 0x00, 0x00, 0x00, 0x00, 0x66, 0x50, 0x06,
0x53, 0x68, 0x01, 0x00, 0x68, 0x10, 0x00, 0xB4, 0x42, 0x8A, 0x16, 0x0E, 0x00, 0x16, 0x1F, 0x8B,
0xF4, 0xCD, 0x13, 0x66, 0x59, 0x5B, 0x5A, 0x66, 0x59, 0x66, 0x59, 0x1F, 0x0F, 0x82, 0x16, 0x00,
0x66, 0xFF, 0x06, 0x11, 0x00, 0x03, 0x16, 0x0F, 0x00, 0x8E, 0xC2, 0xFF, 0x0E, 0x16, 0x00, 0x75,
0xBC, 0x07, 0x1F, 0x66, 0x61, 0xC3, 0xA1, 0xF6, 0x01, 0xE8, 0x09, 0x00, 0xA1, 0xFA, 0x01, 0xE8,
0x03, 0x00, 0xF4, 0xEB, 0xFD, 0x8B, 0xF0, 0xAC, 0x3C, 0x00, 0x74, 0x09, 0xB4, 0x0E, 0xBB, 0x07,
0x00, 0xCD, 0x10, 0xEB, 0xF2, 0xC3, 0x0D, 0x0A, 0x41, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x72,
0x65, 0x61, 0x64, 0x20, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x20, 0x6F, 0x63, 0x63, 0x75, 0x72, 0x72,
0x65, 0x64, 0x00, 0x0D, 0x0A, 0x42, 0x4F, 0x4F, 0x54, 0x4D, 0x47, 0x52, 0x20, 0x69, 0x73, 0x20,
0x63, 0x6F, 0x6D, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x00, 0x0D, 0x0A, 0x50, 0x72, 0x65,
0x73, 0x73, 0x20, 0x43, 0x74, 0x72, 0x6C, 0x2B, 0x41, 0x6C, 0x74, 0x2B, 0x44, 0x65, 0x6C, 0x20,
0x74, 0x6F, 0x20, 0x72, 0x65, 0x73, 0x74, 0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x8A, 0x01, 0xA7, 0x01, 0xBF, 0x01, 0x00, 0x00
};
unsigned char NtldrCode = {
0xFA, 0x33, 0xC0, 0x8E, 0xD0, 0xBC, 0x00, 0x7C, 0xFB, 0xB8, 0xC0, 0x07, 0x8E, 0xD8, 0xE8, 0x16,
0x00, 0xB8, 0x00, 0x0D, 0x8E, 0xC0, 0x33, 0xDB, 0xC6, 0x06, 0x0E, 0x00, 0x10, 0xE8, 0x53, 0x00,
0x68, 0x00, 0x0D, 0x68, 0x6A, 0x02, 0xCB, 0x8A, 0x16, 0x24, 0x00, 0xB4, 0x08, 0xCD, 0x13, 0x73,
0x05, 0xB9, 0xFF, 0xFF, 0x8A, 0xF1, 0x66, 0x0F, 0xB6, 0xC6, 0x40, 0x66, 0x0F, 0xB6, 0xD1, 0x80,
0xE2, 0x3F, 0xF7, 0xE2, 0x86, 0xCD, 0xC0, 0xED, 0x06, 0x41, 0x66, 0x0F, 0xB7, 0xC9, 0x66, 0xF7,
0xE1, 0x66, 0xA3, 0x20, 0x00, 0xC3, 0xB4, 0x41, 0xBB, 0xAA, 0x55, 0x8A, 0x16, 0x24, 0x00, 0xCD,
0x13, 0x72, 0x0F, 0x81, 0xFB, 0x55, 0xAA, 0x75, 0x09, 0xF6, 0xC1, 0x01, 0x74, 0x04, 0xFE, 0x06,
0x14, 0x00, 0xC3, 0x66, 0x60, 0x1E, 0x06, 0x66, 0xA1, 0x10, 0x00, 0x66, 0x03, 0x06, 0x1C, 0x00,
0x66, 0x3B, 0x06, 0x20, 0x00, 0x0F, 0x82, 0x3A, 0x00, 0x1E, 0x66, 0x6A, 0x00, 0x66, 0x50, 0x06,
0x53, 0x66, 0x68, 0x10, 0x00, 0x01, 0x00, 0x80, 0x3E, 0x14, 0x00, 0x00, 0x0F, 0x85, 0x0C, 0x00,
0xE8, 0xB3, 0xFF, 0x80, 0x3E, 0x14, 0x00, 0x00, 0x0F, 0x84, 0x61, 0x00, 0xB4, 0x42, 0x8A, 0x16,
0x24, 0x00, 0x16, 0x1F, 0x8B, 0xF4, 0xCD, 0x13, 0x66, 0x58, 0x5B, 0x07, 0x66, 0x58, 0x66, 0x58,
0x1F, 0xEB, 0x2D, 0x66, 0x33, 0xD2, 0x66, 0x0F, 0xB7, 0x0E, 0x18, 0x00, 0x66, 0xF7, 0xF1, 0xFE,
0xC2, 0x8A, 0xCA, 0x66, 0x8B, 0xD0, 0x66, 0xC1, 0xEA, 0x10, 0xF7, 0x36, 0x1A, 0x00, 0x86, 0xD6,
0x8A, 0x16, 0x24, 0x00, 0x8A, 0xE8, 0xC0, 0xE4, 0x06, 0x0A, 0xCC, 0xB8, 0x01, 0x02, 0xCD, 0x13,
0x0F, 0x82, 0x19, 0x00, 0x8C, 0xC0, 0x05, 0x20, 0x00, 0x8E, 0xC0, 0x66, 0xFF, 0x06, 0x10, 0x00,
0xFF, 0x0E, 0x0E, 0x00, 0x0F, 0x85, 0x6F, 0xFF, 0x07, 0x1F, 0x66, 0x61, 0xC3, 0xA0, 0xF8, 0x01,
0xE8, 0x09, 0x00, 0xA0, 0xFB, 0x01, 0xE8, 0x03, 0x00, 0xFB, 0xEB, 0xFE, 0xB4, 0x01, 0x8B, 0xF0,
0xAC, 0x3C, 0x00, 0x74, 0x09, 0xB4, 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2, 0xC3, 0x0D,
0x0A, 0x41, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x72, 0x65, 0x61, 0x64, 0x20, 0x65, 0x72, 0x72,
0x6F, 0x72, 0x20, 0x6F, 0x63, 0x63, 0x75, 0x72, 0x72, 0x65, 0x64, 0x00, 0x0D, 0x0A, 0x4E, 0x54,
0x4C, 0x44, 0x52, 0x20, 0x69, 0x73, 0x20, 0x6D, 0x69, 0x73, 0x73, 0x69, 0x6E, 0x67, 0x00, 0x0D,
0x0A, 0x4E, 0x54, 0x4C, 0x44, 0x52, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x72, 0x65,
0x73, 0x73, 0x65, 0x64, 0x00, 0x0D, 0x0A, 0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x43, 0x74, 0x72,
0x6C, 0x2B, 0x41, 0x6C, 0x74, 0x2B, 0x44, 0x65, 0x6C, 0x20, 0x74, 0x6F, 0x20, 0x72, 0x65, 0x73,
0x74, 0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x83, 0xA0, 0xB3, 0xC9, 0x00, 0x00
};
BOOL ReadSector(HANDLE hDisk, ULONGLONG Sector, LPVOID Buffer)
{
LARGE_INTEGER Offset = { 0 }, NewOffset = { 0 };
Offset.QuadPart = Sector * 512;
SetFilePointerEx(hDisk, Offset, &NewOffset, FILE_BEGIN);
DWORD dwRead;
if (!ReadFile(hDisk, Buffer, 512, &dwRead, NULL) || dwRead < 512)
{
fprintf(stderr, "Read sector error: %llu\n", Sector);
return FALSE;
}
return TRUE;
}
LONGLONG ParseFileFirstCluster(const PUCHAR FileRecord)
{
PFILE_RECORD_SEGMENT_HEADER pHeader = (PFILE_RECORD_SEGMENT_HEADER)FileRecord;
PATTRIBUTE_RECORD_HEADER pAttr = (PATTRIBUTE_RECORD_HEADER)((PUCHAR)pHeader + pHeader->FirstAttributeOffset);
while (pAttr->TypeCode != ATTR_END)
{
ATTRIBUTE_TYPE_CODE typeCode = pAttr->TypeCode;
if (pAttr->FormCode)
{
if (typeCode == ATTR_DATA)
{
PUCHAR dataRun = (PUCHAR)pAttr + pAttr->Form.Nonresident.MappingPairsOffset;
LONGLONG LCN = 0;
ULONGLONG VCN = 0;
while (*dataRun)
{
UCHAR lengthBytes = *dataRun & 0x0F;
UCHAR offsetBytes = *dataRun >> 4;
dataRun++;
LONGLONG length = 0;
memcpy(&length, dataRun, lengthBytes);
dataRun += lengthBytes;
LONGLONG lcnOffset = 0;
if (offsetBytes)
{
if (dataRun & 0x80)
lcnOffset = -1;
memcpy(&lcnOffset, dataRun, offsetBytes);
dataRun += offsetBytes;
}
LCN += lcnOffset;
return lcnOffset == 0 ? 0 : LCN;
//printf("VCN %llu LCN %lld Clusters %lld\n", VCN, lcnOffset == 0 ? 0 : LCN, length);
VCN += length;
}
}
}
pAttr = (PATTRIBUTE_RECORD_HEADER)((PUCHAR)pAttr + pAttr->RecordLength);
}
return -1;
}
BOOL ProcessVolume(HANDLE hDisk, ULONGLONG StartSector, ULONGLONG TotalSectors, BOOL IsBootmgr, LPCWSTR FileName)
{
printf("Hidden sectors: %llu\n", StartSector - 1);
ULONGLONG MftMirrStart = StartSector;
char buf;
for (; MftMirrStart < TotalSectors; MftMirrStart++)
{
printf("\rSearching $MFTMirr sector %llu/%llu...", MftMirrStart, TotalSectors);
if (!ReadSector(hDisk, MftMirrStart, buf))
{
continue;
}
unsigned char Magic = { 0x46, 0x49, 0x4C, 0x45, 0x30 };
if (!memcmp(buf, Magic, 5))
{
printf("\nFound $MFTMirr start sector: %llu\n", MftMirrStart);
break;
}
}
if (MftMirrStart == TotalSectors)
{
printf("\n$MFTMirr sector not found. This is not a vaild NTFS partition.\n");
return FALSE;
}
if (!ReadSector(hDisk, MftMirrStart + 2, buf) || !ReadSector(hDisk, MftMirrStart + 3, buf + 512))
{
printf("Critical sector read failed, could not recovery this partition.\n");
return FALSE;
}
ULONGLONG MftMirrStartCluster = ParseFileFirstCluster((PUCHAR)buf);
if (MftMirrStartCluster == -1)
{
printf("Could not get $MFTMirr start cluster.\n");
return FALSE;
}
printf("$MFTMirr start cluster: %llu\n", MftMirrStartCluster);
DWORD SectorsPerCluster = (MftMirrStart - StartSector + 1) / MftMirrStartCluster;
printf("Sectors per cluster: %d\n", SectorsPerCluster);
if (!ReadSector(hDisk, MftMirrStart, buf) || !ReadSector(hDisk, MftMirrStart + 1, buf + 512))
{
printf("Critical sector read failed, could not recovery this partition.\n");
return FALSE;
}
ULONGLONG MftStartCluster = ParseFileFirstCluster((PUCHAR)buf);
if (MftStartCluster == -1)
{
printf("Could not get $MFT start cluster.\n");
return FALSE;
}
printf("$MFT start cluster: %llu\n", MftStartCluster);
NTFS_BOOT_SECTOR bootsect;
memcpy(bootsect.JmpCode, "\xEB\x52\x90", 3);
memcpy(bootsect.OemID, "NTFS ", 8);
bootsect.PackedBpb.BytesPerSector = 512;
bootsect.PackedBpb.SectorsPerCluster = SectorsPerCluster;
bootsect.PackedBpb.ReservedSectors = 0;
bootsect.PackedBpb.Fats = 0;
bootsect.PackedBpb.RootEntries = 0;
bootsect.PackedBpb.Sectors = 0;
bootsect.PackedBpb.Media = 0xF8;
bootsect.PackedBpb.SectorsPerFat = 0;
bootsect.PackedBpb.SectorsPerTrack = 63; // TODO
bootsect.PackedBpb.Heads = 255; // TODO
bootsect.PackedBpb.HiddenSectors = StartSector - 1;
bootsect.PackedBpb.LargeSectors = 0;
memcpy(bootsect.Unused, "\x80\x00\x80\x00", 4);
bootsect.NumberSectors = TotalSectors - StartSector + 1; // TODO
bootsect.MftStartLcn = MftStartCluster;
bootsect.Mft2StartLcn = MftMirrStartCluster;
bootsect.ClustersPerFileRecordSegment = 0xF6;
memcpy(bootsect.Reserved0, "\x00\x00\x00", 3);
bootsect.DefaultClustersPerIndexAllocationBuffer = SectorsPerCluster > 8 ? 0xF4 : 1; // TODO
memcpy(bootsect.Reserved1, "\x00\x00\x00", 3);
bootsect.SerialNumber = 1ull * rand() * rand() * rand() * rand();
bootsect.Checksum = 0;
memcpy(bootsect.BootStrap, IsBootmgr ? BootmgrCode : NtldrCode, 426);
bootsect.RecordEndSign = 0xAA55;
HANDLE hFile = CreateFileW(FileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("Failed to open output file.\n");
return TRUE;
}
DWORD dwWrite;
if (!WriteFile(hFile, &bootsect, 512, &dwWrite, NULL))
{
printf("Failed to write output file.\n");
CloseHandle(hFile);
return TRUE;
}
CloseHandle(hFile);
printf("Boot sector saved to file: %ls\n", FileName);
return TRUE;
}
BOOL UpdatePartSize(LPCWSTR FileName, ULONGLONG NumberSectors)
{
HANDLE hFile = CreateFileW(FileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("Failed to open output file.\n");
return FALSE;
}
NTFS_BOOT_SECTOR bootsect;
DWORD dwRead;
if (!ReadFile(hFile, &bootsect, 512, &dwRead, NULL))
{
printf("Failed to read output file.\n");
CloseHandle(hFile);
return FALSE;
}
CloseHandle(hFile);
hFile = CreateFileW(FileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
printf("Failed to open output file.\n");
return FALSE;
}
bootsect.NumberSectors = NumberSectors;
DWORD dwWrite;
if (!WriteFile(hFile, &bootsect, 512, &dwWrite, NULL))
{
printf("Failed to write output file.\n");
CloseHandle(hFile);
return FALSE;
}
CloseHandle(hFile);
printf("Partition size updated, boot sector saved to file: %ls\n", FileName);
return TRUE;
}
void hexdump(LPCVOID buf, DWORD len)
{
const char *p = (const char *)buf;
for (DWORD i = 0; i < len; i++)
{
printf("%.2X ", (UCHAR)p);
if ((i + 1) % 16 == 0)
{
printf("| ");
for (DWORD j = i - 15; j <= i; j++)
{
if (isprint(p)) printf("%c", p);
else printf(".");
}
printf("\n");
}
}
printf("\n");
}
int main()
{
printf("Please input the disk id: ");
int DiskId;
scanf_s("%d", &DiskId);
WCHAR DiskName;
swprintf_s(DiskName, L"\\\\.\\PhysicalDrive%d", DiskId);
HANDLE hDisk = CreateFile(DiskName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDisk == INVALID_HANDLE_VALUE)
{
printf("Failed to open the disk.\n");
return 1;
}
DISK_GEOMETRY_EX DiskInfo;
DWORD dwRet = 0;
if (!DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &DiskInfo, sizeof(DiskInfo), &dwRet, NULL))
{
printf("Failed to get disk geometry.\n");
return 1;
}
ULONGLONG TotalSectors = DiskInfo.DiskSize.QuadPart / 512;
DWORD SectorsPerCylinder = DiskInfo.Geometry.SectorsPerTrack * DiskInfo.Geometry.TracksPerCylinder;
ULONGLONG TotalCylinders = DiskInfo.Geometry.Cylinders.QuadPart;
printf("Total sectors = %llu\n", TotalSectors);
printf("Total cylinders = %llu\n", TotalCylinders);
printf("Sectors per cylinder = %d\n", SectorsPerCylinder);
ULONGLONG StartCylinder = 0;
ULONGLONG LastStartSector = -1;
char lastbuf;
memset(lastbuf, 0x22, sizeof(lastbuf));
char *buf = (char *)malloc(SectorsPerCylinder * 512);
for (; StartCylinder < TotalCylinders; StartCylinder++)
{
printf("\rSearching cylinder %llu/%llu...", StartCylinder, TotalCylinders);
ULONGLONG CurSector = StartCylinder * SectorsPerCylinder;
LARGE_INTEGER Offset = { 0 }, NewOffset = { 0 };
Offset.QuadPart = CurSector * 512;
SetFilePointerEx(hDisk, Offset, &NewOffset, FILE_BEGIN);
DWORD SectorsRead = min(SectorsPerCylinder, TotalSectors - CurSector);
DWORD BytesRead = SectorsRead * 512;
if (!ReadFile(hDisk, buf, BytesRead, &dwRet, NULL) || dwRet < BytesRead)
{
fprintf(stderr, "\nRead cylinder error: %llu\n", StartCylinder);
memset(lastbuf, 0x22, sizeof(lastbuf));
continue;
}
unsigned char Magic1 = { 0x07, 0x00, 0x42, 0x00, 0x4F, 0x00, 0x4F, 0x00, 0x54, 0x00, 0x4D, 0x00, 0x47, 0x00, 0x52, 0x00 };
unsigned char Magic2 = { 0x05, 0x00, 0x4E, 0x00, 0x54, 0x00, 0x4C, 0x00, 0x44, 0x00, 0x52, 0x00, 0x04, 0x00, 0x24, 0x00 };
for (DWORD i = 0; i < SectorsRead; i++)
{
BOOL IsBootmgr = !memcmp(buf + i * 512, Magic1, 16);
BOOL IsNtldr = !memcmp(buf + i * 512, Magic2, 16);
if (IsEmptySector(i == 0 ? lastbuf : buf + (i - 1) * 512) && (IsBootmgr || IsNtldr))
{
ULONGLONG StartSector = CurSector + i;
printf("\nFound boot code in sector %llu\n", StartSector);
WCHAR filename;
swprintf_s(filename, L"PBR_Sector_%llu.bin", StartSector - 1);
ProcessVolume(hDisk, StartSector, TotalSectors, IsBootmgr, filename);
if (LastStartSector != -1)
{
swprintf_s(filename, L"PBR_Sector_%llu.bin", LastStartSector);
UpdatePartSize(filename, StartSector - 1 - LastStartSector - 1);
}
LastStartSector = StartSector - 1;
}
}
memcpy(lastbuf, buf + (SectorsRead - 1) * 512, sizeof(lastbuf));
}
CloseHandle(hDisk);
printf("\nSearch completed, press any key to exit...\n");
_getch();
return 0;
}
YWFhYmJi 发表于 2025-4-13 21:16
你这个新的程序也是一个示例程序,而且有很多bug
1. numpy库版本冲突
好的,谢谢,受教了
页:
[1]