c++实现查看文件被占用功能

方法一:修改句柄访问权限 
      所有被占用的文件通常都可以用读属性(FILE_READ_ATTRIBUTES)打开,这样就可以读取文件的属性,取得它的大小,枚举NTSF 
      stream,但遗憾的是,ReadFile就不能成功调用了。打开文件时各种访问属性的区别在哪里呢?显然,打开文件时,系统会记录访问属性,之后会用 这个属性与请求的访问作比较。如果找到了系统保存这个属性的位置并修该掉它,那就不只可以读取,甚至可以写入任何已打开的文件。 
      在用户这一级别上我们并不是直接与文件打交道,而是通过它的句柄(这个句柄指向FileObject),而函数ReadFile/WriteFile调用 ObReferenceObjectByHandle,并指明了相应的访问类型。由此我们可以得出结论,访问权限保存在描述句柄的结构体里。实际 上,HANDLE_TABLE_ENTRY结构体包含有一个GrantedAccess域,这个域不是别的,就是句柄的访问权限。遗憾的 是,Microsoft的程序员们没有提供修改句柄访问权的API,所以我们不得不编写驱动自己来做这项工作。 
      我在《隐藏进程检测》一文中讲到过Windows 2000和XP的句柄表结构体,我想补充的只有一点,就是Windows 
      2003中的句柄表与XP的完全一样。与那篇文章不同,我们这里不需要枚举表中的句柄,而只需要找到某个具体的(已知的)句柄,我们不用管 PspCidTable,而只操作自己进程的句柄表,表的指针位于进程的EPROCESS结构体里(2000下的偏移为0x128,XP下的为 0x0C4)。 
      为了取得句柄结构体指针需要调用未导出函数ExpLookupHandleTableEntry,但我们不会去搜索它,因为在导出函数中没有对它的直接引 用,搜索结果也很不可靠,除此之外我们此时还需要ExUnlockHandleTableEntry函数。最好的办法就是编写自己的句柄表lookup函 数。考虑到Windows 
      2000与XP下句柄表的差异,我们将编写不同的函数。 
      首先是Windows 2000下的: 
      PHANDLE_TABLE_ENTRY
         Win2kLookupHandleTableEntry(
             IN PWIN2K_HANDLE_TABLE HandleTable,
             IN EXHANDLE             Handle )
      {
           ULONG i, j, k; 
           i = (Handle.Index >> 16) & 255;
           j = (Handle.Index >> 8)   & 255;
           k = (Handle.Index)        & 255;
        
           if (HandleTable->Table[i]) 
           {
               if (HandleTable->Table[i][j])
                   return &(HandleTable->Table[i][j][k]);
           }
           return NULL;    
      } 
      这段代码简单易懂。因为句柄的值本身是个三维表的三个索引,所以我们只需其中的各个部分并查看表中相应的元素(当然如果存在的话)。因为Windows 
      XP中的句柄表可以有一到三个级别,所以相应的lookup代码就要更为复杂一些: 
      PHANDLE_TABLE_ENTRY
         XpLookupHandleTableEntry(
                    IN PXP_HANDLE_TABLE HandleTable,
                    IN EXHANDLE          Handle )
      {
           ULONG i, j, k;
           PHANDLE_TABLE_ENTRY Entry = NULL;
           ULONG TableCode = HandleTable->TableCode & ~TABLE_LEVEL_MASK;


p;   i = (Handle.Index >> 17) & 0x1FF;
           j = (Handle.Index >> 9)   & 0x1FF;
           k = (Handle.Index)        & 0x1FF; 
           switch (HandleTable->TableCode & TABLE_LEVEL_MASK)
           {
               case 0 :
                 Entry = &((PHANDLE_TABLE_ENTRY)TableCode)[k];
               break;
          
               case 1 :
                 if (((PVOID *)TableCode)[j]) 
                 {
                    Entry = &((PHANDLE_TABLE_ENTRY *)TableCode)[j][k];      
                 }
               break; 
               case 2 :
                 if (((PVOID *)TableCode)[i])
                 if (((PVOID **)TableCode)[i][j])
                 {
                    Entry = &((PHANDLE_TABLE_ENTRY **)TableCode)[i][j][k];       
              
                 }
               break;
           }
           return Entry;
      } 
      我们看到,这段代码中的句柄并不是ULONG型的值,而是EXHANDLE结构体: 
      typedef struct _EXHANDLE 
      {
           union 
           {
               struct 
               {
                   ULONG TagBits : 02;
                   ULONG Index    : 30;
               };
               HANDLE GenericHandleOverlay;
           };
      } EXHANDLE, *PEXHANDLE; 
      我们看到,句柄不知包含了表的索引,还包含了一个2 bit的标志。您可能已经察觉到,一个句柄可以有着几种不同的意义,这一点与这样一个事实有关,那就是并非句柄中所有的位都被使用到(依赖于在表中的级别)。这是Windows 
      XP最具个性的特点。 
      现在我们就可以获取句柄表中所需的元素了,该编写为句柄设置所需访问属性的函数了: 
      BOOLEAN SetHandleAccess(
                       IN HANDLE       Handle,
                       IN ACCESS_MASK GrantedAccess
                       )
      {
         PHANDLE_TABLE        ObjectTable = *(PHANDLE_TABLE 
      *)RVATOVA(PsGetCurrentProcess(), ObjectTableOffset);
         PHANDLE_TABLE_ENTRY Entry;
         EXHANDLE             ExHandle; 
         ExHandle.GenericHandleOverlay = Handle; 
         Entry = ExLookupHandleTableEntry(ObjectTable, ExHandle); 
         if (Entry) Entry->GrantedAccess = GrantedAccess; 
         return Entry > 0;
      } 
      现在编写驱动,设置句柄的访问属性,通过DeviceIoControl向驱动传递句柄。代码如下: 
      NTSTATUS DriverIoControl(
           IN PDEVICE_OBJECT DeviceObject,
           IN PIRP Irp)
      {
           PIO_STACK_LOCATION pisl      = IoGetCurrentIrpStackLocation(Irp);
           NTSTATUS            status    = STATUS_UNSUCCESSFUL;
           ULONG               BuffSize = 
      pisl->Parameters.DeviceIoControl.InputBufferLength;
           PUCHAR              pBuff     = Irp->AssociatedIrp.SystemBuffer;
           HANDLE              Handle;
           ACCESS_MASK         GrantedAccess; 
           Irp->IoStatus.Information = 0; 
           switch(pisl->Parameters.DeviceIoControl.IoControlCode)
           {
               case IOCTL1: 
                   if (pBuff && BuffSize >= sizeof(HANDLE) + 
sizeof(ACCESS_MASK))
                   {
Handle         =*(HANDLE*)pBuff;
                       GrantedAccess = *(ACCESS_MASK*)(pBuff + sizeof(HANDLE)); 
                       if (Handle != (HANDLE)-1 && SetHandleAccess(Handle, 
      GrantedAccess)) status = STATUS_SUCCESS;
              
                   }    
                break;
            }   
           Irp->IoStatus.Status = status;
           IoCompleteRequest(Irp, IO_NO_INCREMENT);
           return status;
      } 
      NTSTATUS DriverCreateClose(
           IN PDEVICE_OBJECT DeviceObject,
           IN PIRP Irp)
      {
           Irp->IoStatus.Information = 0;
           Irp->IoStatus.Status = STATUS_SUCCESS;
           IoCompleteRequest(Irp, IO_NO_INCREMENT);
           return STATUS_SUCCESS;
      }

      NTSTATUS DriverEntry(
                   IN PDRIVER_OBJECT DriverObject,
                   IN PUNICODE_STRING RegistryPath
                   )
      {
           PCWSTR    dDeviceName        = L"\\Device\\fread";
           PCWSTR    dSymbolicLinkName = L"\\DosDevices\\fread";
           NTSTATUS status;
           PDRIVER_DISPATCH *ppdd; 
           RtlInitUnicodeString(&DeviceName,        dDeviceName);
           RtlInitUnicodeString(&SymbolicLinkName, dSymbolicLinkName); 
           switch (*NtBuildNumber)
           {
               case 2600:
                   ObjectTableOffset = 0x0C4;
                   ExLookupHandleTableEntry = XpLookupHandleTableEntry;
               break; 
               case 2195:
                   ObjectTableOffset = 0x128;
                   ExLookupHandleTableEntry = Win2kLookupHandleTableEntry;
               break; 
               default: return STATUS_UNSUCCESSFUL;
           } 
           status = IoCreateDevice(DriverObject, 
                                   0, 
                                   &DeviceName, 
                                   FILE_DEVICE_UNKNOWN, 
                                   0, 
                                   TRUE, 
                                   &deviceObject);
        
           if (NT_SUCCESS(status)) 
           {
               status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName); 
               if (!NT_SUCCESS(status)) IoDeleteDevice(deviceObject); 
               DriverObject->DriverUnload = DriverUnload;
           } 
           ppdd = DriverObject->MajorFunction;
         
           ppdd [IRP_MJ_CREATE] =
           ppdd [IRP_MJ_CLOSE ] = DriverCreateClose;
           ppdd [IRP_MJ_DEVICE_CONTROL ] = DriverIoControl; 
           return status;
      } 
      遗憾的是句柄结构体中的GrantedAccess域并没有和文件打开的属性(GENERIC_READ、GENERIC_WRITE等)对应起来,所以在设置新的属性时我们需要以下constants: 
      #define AC_GENERIC_READ         0x120089
      #define AC_GENERIC_WRITE        0x120196
      #define AC_DELETE               0x110080
      #define AC_READ_CONTROL         0x120080
      #define AC_WRITE_DAC            0x140080
