1

I need to load a file from the hard drive to a virtual file in RAM. I then need to access this virtual file using the fopen function (I need to get the FILE pointer from it). Is this possible?

For now, I only loaded the file to stringstream:

std::ifstream file("File.zip", ios::in | ios::binary);
if (file.is_open())
{
//clear:
ProtectedZipBuffer.str(std::string());
ProtectedZipBuffer << file.rdbuf();
file.close();
}

What I'm trying to do is to mod an old game. I would need to load an encrypted file (that's for later, at the moment I'm just trying to see if the virtual file works), decrypt it to a virtual file and return the read-only FILE pointer to game (from my DLL file to the main game process). This is needed, because I would like to use the purchased / licensed sound files, but the End User License forces me to encrypt files in order to disallow users from accessing copyrighted audio files by exploring game files.

Could somebody please give me some ideas how to do this?

EDIT:

I've tried the FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE method, unfortunately no matter what, it's always writing data (around 100MB) to disk (even though I have over 8GB RAM free).

Here's the code I am using:

string SourceFileStr = "TEST_FILE_SOURCE.bin";
string DestinationFileStr = "DESTINATION_FILE.bin";
wstring DestinationFileWstr = L"DESTINATION_FILE.bin";

if (FileExistsW(DestinationFileWstr.c_str())) {
    DeleteFileW(DestinationFileWstr.c_str());
}

SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;

if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
    DWORD dwLastError = ::GetLastError();
    //LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'.  Failed to initialize security descriptor.  GetLastError=" << dwLastError);
    //return dwLastError;
    return 0;
}

if (!SetSecurityDescriptorDacl(
    &sd,    // A pointer to the SECURITY_DESCRIPTOR structure to which the function adds the DACL
    TRUE,   // presence of a DACL in the security descriptor
    NULL,   // allows all access to the object
    FALSE   // DACL has been explicitly specified by a user
))
{
    DWORD dwLastError = ::GetLastError();
    //LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'.  Failed to set security descriptor DACL.  GetLastError=" << dwLastError);
    //return dwLastError;
    return 0;
}

if (!SetSecurityDescriptorGroup(
    &sd,    // A pointer to the SECURITY_DESCRIPTOR structure whose primary group is set by this function
    NULL,   // no primary group
    FALSE   // Indicates whether the primary group information was derived from a default mechanism
))
{
    DWORD dwLastError = ::GetLastError();
    //LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'.  Failed to set security descriptor primary group.  GetLastError=" << dwLastError);
    //return dwLastError;
    return 0;
}

if (!SetSecurityDescriptorOwner(
    &sd,    // A pointer to the SECURITY_DESCRIPTOR structure whose owner is set by this function.
    NULL,   // If this parameter is NULL, the function clears the security descriptor's owner information. This marks the security descriptor as having no owner.
    FALSE   // Indicates whether the owner information is derived from a default mechanism.
))
{
    DWORD dwLastError = ::GetLastError();
    //LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'.  Failed to set security descriptor owner information.  GetLastError=" << dwLastError);
    //return dwLastError;
    return 0;
}

if (!SetSecurityDescriptorSacl(
    &sd,    // A pointer to the SECURITY_DESCRIPTOR structure to which the function adds the SACL
    FALSE,  // the security descriptor does not contain a SACL
    NULL,   // security descriptor has a NULL SACL
    FALSE   // A pointer to a flag that is set to the value of the SE_SACL_DEFAULTED flag in the SECURITY_DESCRIPTOR_CONTROL structure if a SACL exists for the security descriptor
))
{
    DWORD dwLastError = ::GetLastError();
    //LOG4CPLUS_ERROR(m_Logger, "Cannot launch file '" << strFullFilePath << "'.  Failed to set security descriptor SACL.  GetLastError=" << dwLastError);
    //return dwLastError;
    return 0;
}

sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = TRUE;

DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
//DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;

//          DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE;
//DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY;
hFileDecryptedWrite = ::CreateFile(DestinationFileWstr.c_str(), GENERIC_WRITE, dwShareMode, &sa, CREATE_NEW, dwFlagsAndAttributes, NULL);

//verify we created the file.
if (hFileDecryptedWrite == INVALID_HANDLE_VALUE) {
    DWORD dwLastError = ::GetLastError();
    //return dwLastError;
    return 0;
}

