4

I am trying to understand how self-extracting PE files work. Can somebody explain why my code isn't working, or fix the main() part.

#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);

}

I have 4 dll-files in resources, but I can't extract them using this. The resource ID´s should be correct, I checked it from resource header.

Is the problem in hInst or what else there could be wrong? I hope somebody could help me :) I have just a ~month ago started learning C & C++ so forgive me.

user2404495
  • 195
  • 1
  • 6
  • 17
  • Define "can't". Exactly which API call fails with exactly which error? Use debugger to find out. – Igor Tandetnik Sep 21 '13 at 00:35
  • It builds fine, but it doesn't extract the resources. I am using VS2012. – user2404495 Sep 21 '13 at 00:39
  • So, debug your program to find out why. – Igor Tandetnik Sep 21 '13 at 00:40
  • Debugger doesn't say/do anything when I try to debug it. Seems like it's working, but the only problem is that it doesn't really extract the files. They should be extracted into same folder but nothing happens. – user2404495 Sep 21 '13 at 00:43
  • While stepping through the program under debugger, check the return value of every API call. Find the one that fails. – Igor Tandetnik Sep 21 '13 at 00:45
  • Also, your code attempts to create files in the current working directory. This may or may not be the directory where the EXE is located. In particular, when you run a program from within Visual Studio IDE, the current working directory is, by default, different from the one where EXE is. – Igor Tandetnik Sep 21 '13 at 00:47
  • Yes, I know. That's why I at first tried to build it, move to desktop and test it. But it didn't work. This is what I got from VS Debugger: "'Project1.exe' (Win32): Loaded 'C:\Windows\SysWOW64\ntdll.dll'. Cannot find or open the PDB file.", same thing for kernel32.dll and msvcr110d.dll. :s How I can fix that code? – user2404495 Sep 21 '13 at 00:58
  • "Cannot find or open the PDB file" is harmless. Ignore them. This is not the problem you are looking for. Dig further. You can set the working directory when running the program from the IDE under Project > Properties > Debugging > Working Directory – Igor Tandetnik Sep 21 '13 at 01:29

2 Answers2

3

I do not see anything wrong with your code other than you not checking the return values of functions and printing appropriate messages when something goes wrong. Also note that you can replace the hInstance with nullptr and it still works.

Also note that if you are extracting to a location that requires extra permissions, you might have to run your program as administrator or add a manifest that forces it to ask for administrator privileges.

Personally, I use these in any of my resource applications:

    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));
}

Again, be very wary that you are using a CONSOLE application which may or may not have a resource embedded in it.

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • That seemed quite complicated for me, like I said am just a beginner :) Is there any _simple_ way to make work that code I posted in the OP? The project currently has only the files in resources, and just main.cpp file. Do I need something else there? And no I am not extracting the files in any place that needs an administrator privileges, only tried to run it in desktop but nothing happens. If somebody could do me a huge favor where to learn from, and fix the original code, I would really appreciate that. Also thanks for mentioning that nullptr thing, didn't know about that. – user2404495 Sep 21 '13 at 02:54
  • Uh.. one sec. I'll try and simplify what I have. It should be pretty much the same as yours :l – Brandon Sep 21 '13 at 03:05
  • Also note that it may not work if I simplify it because you seem to be using a console application which may or may not allow resources. Usually when we speak of resources, we are referring to that of a GUI program and not a console. However, I will assume you have the resources already embedded into your program and so I will simplify the answer based on this assumption. – Brandon Sep 21 '13 at 03:14
  • I see you edited your post, good. :) I will try this in few hours. I hope it works for me. But if it doesn't, can I just make an empty project, paste that code there and change entry point from int main to Win32 WinMain ? We'll see. Thanks anyway, I'll test that as soon as possible. – user2404495 Sep 21 '13 at 05:09
  • Thank you! Few changes and it works like a charm. I changed all "uint32_t"´s to just integers and it works like I want. Good. Was it bad to change it to int? – user2404495 Sep 21 '13 at 05:40
  • No it is not bad. std::uint32_t is the same as unsigned int. Only difference is that std::uint32_t is guaranteed to be 32 bits whereas unsigned int can be anything from 16 to 32 bits. In most cases, unsigned int = std::uint32_t as std::uint32_t is just a typedef for unsigned int. Moral of the story: No it isn't bad in this case. Glad you got it to work. – Brandon Sep 21 '13 at 06:31
0

While the accepted answer works very well in most cases, it might fail when having very large files on x86 systems. MapViewOfFile in this case returns a NULL pointer and GetLastError returns ERROR_NOT_ENOUGH_MEMORY.

To overcome this issue, I changed the code to write the file in chunks and add it here in case anyone encounters the same issue:

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;
}
JBartlau
  • 772
  • 5
  • 23