安全开发专题

3、资源释放

1、课程目标

  1. 理解Windows资源管理机制
  2. 掌握程序资源的嵌入和释放技术
  3. 实现安全的资源释放功能
  4. 了解资源加密和保护方法

2、名词解释

术语 全称 解释
Resource 资源 程序中嵌入的数据文件
PE Resources PE资源 Windows可执行文件中的资源段
Embedding 嵌入 将数据包含在可执行文件中
Extraction 释放/提取 从程序中取出嵌入的资源

3、技术原理

1. Windows资源管理

PE文件资源结构:
┌─────────────────────────────────────┐
│  PE Header                          │
├─────────────────────────────────────┤
│  Resource Directory                 │
│  ├── RT_ICON                        │
│  ├── RT_BITMAP                      │
│  ├── RT_MENU                        │
│  ├── RT_DIALOG                      │
│  ├── RT_STRING                      │
│  ├── RT_VERSION                     │
│  └── RT_RCDATA (自定义数据)         │
├─────────────────────────────────────┤
│  Resource Data                      │
└─────────────────────────────────────┘

资源访问API:
FindResource()   - 查找资源
LoadResource()   - 加载资源
LockResource()   - 锁定资源内存
SizeofResource() - 获取资源大小

2. 资源释放流程

资源释放步骤:
1. 定位资源 (FindResource)
2. 加载资源 (LoadResource)  
3. 锁定内存 (LockResource)
4. 获取大小 (SizeofResource)
5. 复制数据到目标位置
6. 释放资源句柄

3、代码实现

1. 基础资源操作

// resource_manager.cpp
// 资源管理基础实现

#include <windows.h>
#include <stdio.h>
#include <string>
#include <vector>

// 资源类型定义
#define RT_FILEDATA MAKEINTRESOURCE(100)  // 自定义文件数据类型

// 查找并加载资源
HGLOBAL LoadResourceData(LPCTSTR resourceName, LPCTSTR resourceType) {
    // 查找资源
    HRSRC hRes = FindResourceA(NULL, resourceName, resourceType);
    if (!hRes) {
        printf("[-] Resource not found: %s\n", resourceName);
        return NULL;
    }
    
    // 获取资源大小
    DWORD resSize = SizeofResource(NULL, hRes);
    if (resSize == 0) {
        printf("[-] Failed to get resource size\n");
        return NULL;
    }
    
    // 加载资源
    HGLOBAL hGlobal = LoadResource(NULL, hRes);
    if (!hGlobal) {
        printf("[-] Failed to load resource\n");
        return NULL;
    }
    
    printf("[+] Loaded resource: %s, Size: %lu bytes\n", resourceName, resSize);
    return hGlobal;
}

// 释放资源到文件
bool ExtractResourceToFile(LPCTSTR resourceName, LPCTSTR resourceType, 
                          const char* outputPath) {
    HGLOBAL hGlobal = LoadResourceData(resourceName, resourceType);
    if (!hGlobal) {
        return false;
    }
    
    // 锁定资源内存
    LPVOID pData = LockResource(hGlobal);
    if (!pData) {
        printf("[-] Failed to lock resource\n");
        return false;
    }
    
    DWORD dataSize = SizeofResource(NULL, 
        FindResourceA(NULL, resourceName, resourceType));
    
    // 创建输出文件
    HANDLE hFile = CreateFileA(
        outputPath,
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("[-] Failed to create output file: %s\n", outputPath);
        return false;
    }
    
    // 写入数据
    DWORD written;
    if (!WriteFile(hFile, pData, dataSize, &written, NULL)) {
        printf("[-] Failed to write resource data\n");
        CloseHandle(hFile);
        return false;
    }
    
    CloseHandle(hFile);
    
    // 释放资源
    FreeResource(hGlobal);
    
    printf("[+] Resource extracted to: %s (%lu bytes)\n", outputPath, written);
    return true;
}

// 释放资源到内存
std::vector<BYTE> ExtractResourceToMemory(LPCTSTR resourceName, LPCTSTR resourceType) {
    HGLOBAL hGlobal = LoadResourceData(resourceName, resourceType);
    if (!hGlobal) {
        return std::vector<BYTE>();
    }
    
    LPVOID pData = LockResource(hGlobal);
    DWORD dataSize = SizeofResource(NULL, 
        FindResourceA(NULL, resourceName, resourceType));
    
    std::vector<BYTE> data((BYTE*)pData, (BYTE*)pData + dataSize);
    
    FreeResource(hGlobal);
    
    printf("[+] Resource loaded to memory: %lu bytes\n", dataSize);
    return data;
}

