远程控制开发基础
3、客户端硬盘文件信息获取
1、课程目标
- 掌握Windows文件系统枚举技术
- 实现目录和文件列表获取功能
- 获取文件详细信息(大小、时间、属性等)
- 理解文件系统权限和访问控制
2、名词解释
| 术语 | 全称 | 解释 |
|---|---|---|
| File System | 文件系统 | 管理文件和目录的系统 |
| Directory | 目录 | 文件夹,包含文件和其他目录 |
| Attributes | 属性 | 文件的元数据(只读、隐藏等) |
| FindFirstFile | - | Windows文件查找API |
3、技术原理
1. 文件枚举方法
方法1: FindFirstFile/FindNextFile
- 最常用的文件枚举方法
- 支持通配符匹配
- 返回WIN32_FIND_DATA结构
方法2: SHBrowseForFolder
- 显示文件夹选择对话框
- 用户交互式选择
方法3: GetLogicalDrives
- 获取系统逻辑驱动器列表
方法4: DeviceIoControl
- 底层磁盘操作
- 需要管理员权限
2. 文件信息结构
// WIN32_FIND_DATA 结构
typedef struct _WIN32_FIND_DATA {
DWORD dwFileAttributes; // 文件属性
FILETIME ftCreationTime; // 创建时间
FILETIME ftLastAccessTime; // 最后访问时间
FILETIME ftLastWriteTime; // 最后写入时间
DWORD nFileSizeHigh; // 文件大小高位
DWORD nFileSizeLow; // 文件大小低位
DWORD dwReserved0; // 保留
DWORD dwReserved1; // 保留
TCHAR cFileName[MAX_PATH]; // 文件名
TCHAR cAlternateFileName[14]; // 短文件名
} WIN32_FIND_DATA;
3、代码实现
1. 基础文件枚举
// file_enum.cpp
// 文件系统信息获取
#include <windows.h>
#include <stdio.h>
#include <vector>
#include <string>
#include <algorithm>
// 文件信息结构
typedef struct _FILE_INFO {
std::string name;
std::string fullPath;
ULONGLONG size;
FILETIME createTime;
FILETIME accessTime;
FILETIME writeTime;
DWORD attributes;
bool isDirectory;
} FILE_INFO, *PFILE_INFO;
// 文件属性转换
std::string AttributesToString(DWORD attrs) {
std::string result;
if (attrs & FILE_ATTRIBUTE_DIRECTORY) result += "D";
if (attrs & FILE_ATTRIBUTE_READONLY) result += "R";
if (attrs & FILE_ATTRIBUTE_HIDDEN) result += "H";
if (attrs & FILE_ATTRIBUTE_SYSTEM) result += "S";
if (attrs & FILE_ATTRIBUTE_ARCHIVE) result += "A";
if (attrs & FILE_ATTRIBUTE_COMPRESSED) result += "C";
if (attrs & FILE_ATTRIBUTE_ENCRYPTED) result += "E";
return result.empty() ? "-" : result;
}
// 文件时间转换
std::string FileTimeToString(const FILETIME& ft) {
if (ft.dwHighDateTime == 0 && ft.dwLowDateTime == 0) {
return "-";
}
FILETIME localTime;
SYSTEMTIME st;
FileTimeToLocalFileTime(&ft, &localTime);
FileTimeToSystemTime(&localTime, &st);
char buffer[64];
sprintf_s(buffer, "%04d-%02d-%02d %02d:%02d:%02d",
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
return std::string(buffer);
}
// 文件大小格式化
std::string FormatFileSize(ULONGLONG size) {
const char* units[] = {"B", "KB", "MB", "GB", "TB"};
int unitIndex = 0;
double dSize = (double)size;
while (dSize >= 1024.0 && unitIndex < 4) {
dSize /= 1024.0;
unitIndex++;
}
char buffer[32];
if (unitIndex == 0) {
sprintf_s(buffer, "%llu %s", size, units[unitIndex]);
} else {
sprintf_s(buffer, "%.1f %s", dSize, units[unitIndex]);
}
return std::string(buffer);
}
// 枚举目录中的文件
std::vector<FILE_INFO> EnumFiles(const std::string& path) {
std::vector<FILE_INFO> files;
std::string searchPath = path;
if (searchPath.back() != '\\' && searchPath.back() != '/') {
searchPath += "\\";
}
searchPath += "*";
WIN32_FIND_DATAA findData;
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind == INVALID_HANDLE_VALUE) {
return files;
}
do {
// 跳过当前目录和父目录
if (strcmp(findData.cFileName, ".") == 0 ||
strcmp(findData.cFileName, "..") == 0) {
continue;
}
FILE_INFO info;
info.name = findData.cFileName;
info.fullPath = path + "\\" + findData.cFileName;
info.attributes = findData.dwFileAttributes;
info.isDirectory = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.createTime = findData.ftCreationTime;
info.accessTime = findData.ftLastAccessTime;
info.writeTime = findData.ftLastWriteTime;
// 文件大小
info.size = ((ULONGLONG)findData.nFileSizeHigh << 32) | findData.nFileSizeLow;
files.push_back(info);
} while (FindNextFileA(hFind, &findData));
FindClose(hFind);
// 按名称排序
std::sort(files.begin(), files.end(),
[](const FILE_INFO& a, const FILE_INFO& b) {
if (a.isDirectory != b.isDirectory) {
return a.isDirectory > b.isDirectory; // 目录排在前面
}
return _stricmp(a.name.c_str(), b.name.c_str()) < 0;
});
return files;
}
// 获取驱动器列表
std::vector<std::string> GetDriveList() {
std::vector<std::string> drives;
DWORD driveMask = GetLogicalDrives();
char driveLetter = 'A';
while (driveMask) {
if (driveMask & 1) {
std::string drivePath = std::string(1, driveLetter) + ":\\";
drives.push_back(drivePath);
}
driveMask >>= 1;
driveLetter++;
}
return drives;
}
// 获取驱动器信息
typedef struct _DRIVE_INFO {
std::string path;
ULONGLONG totalSize;
ULONGLONG freeSpace;
std::string type;
std::string label;
} DRIVE_INFO, *PDRIVE_INFO;
DRIVE_INFO GetDriveInfo(const std::string& drivePath) {
DRIVE_INFO info;
info.path = drivePath;
// 获取卷信息
char volumeName[MAX_PATH];
char fileSystemName[MAX_PATH];
DWORD serialNumber, maxComponentLength, fileSystemFlags;
if (GetVolumeInformationA(
drivePath.c_str(),
volumeName, MAX_PATH,
&serialNumber,
&maxComponentLength,
&fileSystemFlags,
fileSystemName, MAX_PATH)) {
info.label = volumeName;
}
// 获取空间信息
ULARGE_INTEGER totalSize, freeSpace, callerFreeSpace;
if (GetDiskFreeSpaceExA(
drivePath.c_str(),
&callerFreeSpace,
&totalSize,
&freeSpace)) {
info.totalSize = totalSize.QuadPart;
info.freeSpace = freeSpace.QuadPart;
}
// 获取驱动器类型
UINT driveType = GetDriveTypeA(drivePath.c_str());
switch (driveType) {
case DRIVE_FIXED: info.type = "Fixed"; break;
case DRIVE_REMOVABLE: info.type = "Removable"; break;
case DRIVE_CDROM: info.type = "CD-ROM"; break;
case DRIVE_REMOTE: info.type = "Network"; break;
case DRIVE_RAMDISK: info.type = "RAM Disk"; break;
default: info.type = "Unknown";
}
return info;
}
// 格式化输出文件列表
void PrintFileList(const std::vector<FILE_INFO>& files) {
printf("\n%-15s %-10s %-8s %-20s %s\n",
"Attribute", "Size", "Modified", "Name", "Full Path");
printf("------------------------------------------------------------------------\n");
for (const auto& file : files) {
printf("%-15s %-10s %-8s %-20s %s\n",
AttributesToString(file.attributes).c_str(),
file.isDirectory ? "<DIR>" : FormatFileSize(file.size).c_str(),
FileTimeToString(file.writeTime).c_str(),
file.name.c_str(),
file.fullPath.c_str());
}
printf("\nTotal: %zu items\n", files.size());
}
// 格式化输出驱动器列表
void PrintDriveList(const std::vector<DRIVE_INFO>& drives) {
printf("\n%-10s %-15s %-15s %-15s %s\n",
"Drive", "Type", "Total Size", "Free Space", "Label");
printf("----------------------------------------------------------------\n");
for (const auto& drive : drives) {
printf("%-10s %-15s %-15s %-15s %s\n",
drive.path.c_str(),
drive.type.c_str(),
FormatFileSize(drive.totalSize).c_str(),
FormatFileSize(drive.freeSpace).c_str(),
drive.label.c_str());
}
}
// 搜索文件
std::vector<FILE_INFO> SearchFiles(const std::string& basePath,
const std::string& pattern,
bool recursive = false) {
std::vector<FILE_INFO> results;
std::string searchPath = basePath;
if (searchPath.back() != '\\' && searchPath.back() != '/') {
searchPath += "\\";
}
searchPath += pattern;
WIN32_FIND_DATAA findData;
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (strcmp(findData.cFileName, ".") == 0 ||
strcmp(findData.cFileName, "..") == 0) {
continue;
}
FILE_INFO info;
info.name = findData.cFileName;
info.fullPath = basePath + "\\" + findData.cFileName;
info.attributes = findData.dwFileAttributes;
info.isDirectory = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.size = ((ULONGLONG)findData.nFileSizeHigh << 32) | findData.nFileSizeLow;
info.createTime = findData.ftCreationTime;
info.accessTime = findData.ftLastAccessTime;
info.writeTime = findData.ftLastWriteTime;
results.push_back(info);
} while (FindNextFileA(hFind, &findData));
FindClose(hFind);
}
// 递归搜索子目录
if (recursive) {
auto dirs = EnumFiles(basePath);
for (const auto& dir : dirs) {
if (dir.isDirectory && dir.name != "." && dir.name != "..") {
auto subResults = SearchFiles(dir.fullPath, pattern, true);
results.insert(results.end(), subResults.begin(), subResults.end());
}
}
}
return results;
}
// 远程控制集成
std::string GetFileListAsString(const std::string& path) {
auto files = EnumFiles(path);
std::string result;
char buffer[1024];
sprintf_s(buffer, "%-15s %-10s %-20s %s\n",
"Attr", "Size", "Modified", "Name");
result += buffer;
sprintf_s(buffer, "------------------------------------------------------------\n");
result += buffer;
for (const auto& file : files) {
sprintf_s(buffer, "%-15s %-10s %-20s %s\n",
AttributesToString(file.attributes).c_str(),
file.isDirectory ? "<DIR>" : FormatFileSize(file.size).c_str(),
FileTimeToString(file.writeTime).c_str(),
file.name.c_str());
result += buffer;
}
sprintf_s(buffer, "\nTotal: %zu items\n", files.size());
result += buffer;
return result;
}
// 测试函数
void TestFileEnumeration() {
printf("=== File System Enumeration Test ===\n\n");
// 获取驱动器列表
printf("[*] Drive List:\n");
auto drives = GetDriveList();
std::vector<DRIVE_INFO> driveInfos;
for (const auto& drive : drives) {
auto info = GetDriveInfo(drive);
driveInfos.push_back(info);
printf(" %s (%s) - %s free of %s\n",
info.path.c_str(),
info.type.c_str(),
FormatFileSize(info.freeSpace).c_str(),
FormatFileSize(info.totalSize).c_str());
}
// 枚举C盘根目录
printf("\n[*] C:\\ Root Directory:\n");
auto files = EnumFiles("C:\\");
// 显示前20个项目
size_t showCount = min(20, files.size());
for (size_t i = 0; i < showCount; i++) {
const auto& file = files[i];
printf(" %s %s %s\n",
AttributesToString(file.attributes).c_str(),
file.isDirectory ? "<DIR>" : FormatFileSize(file.size).c_str(),
file.name.c_str());
}
if (files.size() > showCount) {
printf(" ... and %zu more items\n", files.size() - showCount);
}
// 搜索特定文件
printf("\n[*] Searching for *.exe files in C:\\Windows:\n");
auto exeFiles = SearchFiles("C:\\Windows", "*.exe");
printf(" Found %zu .exe files\n", exeFiles.size());
// 显示前5个
size_t exeShowCount = min(5, exeFiles.size());
for (size_t i = 0; i < exeShowCount; i++) {
printf(" %s (%s)\n",
exeFiles[i].name.c_str(),
FormatFileSize(exeFiles[i].size).c_str());
}
}
int main() {
printf("========================================\n");
printf(" File System Information Enumerator \n");
printf("========================================\n");
TestFileEnumeration();
// 演示字符串输出
printf("\n=== Formatted Output ===\n");
std::string formatted = GetFileListAsString("C:\\");
printf("%s", formatted.c_str());
return 0;
}
2. 高级文件操作
// advanced_file.cpp
// 高级文件系统操作
#include <windows.h>
#include <stdio.h>
#include <string>
// 文件详细信息
typedef struct _DETAILED_FILE_INFO {
std::string name;
std::string fullPath;
ULONGLONG size;
FILETIME createTime;
FILETIME accessTime;
FILETIME writeTime;
DWORD attributes;
bool isDirectory;
// 高级信息
std::string owner;
std::string permissions;
ULONGLONG allocatedSize;
DWORD sectorSize;
std::string compression;
bool isEncrypted;
bool isCompressed;
} DETAILED_FILE_INFO;
// 获取文件所有者
std::string GetFileOwner(const std::string& filePath) {
HANDLE hFile = CreateFileA(
filePath.c_str(),
READ_CONTROL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return "Unknown";
}
DWORD sidSize = 0, domainSize = 0;
SID_NAME_USE sidType;
// 第一次调用获取所需大小
GetFileSecurityA(
filePath.c_str(),
OWNER_SECURITY_INFORMATION,
NULL,
0,
&sidSize);
PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)malloc(sidSize);
if (!GetFileSecurityA(
filePath.c_str(),
OWNER_SECURITY_INFORMATION,
sd,
sidSize,
&sidSize)) {
free(sd);
CloseHandle(hFile);
return "Unknown";
}
PSID ownerSid;
if (!GetSecurityDescriptorOwner(sd, &ownerSid, NULL)) {
free(sd);
CloseHandle(hFile);
return "Unknown";
}
char domainName[256];
domainSize = sizeof(domainName);
char userName[256];
DWORD userNameSize = sizeof(userName);
LookupAccountSidA(
NULL,
ownerSid,
userName,
&userNameSize,
domainName,
&domainSize,
&sidType);
free(sd);
CloseHandle(hFile);
std::string owner = std::string(domainName) + "\\" + std::string(userName);
return owner;
}
// 获取文件权限
std::string GetFilePermissions(const std::string& filePath) {
// 简化实现,实际需要解析ACL
return "Read/Write";
}
// 获取文件分配大小
ULONGLONG GetFileAllocatedSize(const std::string& filePath) {
HANDLE hFile = CreateFileA(
filePath.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return 0;
}
FILE_STANDARD_INFO fileInfo;
if (GetFileInformationByHandleEx(
hFile,
FileStandardInfo,
&fileInfo,
sizeof(fileInfo))) {
CloseHandle(hFile);
return fileInfo.AllocationSize.QuadPart;
}
CloseHandle(hFile);
return 0;
}
// 检查文件压缩状态
bool IsFileCompressed(const std::string& filePath) {
DWORD attrs = GetFileAttributesA(filePath.c_str());
return (attrs & FILE_ATTRIBUTE_COMPRESSED) != 0;
}
// 检查文件加密状态
bool IsFileEncrypted(const std::string& filePath) {
DWORD attrs = GetFileAttributesA(filePath.c_str());
return (attrs & FILE_ATTRIBUTE_ENCRYPTED) != 0;
}
// 获取卷扇区大小
DWORD GetVolumeSectorSize(const std::string& filePath) {
char rootPath[MAX_PATH];
if (!GetVolumePathNameA(filePath.c_str(), rootPath, MAX_PATH)) {
return 512; // 默认值
}
DWORD sectorsPerCluster, bytesPerSector, numberOfFreeClusters, totalNumberOfClusters;
if (GetDiskFreeSpaceA(
rootPath,
§orsPerCluster,
&bytesPerSector,
&numberOfFreeClusters,
&totalNumberOfClusters)) {
return bytesPerSector;
}
return 512;
}
// 获取详细文件信息
DETAILED_FILE_INFO GetDetailedFileInfo(const std::string& filePath) {
DETAILED_FILE_INFO info = {0};
WIN32_FIND_DATAA findData;
HANDLE hFind = FindFirstFileA(filePath.c_str(), &findData);
if (hFind != INVALID_HANDLE_VALUE) {
info.name = findData.cFileName;
info.fullPath = filePath;
info.attributes = findData.dwFileAttributes;
info.isDirectory = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
info.createTime = findData.ftCreationTime;
info.accessTime = findData.ftLastAccessTime;
info.writeTime = findData.ftLastWriteTime;
info.size = ((ULONGLONG)findData.nFileSizeHigh << 32) | findData.nFileSizeLow;
FindClose(hFind);
}
// 高级信息
info.owner = GetFileOwner(filePath);
info.permissions = GetFilePermissions(filePath);
info.allocatedSize = GetFileAllocatedSize(filePath);
info.sectorSize = GetVolumeSectorSize(filePath);
info.isCompressed = IsFileCompressed(filePath);
info.isEncrypted = IsFileEncrypted(filePath);
if (info.isCompressed) info.compression = "Compressed";
if (info.isEncrypted) info.compression = "Encrypted";
if (!info.isCompressed && !info.isEncrypted) info.compression = "None";
return info;
}
// 打印详细文件信息
void PrintDetailedFileInfo(const DETAILED_FILE_INFO& info) {
printf("\n=== Detailed File Information ===\n");
printf("Name: %s\n", info.name.c_str());
printf("Path: %s\n", info.fullPath.c_str());
printf("Size: %s (%llu bytes)\n",
FormatFileSize(info.size).c_str(), info.size);
printf("Allocated: %s\n", FormatFileSize(info.allocatedSize).c_str());
printf("Attributes: %s\n", AttributesToString(info.attributes).c_str());
printf("Created: %s\n", FileTimeToString(info.createTime).c_str());
printf("Modified: %s\n", FileTimeToString(info.writeTime).c_str());
printf("Accessed: %s\n", FileTimeToString(info.accessTime).c_str());
printf("Owner: %s\n", info.owner.c_str());
printf("Permissions: %s\n", info.permissions.c_str());
printf("Sector Size: %lu\n", info.sectorSize);
printf("Compression: %s\n", info.compression.c_str());
}
// 文件系统统计
typedef struct _FILESYSTEM_STATS {
ULONGLONG totalFiles;
ULONGLONG totalDirectories;
ULONGLONG totalSize;
ULONGLONG hiddenFiles;
ULONGLONG systemFiles;
} FILESYSTEM_STATS, *PFILESYSTEM_STATS;
FILESYSTEM_STATS GetFileSystemStats(const std::string& path) {
FILESYSTEM_STATS stats = {0};
std::string searchPath = path + "\\*";
WIN32_FIND_DATAA findData;
HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (strcmp(findData.cFileName, ".") == 0 ||
strcmp(findData.cFileName, "..") == 0) {
continue;
}
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
stats.totalDirectories++;
// 递归统计子目录
std::string subPath = path + "\\" + findData.cFileName;
auto subStats = GetFileSystemStats(subPath);
stats.totalFiles += subStats.totalFiles;
stats.totalDirectories += subStats.totalDirectories;
stats.totalSize += subStats.totalSize;
stats.hiddenFiles += subStats.hiddenFiles;
stats.systemFiles += subStats.systemFiles;
} else {
stats.totalFiles++;
stats.totalSize += ((ULONGLONG)findData.nFileSizeHigh << 32) | findData.nFileSizeLow;
if (findData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
stats.hiddenFiles++;
}
if (findData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
stats.systemFiles++;
}
}
} while (FindNextFileA(hFind, &findData));
FindClose(hFind);
}
return stats;
}
void PrintFileSystemStats(const FILESYSTEM_STATS& stats) {
printf("\n=== File System Statistics ===\n");
printf("Files: %llu\n", stats.totalFiles);
printf("Directories: %llu\n", stats.totalDirectories);
printf("Total Size: %s\n", FormatFileSize(stats.totalSize).c_str());
printf("Hidden Files: %llu\n", stats.hiddenFiles);
printf("System Files: %llu\n", stats.systemFiles);
}
int main() {
printf("========================================\n");
printf(" Advanced File System Operations \n");
printf("========================================\n");
// 获取详细文件信息
DETAILED_FILE_INFO info = GetDetailedFileInfo("C:\\Windows\\notepad.exe");
PrintDetailedFileInfo(info);
// 获取目录统计信息
FILESYSTEM_STATS stats = GetFileSystemStats("C:\\Windows");
PrintFileSystemStats(stats);
return 0;
}
3、课后作业
3.1、作业1:实现文件内容搜索
添加在文件内容中搜索指定文本的功能。
3.2、作业2:添加文件监控功能
实现实时监控目录变化(文件创建、删除、修改)。
3.3、作业3:实现文件属性修改
添加修改文件属性(只读、隐藏等)的功能。