&n

bsp;   #define AC_WRITE_OWNER          0x180080
      #define AC_GENERIC_ALL          0x1f01ff
      #define AC_STANDARD_RIGHTS_ALL 0x1f0080 
      为了使用这个驱动将SAM文件拷贝到c盘根目录,我们可以写一个最简单的程序: 
      #include <windows.h>
      #include "hchange.h" 
      BOOLEAN SetHandleAccess(
                    HANDLE Handle, 
                    ACCESS_MASK GrantedAccess
                    )
      {
           HANDLE   hDriver;
           ULONG    Bytes;
           ULONG    Buff[2];
           BOOLEAN Result = FALSE; 
           hDriver = CreateFile("\\\\.\\haccess", GENERIC_READ, 0, NULL, 
      OPEN_EXISTING, 0, 0); 
           if (hDriver != INVALID_HANDLE_VALUE)
           {
               Buff[0] = (ULONG)Handle;
               Buff[1] = GrantedAccess; 
               Result = DeviceIoControl(hDriver, IOCTL1, Buff, sizeof(Buff), 
      NULL, 0, &Bytes, NULL); 
               CloseHandle(hDriver);
           }
      } 
      void main()
      {
           HANDLE hFile, hDest;
           ULONG   Size, Bytes;
           PVOID   Data;
           CHAR    Name[MAX_PATH]; 
           GetSystemDirectory(Name, MAX_PATH); 
           lstrcat(Name, "\\config\\SAM"); 
           hFile = CreateFile(Name, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | 
      FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                              NULL, OPEN_EXISTING, 0, 0);
        
           if (hFile != INVALID_HANDLE_VALUE)
           {
               if (SetHandleAccess(hFile, AC_GENERIC_READ))
               {
                   Size = GetFileSize(hFile, NULL); 
                   Data = VirtualAlloc(NULL, Size, MEM_COMMIT | MEM_RESERVE, 
      PAGE_READWRITE); 
                   if (Data)
                   {
                       ReadFile(hFile, Data, Size, &Bytes, NULL); 
                       hDest = CreateFile("c:\\SAM", GENERIC_WRITE, 0, NULL, 
      CREATE_NEW, 0, 0); 
                       if (hDest != INVALID_HANDLE_VALUE)
                       {
                           WriteFile(hDest, Data, Size, &Bytes, NULL); 
                           CloseHandle(hDest);
                       }
                       VirtualFree(Data, 0, MEM_RELEASE);
                   }
               }
               CloseHandle(hFile);
           }
      }