std::streamsize const  buffer_size = 64 * 1024;
std::ifstream file(SourceFileStr.c_str(), ios::in | ios::binary);
if (file.is_open()) {
        if (file)
        {
            do
            {
                char  buffer[buffer_size];
                file.read(buffer, buffer_size);
                size_t extracted = file.gcount();
                //std::streamsize bytes_read = file.gcount();
                //size_t nBytesRead = file.readsome(buffer, buffer_size);

                //MessageBoxA(NULL, to_string(extracted).c_str(), "bytes read now:", MB_OK);
                //result.process_bytes(buffer, ifs.gcount());
                DWORD numBytesWritten;
                if (!::WriteFile(hFileDecryptedWrite, buffer, extracted, &numBytesWritten, (LPOVERLAPPED)NULL)) {
                    DWORD dwLastError = ::GetLastError();
                    //LOG4CPLUS_ERROR(m_Logger, "Failed to write file to %TEMP% folder.  GetLastError=" << dwLastError);
                    //return dwLastError;
                    MessageBoxA(NULL,"FAILED TO WRITE TO FILE", "ERROR", MB_OK);
                    return 0;
                }
            } while (file);
        }
}
file.close();
//CloseHandle(hFileDecryptedWrite);

I am also using the CreateFileMapping / MapViewOfFile method for some other things. Unfortunately, there's no way to get a FILE * pointer which I could pass to the game process.

The fmem library: https://github.com/Snaipe/fmem Seems to do exactly the same thing on Windows as creating the file with FILE_ATTRIBUTE_TEMPORARY

Is there anything else I could try?

Mona
  • 337
  • 3
  • 15
  • Is [this](https://stackoverflow.com/a/2096771/4123703) helping? – Louis Go Jul 27 '21 at 05:41
  • This won't help. Alternate Data Streams are stored on disk just like a file's primary data stream. – IInspectable Jul 27 '21 at 05:46
  • Thanks for the replies. I would rather try to avoid writing data to disk. – Mona Jul 27 '21 at 05:48
  • Seems related: https://stackoverflow.com/questions/33051067/c-boost-write-memory-mapped-file – prehistoricpenguin Jul 27 '21 at 06:05
  • alternatively, you could always intercept the various `fopen()`/`read()`/`write()`/etc calls so that the game is fooled into thinking it's loading a file, while reading from memory instead. –  Jul 27 '21 at 06:10
  • This [other question](https://stackoverflow.com/q/61691334/3545273) links to interesting points (do not forget the proposed duplicates). Specifically this [answer](https://stackoverflow.com/a/50087392/3545273) seems both detailed and relevant for your question (based on Win32 API using `CreateFile`, `FILE_ATTRIBUTE_TEMPORARY` and `FILE_FLAG_DELETE_ON_CLOSE`). – Serge Ballesta Jul 27 '21 at 06:11
  • @SergeBallesta A file created with "FILE_ATTRIBUTE_TEMPORARY" still creates a file on disk which can be opened by users, even if it's in a temp folder. Maybe it's not writing data to hdd when not needed, but I can not do it not to violate the license. – Mona Jul 27 '21 at 06:16
  • @Mona, then you could look at the 2 other ways: `CreateFileMapping` and `MapViewOfFile`... – Serge Ballesta Jul 27 '21 at 07:13
  • @SergeBallesta I updated my question. I'm using CreateFileMapping / MapViewOfFile for the other things, but it does not allow to get a FILE pointer that is needed for the game. – Mona Jul 28 '21 at 00:14
  • 1
    @Mona, according to this other SO [answer](https://stackoverflow.com/a/7369662/3545273), you can obtain a FILE* from a file handle by using `_open_osfhandle` followed by `_fdopen`. – Serge Ballesta Jul 28 '21 at 06:26

1 Answers1

1

Some C++ runtimes allow FILE* to be opened from memory:

On Windows, however, Windows API does not include C++ runtime. You would usually deal with Visual Studio implementation of FILE*.

Visual Studio FILE* does not have a documented way to be backed by memory without file handle. Windows API does not provide a way to create a file out of memory (FILE_ATTRIBUTE_TEMPORARY and FILE_FLAG_DELETE_ON_CLOSE does not guarantee that the file is not actually written; and a pipe does not support fseek so may be not an option)

When posed with similar task I ended up in intercepting (hooking) Windows APIs. IAT patching is likely to do, without advanced hooking, such as trampolines.

Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79
  • A pipe might not be "memory", but it's definitely not a file either. A pipe handle is valid for `ReadFile`, and `_open_osfhandle` will get you a `FILE*` from an `HANDLE`. And you can create a pipe to your own process. – MSalters Jul 27 '21 at 09:07
  • @MSalters, it might work, if only `ReadFile` is enough. But `SetFilePointer` may be needed as well. – Alex Guteniev Jul 27 '21 at 09:20
  • On Windows, you can open a file with `FILE_ATTRIBUTE_TEMPORARY` and `FILE_FLAG_DELETE_ON_CLOSE`, OS will avoid touching disk as much as possible. Still needs to be a valid file, though, and might have to write if memory gets tight. – peterchen Jul 27 '21 at 09:28
  • Thanks for your answer. What do you think about the `CreateFileMapping` and `MapViewOfFile` solution followed by `_open_osfhandle` and `_fdopen` as posted in comments to my question above? Do you think has a chance to work? – Mona Jul 28 '21 at 15:54
  • I don't think so. – Alex Guteniev Jul 28 '21 at 15:56