如何使用ntquerydirectoryfile函数钩子隐藏文件夹

时间:2012-12-26 05:00:23

标签: c++ visual-c++ directory api-hook

我正在挂钩函数'NtQueryDirectoryFile'来隐藏系统中的文件夹,我的代码如下。

 #include "stdafx.h"
 #include <windows.h>
 #include <stdio.h>
 #include "MinHook.h"
 #include <shlwapi.h>
 #include <winsock2.h>

 #if defined _M_X64
 #pragma comment(lib, "libMinHook.x64.lib")
 #elif defined _M_IX86
 #pragma comment(lib, "libMinHook.x86.lib")
 #endif

 #define NT_SUCCESS(Status) ((NTSTATUS)(Status)>=0)

 typedef LONG NTSTATUS;
 #define STATUS_SUCCESS   ((NTSTATUS)0x00000000L)

 typedef struct _IO_STATUS_BLOCK 
 { 
    NTSTATUS Status; 
    ULONG Information;
 } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; 

 typedef struct _UNICODE_STRING 
 { 
   USHORT Length; 
   USHORT MaximumLength; 
   PWSTR Buffer;
 } UNICODE_STRING, *PUNICODE_STRING; 

 typedef struct _STRING 
 {
    USHORT Length;
    USHORT MaximumLength;
    PCHAR  Buffer;
 } ANSI_STRING, *PANSI_STRING;

 typedef enum _FILE_INFORMATION_CLASS
 {
   FileDirectoryInformation = 1, 
   FileFullDirectoryInformation,
   FileIdFullDirectoryInformation,
   FileBothDirectoryInformation,
   FileIdBothDirectoryInformation,
   FileBasicInformation,
   FileStandardInformation,
   FileInternalInformation,
   FileEaInformation,
   FileAccessInformation,
   FileNameInformation,
   FileRenameInformation,
   FileLinkInformation,
   FileNamesInformation,
   FileDispositionInformation,
   FilePositionInformation,
   FileFullEaInformation,
   FileModeInformation,
   FileAlignmentInformation,
   FileAllInformation,
   FileAllocationInformation,
   FileEndOfFileInformation,
   FileAlternateNameInformation,
   FileStreamInformation,
   FilePipeInformation,
   FilePipeLocalInformation,
   FilePipeRemoteInformation,
   FileMailslotQueryInformation,
   FileMailslotSetInformation,
   FileCompressionInformation,
   FileObjectIdInformation,
   FileCompletionInformation,
   FileMoveClusterInformation,
   FileQuotaInformation,
   FileReparsePointInformation,
   FileNetworkOpenInformation,
   FileAttributeTagInformation,
   FileTrackingInformation,
   FileMaximumInformation 
 } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;

 typedef VOID (NTAPI *PIO_APC_ROUTINE)( IN PVOID ApcContext, IN PIO_STATUS_BLOCK   IoStatusBlock, IN ULONG Reserved); 

 typedef struct _FILE_BOTH_DIRECTORY_INFORMATION 
 { 
  ULONG NextEntryOffset;
  ULONG Unknown;
  LARGE_INTEGER CreationTime; 
  LARGE_INTEGER LastAccessTime;
  LARGE_INTEGER LastWriteTime;
  LARGE_INTEGER ChangeTime;
  LARGE_INTEGER EndOfFile;
  LARGE_INTEGER AllocationSize;
  ULONG FileAttributes;
  ULONG FileNameLength;
  ULONG EaInformationLength;
  UCHAR AlternateNameLength;
  WCHAR AlternateName[12];
  WCHAR FileName[1];
 } FILE_BOTH_DIR_INFORMATION,*PFILE_BOTH_DIR_INFORMATION;

 typedef struct _FILE_DIRECTORY_INFORMATION
 {
      ULONG         NextEntryOffset;
      ULONG         FileIndex;
      LARGE_INTEGER CreationTime;
      LARGE_INTEGER LastAccessTime;
      LARGE_INTEGER LastWriteTime;
      LARGE_INTEGER ChangeTime;
      LARGE_INTEGER EndOfFile;
      LARGE_INTEGER AllocationSize;
      ULONG         FileAttributes;
      ULONG         FileNameLength;
      WCHAR         FileName[1];
  } FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;


  typedef struct _FILE_FULL_DIR_INFORMATION 
  {
      ULONG         NextEntryOffset;
      ULONG         FileIndex;
      LARGE_INTEGER CreationTime;
      LARGE_INTEGER LastAccessTime;
      LARGE_INTEGER LastWriteTime;
      LARGE_INTEGER ChangeTime;
      LARGE_INTEGER EndOfFile;
      LARGE_INTEGER AllocationSize;
      ULONG         FileAttributes;
      ULONG         FileNameLength;
      ULONG         EaSize;
      WCHAR         FileName[1];
  } FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;

  typedef struct _FILE_ID_FULL_DIR_INFORMATION 
  {
       ULONG         NextEntryOffset;
       ULONG         FileIndex;
       LARGE_INTEGER CreationTime;
       LARGE_INTEGER LastAccessTime;
       LARGE_INTEGER LastWriteTime;
       LARGE_INTEGER ChangeTime;
       LARGE_INTEGER EndOfFile;
       LARGE_INTEGER AllocationSize;
       ULONG         FileAttributes;
       ULONG         FileNameLength;
       ULONG         EaSize;
       LARGE_INTEGER FileId;
       WCHAR         FileName[1];
   } FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;

   typedef struct _FILE_ID_BOTH_DIR_INFORMATION 
   {
        ULONG         NextEntryOffset;
        ULONG         FileIndex;
        LARGE_INTEGER CreationTime;
        LARGE_INTEGER LastAccessTime;
        LARGE_INTEGER LastWriteTime;
        LARGE_INTEGER ChangeTime;
        LARGE_INTEGER EndOfFile;
        LARGE_INTEGER AllocationSize;
        ULONG         FileAttributes;
        ULONG         FileNameLength;
        ULONG         EaSize;
        CCHAR         ShortNameLength;
        WCHAR         ShortName[12];
        LARGE_INTEGER FileId;
        WCHAR         FileName[1];
   } FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION;

   typedef struct _FILE_NAMES_INFORMATION
   {
       ULONG NextEntryOffset;
       ULONG FileIndex;
       ULONG FileNameLength;
       WCHAR FileName[1];
   } FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;


    ULONG getDirEntryFileLength(PVOID FileInformationBuffer,FILE_INFORMATION_CLASS FileInfoClass)
    {
    ULONG ulResult=0;
    switch(FileInfoClass)
    {
        case FileDirectoryInformation:
            ulResult=(ULONG)((PFILE_DIRECTORY_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileFullDirectoryInformation:
            ulResult=(ULONG)((PFILE_FULL_DIR_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileIdFullDirectoryInformation:
            ulResult=(ULONG)((PFILE_ID_FULL_DIR_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileBothDirectoryInformation:
            ulResult=(ULONG)((PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileIdBothDirectoryInformation:
            ulResult=(ULONG)((PFILE_ID_BOTH_DIR_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileNamesInformation:
            ulResult=(ULONG)((PFILE_NAMES_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
    }
    return ulResult;
}

void setDirEntryLenToNext( PVOID FileInformationBuffer, FILE_INFORMATION_CLASS FileInfoClass, DWORD value)
{
     switch(FileInfoClass)
    {
        case FileDirectoryInformation:
            ((PFILE_DIRECTORY_INFORMATION)FileInformationBuffer)->NextEntryOffset=value;
            break;
        case FileFullDirectoryInformation:
            ((PFILE_FULL_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset=value;
            break;
        case FileIdFullDirectoryInformation:
            ((PFILE_ID_FULL_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset=value;
            break;
        case FileBothDirectoryInformation:
            ((PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset=value;
            break;
        case FileIdBothDirectoryInformation:
            ((PFILE_ID_BOTH_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset=value;
            break;
        case FileNamesInformation:
            ((PFILE_NAMES_INFORMATION)FileInformationBuffer)->NextEntryOffset=value;
            break;
    }
}

PVOID getDirEntryFileName(PVOID FileInformationBuffer, FILE_INFORMATION_CLASS FileInfoClass)
{
    PVOID pvResult=NULL;
    switch(FileInfoClass)
    {
        case FileDirectoryInformation:
            pvResult=(PVOID)&((PFILE_DIRECTORY_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileFullDirectoryInformation:
            pvResult=(PVOID)&((PFILE_FULL_DIR_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileIdFullDirectoryInformation:
            pvResult=(PVOID)&((PFILE_ID_FULL_DIR_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileBothDirectoryInformation:
            pvResult=(PVOID)&((PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileIdBothDirectoryInformation:
            pvResult=(PVOID)&((PFILE_ID_BOTH_DIR_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileNamesInformation:
            pvResult=(PVOID)&((PFILE_NAMES_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
    }
    return pvResult;
}

DWORD getDirEntryLenToNext(PVOID FileInformationBuffer, FILE_INFORMATION_CLASS FileInfoClass)
{
    DWORD dwResult=0;
    switch(FileInfoClass)
    {
        case FileDirectoryInformation:
            dwResult=((PFILE_DIRECTORY_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileFullDirectoryInformation:
            dwResult=((PFILE_FULL_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileIdFullDirectoryInformation:
            dwResult=((PFILE_ID_FULL_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileBothDirectoryInformation:
            dwResult=((PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileIdBothDirectoryInformation:
            dwResult=((PFILE_ID_BOTH_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileNamesInformation:
            dwResult=((PFILE_NAMES_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
    }
    return dwResult;
}

BOOL config_CheckString(char * str, char *str2)
{
if(StrStrIA(str, str2) != NULL)
    return TRUE;
return FALSE;
}

extern "C" NTSYSAPI NTSTATUS NTAPI RtlUnicodeStringToAnsiString(PANSI_STRING  DestinationString, PUNICODE_STRING SourceString,
                                                            BOOLEAN AllocateDestinationString);

 extern "C" NTSYSAPI VOID NTAPI RtlFreeAnsiString(PANSI_STRING AnsiString);

 extern "C" NTSYSAPI NTSTATUS NTAPI NtQueryDirectoryFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext,
                        PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass,
                        BOOLEAN ReturnSingleEntry, PUNICODE_STRING FileName, BOOLEAN RestartScan);


 typedef NTSTATUS (WINAPI *NtQueryDirectoryFileNext)(HANDLE hFile, HANDLE hEvent, PIO_APC_ROUTINE IoApcRoutine, PVOID IoApcContext,
                        PIO_STATUS_BLOCK pIoStatusBlock,  PVOID FileInformationBuffer, ULONG FileInformationBufferLength, FILE_INFORMATION_CLASS FileInfoClass,
                        BOOLEAN bReturnOnlyOneEntry, PUNICODE_STRING PathMask, BOOLEAN bRestartQuery);

 NtQueryDirectoryFileNext Real_NtQueryDirectoryFile = NULL;

 NTSTATUS WINAPI NtQueryDirectoryFileCallback(HANDLE hFile, HANDLE hEvent, PIO_APC_ROUTINE IoApcRoutine, PVOID IoApcContext,
                        PIO_STATUS_BLOCK pIoStatusBlock, PVOID FileInformationBuffer, ULONG FileInformationBufferLength, FILE_INFORMATION_CLASS FileInfoClass,
                        BOOLEAN bReturnOnlyOneEntry,   PUNICODE_STRING PathMask, BOOLEAN bRestartQuery)
{

 NTSTATUS rc;
 char * ConfigHiddenFileDir = "pawaa software";//Name of the Folder to be Hide
 rc = Real_NtQueryDirectoryFile( hFile, hEvent, IoApcRoutine, IoApcContext,  pIoStatusBlock,
                                  FileInformationBuffer, FileInformationBufferLength, FileInfoClass, bReturnOnlyOneEntry,
                                PathMask, bRestartQuery );

 if(NT_SUCCESS(rc) && (FileInfoClass==FileDirectoryInformation || FileInfoClass == FileFullDirectoryInformation||
    FileInfoClass == FileIdFullDirectoryInformation || FileInfoClass==FileBothDirectoryInformation||
    FileInfoClass == FileIdBothDirectoryInformation || FileInfoClass==FileNamesInformation))
{

      PVOID p = FileInformationBuffer;
      PVOID pLast = NULL;
      BOOL bLastOne,bFound;
      UNICODE_STRING usName;
      ANSI_STRING asName;

      if (bReturnOnlyOneEntry) // if only one entry returned we should give the next if it suppose to be hidden
      {
          usName.Buffer = (PWSTR)getDirEntryFileName(FileInformationBuffer, FileInfoClass);
          usName.Length=(USHORT)getDirEntryFileLength(FileInformationBuffer,FileInfoClass);
          RtlUnicodeStringToAnsiString(&asName,&usName,TRUE);
          bFound = config_CheckString(ConfigHiddenFileDir, asName.Buffer);
          RtlFreeAnsiString(&asName);

          if( bFound)
           {
                rc = Real_NtQueryDirectoryFile( hFile, hEvent, IoApcRoutine, IoApcContext, pIoStatusBlock,
                                    FileInformationBuffer, FileInformationBufferLength, FileInfoClass, bReturnOnlyOneEntry,
                                    PathMask, bRestartQuery);

                        if (rc != STATUS_SUCCESS)
                         return(rc);

            usName.Buffer = (PWSTR)getDirEntryFileName(FileInformationBuffer,FileInfoClass);
            usName.Length = (USHORT)getDirEntryFileLength(FileInformationBuffer,FileInfoClass);
            RtlUnicodeStringToAnsiString(&asName,&usName,TRUE);
            bFound = config_CheckString(ConfigHiddenFileDir,asName.Buffer);
            RtlFreeAnsiString(&asName);
           }
      }
      else // if full list hide the ones that should be hidden
      {     
        do 
        {
            bLastOne =!getDirEntryLenToNext(p,FileInfoClass);

            // compare directory-name 
            if (getDirEntryFileLength(p,FileInfoClass)) 
            {
                usName.Buffer = (PWSTR) getDirEntryFileName(p,FileInfoClass);
                usName.Length =(USHORT)getDirEntryFileLength(p,FileInfoClass);
                RtlUnicodeStringToAnsiString(&asName,&usName,TRUE);
                if (config_CheckString(ConfigHiddenFileDir,asName.Buffer))
                {
                    RtlFreeAnsiString(&asName);
                    if(bLastOne) 
                    {
                        if(p == FileInformationBuffer) rc = 0x80000006;
                        else setDirEntryLenToNext(pLast,FileInfoClass,0);
                        break;
                    } 
                    else    
                    {   
                        int iPos = ((ULONG)p)-(ULONG)FileInformationBuffer;
                        int iLeft =(DWORD)FileInformationBufferLength - iPos - getDirEntryLenToNext(p,FileInfoClass);
                        RtlCopyMemory(p,(PVOID)((char*)p + getDirEntryLenToNext(p,FileInfoClass)),(DWORD)iLeft);
                        continue;
                    }
                }
                RtlFreeAnsiString(&asName);
            }

            pLast = p;
            p =((char*)p+getDirEntryLenToNext(p,FileInfoClass));
        } while(!bLastOne);
    }
}
return rc;
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:

    if (MH_Initialize() != MH_OK)
    {
        MessageBoxW(NULL, L"Failed Initialize", L"Info!", MB_ICONWARNING|MB_OK);    
    }
    if (MH_CreateHook(&NtQueryDirectoryFile, &NtQueryDirectoryFileCallback, reinterpret_cast<void**>(&Real_NtQueryDirectoryFile)) != MH_OK)
    {
        MessageBoxW(NULL, L"connect Disabled by hooking", L"Info!", MB_ICONWARNING|MB_OK);
    }
    if (MH_EnableHook(&NtQueryDirectoryFile) != MH_OK)
    {
        MessageBoxW(NULL, L"Failed EnableHook connect", L"Info!", MB_ICONWARNING|MB_OK);
    }
    break;
case DLL_PROCESS_DETACH:
    if (MH_DisableHook(&NtQueryDirectoryFile) != MH_OK){}
    if (MH_Uninitialize() != MH_OK){}
    break;
}
return TRUE;
} 

从上面的代码我试图隐藏文件夹,但它不起作用,所以请帮助我,如果上面的代码有任何错误,或者建议我如何实现文件夹隐藏.... < / p>

1 个答案:

答案 0 :(得分:1)

如果还有必要...... 我使用此函数从explorer.exe隐藏了WIN 7上的文件夹(不是最终版本,可能有错误,也必须在WIN XP和VISTA上工作):

NTSTATUS WINAPI NewZwQueryDirectoryFile(  HANDLE FileHandle,
HANDLE Event,
PIO_APC_ROUTINE ApcRoutine,
PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
PVOID FileInformation,
ULONG Length,
FILE_INFORMATION_CLASS FileInformationClass,
BOOLEAN ReturnSingleEntry,
PUNICODE_STRING FileName,
BOOLEAN RestartScan
){

      char infobuf[4096];
     ULONG ResultLength = 0; 
NtQueryObject(FileHandle,ObjectNameInformation,infobuf,sizeof(infobuf),&ResultLength);
         OBJECT_NAME_INFORMATION * pinfo = (OBJECT_NAME_INFORMATION *) infobuf;
wchar_t * ps = pinfo->NameBuffer;
ps[ pinfo->Name.Length / 2 ] = 0;
NTSTATUS result =  TrueZwQueryDirectoryFile(FileHandle,Event,ApcRoutine,ApcContext,IoStatusBlock,FileInformation,Length,
                          FileInformationClass,ReturnSingleEntry,FileName,RestartScan);
if ((FileInformationClass==FileBothDirectoryInformation || FileInformationClass==FileIdBothDirectoryInformation) && result == STATUS_SUCCESS){
if (lstrcmpi(ps,diskName)==0) {
    if (FileInformationClass==FileBothDirectoryInformation) {
PFILE_BOTH_DIR_INFORMATION Prev,info = (PFILE_BOTH_DIR_INFORMATION)FileInformation;
UINT cnt=0;
    while (info->NextEntryOffset>0) {
        Prev=info;
        info =(PFILE_BOTH_DIR_INFORMATION)((DWORD)info+(DWORD)info->NextEntryOffset);
        UINT bl;
        if (folderName[0]==0) bl =  lstrcmp(info->FileName,nameProcess); else bl =  lstrcmp(info->FileName,folderName);
        if (bl){

            if (info->NextEntryOffset==0){
                Prev->NextEntryOffset=0; 
                Prev->FileIndex=info->FileIndex;
                if (cnt==0) return STATUS_NO_SUCH_FILE;
            }
            else {
            Prev->NextEntryOffset = Prev->NextEntryOffset+info->NextEntryOffset;
            Prev->FileIndex = info->FileIndex;
            }
                }

    cnt++;
    }
    }
 else {
        PFILE_ID_BOTH_DIR_INFORMATION Prev,info = (PFILE_ID_BOTH_DIR_INFORMATION)FileInformation;
UINT cnt=0;
    while (info->NextEntryOffset>0) {
        Prev=info;
        info =(PFILE_ID_BOTH_DIR_INFORMATION)((DWORD)info+(DWORD)info->NextEntryOffset);
        UINT bl;
        if (folderName[0]==0) bl =  lstrcmp(info->FileName,nameProcess); else bl =  lstrcmp(info->FileName,folderName);
        if (!bl){

            if (info->NextEntryOffset==0){
                Prev->NextEntryOffset=0; 
                Prev->FileIndex=info->FileIndex;
                if (cnt==0) return STATUS_NO_SUCH_FILE;
            }
            else {
            Prev->NextEntryOffset = Prev->NextEntryOffset+info->NextEntryOffset;
            Prev->FileIndex = info->FileIndex;
            }
                }

    cnt++;
    }
}


}
}
return result;

}

的Structs:

typedef struct _FILE_BOTH_DIR_INFORMATION {
ULONG         NextEntryOffset;
ULONG         FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG         FileAttributes;
ULONG         FileNameLength;
ULONG         EaSize;
CCHAR         ShortNameLength;
WCHAR         ShortName[12];
WCHAR         FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;

typedef struct _FILE_ID_BOTH_DIR_INFORMATION {
 ULONG         NextEntryOffset;
 ULONG         FileIndex;
 LARGE_INTEGER CreationTime;
 LARGE_INTEGER LastAccessTime;
 LARGE_INTEGER LastWriteTime;
 LARGE_INTEGER ChangeTime;
 LARGE_INTEGER EndOfFile;
 LARGE_INTEGER AllocationSize;
 ULONG         FileAttributes;
  ULONG         FileNameLength;
  ULONG         EaSize;
 CCHAR         ShortNameLength;
 WCHAR         ShortName[12];
 CHAR           Reserver[2];
 LARGE_INTEGER FileId;
WCHAR         FileName[1];
} FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION;

 typedef enum _OBJECT_INFORMATION_CLASS {
 ObjectBasicInformation, ObjectNameInformation, ObjectTypeInformation, ObjectAllInformation, ObjectDataInformation
  } OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;

 typedef struct _OBJECT_NAME_INFORMATION {

 UNICODE_STRING Name;
 WCHAR NameBuffer[1];

 } OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

TrueZwQueryDirectoryFile必须调用原始的ZwQueryDirectoryFile(如果挂钩它,则调用NtQueryDirectoryFile)。 NtQueryOvbject以内核格式从“FileHandle”返回文件夹的名称。所以,如果你想隐藏例如“C:\ Windows”,请用当前格式的GetLogicalDriveStrings和QueryDosDevice进行转换。