这个方法最大的缺陷就是强烈依赖于操作系统,而且还需要加载驱动程序,而这并不总是能实现的。但是从可靠性上来看,这种方法是最好的,所以我建议将其用在backup程序中(只是要经过长期的测试和调试!)。因为这种方法有不能胜任的情形,我们转入下一种方法。


方法二:使用直接硬盘访问读取文件 
      “直接访问硬盘”这个想法当然很酷,但很快DOS编程爱好者们就会失望,这里没有硬件操作,因为微软很关心我们的疾苦,提供了方便简单的API,通过这些 API可以几乎“直接地”操作硬盘。这样大家就明白了吧,实际上我们是想以RAW模式打开volume,并按cluster来读取文件。希望大家没有被吓 到:) 
      如果直接入手解决这个问题,就需要手动地分析文件系统结构,这样我们就需要编写很多多余的代码,所以我们不会这样去做,而是再一次参考微软伟大的手册――MSDN。"Defragmenting 
      Files "和"Disk Management Control 
      Codes"部分对于我们来说非常有用,那里面有文件系统驱动的控制代码,这些代码可以用在各种磁盘整理程序中。打开MSDN,无疑会发现,使用 IOCTL代码FSCTL_GET_RETRIEVAL_POINTERS可以获取文件分配图。也就是说我们只需要借助于这个IOCTL就可以获取被占用 文件的cluster 
      list并进行读取。 
      用此代码调用DeviceIoControl时,InputBuffer应该包含有STARTING_VCN_INPUT_BUFFER结构体,这个结构 体描述了文件cluster链的首元素,函数成功执行后,OutputBuffer将装有RETRIEVAL_POINTERS_BUFFER结构体,这 个结构体描述了分配图。我们来详细地看一下这个结构体: 
      typedef struct 
      { 
          LARGE_INTEGER StartingVcn;
      } STARTING_VCN_INPUT_BUFFER, *PSTARTING_VCN_INPUT_BUFFER; 
      typedef struct RETRIEVAL_POINTERS_BUFFER 
      { 
           DWORD ExtentCount; 
           LARGE_INTEGER StartingVcn; 
           struct 
        {    
               LARGE_INTEGER NextVcn;    
               LARGE_INTEGER Lcn; 
           } Extents[1];
      } RETRIEVAL_POINTERS_BUFFER, *PRETRIEVAL_POINTERS_BUFFER; 
      第一个结构体很容易懂,我们只需要向StartingVcn.QuadPart传递0,而第二个结构体的格式需要好好研究一下。第一个域 (ExtentCount)包含着结构体中Extents元素的数目。StartingVcn文件第一个cluster链的链号。每一个Extents元 素都包含有一个NextVcn,其含有链中cluser的数目,而Lcn――其第一个cluster的cluster号。也就是说所返回的信息就是 cluster链的描述符,其中每一个链都包含有某些个cluster。 
      现在返回信息的结构体的含义就已经明了了,到了编写函数的时候了,这个函数获取文件完整的cluster list并将其整理为数组形式。 
      ULONGLONG *GetFileClusters(
                           PCHAR lpFileName,
                           ULONG ClusterSize, 
                           ULONG *ClCount,
                           ULONG *FileSize 
                           )
      {
           HANDLE   hFile;
           ULONG    OutSize;
           ULONG    Bytes, Cls, CnCount, r;
           ULONGLONG *Clusters = NULL;
           BOOLEAN Result = FALSE;
           LARGE_INTEGER PrevVCN, Lcn;
           STARTING_VCN_INPUT_BUFFER   InBuf;
           PRETRIEVAL_POINTERS_BUFFER OutBuf; 
           hFile = CreateFile(lpFileName, FILE_READ_ATTRIBUTES,
                              FILE_SHARE_READ | FILE_SHARE_WRITE | 
      FILE_SHARE_DELETE,
                              NULL, OPEN_EXISTING, 0, 0); 
           if (hFile != INVALID_HANDLE_VALUE)
           {
               *FileSize = GetFileSize(hFile, NULL); 
               OutSize = sizeof(RETRIEVAL_POINTERS_BUFFER) + (*FileSize / 
      ClusterSize) * sizeof(OutBuf->Extents); 
               OutBuf = malloc(OutSize); 
               InBuf.StartingVcn.QuadPart = 0;
          
               if (DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &InBuf, 
                                   sizeof(InBuf), OutBuf, OutSize, &Bytes, 
NULL))
               {
                   *ClCount = (*FileSize + ClusterSize - 1) / ClusterSize; 
                   Clusters = malloc(*ClCount * sizeof(ULONGLONG)); 
                   PrevVCN = OutBuf->StartingVcn; 
                   for (r = 0, Cls = 0; r < OutBuf->ExtentCount; r++)
                   {
                       Lcn = OutBuf->Extents[r].Lcn; 
                       for (CnCount = OutBuf->Extents[r].NextVcn.QuadPart - 
      PrevVCN.QuadPart;
                            CnCount; CnCount--, Cls++, Lcn.QuadPart++) 
      Clusters[Cls] = Lcn.QuadPart; 
                       PrevVCN = OutBuf->Extents[r].NextVcn;
                   }
               }
            
               free(OutBuf); 
               CloseHandle(hFile);
           }
           return Clusters;
      } 
      函数完成后我们就得到了描述文件clusters的数组以及clusters的数目,现在可以很容易地拷贝文件了: 
      void FileCopy(
               PCHAR lpSrcName,
               PCHAR lpDstName
               )
      {
           ULONG          ClusterSize, BlockSize;
           ULONGLONG     *Clusters;
           ULONG          ClCount, FileSize, Bytes;
           HANDLE         hDrive, hFile;
           ULONG          SecPerCl, BtPerSec, r;
           PVOID          Buff;
           LARGE_INTEGER Offset;
           CHAR           Name[7];
        
           Name[0] = lpSrcName[0];
           Name[1] = ':';
           Name[2] = 0; 
           GetDiskFreeSpace(Name, &SecPerCl, &BtPerSec, NULL, NULL); 
           ClusterSize = SecPerCl * BtPerSec;
        
           Clusters = GetFileClusters(lpSrcName, ClusterSize, &ClCount, 
      &FileSize); 
           if (Clusters)
           {
               Name[0] = '\\';
               Name[1] = '\\';
               Name[2] = '.';
               Name[3] = '\\';
               Name[4] = lpSrcName[0];
               Name[5] = ':';
               Name[6] = 0; 
               hDrive = CreateFile(Name, GENERIC_READ, FILE_SHARE_READ | 
      FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0); 
               if (hDrive != INVALID_HANDLE_VALUE)
               {
                   hFile = CreateFile(lpDstName, GENERIC_WRITE, 0, NULL, 
      CREATE_NEW, 0, 0); 
                   if (hFile != INVALID_HANDLE_VALUE)
                   {
                       Buff = malloc(ClusterSize); 
                       for (r = 0; r < ClCount; r++, FileSize -= BlockSize)
                       {
                           Offset.QuadPart = ClusterSize * Clusters[r]; 
                           SetFilePointer(hDrive, Offset.LowPart, 
      &Offset.HighPart, FILE_BEGIN); 
                           ReadFile(hDrive, Buff, ClusterSize, &Bytes, NULL); 
                           BlockSize = FileSize < ClusterSize ? FileSize : 
      ClusterSize; 
                           WriteFile(hFile, Buff, BlockSize, &Bytes, NULL);
                       } 
                       free(Buff); 
                       CloseHandle(hFile);
                   }
                   CloseHandle(hDrive);
               }
               free(Clusters);
           }
      } 
      文章到这里其实就结束了,现在要拷贝SAM简直易如反掌:)。在配套的示例中有将SAM拷贝到命令行指定的文件中的代码。 
      无疑,这种方法形式简单而功能强大,但遗憾的是它有着本质上的缺陷。这种方法只能用来读取以FILE_READ_ATTRIBUTES属性打开的文件,文 件不能压缩,不能加密,而且应该有自己的cluster(在NTFS下小文件可以整个放在MFT里)。同时要考虑到,在读取文件时文件可能被修改。 
      我想,如何与底层文件系统打交道大家都已经明白了。这个方法为rootkit提供了诸多的便利。系统里有保护文件不被修改的程序(比如说反病毒软件),但 是拥有了以RAW模式打开volume的权限之后,这些就形同虚设。再有,好的管理员会在自己的server上将重要文件的读写记录入日志文件,而直接访 问是逃不过日志记录的。要实现对文件的完全访问就不得不编写自己的NTFS驱动了。 


