2

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.)

user2665887
  • 793
  • 4
  • 18
  • 1
    @Jean-FrançoisFabre - this not true. file can be renamed while running. – RbMm Nov 01 '16 at 21:00
  • @RbMm: you are right. On Windows 10 it can be done. I was sure of the contrary, but on windows 10 it seems to be possible!! – Jean-François Fabre Nov 01 '16 at 21:03
  • @Jean-FrançoisFabre - in what problem ? run any exe and then rename in in explorer or any file browser. test this and view result. file can be remaned. deleted can not – RbMm Nov 01 '16 at 21:03
  • @Jean-FrançoisFabre - not only on win10 but even on xp. in any windows. open section on file prevent only from deletion but not from rename operation – RbMm Nov 01 '16 at 21:05
  • @RbMm if was sure of the contrary. Well at least I learned something today. – Jean-François Fabre Nov 01 '16 at 21:06
  • 1
    I tried (confirmed just now on Windows 8.1), they can be renamed and file with the same name can be created. – user2665887 Nov 01 '16 at 21:09
  • Debug information embedded into the executable is normally pretty minimal if you're using the Visual Studio tools, just a pointer to the PDB file where the debug info actually is. You could just embed that as a normal string in you executable if that's what you're trying to access. – Ross Ridge Nov 01 '16 at 21:46
  • I need to work with MinGW-w64 and DWARF debuginfo, actually. And it have to be a normal debuginfo, readable by third parties, not just a string, unfortunately. – user2665887 Nov 01 '16 at 22:02
  • If you're using DWARF debugging info you can probably just change the section attributes so that it all gets loaded into memory. – Ross Ridge Nov 01 '16 at 22:33
  • Can you sign the exe? - fetch the path from the processid, open it with a lock so it cannot be renamed, verify with WinVerifyTrust, read it. – Alex K. Nov 02 '16 at 14:00

1 Answers1

1

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

RbMm
  • 25,803
  • 2
  • 21
  • 40
  • Interesting! I thought about checking the opened file post-factum, but haven't found a way to do that without races either. (I need to test and understand this in more detail before accepting the answer.) – user2665887 Nov 01 '16 at 23:00
  • @user2665887 - yes, opened file post-factum check in loop - if not same files - start loop again. this possible because MemoryMappedFilenameInformation always return current file name at time, when called. and NtAreMappedFilesTheSame do post-factum check without races – RbMm Nov 01 '16 at 23:08
  • As always, when proposing to use undocumented functions, explicitly state, that this solution is working off of undocumented implementation details, may not be supported for an undisclosed set of operating systems or configurations, and may stop working at any time. – IInspectable Nov 02 '16 at 10:56
  • @IInspectable - As always, are you have some concrete technical note about solution ? say it incorrect because *. or you can propose self better or more "documented" solution ? this solution work even in win2000 and up to latest 10.1607 -more then 16 years, but some time will be stop working – RbMm Nov 02 '16 at 12:32
  • *"are you have some concrete technical note about solution ?"* - Yes. The technical note is: Please point out, that none of this is officially supported. Even though you are not, a professional software developer will want to know about this aspect. – IInspectable Nov 02 '16 at 12:41
  • @IInspectable - no problem. i add this. or you can edit post by self. about technical note i mean your view - are this solution at all ? may be containing races conditions ? are correct, stable, effective in current versions (xp - latest win10) ? or nothing to say here ? – RbMm Nov 02 '16 at 12:49
  • To a future reader: the "complete working example" uses much more undocumented functionality than required, `NtAreMappedFilesTheSame` is the only non-WinAPI thing that are needed. Also, the code as provided doesn't compile with VC++2015 and doesn't compile *at all* without WDK, so it needs to be rewritten anyway (but the general approach is correct). – user2665887 Nov 05 '16 at 16:32
  • @user2665887 - yes, of course need WDK and special environment to compile this code. however i copy-paste this code as is from self test app - so i compiled and linked it ) – RbMm Nov 05 '16 at 18:37