NtfsSetDispositionInfoMmFlushImageSection pSectionObjectPointer = fileObject->SectionObjectPointer; pSectionObjectPointer->ImageSectionObject = 0; pSectionObjectPointer->DataSectionObject = 0;
核心代码代码:
NTSTATUS dfSkillSetFileCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { Irp->UserIosb->Status = Irp->IoStatus.Status; Irp->UserIosb->Information = Irp->IoStatus.Information; KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE); IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } BOOLEAN dfDelFile(WCHAR* name) { NTSTATUS ntStatus = STATUS_SUCCESS; PFILE_OBJECT fileObject; PDEVICE_OBJECT DeviceObject; PIRP Irp; KEVENT event; FILE_DISPOSITION_INFORMATION FileInformation; IO_STATUS_BLOCK ioStatus; PIO_STACK_LOCATION irpSp; PSECTION_OBJECT_POINTERS pSectionObjectPointer; HANDLE handle; ntStatus = dfOpenFile(name, &handle, FILE_READ_ATTRIBUTES|DELETE,FILE_SHARE_DELETE); if (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND || ntStatus == STATUS_OBJECT_PATH_NOT_FOUND ) { KdPrint(("No such file")); return FALSE; } else if (!NT_SUCCESS(ntStatus)) { if (dfCloseFileHandle(name)) { ntStatus = dfOpenFile(name, &handle, FILE_READ_ATTRIBUTES|DELETE,FILE_SHARE_DELETE); if (!NT_SUCCESS(ntStatus)) return FALSE; } else { return FALSE; } } ntStatus = ObReferenceObjectByHandle(handle, DELETE, *IoFileObjectType, KernelMode, &fileObject, NULL); if (!NT_SUCCESS(ntStatus)) { DbgPrint("ObReferenceObjectByHandle()"); ZwClose(handle); return FALSE; } DeviceObject = IoGetRelatedDeviceObject(fileObject); Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE); if (Irp == NULL) { ObDereferenceObject(fileObject); ZwClose(handle); return FALSE; } KeInitializeEvent(&event, SynchronizationEvent, FALSE); FileInformation.DeleteFile = TRUE; Irp->AssociatedIrp.SystemBuffer = &FileInformation; Irp->UserEvent = &event; Irp->UserIosb = &ioStatus; Irp->Tail.Overlay.OriginalFileObject = fileObject; Irp->Tail.Overlay.Thread = (PETHREAD)KeGetCurrentThread(); Irp->RequestorMode = KernelMode; irpSp = IoGetNextIrpStackLocation(Irp); irpSp->MajorFunction = IRP_MJ_SET_INFORMATION; irpSp->DeviceObject = DeviceObject; irpSp->FileObject = fileObject; irpSp->Parameters.SetFile.Length = sizeof(FILE_DISPOSITION_INFORMATION); irpSp->Parameters.SetFile.FileInformationClass = FileDispositionInformation; irpSp->Parameters.SetFile.FileObject = fileObject; IoSetCompletionRoutine( Irp, dfSkillSetFileCompletion, &event, TRUE, TRUE, TRUE); pSectionObjectPointer = fileObject->SectionObjectPointer; if(pSectionObjectPointer) { pSectionObjectPointer->ImageSectionObject = 0; pSectionObjectPointer->DataSectionObject = 0; } ntStatus = IoCallDriver(DeviceObject, Irp); if (!NT_SUCCESS(ntStatus)) { ObDereferenceObject(fileObject); ZwClose(handle); return FALSE; } KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, NULL); //IoFreeIrp(Irp); ObDereferenceObject(fileObject); ZwClose(handle); return TRUE; }
DelFile.c
#include <ntddk.h> #include <ntimage.h> #include <ntdef.h> #include "DelFile.h" PDEVICE_OBJECT g_HookDevice; NTSTATUS dfQuerySymbolicLink( IN PUNICODE_STRING SymbolicLinkName, OUT PUNICODE_STRING LinkTarget ) { OBJECT_ATTRIBUTES oa; NTSTATUS status; HANDLE handle; InitializeObjectAttributes( &oa, SymbolicLinkName, OBJ_CASE_INSENSITIVE, 0, 0); status = ZwOpenSymbolicLinkObject(&handle, GENERIC_READ, &oa); if (!NT_SUCCESS(status)) { return status; } LinkTarget->MaximumLength = 1024*sizeof(WCHAR); LinkTarget->Length = 0; LinkTarget->Buffer = ExAllocatePoolWithTag(PagedPool, LinkTarget->MaximumLength, 'A0'); if (!LinkTarget->Buffer) { ZwClose(handle); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(LinkTarget->Buffer, LinkTarget->MaximumLength); status = ZwQuerySymbolicLinkObject(handle, LinkTarget, NULL); ZwClose(handle); if (!NT_SUCCESS(status)) { ExFreePool(LinkTarget->Buffer); } return status; } BOOLEAN dfCloseFileHandle(WCHAR *name) { NTSTATUS status; PVOID buf = NULL; PSYSTEM_HANDLE_INFORMATION pSysHandleInfo; SYSTEM_HANDLE_TABLE_ENTRY_INFO handleTEI; ULONG size = 1; ULONG NumOfHandle = 0; ULONG i; CLIENT_ID cid; HANDLE hHandle; HANDLE hProcess; HANDLE hDupObj; HANDLE hFile; HANDLE link_handle; OBJECT_ATTRIBUTES oa; ULONG FileType; ULONG processID; UNICODE_STRING uLinkName; UNICODE_STRING uLink; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK IoStatus; ULONG ulRet; PVOID fileObject; POBJECT_NAME_INFORMATION pObjName; UNICODE_STRING delFileName = {0}; int length; WCHAR wVolumeLetter[3]; WCHAR *pFilePath; UNICODE_STRING uVolume; UNICODE_STRING uFilePath; UNICODE_STRING NullString = RTL_CONSTANT_STRING(L""); BOOLEAN bRet = FALSE; for ( size = 1; ; size *= 2 ) { if ( NULL == ( buf = ExAllocatePoolWithTag(NonPagedPool,size, 'FILE') ) ) { DbgPrint(("alloc mem failed\n")); goto Exit; } RtlZeroMemory( buf ,size ); status = ZwQuerySystemInformation( SystemHandleInformation, buf, size, NULL ); if ( !NT_SUCCESS( status ) ) { if ( STATUS_INFO_LENGTH_MISMATCH == status ) { ExFreePool( buf ); buf = NULL; } else { DbgPrint(( "ZwQuerySystemInformation() failed")); goto Exit; } } else { break; } } pSysHandleInfo = (PSYSTEM_HANDLE_INFORMATION)buf; NumOfHandle = pSysHandleInfo->NumberOfHandles; /* Get the volume character like C: */ wVolumeLetter[0] = name[4]; wVolumeLetter[1] = name[5]; wVolumeLetter[2] = 0; uLinkName.Buffer = ExAllocatePoolWithTag(NonPagedPool, 256 + sizeof(ULONG), 'A1'); uLinkName.MaximumLength = 256; RtlInitUnicodeString(&uVolume, wVolumeLetter); RtlInitUnicodeString( &uLink, L"\\DosDevices\\"); RtlCopyUnicodeString(&uLinkName, &uLink); status = RtlAppendUnicodeStringToString(&uLinkName, &uVolume); if (!NT_SUCCESS(status)) { KdPrint(("RtlAppendUnicodeStringToString() failed")); return FALSE; } dfQuerySymbolicLink(&uLinkName, &delFileName); RtlFreeUnicodeString(&uLinkName); KdPrint(("delFileName:%wZ", &delFileName)); pFilePath = (WCHAR *) &name[6]; RtlInitUnicodeString( &uFilePath, pFilePath); RtlAppendUnicodeStringToString(&delFileName, &uFilePath); if (!NT_SUCCESS(status)) { KdPrint(("RtlAppendUnicodeStringToString() failed")); return FALSE; } KdPrint(("delFile:%wZ", &delFileName)); for(i = 0; i < NumOfHandle ;i++) { handleTEI = pSysHandleInfo->Handles[i]; if (handleTEI.ObjectTypeIndex != 25 && handleTEI.ObjectTypeIndex != 28)//28文件,25设备对象 continue; processID = (ULONG) handleTEI.UniqueProcessId; cid.UniqueProcess = (HANDLE)processID; cid.UniqueThread = (HANDLE)0; hHandle = (HANDLE)handleTEI.HandleValue; InitializeObjectAttributes( &oa ,NULL ,0 ,NULL ,NULL ); status = ZwOpenProcess( &hProcess ,PROCESS_DUP_HANDLE ,&oa ,&cid ); if ( !NT_SUCCESS( status ) ) { KdPrint(( "ZwOpenProcess:%d Fail ", processID)); continue; } status = ZwDuplicateObject( hProcess ,hHandle ,NtCurrentProcess() ,&hDupObj ,\ PROCESS_ALL_ACCESS ,0 ,DUPLICATE_SAME_ACCESS ); if ( !NT_SUCCESS( status ) ) { DbgPrint(( "ZwDuplicateObject1 : Fail " )); continue; } status = ObReferenceObjectByHandle( hDupObj, FILE_ANY_ACCESS, 0, KernelMode, &fileObject, NULL); if (!NT_SUCCESS(status)) { DbgPrint(( "ObReferenceObjectByHandle : Fail " )); continue; } pObjName = (POBJECT_NAME_INFORMATION) ExAllocatePoolWithTag(NonPagedPool, \ sizeof (OBJECT_NAME_INFORMATION) + 1024 * sizeof (WCHAR), 'A1'); if (STATUS_SUCCESS != (status = ObQueryNameString(fileObject, pObjName, \ sizeof (OBJECT_NAME_INFORMATION) + 1024 * sizeof (WCHAR), &ulRet))) { ObDereferenceObject(fileObject); continue; } if (RtlCompareUnicodeString(&pObjName->Name, &delFileName, TRUE) == 0) { ObDereferenceObject(fileObject); ZwClose(hDupObj); status = ZwDuplicateObject( hProcess ,hHandle ,NtCurrentProcess() ,&hDupObj ,\ PROCESS_ALL_ACCESS ,0 ,DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE ); if ( !NT_SUCCESS( status ) ) { DbgPrint(( "ZwDuplicateObject2 : Fail " )); //return FALSE; } else { ZwClose(hDupObj); bRet = TRUE; //return TRUE; } break; } ExFreePool(pObjName); pObjName = NULL; ObDereferenceObject(fileObject); ZwClose( hDupObj ); ZwClose( hProcess ); } Exit: if (pObjName != NULL) { ExFreePool(pObjName); pObjName = NULL; } if (delFileName.Buffer != NULL) { ExFreePool(delFileName.Buffer); } if ( buf != NULL ) { ExFreePool( buf ); buf = NULL; } return(bRet); } NTSTATUS dfOpenFile(WCHAR* name,PHANDLE phFileHandle, ACCESS_MASK access,ULONG share) { IO_STATUS_BLOCK iosb; NTSTATUS stat; OBJECT_ATTRIBUTES oba; UNICODE_STRING nameus; if(KeGetCurrentIrql()>PASSIVE_LEVEL){return 0;} RtlInitUnicodeString(&nameus,name); InitializeObjectAttributes( &oba, &nameus, OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE, 0, 0); stat=IoCreateFile( phFileHandle, access, &oba, &iosb, 0, FILE_ATTRIBUTE_NORMAL, share, FILE_OPEN, 0, NULL, 0, 0, NULL, IO_NO_PARAMETER_CHECKING); return stat; } NTSTATUS dfSkillSetFileCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { Irp->UserIosb->Status = Irp->IoStatus.Status; Irp->UserIosb->Information = Irp->IoStatus.Information; KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE); IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } BOOLEAN dfDelFile(WCHAR* name) { NTSTATUS ntStatus = STATUS_SUCCESS; PFILE_OBJECT fileObject; PDEVICE_OBJECT DeviceObject; PIRP Irp; KEVENT event; FILE_DISPOSITION_INFORMATION FileInformation; IO_STATUS_BLOCK ioStatus; PIO_STACK_LOCATION irpSp; PSECTION_OBJECT_POINTERS pSectionObjectPointer; HANDLE handle; ntStatus = dfOpenFile(name, &handle, FILE_READ_ATTRIBUTES|DELETE,FILE_SHARE_DELETE); if (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND || ntStatus == STATUS_OBJECT_PATH_NOT_FOUND ) { KdPrint(("No such file")); return FALSE; } else if (!NT_SUCCESS(ntStatus)) { if (dfCloseFileHandle(name)) { ntStatus = dfOpenFile(name, &handle, FILE_READ_ATTRIBUTES|DELETE,FILE_SHARE_DELETE); if (!NT_SUCCESS(ntStatus)) return FALSE; } else { return FALSE; } } ntStatus = ObReferenceObjectByHandle(handle, DELETE, *IoFileObjectType, KernelMode, &fileObject, NULL); if (!NT_SUCCESS(ntStatus)) { DbgPrint("ObReferenceObjectByHandle()"); ZwClose(handle); return FALSE; } DeviceObject = IoGetRelatedDeviceObject(fileObject); Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE); if (Irp == NULL) { ObDereferenceObject(fileObject); ZwClose(handle); return FALSE; } KeInitializeEvent(&event, SynchronizationEvent, FALSE); FileInformation.DeleteFile = TRUE; Irp->AssociatedIrp.SystemBuffer = &FileInformation; Irp->UserEvent = &event; Irp->UserIosb = &ioStatus; Irp->Tail.Overlay.OriginalFileObject = fileObject; Irp->Tail.Overlay.Thread = (PETHREAD)KeGetCurrentThread(); Irp->RequestorMode = KernelMode; irpSp = IoGetNextIrpStackLocation(Irp); irpSp->MajorFunction = IRP_MJ_SET_INFORMATION; irpSp->DeviceObject = DeviceObject; irpSp->FileObject = fileObject; irpSp->Parameters.SetFile.Length = sizeof(FILE_DISPOSITION_INFORMATION); irpSp->Parameters.SetFile.FileInformationClass = FileDispositionInformation; irpSp->Parameters.SetFile.FileObject = fileObject; IoSetCompletionRoutine( Irp, dfSkillSetFileCompletion, &event, TRUE, TRUE, TRUE); pSectionObjectPointer = fileObject->SectionObjectPointer; if(pSectionObjectPointer) { pSectionObjectPointer->ImageSectionObject = 0; pSectionObjectPointer->DataSectionObject = 0; } ntStatus = IoCallDriver(DeviceObject, Irp); if (!NT_SUCCESS(ntStatus)) { ObDereferenceObject(fileObject); ZwClose(handle); return FALSE; } KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, NULL); //IoFreeIrp(Irp); ObDereferenceObject(fileObject); ZwClose(handle); return TRUE; } NTSTATUS OnUnload(IN PDRIVER_OBJECT DriverObject) { UNICODE_STRING deviceLinkUnicodeString; PDEVICE_OBJECT p_NextObj; DbgPrint("OnUnload called\n"); p_NextObj = DriverObject->DeviceObject; if (p_NextObj != NULL) { RtlInitUnicodeString( &deviceLinkUnicodeString, deviceLinkBuffer ); IoDeleteSymbolicLink( &deviceLinkUnicodeString ); IoDeleteDevice( DriverObject->DeviceObject ); } return STATUS_SUCCESS; } NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { NTSTATUS ntStatus; UNICODE_STRING deviceNameUnicodeString; UNICODE_STRING deviceLinkUnicodeString; RtlInitUnicodeString (&deviceNameUnicodeString, deviceNameBuffer ); RtlInitUnicodeString (&deviceLinkUnicodeString, deviceLinkBuffer ); ntStatus = IoCreateDevice ( DriverObject, 0, &deviceNameUnicodeString, FILE_DEVICE_SWAP, 0, TRUE, &g_HookDevice ); if(! NT_SUCCESS(ntStatus)) { DbgPrint(("Failed to create device!\n")); return ntStatus; } /* We test the DelFile() function here */ if (dfDelFile(L"\\??\\c:\\haha.doc")) { KdPrint(("Deleted")); } else { KdPrint(("Failed")); } if (dfDelFile(L"\\??\\c:\\filedelet.exe")) { KdPrint(("Deleted")); } else { KdPrint(("Failed")); } ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString, &deviceNameUnicodeString ); if(! NT_SUCCESS(ntStatus)) { IoDeleteDevice(DriverObject->DeviceObject); DbgPrint("Failed to create symbolic link!\n"); return ntStatus; } DriverObject->DriverUnload = OnUnload; return STATUS_SUCCESS; }
#ifndef _MAIN_H_ #define _MAIN_H_ const WCHAR deviceLinkBuffer[] = L"\\DosDevices\\Delfile"; const WCHAR deviceNameBuffer[] = L"\\Device\\Delfile"; typedef unsigned long DWORD; #define SystemHandleInformation 16 #define INVALID_PID_VALUE 0xFFFFFFFF #define FILE_DEVICE_SWAP 0x0000800a typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO { USHORT UniqueProcessId; USHORT CreatorBackTraceIndex; UCHAR ObjectTypeIndex; UCHAR HandleAttributes; USHORT HandleValue; PVOID Object; ULONG GrantedAccess; } SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO; typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG NumberOfHandles; SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[]; } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; NTSTATUS ObQueryNameString( IN PVOID Object, OUT POBJECT_NAME_INFORMATION ObjectNameInfo, IN ULONG Length, OUT PULONG ReturnLength ); NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation( ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ); NTSYSAPI NTSTATUS NTAPI ZwDuplicateObject( IN HANDLE SourceProcessHandle, IN HANDLE SourceHandle, IN HANDLE TargetProcessHandle OPTIONAL, OUT PHANDLE TargetHandle OPTIONAL, IN ACCESS_MASK DesiredAccess, IN ULONG HandleAttributes, IN ULONG Options ); NTSYSAPI NTSTATUS NTAPI ZwOpenProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK AccessMask, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PCLIENT_ID ClientId ); /* The file name looks like L"\\??\\C:\\hello.doc" */ BOOLEAN dfDelFile(WCHAR* name); #endif
...... //IRP是发向设备对象的 下面这句根据文件对象得到设备对象 DeviceObject = IoGetRelatedDeviceObject(fileObject); //申请一个IRP Irp = IoAllocateIrp(DeviceObject->StackSize, TRUE); if (Irp == NULL) { ObDereferenceObject(fileObject); return FALSE; } 初始化一个信号 等待下面的处理结果 KeInitializeEvent(&event, SynchronizationEvent, FALSE); //设置FILE_DISPOSITION_INFORMATION 结构DeleteFile 为TRUE; FileInformation.DeleteFile = TRUE; //将这个结构放到缓冲区buff中 Irp->AssociatedIrp.SystemBuffer = &FileInformation; //设置事件 Irp->UserEvent = &event; //设置处理返回结果 Irp->UserIosb = &ioStatus; //设置原始文件对象 Irp->Tail.Overlay.OriginalFileObject = fileObject; //设置原始线程 Irp->Tail.Overlay.Thread = (PETHREAD)KeGetCurrentThread(); //设置请求模式是内核模式 Irp->RequestorMode = KernelMode; //以上是设置头部分 //下面是设置栈上面 //填充给下一个设备的信息 //获取下一个设备的栈 irpSp = IoGetNextIrpStackLocation(Irp); //要删除文件 要设置一个东西所以主功能号是IRP_MJ_SET_INFORMATION irpSp->MajorFunction = IRP_MJ_SET_INFORMATION; //设备对象是前面获取到的设备对象 irpSp->DeviceObject = DeviceObject; //文件对象是得到的文件对象 irpSp->FileObject = fileObject; //设置参数大小 irpSp->Parameters.SetFile.Length = sizeof(FILE_DISPOSITION_INFORMATION); //设置参数类型 次功能号FileDispositionInformation就是删除文件 irpSp->Parameters.SetFile.FileInformationClass = FileDispositionInformation; //设置参数文件对象 irpSp->Parameters.SetFile.FileObject = fileObject; //设置完成例程 IoSetCompletionRoutine( Irp, dfSkillSetFileCompletion, &event, TRUE, TRUE, TRUE); //得到文件对象的内存指针 pSectionObjectPointer = fileObject->SectionObjectPointer; if(pSectionObjectPointer) { //这两个可以让操作系统以为这个程序不是PE文件 pSectionObjectPointer->ImageSectionObject = 0; pSectionObjectPointer->DataSectionObject = 0; } //下发IRP ntStatus = IoCallDriver(DeviceObject, Irp); if (!NT_SUCCESS(ntStatus)) { ObDereferenceObject(fileObject); ZwClose(handle); return FALSE; } //等待处理结果 KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, NULL); //IoFreeIrp(Irp); ObDereferenceObject(fileObject); ZwClose(handle); return TRUE; .....
NTSTATUS dfSkillSetFileCompletion( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { Irp->UserIosb->Status = Irp->IoStatus.Status; Irp->UserIosb->Information = Irp->IoStatus.Information; //设置信号 KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE); //不用说了吧 IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED;//这个设置可以再处理后还可以访问这个IRP 如果不设置这个 在完成后还访问IRP就会BSOD }
关于磁盘读写 本人不才 只有思路 因为涉及NTFS 所以要推迟一些了 不过不会太久了
hDrive = CreateFile( "\\\\.\\PHYSICALDRIVE0", GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
TCHAR _devicename[] = _T("\\\\.\\C:");
writefile/readfile
CNtfsFileSys::ReadSector(ULONGLONG sector, ULONG count, PVOID buffer)
WIN7权限问题:DeviceIoControl向逻辑分区发一个FSCTL_LOCK_VOLUME指令,把它“锁住”,然后就可以WriteFile写扇区了
其它的一些资料
围观注册表穿越操作_Returns' Station
围观文件穿越操作_Returns' Station
NTFS之HARDLINK攻防第二版 - 电脑系统安全 - 红黑联盟
VPB、VCB、FCB、CCB、SCB
http://bbs.pediy.com/showthread.php?t=87741&highlight=ntfs
【原创】NTFS文件系统底层挖掘 - 看雪安全论坛
【文件7】所谓hardlink 所谓XCB大法 - Returns' Station - 博客频道
文件穿越与注册表穿越
打开文件用IoCreateFile
其他比较好发irp的(比如删除操作)走FSD irp
自己实现了所有Nt系列操作文件的功能
文件删除部分有关闭其他进程里的句柄(硬链接无效)
硬链接:mklink /h link.txt gb.txt link.txt是对gb.txt的一个alias,链接计数(删除减一)。硬连接是不能跨卷的,只有在同一文件系统中的文件之间才能创建链接。
软链接(也叫符号链接)与硬链接不同,文件用户数据块中存放的内容是另一文件的路径名的指向。软链接就是一个普通文件,只是数据块内容有点特殊。删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接就变成了死链接。
MJ XCB大法: FCB、 VCB、CCB、SCB 、LCB