方法三:寻找打开文件的句柄 
      如果文件由某个进程打开,那么这个进程就拥有了它的句柄。在我第二篇关于API拦截的文章里我讲解了如何搜索需要的句柄并用它打开进程,要访问已打开的文 件,我们也可以使用这种方法。我们需要使用ZwQuerySystemInformation函数来枚举句柄,将每一个句柄都用 DuplicateHandle进行复制,确定句柄属于那个文件(ZwQueryInformationFile),如果是要找的文件,就将句柄拷贝。 
      这些在理论上都讲得通,但在实践中会遇到两处难点。第一,在对打开的named pipe(工作于block 
      mode)的句柄调用ZwQueryInformationFile的时候,调用线程会等待pipe中的消息,而pipe中却可能没有消息,也就是说,调 用ZwQueryInformationFile的线程实际上永久性地挂起了。所以命名文件的获取不用在挑选句柄的主线程中进行,可以启动独立的线程并设 置一个timeout值来避免挂起。第二,在拷贝句柄后,两个句柄(我们进程的和打开文件进程的)将会指向同一个FileObject,从而当前的输入输 出模式、在文件中的位置以及其它与文件相关的信息就会由两个进程来共享。这时,甚至只是读取文件都会引起读取位置的改变,从而破坏了打开文件程序的正常运 行。为了避免这种情形,我们需要需要停止占用文件进程的线程、保存当前位置、拷贝文件、恢复当前位置以及重新启动占用文件的进程。这种方法不能用于许多情 形,比如要在运行的系统中拷贝注册表文件,用这种方法就不会成功。 
      我们先来试着实现对系统中所有已打开文件的句柄的枚举。为枚举句柄,每个句柄都由以下结构体描述: 
      typedef struct _SYSTEM_HANDLE
      {
         ULONG        uIdProcess;
         UCHAR        ObjectType; 
         UCHAR        Flags;
         USHORT       Handle;
         POBJECT      pObject;
         ACCESS_MASK GrantedAccess;
      } SYSTEM_HANDLE, *PSYSTEM_HANDLE; 
      这里的ObjectType域定义了句柄所属的对象类型。这里我们又遇到了问题――File类型的ObjectType在Windows 
      2000、XP和2003下的取值各不相同,所以我们不得不动态的定义这个值。为此我们用CreateFile来打开NUL设备,找到它的句柄并记下它的类型: 
      UCHAR GetFileHandleType()
      {
         HANDLE                      hFile;
         PSYSTEM_HANDLE_INFORMATION Info;
         ULONG                       r;
         UCHAR                       Result = 0; 
         hFile = CreateFile("NUL", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); 
         if (hFile != INVALID_HANDLE_VALUE)
         {
           Info = GetInfoTable(SystemHandleInformation); 
           if (Info)
           {
             for (r = 0; r < Info->uCount; r++)
             {
               if (Info->aSH[r].Handle == (USHORT)hFile && 
                                           Info->aSH[r].uIdProcess == GetCurrentProcessId())
               {
                 Result = Info->aSH[r].ObjectType;
                 break;
               }
             } 
             HeapFree(hHeap, 0, Info);
           } 
           CloseHandle(hFile);
         }
         return Result;
      } 
      现在知道了句柄的类型我们就可以枚举系统中打开的文件了。首先我们来用句柄获取打开文件的文件名: 
      typedef struct _NM_INFO
      {
         HANDLE   hFile;
         FILE_NAME_INFORMATION Info;
         WCHAR Name[MAX_PATH];
      } NM_INFO, *PNM_INFO; 
      DWORD WINAPI 
         GetFileNameThread(PVOID lpParameter) 
      {
         PNM_INFO         NmInfo = lpParameter;
         IO_STATUS_BLOCK IoStatus;
         int r; 
         NtQueryInformationFile(NmInfo->hFile, &IoStatus, &NmInfo->Info, 
                              sizeof(NM_INFO) - sizeof(HANDLE), 
      FileNameInformation); 
         return 0;
      } 
      void GetFileName(HANDLE hFile, PCHAR TheName)
      {
         HANDLE    hThread;
         PNM_INFO Info = HeapAlloc(hHeap, 0, sizeof(NM_INFO)); 
         Info->hFile = hFile; 
         hThread = CreateThread(NULL, 0, GetFileNameThread, Info, 0, NULL); 
         if (WaitForSingleObject(hThread, INFINITE) == WAIT_TIMEOUT) 
      TerminateThread(hThread, 0); 
         CloseHandle(hThread); 
         memset(TheName, 0, MAX_PATH); 
         WideCharToMultiByte(CP_ACP, 0, Info->Info.FileName, 
      Info->Info.FileNameLength >> 1, TheName, MAX_PATH, NULL, NULL); 
         HeapFree(hHeap, 0, Info);
      } 
      现在来枚举打开的文件: 
      void main()
      {
       PSYSTEM_HANDLE_INFORMATION Info;
         ULONG                       r;
         CHAR                        Name[MAX_PATH];
         HANDLE                      hProcess, hFile; 
         hHeap = GetProcessHeap(); 
         ObFileType = GetFileHandleType(); 
         Info = GetInfoTable(SystemHandleInformation); 
         if (Info)
         {
           for (r = 0; r < Info->uCount; r++)
           {
             if (Info->aSH[r].ObjectType == ObFileType)
             {
               hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, 
      Info->aSH[r].uIdProcess);
              
               if (hProcess)
               {
                 if (DuplicateHandle(hProcess, (HANDLE)Info->aSH[r].Handle,
                               GetCurrentProcess(), &hFile, 0, FALSE, 
      DUPLICATE_SAME_ACCESS))
                 {
                   GetFileName(hFile, Name); 
                   printf("%s\n", Name); 
                   CloseHandle(hFile);
                 } 
                 CloseHandle(hProcess);
               }        
             }            
           } 
           HeapFree(hHeap, 0, Info);
         }
      } 
      现在对于文件的拷贝我们剩下的工作只是找到所需句柄后用ReadFile读取它。这里一定要使用前面提到的机制,不可疏忽。 
      这种方法的优点是实现简单,但是其缺点更多,所以这个方法只适用于确定文件被那个进程占用。


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 当我们尝试删除一个文件时,有时会收到一个错误提示,指示文件正在被进程占用,因此删除操作无法完成。这种情况通常是因为操作系统或其他程序仍在使用该文件文件被进程占用的情况可能有多种原因。其中一种可能是文件当前正在被其他程序打开或正在被其他进程使用,这可能是由于文件被用作输入或输出,或者作为其他程序的配置文件。在这种情况下,操作系统会保护文件不被删除,以避免对其他程序或系统造成损害。 为了解决这个问题,我们可以尝试以下几种方法: 1. 关闭使用文件的程序:首先,我们可以尝试关闭正在使用文件的程序。这可以通过在任务管理器中结束进程或在应用程序中关闭文件选项来实现。关闭程序后,我们可以尝试再次删除文件。 2. 重启计算机:如果无法确定哪个程序正在使用文件,我们可以尝试重启计算机。重启计算机将关闭所有正在运行的程序和进程,并释放对文件占用。在重启后,我们可以尝试删除文件。 3. 使用特殊软件:如果上述方法无效,我们可以考虑使用一些专门删除被进程占用文件的软件。这些软件可以强制关闭进程,释放对文件占用,并删除文件。 无论哪种方法,我们都需要谨慎操作,确保我们正在删除正确的文件,并且不会影响其他程序或系统的正常运行。另外,我们还应该定期清理不再需要的文件,以避免出现文件被进程占用的情况。 ### 回答2: 当我们尝试删除一个文件时,有时候会收到一个错误消息提示文件被进程占用,导致删除失败。这通常发生在文件正被另一个程序使用,因此操作系统不允许我们删除它。 当一个文件被打开并被运行的程序占用时,操作系统为了维护文件的完整性和一致性,会阻止其他程序访问或修改这个文件。这样做是为了避免数据丢失或损坏的风险。因此,当我们试图删除被占用文件时,操作系统会提示文件被进程占用。 要解决这个问题,我们需要找出并关闭占用文件的进程。有几种方法可以做到这一点: 1. 重新启动计算机:这是最简单的方法之一。通过重新启动计算机,所有正在运行的进程都会被终止,包括占用文件的进程。在计算机重新启动后,我们可以尝试删除文件。但是请注意,这种方法可能会中断其他正在进行的任务,因此请确保保存了所有正在进行的工作。 2. 使用任务管理器或进程管理工具:我们可以使用任务管理器(Windows操作系统)或类似的进程管理工具来查找和结束占用文件的进程。打开任务管理器,切换到"进程"或"详细信息"选项卡,并找到占用文件的进程。选中该进程,然后点击"结束任务"或"结束进程"按钮。完成后,尝试删除文件。 3. 修改文件名:有时,我们可以通过将文件名更改为其他名称来绕过文件占用的问题。这样做可能会解除其他进程对该文件占用,允许我们删除文件。在修改文件名之后,我们可以尝试删除文件。如果删除成功,我们还可以将文件名改回原来的名称。 无论选择哪种方法,我们应该谨慎行事,并确保不会对其他进程或文件造成不必要的干扰。此外,记得在进行任何操作之前,备份重要的文件是非常重要的,以防止意外数据丢失。 ### 回答3: 当我们尝试删除一个文件时,如果收到“文件被进程占用”或类似的错误提示,这意味着该文件正在被操作系统中的一个进程使用。这种情况可能发生在多种情况下,我将提供一些可能的原因和解决方案。 首先,可能是由于其他应用程序或后台进程正在访问该文件。这可能是因为文件正在被其他程序读取、写入或修改。解决这个问题的一种方法是找到并关闭正在使用该文件的进程。我们可以尝试通过任务管理器或类似的工具找到并终止相关进程,然后再尝试删除文件。 另一种可能性是文件被操作系统本身使用或锁定。例如,文件可能是操作系统的一部分,或者正在被用于系统的关键功能。在这种情况下,我们不能直接删除文件,因为这可能会导致系统不稳定或造成其他问题。解决这个问题的方法是在安全模式下启动计算机,并尝试删除文件。在安全模式下,操作系统和其他进程的访问权限受到限制,这可能允许我们删除被进程占用文件。 还有一种可能是文件被某个程序意外地损坏或占用。在这种情况下,我们可以尝试进行系统检测和修复。在Windows操作系统中,我们可以使用系统文件检查工具(sfc /scannow)来扫描和修复系统文件。此外,还可以尝试使用一些第三方工具来修复文件系统错误,例如磁盘清理和修复工具。 总之,当我们尝试删除一个文件时,如果收到“文件被进程占用”的错误提示,我们应该首先尝试找到并关闭正在使用该文件的进程。如果这不起作用,我们可以尝试在安全模式下删除文件,或者进行系统检测和修复。请记住,在删除文件之前,我们应该确保文件不再被任何进程使用,以免导致系统不稳定或其他问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值