首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

C++ 將可執行檔作為資源內嵌並於運行時釋放重建

     編輯:VC 教學

前言

將另一個執行檔(如 WordPad write.exe)作為自訂資源嵌入本程式,運行時解壓寫入磁碟並啟動,是「單一執行檔部署」的經典技巧。本文示範如何在 C++Builder 中使用 Windows Resource API(FindResource、LoadResource、LockResource)讀取內嵌資源並重建為獨立檔案。

資源準備步驟

  1. 準備目標執行檔(如 write.exe,大小 204,800 位元組)。
  2. 使用資源編輯器(如 VB6 自帶 Resource Editor 或 ResHacker)建立 .res 檔:
    • 資源類型:CUSTOM(或 RT_RCDATA)
    • 資源 ID:1111(自訂整數)
    • 資源資料:目標執行檔二進位內容
  3. 將 .res 檔加入專案(Project → Add to Project)。

核心釋放代碼

// 資源參數
const DWORD RES_ID = 1111;
const char* RES_TYPE = "CUSTOM";
const DWORD RES_SIZE = 204800;  // 預先已知的資源大小

void ExtractEmbeddedExe()
{
    HRSRC hMyRes = NULL;       // 資源控制代碼
    HGLOBAL hgpt = NULL;       // 資源記憶體區塊控制代碼
    LPVOID lpBuff = NULL;      // 資源資料指標
    DWORD rcSize = RES_SIZE;
    LPDWORD dwByte = &rcSize;
    HANDLE hFile = INVALID_HANDLE_VALUE;

    // 1. 定位資源
    hMyRes = FindResource(
        (HMODULE)GetWindowLong(Handle, GWL_HINSTANCE),
        MAKEINTRESOURCE(RES_ID),
        RES_TYPE
    );
    if (hMyRes == NULL)
    {
        ShowMessage("FindResource 失敗: " + SysErrorMessage(GetLastError()));
        return;
    }

    // 2. 載入資源至記憶體
    hgpt = LoadResource(NULL, hMyRes);
    if (hgpt == NULL)
    {
        ShowMessage("LoadResource 失敗: " + SysErrorMessage(GetLastError()));
        return;
    }

    // 3. 鎖定資源取得資料指標
    lpBuff = LockResource(hgpt);
    if (lpBuff == NULL)
    {
        ShowMessage("LockResource 失敗");
        return;
    }

    // 4. 寫入檔案
    try
    {
        hFile = CreateFile(
            "e:\\mywriter.exe",
            GENERIC_WRITE, 0, NULL,
            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
        );
        if (hFile == INVALID_HANDLE_VALUE)
            throw "CreateFile 失敗";

        if (!WriteFile(hFile, lpBuff, rcSize, dwByte, NULL))
            throw "WriteFile 失敗";

        if (*dwByte != RES_SIZE)
            throw "寫入位元組數不符";

        ShowMessage("釋放成功: e:\\mywriter.exe");
    }
    __finally
    {
        if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
        // 注意:LoadResource 返回的 HGLOBAL 無需 UnlockResource/FreeResource
        // 系統會在模組卸載時自動釋放
    }
}

關鍵 API 流程圖解

FindResource(hInst, MAKEINTRESOURCE(ID), "CUSTOM")
        │
        ▼  返回 HRSRC (資源目錄項)
LoadResource(hInst, hMyRes)
        │
        ▼  返回 HGLOBAL (全域記憶體控制代碼)
LockResource(hgpt)
        │
        ▼  返回 LPVOID (資料實際位址)
WriteFile(hFile, lpBuff, size, ...)
        │
        ▼  完成釋放

常見坑點與解決方案

問題原因解決
FindResource 返回 NULL資源類型/ID 不符、未正確連結 .res用 Resource Hacker 打開編譯後的 .exe 驗證資源是否存在
釋出檔案無法執行資源大小常數 RES_SIZE 與實際不符用 SizeofResource 動態取得大小,勿硬編碼
LockResource 返回資料不全資源未正確對齊或被截斷確認 .res 檔建立時為二進位模式,無額外標頭
執行釋出檔案報錯目標機缺少相依 DLL(如 MFC、VB6 執行庫)確保目標執行檔為獨立靜態連結或附帶相依檔

進階:動態取得資源大小

// 替代硬編碼 RES_SIZE
DWORD resSize = SizeofResource(
    (HMODULE)GetWindowLong(Handle, GWL_HINSTANCE), hMyRes
);

安全性考量

  • 內嵌執行檔會增加主程式體積,需評估配發大小限制。
  • 釋出至暫存目錄(GetTempPath)較固定路徑安全,用完刪除。
  • 若內嵌檔案為敏感工具(如系統管理程式),建議加密資源資料,運行時解密後寫入。
  • 此技巧常被惡意軟體濫用(Dropper),簽名驗證與行為監控可能誤判,需簽名部署。

掌握「資源內嵌 → 運行時釋放」模式,不僅能實現單一檔案部署,更是理解 Windows 資源管理機制的絕佳切入點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved