2011年8月2日星期二

C++中使用NtQueryDirectoryFile枚舉文件和文件夾

作者:腾袭
為了方便使用,我將其封裝成NtQuery類,這樣會更方便使用的!
支持UNICODE和非UNICODE的~

首先是頭文件NtQuery.h:

//NtQuery.h By 騰襲 2010-7-25
 
#ifndef _NT_QUERY_H_INCLUDE_
#define _NT_QUERY_H_INCLUDE_ 1
 
#pragma once
 
typedef struct _LSA_STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} LSA_STRING, *PLSA_STRING, ANSI_STRING, *PANSI_STRING;
 
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
 
typedef struct _IO_STATUS_BLOCK {
NTSTATUS Status;
ULONG Information;
}IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
 
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
 
typedef struct _FILE_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;
WCHAR FileName[1];
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
 
//FileFullDirectoryInformation:
typedef struct _FILE_FULL_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;
WCHAR FileName[1];
} FILE_FULL_DIRECTORY_INFORMATION, *PFILE_FULL_DIRECTORY_INFORMATION;
 
//FileBothDirectoryInformation:
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_DIRECTORY_INFORMATION, *PFILE_BOTH_DIRECTORY_INFORMATION;
 
//FileNamesInformation:
typedef struct _FILE_NAMES_INFORMATION {
ULONG NextEntryOffset;
ULONG Unknown;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;
 
typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation = 1,
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4 wdm
FileStandardInformation, // 5 wdm
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14 wdm
FileFullEaInformation, // 15
FileModeInformation, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20 wdm
FileAlternateNameInformation, // 21
FileStreamInformation, // 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilePipeRemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotSetInformation, // 27
FileCompressionInformation, // 28
FileObjectIdInformation, // 29
FileCompletionInformation, // 30
FileMoveClusterInformation, // 31
FileQuotaInformation, // 32
FileReparsePointInformation, // 33
FileNetworkOpenInformation, // 34
FileAttributeTagInformation, // 35
FileTrackingInformation, // 36
FileIdBothDirectoryInformation, // 37
FileIdFullDirectoryInformation, // 38
FileValidDataLengthInformation, // 39
FileShortNameInformation, // 40
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
 
#ifndef SYNCHRONIZE
#define SYNCHRONIZE 0x100000
#endif
 
#ifndef FILE_ANY_ACCESS
#define FILE_ANY_ACCESS 0
#endif
 
#ifndef FILE_LIST_DIRECTORY
#define FILE_LIST_DIRECTORY 1
#endif
 
#ifndef FILE_DIRECTORY_FILE
#define FILE_DIRECTORY_FILE 1
#endif
 
#ifndef FILE_SYNCHRONOUS_IO_NONALERT
#define FILE_SYNCHRONOUS_IO_NONALERT 0x20
#endif
 
#ifndef FILE_OPEN_FOR_BACKUP_INTENT
#define FILE_OPEN_FOR_BACKUP_INTENT 0x4000
#endif
 
#ifndef OBJ_CASE_INSENSITIVE
#define OBJ_CASE_INSENSITIVE 0x40
#endif
 
typedef NTSTATUS (NTAPI *FN_NtQueryDirectoryFile)( IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PVOID ApcRoutine OPTIONAL, /*PIO_APC_ROUTINE*/
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG FileInformationLength,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileName OPTIONAL,
IN BOOLEAN RestartScan );
 
typedef NTSTATUS (NTAPI *FN_NtOpenFile)(
__out PHANDLE FileHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__out PIO_STATUS_BLOCK IoStatusBlock,
__in ULONG ShareAccess,
__in ULONG OpenOptions
);
 
typedef NTSTATUS (NTAPI *FN_NtClose)(
__in HANDLE Handle
);
 
typedef void (NTAPI *FN_RtlInitUnicodeString)(
__inout PUNICODE_STRING DestinationString,
__in_opt PCWSTR SourceString
);
 
typedef void (NTAPI *FN_RtlInitAnsiString)(
__inout PANSI_STRING DestinationString,
__inout PCSTR SourceString
);
 
typedef NTSTATUS (NTAPI *FN_RtlAnsiStringToUnicodeString)(
PUNICODE_STRING DestinationString,
PANSI_STRING SourceString,
BOOLEAN AllocateDestinationString
);
 
typedef NTSTATUS (NTAPI *FN_RtlUnicodeStringToAnsiString)(
PANSI_STRING DestinationString,
PUNICODE_STRING SourceString,
BOOLEAN AllocateDestinationString
);
 
typedef void (NTAPI *FN_RtlFreeUnicodeString)(
PUNICODE_STRING UnicodeString
);
 
typedef void (NTAPI *FN_RtlFreeAnsiString)(
PANSI_STRING AnsiString
);
 
class CNtQuery
{
public:
CNtQuery(void);
virtual ~CNtQuery(void);
static bool CNtQuery::GetAddr();
static bool m_bInit;
 
HANDLE FindFirstFile(LPCTSTR strDirectory,PBYTE bytBuffer,ULONG size);
bool FindNextFile(HANDLE hFind,PBYTE bytBuffer,ULONG size);
void Enum(LPCTSTR lpszPath);
 
NTSTATUS CNtQuery::ANSI2UNICODE(LPCSTR lpBuf, PUNICODE_STRING pUnicodeString, BOOLEAN AllocateDestinationString = TRUE);
NTSTATUS CNtQuery::UNICODE2ANSI(LPCWSTR lpBuf, PANSI_STRING pAnsiString, BOOLEAN AllocateDestinationString = TRUE);
private:
static HMODULE m_hNtdll;
protected:
static FN_NtQueryDirectoryFile fn_NtQueryDirectoryFile;
static FN_NtOpenFile fn_NtOpenFile;
static FN_NtClose fn_NtClose;
static FN_RtlInitUnicodeString fn_RtlInitUnicodeString;
static FN_RtlInitAnsiString fn_RtlInitAnsiString;
static FN_RtlAnsiStringToUnicodeString fn_RtlAnsiStringToUnicodeString;
static FN_RtlUnicodeStringToAnsiString fn_RtlUnicodeStringToAnsiString;
static FN_RtlFreeUnicodeString fn_RtlFreeUnicodeString;
static FN_RtlFreeAnsiString fn_RtlFreeAnsiString;
};
 
#endif

然後是NtQuery.cpp:


//NtQuery.cpp By 騰襲 2010-7-25
 
#include "StdAfx.h"
#include "NtQuery.h"
 
HMODULE CNtQuery::m_hNtdll = NULL;
FN_NtQueryDirectoryFile CNtQuery::fn_NtQueryDirectoryFile;
FN_NtOpenFile CNtQuery::fn_NtOpenFile;
FN_NtClose CNtQuery::fn_NtClose;
FN_RtlInitUnicodeString CNtQuery::fn_RtlInitUnicodeString;
FN_RtlInitAnsiString CNtQuery::fn_RtlInitAnsiString;
FN_RtlAnsiStringToUnicodeString CNtQuery::fn_RtlAnsiStringToUnicodeString;
FN_RtlUnicodeStringToAnsiString CNtQuery::fn_RtlUnicodeStringToAnsiString;
FN_RtlFreeUnicodeString CNtQuery::fn_RtlFreeUnicodeString;
FN_RtlFreeAnsiString CNtQuery::fn_RtlFreeAnsiString;
 
bool CNtQuery::m_bInit = false;
 
bool CNtQuery::GetAddr()
{
m_hNtdll = GetModuleHandle(_T("ntdll.dll"));
if (m_hNtdll == NULL)
return false;
 
fn_NtQueryDirectoryFile = (FN_NtQueryDirectoryFile)GetProcAddress(m_hNtdll,"NtQueryDirectoryFile");
fn_NtOpenFile = (FN_NtOpenFile)GetProcAddress(m_hNtdll,"NtOpenFile");
fn_NtClose = (FN_NtClose)GetProcAddress(m_hNtdll,"NtClose");
fn_RtlInitUnicodeString = (FN_RtlInitUnicodeString)GetProcAddress(m_hNtdll,"RtlInitUnicodeString");
fn_RtlInitAnsiString = (FN_RtlInitAnsiString)GetProcAddress(m_hNtdll,"RtlInitAnsiString");
fn_RtlAnsiStringToUnicodeString = (FN_RtlAnsiStringToUnicodeString)GetProcAddress(m_hNtdll,"RtlAnsiStringToUnicodeString");
fn_RtlUnicodeStringToAnsiString = (FN_RtlUnicodeStringToAnsiString)GetProcAddress(m_hNtdll,"RtlUnicodeStringToAnsiString");
fn_RtlFreeUnicodeString = (FN_RtlFreeUnicodeString)GetProcAddress(m_hNtdll,"RtlFreeUnicodeString");
fn_RtlFreeAnsiString = (FN_RtlFreeAnsiString)GetProcAddress(m_hNtdll,"RtlFreeAnsiString");
 
if (!(fn_NtQueryDirectoryFile && fn_NtOpenFile && fn_NtClose && fn_RtlInitUnicodeString))
{
return false;
}
return true;
}
 
CNtQuery::CNtQuery(void)
{
if (!m_bInit)
m_bInit = GetAddr();
}
 
CNtQuery::~CNtQuery(void)
{
}
 
HANDLE CNtQuery::FindFirstFile(LPCTSTR strDirectory,PBYTE bytBuffer,ULONG size)
{
if (!m_bInit)
return INVALID_HANDLE_VALUE;
 
TCHAR strFolder[MAX_PATH];
OBJECT_ATTRIBUTES obAttr;
IO_STATUS_BLOCK objIoStatus;
NTSTATUS ntStatus;
HANDLE hFind = NULL;
UNICODE_STRING strUnicode;
 
wsprintf(strFolder,_T("\\??\\%s"),strDirectory);
 
//'初始化Unicode字符串
#ifdef UNICODE
fn_RtlInitUnicodeString(&strUnicode, strFolder);
#else
ANSI2UNICODE(strFolder,&strUnicode);
#endif
 
memset(&objIoStatus,0,sizeof(IO_STATUS_BLOCK));
memset(&obAttr,0,sizeof(OBJECT_ATTRIBUTES));
 
obAttr.Length = sizeof(OBJECT_ATTRIBUTES); //'初始化OBJECT_ATTRIBUTES結構
obAttr.Attributes = OBJ_CASE_INSENSITIVE;
obAttr.ObjectName = &strUnicode; //'需要打開的文件夾路徑
obAttr.RootDirectory = NULL;
obAttr.SecurityDescriptor = NULL;
obAttr.SecurityQualityOfService = NULL;
//'獲取文件夾句柄
ntStatus = fn_NtOpenFile(&hFind,
FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_ANY_ACCESS,
&obAttr,
&objIoStatus,
3,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
#ifndef UNICODE
//釋放內存
fn_RtlFreeUnicodeString(&strUnicode);
#endif
if (ntStatus == 0 && hFind != INVALID_HANDLE_VALUE)
{
 
//'獲取文件夾文件/目錄信息,其實這個函數是Kernel32裡的FindFirstFile的封裝
ntStatus = fn_NtQueryDirectoryFile(hFind,
NULL,
NULL,
NULL,
&objIoStatus,
bytBuffer,
size,
FileBothDirectoryInformation,
TRUE,
NULL,
NULL);
if (ntStatus == 0) //'Nt系列函數一般返回大於零表示成功,而NtQueryDirectoryFile返回零表示成功
{
//_tprintf(_T("Succeed!\n"));
return hFind;
}else{
fn_NtClose(hFind);
}
}
return INVALID_HANDLE_VALUE;
}
 
bool CNtQuery::FindNextFile(HANDLE hFind,PBYTE bytBuffer,ULONG size)
{
if (!m_bInit)
return false;
NTSTATUS ntStatus;
IO_STATUS_BLOCK objIoStatus;
//'這是Kernel32 FindNextFile的封裝,只是是一次把所有文件/目錄都取出來了
ntStatus = fn_NtQueryDirectoryFile(hFind,
NULL,
NULL,
NULL,
&objIoStatus,
bytBuffer,
size,
FileBothDirectoryInformation,
FALSE,
NULL,
NULL);
//_tprintf(_T("ntStatus\n"));
return (ntStatus == 0);
}
 
void CNtQuery::Enum(LPCTSTR lpszPath)
{
if (!m_bInit)
return;
#ifndef UNICODE
ANSI_STRING asfn,asan;
#endif
 
PFILE_BOTH_DIRECTORY_INFORMATION pDir;
HANDLE hFind;
LPBYTE bytBuffer;
wchar_t fileName[_MAX_FNAME];
wchar_t AlternateName[13];
TCHAR strPath[MAX_PATH];
TCHAR strFileName[MAX_PATH];
 
ULONG dwDirOffset;
ULONG iCount = 0;
ULONG usize = sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + MAX_PATH * 2 - 3;
bytBuffer = new BYTE[usize];
//memset(bytBuffer,0,usize);
 
lstrcpy(strPath,lpszPath);
 
hFind = FindFirstFile(strPath,bytBuffer,usize); //'獲取第一個文件/目錄對象
if (hFind == INVALID_HANDLE_VALUE)
return ;
pDir = (PFILE_BOTH_DIRECTORY_INFORMATION)bytBuffer;
//獲取FILE_BOTH_DIRECTORY_INFORMATION結構,目的是獲取FileNameLength和NextEntryOffset數據
 
memcpy(fileName, pDir->FileName, pDir->FileNameLength);
memcpy(AlternateName,pDir->AlternateName,pDir->AlternateNameLength); //8.3短文件名
fileName[pDir->FileNameLength / sizeof(wchar_t)] = 0;
AlternateName[pDir->AlternateNameLength / sizeof(wchar_t)] = 0; //這裡的長度都是字節數
 
#ifndef UNICODE
UNICODE2ANSI(fileName, &asfn);
UNICODE2ANSI(AlternateName, &asan);
wsprintf(strFileName,_T("%s%s"),strPath, asfn.Buffer);
_tprintf(_T("%02d %8s\t%s\n"),pDir->AlternateNameLength, asan.Buffer, strFileName);
fn_RtlFreeAnsiString(&asfn);
fn_RtlFreeAnsiString(&asan);
#else
wsprintf(strFileName,_T("%s%s"),strPath,fileName);
_tprintf(_T("%02d %8s\t%s\n"),pDir->AlternateNameLength, AlternateName, strFileName);
#endif
 
delete[] bytBuffer;
 
usize = (sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + (260 * 2 - 3)) * 0x2000;
bytBuffer = new BYTE[usize];
 
iCount = 1;
while (FindNextFile(hFind,bytBuffer,usize)) //雖然分配了許多空間了,但是仍可能不能把全部文件列舉出來,因此,這裡需要進一步測試
{
dwDirOffset = 0;
//遍歷緩存
while(true)
{
//移動指針
pDir = (PFILE_BOTH_DIRECTORY_INFORMATION)(bytBuffer + dwDirOffset);
//得到FILE_BOTH_DIRECTORY_INFORMATION結構
 
memcpy(fileName, pDir->FileName, pDir->FileNameLength); //文件名
memcpy(AlternateName,pDir->AlternateName,pDir->AlternateNameLength); //8.3短文件名
AlternateName[pDir->AlternateNameLength / sizeof(wchar_t)] = 0; //這裡的長度都是字節數
fileName[pDir->FileNameLength / sizeof(wchar_t)] = 0;
 
#ifndef UNICODE
UNICODE2ANSI(fileName, &asfn);
UNICODE2ANSI(AlternateName, &asan);
wsprintf(strFileName,_T("%s%s"),strPath, asfn.Buffer);
_tprintf(_T("%02d %8s\t%s\n"),pDir->AlternateNameLength, asan.Buffer, strFileName);
fn_RtlFreeAnsiString(&asfn);
fn_RtlFreeAnsiString(&asan);
#else
wsprintf(strFileName,_T("%s%s"),strPath,fileName);
_tprintf(_T("%02d %8s\t%s\n"),pDir->AlternateNameLength, AlternateName, strFileName);
#endif
//這裡如果是目錄可以遞歸遍歷目錄下所有文件/目錄,可以自己封裝一個遞歸函數吧
 
iCount++;
if (pDir->NextEntryOffset == 0)
break;
dwDirOffset += pDir->NextEntryOffset; //這裡指向下一個FILE_BOTH_DIRECTORY_INFORMATION結構在內存中的位置
}
}
 
delete[] bytBuffer;
fn_NtClose(hFind);
_tprintf(_T("\nfilecount: %d\n"),iCount);
}
 
//RtlAnsiStringToUnicodeString()
//RtlUnicodeStringToAnsiString()
//當第3個參數為TRUE時,系統將新分配內存,所以要記得調用RtlFreeUnicodeString()和RtlFreeAnsiString()釋放掉。
 
NTSTATUS CNtQuery::ANSI2UNICODE(LPCSTR lpBuf, PUNICODE_STRING pUnicodeString, BOOLEAN AllocateDestinationString)
{
ANSI_STRING as;
fn_RtlInitAnsiString( &as, lpBuf );
return fn_RtlAnsiStringToUnicodeString( pUnicodeString, &as, AllocateDestinationString );
}
 
NTSTATUS CNtQuery::UNICODE2ANSI(LPCWSTR lpBuf, PANSI_STRING pAnsiString, BOOLEAN AllocateDestinationString)
{
UNICODE_STRING ns;
fn_RtlInitUnicodeString( &ns, lpBuf );
return fn_RtlUnicodeStringToAnsiString( pAnsiString, &ns, AllocateDestinationString );
}

没有评论:

发表评论