// 枚举所有资源
void EnumerateResources() {
    printf("\n=== Enumerating Resources ===\n");
    
    EnumResourceTypesA(NULL, [](HMODULE hModule, LPSTR lpType, LONG_PTR lParam) -> BOOL {
        printf("Resource Type: %s\n", lpType);
        
        EnumResourceNamesA(hModule, lpType, [](HMODULE hModule, LPCSTR lpType, 
            LPSTR lpName, LONG_PTR lParam) -> BOOL {
            
            HRSRC hRes = FindResourceA(hModule, lpName, lpType);
            DWORD size = SizeofResource(hModule, hRes);
            
            printf("  %-20s: %lu bytes\n", lpName, size);
            return TRUE;
        }, 0);
        
        return TRUE;
    }, 0);
}

2. 自定义资源类型

// custom_resources.cpp
// 自定义资源类型处理

#include <windows.h>
#include <stdio.h>
#include <string>
#include <vector>

// 自定义资源类型
#define RT_ENCRYPTED_FILE MAKEINTRESOURCE(101)
#define RT_CONFIG_DATA    MAKEINTRESOURCE(102)
#define RT_SCRIPT_DATA    MAKEINTRESOURCE(103)

// 资源信息结构
typedef struct _RESOURCE_INFO {
    std::string name;
    std::string type;
    DWORD size;
    bool encrypted;
} RESOURCE_INFO;

// 加密/解密函数
std::vector<BYTE> XorEncrypt(const BYTE* data, DWORD size, BYTE key) {
    std::vector<BYTE> result(size);
    for (DWORD i = 0; i < size; i++) {
        result[i] = data[i] ^ key;
    }
    return result;
}

