安全开发专题
3、资源释放
1、课程目标
- 理解Windows资源管理机制
- 掌握程序资源的嵌入和释放技术
- 实现安全的资源释放功能
- 了解资源加密和保护方法
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:实现动态资源加载
设计动态加载机制,根据需要按需加载资源。