views:

118

answers:

2

I have the ntdll.dll's NtCreateFile() function hooked to allow/deny the access of certain files. Unlike kernel32.dll's CreateFile() which easily gives you the full path to the file in question, ntdll.dll's NtCreateFile() function only gives you the handle to the file. I need to obtain the full path of the file from a file handle, to consequently allow/deny access. I've searched around and there doesn't seem to be a working C# solution.

This solution is in C++, and documented from Microsoft. I've tried to port it over to C# with not much success. Here is my attempt at the C# equivalent of the C++ version of "obtaining a filename from a file handle":

    public string GetFileNameFromHandle(IntPtr FileHandle)
    {
        string fileName = String.Empty;
        IntPtr fileMap = IntPtr.Zero, fileSizeHi = IntPtr.Zero;
        UInt32 fileSizeLo = 0;

        fileSizeLo = GetFileSize(FileHandle, fileSizeHi);

        if (fileSizeLo == 0 && fileSizeHi == IntPtr.Zero)
        {
            // cannot map an 0 byte file
            return String.Empty;
        }

        fileMap = CreateFileMapping(FileHandle, IntPtr.Zero, FileMapProtection.PageReadonly, 0, 1, null);

        if (fileMap != IntPtr.Zero)
        {
            IntPtr pMem = MapViewOfFile(fileMap, FileMapAccess.FileMapRead, 0, 0, 1);
            if (pMem != IntPtr.Zero)
            {
                StringBuilder fn = new StringBuilder(250);
                GetMappedFileName(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle, pMem, fn, 250);
                if (fileName.Length > 0)
                {
                    UnmapViewOfFile(pMem);
                    CloseHandle(FileHandle);
                    return fn.ToString();
                }
                else
                {
                    UnmapViewOfFile(pMem);
                    CloseHandle(FileHandle);
                    return String.Empty;
                }
            }
        }

        return String.Empty;
    }

I have, of course, all the necessary DLLImports and user-defined types. When I use this function on handles, I get an empty string in return. It's also pretty hard to debug this, since this method is in a DLL that gets injected into a target process, not like something you can set a breakpoint at and enjoy Visual Studio's debugging system. I guess I could write a log file or some trace system, but I'm not that desperate yet. I just need a successful C# version of "get filename from file handle".

Any insight, code fixes, links?

A: 

From http://msdn.microsoft.com/en-us/library/aa366789.aspx

"The following example obtains a file name from a handle to a file object using a file mapping object. It uses the CreateFileMapping and MapViewOfFile functions to create the mapping. Next, it uses the GetMappedFileName function to obtain the file name."

Code looks legit to me, hope that helps.

Shaun Wilson
Uh, I'm not sure if you read my post, but that's what I said :P
Rudi
+2  A: 

Solved it myself. Here's the working code with the references and stuff.

[DllImport("kernel32.dll")]
static extern uint GetFileSize(IntPtr hFile, IntPtr lpFileSizeHigh);

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFileMapping(
    IntPtr hFile,
    IntPtr lpFileMappingAttributes,
    FileMapProtection flProtect,
    uint dwMaximumSizeHigh,
    uint dwMaximumSizeLow,
    [MarshalAs(UnmanagedType.LPTStr)]string lpName);

[Flags]
public enum FileMapProtection : uint
{
    PageReadonly = 0x02,
    PageReadWrite = 0x04,
    PageWriteCopy = 0x08,
    PageExecuteRead = 0x20,
    PageExecuteReadWrite = 0x40,
    SectionCommit = 0x8000000,
    SectionImage = 0x1000000,
    SectionNoCache = 0x10000000,
    SectionReserve = 0x4000000,
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr MapViewOfFile(
    IntPtr hFileMappingObject,
    FileMapAccess dwDesiredAccess,
    uint dwFileOffsetHigh,
    uint dwFileOffsetLow,
    uint dwNumberOfBytesToMap);

[Flags]
public enum FileMapAccess : uint
{
    FileMapCopy = 0x0001,
    FileMapWrite = 0x0002,
    FileMapRead = 0x0004,
    FileMapAllAccess = 0x001f,
    fileMapExecute = 0x0020,
}

[DllImport("psapi.dll", SetLastError = true)]
public static extern uint GetMappedFileName(IntPtr m_hProcess, IntPtr lpv, StringBuilder 
        lpFilename, uint nSize);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);

public static string GetFileNameFromHandle(IntPtr FileHandle)
{
    string fileName = String.Empty;
    IntPtr fileMap = IntPtr.Zero, fileSizeHi = IntPtr.Zero;
    UInt32 fileSizeLo = 0;

    fileSizeLo = GetFileSize(FileHandle, fileSizeHi);

    if (fileSizeLo == 0)
    {
        // cannot map an 0 byte file
        return "Empty file.";
    }

    fileMap = CreateFileMapping(FileHandle, IntPtr.Zero, FileMapProtection.PageReadonly, 0, 1, null);

    if (fileMap != IntPtr.Zero)
    {
        IntPtr pMem = MapViewOfFile(fileMap, FileMapAccess.FileMapRead, 0, 0, 1);
        if (pMem != IntPtr.Zero)
        {
            StringBuilder fn = new StringBuilder(250);
            GetMappedFileName(System.Diagnostics.Process.GetCurrentProcess().Handle, pMem, fn, 250);
            if (fn.Length > 0)
            {
                UnmapViewOfFile(pMem);
                CloseHandle(FileHandle);
                return fn.ToString();
            }
            else
            {
                UnmapViewOfFile(pMem);
                CloseHandle(FileHandle);
                return "Empty filename.";
            }
        }
    }

    return "Empty filemap handle.";
}
Rudi