// 释放加密资源
bool ExtractEncryptedResource(LPCTSTR resourceName, const char* outputPath, BYTE key) {
    HGLOBAL hGlobal = LoadResourceData(resourceName, RT_ENCRYPTED_FILE);
    if (!hGlobal) {
        return false;
    }
    
    LPVOID pData = LockResource(hGlobal);
    DWORD dataSize = SizeofResource(NULL, 
        FindResourceA(NULL, resourceName, RT_ENCRYPTED_FILE));
    
    // 解密数据
    std::vector<BYTE> decrypted = XorEncrypt((BYTE*)pData, dataSize, key);
    
    // 写入文件
    HANDLE hFile = CreateFileA(
        outputPath,
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    
    if (hFile != INVALID_HANDLE_VALUE) {
        DWORD written;
        WriteFile(hFile, decrypted.data(), dataSize, &written, NULL);
        CloseHandle(hFile);
        
        printf("[+] Encrypted resource decrypted and saved: %s\n", outputPath);
        FreeResource(hGlobal);
        return true;
    }
    
    FreeResource(hGlobal);
    return false;
}

// 释放配置资源
std::string ExtractConfigResource(LPCTSTR resourceName) {
    auto data = ExtractResourceToMemory(resourceName, RT_CONFIG_DATA);
    if (data.empty()) {
        return "";
    }
    
    // 假设配置文件是文本格式
    std::string config((char*)data.data(), data.size());
    
    printf("[+] Configuration loaded: %lu characters\n", (DWORD)config.length());
    return config;
}

// 释放脚本资源
bool ExtractScriptResource(LPCTSTR resourceName, const char* outputPath) {
    return ExtractResourceToFile(resourceName, RT_SCRIPT_DATA, outputPath);
}

3. 资源管理器

// resource_extractor.cpp
// 资源提取管理器

#include <windows.h>
#include <stdio.h>
#include <string>
#include <map>
#include <vector>

// 资源管理器类
class ResourceManager {
private:
    std::map<std::string, RESOURCE_INFO> m_resources;
    
public:
    // 加载资源信息
    bool LoadResourceInfo() {
        m_resources.clear();
        
        EnumResourceTypesA(NULL, [](HMODULE hModule, LPSTR lpType, LONG_PTR lParam) -> BOOL {
            ResourceManager* pThis = (ResourceManager*)lParam;
            
            EnumResourceNamesA(hModule, lpType, [](HMODULE hModule, LPCSTR lpType, 
                LPSTR lpName, LONG_PTR lParam) -> BOOL {
                
                ResourceManager* pThis = (ResourceManager*)lParam;
                
                HRSRC hRes = FindResourceA(hModule, lpName, lpType);
                DWORD size = SizeofResource(hModule, hRes);
                
                RESOURCE_INFO info;
                info.name = lpName;
                info.type = lpType;
                info.size = size;
                info.encrypted = (strcmp(lpType, "101") == 0);  // RT_ENCRYPTED_FILE
                
                pThis->m_resources[info.name] = info;
                return TRUE;
            }, (LONG_PTR)pThis);
            
            return TRUE;
        }, (LONG_PTR)this);
        
        printf("[+] Loaded %zu resources\n", m_resources.size());
        return true;
    }
    
    // 列出所有资源
    void ListResources() {
        printf("\n=== Resource List ===\n");
        printf("%-20s %-15s %-10s %s\n", "Name", "Type", "Size", "Encrypted");
        printf("--------------------------------------------------------\n");
        
        for (const auto& pair : m_resources) {
            const RESOURCE_INFO& info = pair.second;
            printf("%-20s %-15s %-10lu %s\n",
                info.name.c_str(),
                info.type.c_str(),
                info.size,
                info.encrypted ? "Yes" : "No");
        }
    }
    
    // 提取指定资源
    bool ExtractResource(const std::string& name, const std::string& outputPath, 
                        BYTE decryptKey = 0) {
        auto it = m_resources.find(name);
        if (it == m_resources.end()) {
            printf("[-] Resource not found: %s\n", name.c_str());
            return false;
        }
        
        const RESOURCE_INFO& info = it->second;
        
        if (info.encrypted && decryptKey != 0) {
            return ExtractEncryptedResource(name.c_str(), outputPath.c_str(), decryptKey);
        } else {
            return ExtractResourceToFile(name.c_str(), info.type.c_str(), outputPath.c_str());
        }
    }
    
    // 获取资源配置
    std::string GetConfig(const std::string& name) {
        return ExtractConfigResource(name.c_str());
    }
    
    // 获取资源到内存
    std::vector<BYTE> GetResourceData(const std::string& name) {
        auto it = m_resources.find(name);
        if (it == m_resources.end()) {
            return std::vector<BYTE>();
        }
        
        const RESOURCE_INFO& info = it->second;
        return ExtractResourceToMemory(name.c_str(), info.type.c_str());
    }
};

// 批量提取资源
bool BatchExtractResources(ResourceManager& manager, 
                          const std::vector<std::pair<std::string, std::string>>& extracts) {
    bool allSuccess = true;
    
    for (const auto& extract : extracts) {
        const std::string& resourceName = extract.first;
        const std::string& outputPath = extract.second;
        
        printf("[*] Extracting %s -> %s\n", resourceName.c_str(), outputPath.c_str());
        
        if (!manager.ExtractResource(resourceName, outputPath)) {
            printf("[-] Failed to extract: %s\n", resourceName.c_str());
            allSuccess = false;
        }
    }
    
    return allSuccess;
}

4. 资源嵌入工具

// resource_embedder.cpp
// 资源嵌入工具

#include <windows.h>
#include <stdio.h>
#include <string>
#include <vector>

// 将文件转换为资源
bool FileToResource(const char* inputPath, const char* outputPath, 
                   const char* resourceName, const char* resourceType) {
    // 读取输入文件
    HANDLE hFile = CreateFileA(
        inputPath,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    
    if (hFile == INVALID_HANDLE_VALUE) {
        printf("[-] Cannot open input file: %s\n", inputPath);
        return false;
    }
    
    DWORD fileSize = GetFileSize(hFile, NULL);
    std::vector<BYTE> fileData(fileSize);
    
    DWORD bytesRead;
    ReadFile(hFile, fileData.data(), fileSize, &bytesRead, NULL);
    CloseHandle(hFile);
    
    if (bytesRead != fileSize) {
        printf("[-] Failed to read complete file\n");
        return false;
    }
    
    // 创建资源文件 (.rc)
    FILE* rcFile = fopen(outputPath, "w");
    if (!rcFile) {
        printf("[-] Cannot create resource file: %s\n", outputPath);
        return false;
    }
    
    fprintf(rcFile, "// Auto-generated resource file\n");
    fprintf(rcFile, "%s %s \"%s\"\n", resourceName, resourceType, inputPath);
    
    fclose(rcFile);
    
    printf("[+] Resource definition created: %s\n", outputPath);
    printf("[+] Resource name: %s, Type: %s, Size: %lu bytes\n", 
           resourceName, resourceType, fileSize);
    
    return true;
}

// 加密文件后嵌入
bool EncryptedFileToResource(const char* inputPath, const char* outputPath,
                            const char* resourceName, BYTE key) {
    // 读取并加密文件
    HANDLE hFile = CreateFileA(
        inputPath,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    
    if (hFile == INVALID_HANDLE_VALUE) {
        return false;
    }
    
    DWORD fileSize = GetFileSize(hFile, NULL);
    std::vector<BYTE> fileData(fileSize);
    
    DWORD bytesRead;
    ReadFile(hFile, fileData.data(), fileSize, &bytesRead, NULL);
    CloseHandle(hFile);
    
    // 加密数据
    std::vector<BYTE> encrypted = XorEncrypt(fileData.data(), fileSize, key);
    
    // 保存加密文件
    std::string encryptedPath = std::string(inputPath) + ".enc";
    HANDLE hEncFile = CreateFileA(
        encryptedPath.c_str(),
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    
    if (hEncFile != INVALID_HANDLE_VALUE) {
        DWORD written;
        WriteFile(hEncFile, encrypted.data(), fileSize, &written, NULL);
        CloseHandle(hEncFile);
        
        // 创建资源定义
        return FileToResource(encryptedPath.c_str(), outputPath, 
                             resourceName, "101");  // RT_ENCRYPTED_FILE
    }
    
    return false;
}

5. 完整示例

// resource_demo.cpp
// 资源管理完整示例

#include <iostream>
#include <fstream>

// 创建测试资源文件
void CreateTestResources() {
    printf("=== Creating Test Resources ===\n");
    
    // 创建测试配置文件
    std::ofstream configFile("test_config.txt");
    configFile << "server=192.168.1.100\n";
    configFile << "port=8080\n";
    configFile << "timeout=30\n";
    configFile.close();
    
    // 创建测试脚本
    std::ofstream scriptFile("test_script.bat");
    scriptFile << "@echo off\n";
    scriptFile << "echo Hello from embedded script\n";
    scriptFile << "pause\n";
    scriptFile.close();
    
    // 创建测试数据文件
    std::ofstream dataFile("test_data.bin", std::ios::binary);
    for (int i = 0; i < 100; i++) {
        dataFile << (char)(i % 256);
    }
    dataFile.close();
    
    printf("[+] Test resource files created\n");
}

// 测试资源管理
void TestResourceManager() {
    printf("\n=== Testing Resource Manager ===\n");
    
    ResourceManager manager;
    
    // 加载资源信息
    if (!manager.LoadResourceInfo()) {
        printf("[-] Failed to load resource info\n");
        return;
    }
    
    // 列出资源
    manager.ListResources();
    
    // 提取资源配置
    std::string config = manager.GetConfig("CONFIG1");
    if (!config.empty()) {
        printf("\n[Config Content]\n%s\n", config.c_str());
    }
    
    // 批量提取资源
    std::vector<std::pair<std::string, std::string>> extracts = {
        {"SCRIPT1", "extracted_script.bat"},
        {"DATA1", "extracted_data.bin"}
    };
    
    if (BatchExtractResources(manager, extracts)) {
        printf("[+] All resources extracted successfully\n");
    }
}

int main() {
    printf("========================================\n");
    printf("     Resource Management Demo           \n");
    printf("========================================\n\n");
    
    // 创建测试资源
    CreateTestResources();
    
    // 测试资源管理
    TestResourceManager();
    
    printf("\n[*] Resource management demo completed\n");
    
    return 0;
}

6、课后作业

6.1、作业1:实现资源压缩

添加资源压缩功能,在嵌入前压缩资源,释放时解压。

6.2、作业2:添加资源校验

实现资源完整性校验,防止资源被篡改。

6.3、作业3:实现动态资源加载

设计动态加载机制,根据需要按需加载资源。