自解压可执行文件 C++

5

我正在尝试理解自解压的PE文件是如何工作的。有人能解释一下为什么我的代码不起作用,或者修复main()部分吗。

#include <iostream>
#include <Windows.h>

using namespace std;

void ExtractResource(const HINSTANCE hInstance, WORD resourceID, const char* outputFilename);

int main()
{
    HINSTANCE hInst = GetModuleHandle (0);
    ExtractResource(hInst, 101, "101.dll");
    ExtractResource(hInst, 102, "102.dll");
    ExtractResource(hInst, 103, "103.dll");
    ExtractResource(hInst, 104, "104.dll");
    cout << "Files are now extracted!";
    Sleep(INFINITE);
}


void ExtractResource(const HINSTANCE hInstance, WORD resourceID, const char* outputFilename){

        // First find and load the required resource          

        HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(resourceID), "BINARY");

        if(hResource==NULL)

                return;

        HGLOBAL hFileResource = LoadResource(hInstance, hResource);



        // Now open and map this to a disk file          

        LPVOID lpFile = LockResource(hFileResource);          

        DWORD dwSize = SizeofResource(hInstance, hResource);            



        // Open the file and filemap          

        HANDLE hFile = CreateFileA(outputFilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW,

                FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY, NULL);          

        HANDLE hFilemap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwSize, NULL);            

        LPVOID lpBaseAddress = MapViewOfFile(hFilemap, FILE_MAP_WRITE, 0, 0, 0);            



        // Write the file

        CopyMemory(lpBaseAddress, lpFile, dwSize);            



        // Unmap the file and close the handles

        UnmapViewOfFile(lpBaseAddress);

        CloseHandle(hFilemap);

        CloseHandle(hFile);

}

我在资源中有4个dll文件,但是使用这种方法无法提取它们。资源ID应该是正确的,我已经从资源头检查过了。
问题出在hInst上还是其他地方出错了?希望有人能帮助我 :) 我刚刚开始学习C和C++,请谅解。

定义“不能”。哪个API调用失败了,出现了什么错误?使用调试器找出来。 - Igor Tandetnik
编译没有问题,但是无法提取资源。我正在使用VS2012。 - user2404495
所以,调试你的程序找出原因。 - Igor Tandetnik
调试器在我尝试调试时没有任何反应。看起来它正在工作,但唯一的问题是它实际上并没有提取文件。它们应该被提取到同一个文件夹中,但什么也没有发生。 - user2404495
此外,您的代码试图在当前工作目录中创建文件。这可能是EXE所在的目录,也可能不是。特别是当您从Visual Studio IDE内运行程序时,默认情况下当前工作目录与EXE所在的目录不同。 - Igor Tandetnik
显示剩余3条评论
2个回答

4

除了你没有检查函数的返回值并在出现问题时打印适当的消息之外,我认为你的代码没有任何问题。同时请注意,你可以将hInstance替换为nullptr并且它仍然有效。

还要注意,如果你提取到需要额外权限的位置,可能需要以管理员身份运行程序或添加强制要求它请求管理员特权的清单。

