远程控制开发基础

3、客户端硬盘文件信息获取

1、课程目标

  1. 掌握Windows文件系统枚举技术
  2. 实现目录和文件列表获取功能
  3. 获取文件详细信息(大小、时间、属性等)
  4. 理解文件系统权限和访问控制

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,
        &sectorsPerCluster,
        &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:实现文件属性修改

添加修改文件属性(只读、隐藏等)的功能。