Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to obtain file handle to the current executable without introducing a filesystem race?

Tags:

c

windows

winapi

nt

I need to read some data from the current executable file (namely, debug info).

This is straightforward to do by calling QueryFullProcessImageName, then using the path returned by it to open the file and read from it.
However, this way introduces a window between retrieving the file path C:\my_program.exe and opening the file named C:\my_program.exe. During that window the original file can be replaced with some other file that I don't want to read, i.e. a filesystem race takes place.
I have an externally imposed requirement that this race should not happen.

Basically, I need something like non-existent QueryFullProcessImageHandle instead of QueryFullProcessImageName so I could read from it without opening the file by name.

From reading ReactOS sources I learned that such handle most probably exists on Windows as well and is kept in EPROCESS structure (as a part of SectionObject) and it's actually used to implement QueryFullProcessImageName.

Is there any way to obtain this handle using WinAPI or at least NT API?
(GetModuleHandleEx seems to return completely different handle.)

like image 218
user2665887 Avatar asked Nov 06 '25 16:11

user2665887


1 Answers

warning - none of this is officially supported. undocumted functions used!

exist 100% clean solution based on NtAreMappedFilesTheSame

NTSYSAPI
NTSTATUS
NTAPI
NtAreMappedFilesTheSame (
    __in PVOID File1MappedAsAnImage,
    __in PVOID File2MappedAsFile
    );

so in general words we need do next

  1. got File1MappedAsAnImage address for exe/dll
  2. with ZwQueryVirtualMemory(,MemoryMappedFilenameInformation,) get FileName (in native format). note: MemoryMappedFilenameInformation always return current file name at time, when called - so if file already renamed - we got it new name
  3. open file by given name
  4. map file and got File2MappedAsFile
  5. call NtAreMappedFilesTheSame(File1MappedAsAnImage, File2MappedAsFile)
  6. if we got STATUS_SUCCESS we open correct file - done here
  7. if we got STATUS_NOT_SAME_DEVICE need unmap File2MappedAsFile and goto 2
  8. if we got other status - some error occurred

here complete working example

NTSTATUS MapModule(void* File1MappedAsAnImage, void** pFile2MappedAsFile)
{
    static volatile UCHAR guz;

    PVOID stack = alloca(guz);
    union {
        PVOID buf;
        PUNICODE_STRING FileName;
    };

    SIZE_T cb = 0, rcb = 256, ViewSize;

    NTSTATUS status, s = STATUS_UNSUCCESSFUL;

    BOOL bSame;

    do 
    {
        bSame = TRUE;

        do 
        {
            if (cb < rcb)
            {
                cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
            }

            if (0 <= (status = NtQueryVirtualMemory(NtCurrentProcess(), File1MappedAsAnImage, MemoryMappedFilenameInformation, buf, cb, &rcb)))
            {
                DbgPrint("%wZ\n", FileName);

                OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, FileName, OBJ_CASE_INSENSITIVE };

                HANDLE hFile, hSection;
                IO_STATUS_BLOCK iosb;

                if (0 <= (s = NtOpenFile(&hFile, FILE_GENERIC_READ, &oa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT)))
                {
                    s = ZwCreateSection(&hSection, SECTION_MAP_READ, 0, 0, PAGE_READONLY, SEC_COMMIT, hFile);

                    NtClose(hFile);

                    if (0 <= s)
                    {
                        *pFile2MappedAsFile = 0;
                        s = ZwMapViewOfSection(hSection, NtCurrentProcess(), pFile2MappedAsFile, 0, 0, 0, &(ViewSize = 0), ViewUnmap, 0, PAGE_READONLY);

                        NtClose(hSection);

                        if (0 <= s)
                        {
                            switch (s = NtAreMappedFilesTheSame(File1MappedAsAnImage, *pFile2MappedAsFile))
                            {
                            case STATUS_SUCCESS:
                                DbgPrint("opened original file!");
                                return STATUS_SUCCESS;
                            case STATUS_NOT_SAME_DEVICE:
                                DbgPrint("opened another file!");
                                bSame = FALSE;
                                break;
                            default:
                                DbgPrint("status = %x\n", s);

                            }

                            ZwUnmapViewOfSection(NtCurrentProcess(), *pFile2MappedAsFile);
                        }
                    }
                }
            }

        } while (status == STATUS_BUFFER_OVERFLOW);

    } while (!bSame);

    return status < 0 ? status : s;
}

void Demo()
{
    PVOID BaseAddress;
    if (0 <= MapModule(GetModuleHandle(0), &BaseAddress))
    {
        ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
    }
}

also you can look for this topic

like image 143
RbMm Avatar answered Nov 09 '25 07:11

RbMm



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!