个人而言,在任何我的资源应用程序中,我都使用下面这些:

    bool ExtractResource(std::uint16_t ResourceID, std::string OutputFileName, const char* ResType)
    {
        try
        {
            HRSRC hResource = FindResource(nullptr, MAKEINTRESOURCE(ResourceID), ResType);
            if (hResource == nullptr)
            {
                return false;
            }

            HGLOBAL hFileResource = LoadResource(nullptr, hResource);
            if (hFileResource == nullptr)
            {
                return false;
            }

            void* lpFile = LockResource(hFileResource);
            if (lpFile == nullptr)
            {
                return false;
            }

            std::uint32_t dwSize = SizeofResource(nullptr, hResource);
            if (dwSize == 0)
            {
                return false;
            }

            HANDLE hFile = CreateFile(OutputFileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
            HANDLE hFilemap = CreateFileMapping(hFile, nullptr, PAGE_READWRITE, 0, dwSize, nullptr);
            if (hFilemap == nullptr)
            {
                return false;
            }

            void* lpBaseAddress = MapViewOfFile(hFilemap, FILE_MAP_WRITE, 0, 0, 0);
            CopyMemory(lpBaseAddress, lpFile, dwSize);
            UnmapViewOfFile(lpBaseAddress);
            CloseHandle(hFilemap);
            CloseHandle(hFile);

            return true;
        }
        catch (...) {}
        return false;
    }

Main.cpp:

#include "Resource.h"

bool Extract(HWND WindowHandle) //WindowHandle for MessageBox parent.
{
    return ExtractResource(101,"101.dll", "BINARY");
}

int main()
{
    std::cout<<"Extracted Successfully: "<<std::boolalpha<<Extract(GetModuleHandle(0));
}

请注意,您正在使用可能包含嵌入资源的控制台应用程序,需谨慎处理。


那似乎对我来说相当复杂,就像我说的,我只是个初学者 :) 有没有什么简单的方法可以让我发布在OP中的代码起作用?该项目目前只有资源文件和main.cpp文件。我需要其他东西吗?不,我没有将文件提取到需要管理员权限的任何位置,只是尝试在桌面上运行它,但什么也没发生。如果有人能帮我找到学习和修复原始代码的好方法,我会非常感激。同时感谢提到nullptr的事情,我不知道这个。 - user2404495
嗯..等一下,我会尝试简化我所拥有的内容。它应该与你的基本相同 :l - Brandon
请注意,如果我简化代码可能无法正常工作,因为您似乎正在使用控制台应用程序,这种应用程序可能允许或不允许资源的使用。通常,当我们谈论资源时,我们指的是 GUI 程序而不是控制台程序。但是,我会假设您已经将资源嵌入到程序中,因此我将根据此假设简化答案。 - Brandon
我看到你编辑了你的帖子,很好。 :) 我会在几个小时内尝试这个。我希望它对我有用。但是如果不行,我可以只创建一个空项目,将代码粘贴进去,并将入口点从int main更改为Win32 WinMain吗?我们拭目以待。无论如何,还是谢谢,我会尽快测试的。 - user2404495
谢谢!稍作修改,它就像魔法般地运行了。我将所有的 "uint32_t" 改成了整数,现在它按照我的要求工作得很好。很好。把它改成 int 是不好的吗? - user2404495
不,这并不是坏事。std::uint32_t与unsigned int相同。唯一的区别在于,std::uint32_t保证为32位,而unsigned int可以是从16到32位的任何值。在大多数情况下,unsigned int = std::uint32_t,因为std::uint32_t只是unsigned int的typedef。故事的寓意是:在这种情况下,这并不是坏事。很高兴你让它工作了。 - Brandon

1

虽然被接受的答案在大多数情况下都能很好地工作,但在x86系统上有非常大的文件时可能会失败。在这种情况下,MapViewOfFile返回一个NULL指针,GetLastError返回ERROR_NOT_ENOUGH_MEMORY

为了解决这个问题,我改变了代码,将文件分块写入,并在此添加代码,以防有人遇到相同的问题:

bool ExtractResource(std::uint16_t ResourceID, std::wstring OutputFileName, LPCWSTR ResType)
{
    try
    {
        HRSRC hResource = FindResource(nullptr, MAKEINTRESOURCE(ResourceID), ResType);
        if (hResource == nullptr)
        {
            return false;
        }

        HGLOBAL hFileResource = LoadResource(nullptr, hResource);
        if (hFileResource == nullptr)
        {
            return false;
        }

        void* lpFile = LockResource(hFileResource);
        if (lpFile == nullptr)
        {
            return false;
        }

        std::uint32_t dwSize = SizeofResource(nullptr, hResource);
        if (dwSize == 0)
        {
            return false;
        }

        HANDLE hFile = CreateFile(OutputFileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
        HANDLE hFilemap = CreateFileMapping(hFile, nullptr, PAGE_READWRITE, 0, dwSize, nullptr);
        if (hFilemap == nullptr)
        {
            return false;
        }
        
        ULONG ulChunkSize(65536);
        if (ULONG n = (ULONG)((dwSize + (ulChunkSize - 1)) / ulChunkSize))
        {
            LARGE_INTEGER offset = {};
            do
            {
                SIZE_T nBytes = (--n ? ulChunkSize : dwSize % ulChunkSize);
                void* lpBaseAddress = MapViewOfFile(hFilemap, FILE_MAP_WRITE, offset.HighPart, offset.LowPart, nBytes);
                if (lpBaseAddress != 0)
                {
                    CopyMemory(lpBaseAddress, lpFile, nBytes);
                    UnmapViewOfFile(lpBaseAddress);
                    lpFile = static_cast<char*>(lpFile) + nBytes;
                }
                else
                    return false;
            } while (offset.QuadPart += ulChunkSize, n);
            CloseHandle(hFilemap);
            CloseHandle(hFile);

            return true;

        }
    }
    catch (...) {}
    return false;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接