Hooking NtCreateFile API from ntdll.dll with EasyH

2019-06-07 19:23发布

问题:

This is the first time I try to hook windows API. My goal is to monitor all files that a process is going to create/open/read/write. In order to be the most verbose possible, I decided to hook the ntdll.dll API such as NtCreateFile() and NtOpenFile(). So, in order to acheive this goal, I went on EasyHook, which seems easy and robust. I've essetially followed the FileMon example, changing what I really wanted: the Hooked function. When I try to read information about the file that is going to be opened, I try to read information from the OBJECT_ATTRIBUTES structure, such as the ObjectName. Those are integer pointers, so I expected to use the function Marshal.PtrToStringAuto(attributes.objectName) in order to get the string value. However, the result is I can only have bad strings, without any meaning. Also, the File access seems to be not working. I guess there's something wrong with this code, maybe in the DllImport signatures. Be adviced I had to replace SafeHandle with IntPtr, because of EasyHook was complaining about marshaling them. Can someone help me?

Here's my specific code of the injected DLL:

Here's the Run method code

public void Run(RemoteHooking.IContext InContext, String inChannelName) 
        {
            // First of all, install all the hooks
            try
            {
                // NtCreateFile
                fileCreationHook = LocalHook.Create(
                    LocalHook.GetProcAddress("ntdll.dll", "NtCreateFile"),
                    new CreateFileDelegate(CreateFile_Hooked),
                    this
                    );

                fileCreationHook = LocalHook.Create(
                    LocalHook.GetProcAddress("ntdll.dll", "NtOpenFile"),
                    new OpenFileDelegate(OpenFile_Hooked),
                    this
                    );

                fileCreationHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
                remoteIf.Log("File creation Hook correctly installed on pid "+RemoteHooking.GetCurrentProcessId());


            }
            catch (Exception e)
            {
                remoteIf.Log(e.Message);
                remoteIf.Log(e.StackTrace);
                return;
            }

            // Wake up the process
            remoteIf.Log("Waiking up process...");
            RemoteHooking.WakeUpProcess();

            while (true)
            {
                Thread.Sleep(500);

                if (queue.Count > 0)
                {
                    String[] package = null;

                    lock (queue)
                    {
                        package = queue.ToArray();
                        queue.Clear();
                    }

                    remoteIf.OnCreateFile(RemoteHooking.GetCurrentProcessId(), package);
                }
                else
                    remoteIf.Ping();
            }

        }

Here's the contructor code:

public InjectedDLL(RemoteHooking.IContext InContext, String inChannelName)
        {
            // Create the structure which will contain all the messages
            queue = new Stack<string>();
            // Initiate the connection to the Injector process, getting back its interface
            remoteIf = RemoteHooking.IpcConnectClient<IPCInterface>(inChannelName);
            // Try invocating a method to test the connection.
            remoteIf.Ping();
        }

Here there are the Hook delegate and the hook function

public delegate int CreateFileDelegate(out  IntPtr handle,
            System.IO.FileAccess access,
            ref OBJECT_ATTRIBUTES objectAttributes,
            out IO_STATUS_BLOCK ioStatus,
            ref long allocSize,
            uint fileAttributes,
            System.IO.FileShare share,
            uint createDisposition,
            uint createOptions,
            IntPtr eaBuffer,
            uint eaLength);

        public int CreateFile_Hooked(
            out  IntPtr handle,
            System.IO.FileAccess access,
            ref OBJECT_ATTRIBUTES objectAttributes,
            out IO_STATUS_BLOCK ioStatus,
            ref long allocSize,
            uint fileAttributes,
            System.IO.FileShare share,
            uint createDisposition,
            uint createOptions,
            IntPtr eaBuffer,
            uint eaLength)
        {

            //string s = Marshal.PtrToStringAuto(objectAttributes.ObjectName);
            int res = NtCreateFile(out handle, access,ref objectAttributes,out ioStatus, ref allocSize,fileAttributes, share,createDisposition,createOptions,eaBuffer,eaLength);
            return res;
        }

Here there are the NtDll.Dll native functions:

[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)]
        public static extern int NtCreateFile(
            out  IntPtr handle,
            System.IO.FileAccess access,
            ref OBJECT_ATTRIBUTES objectAttributes,
            out IO_STATUS_BLOCK ioStatus,
            ref long allocSize,
            uint fileAttributes,
            System.IO.FileShare share,
            uint createDisposition,
            uint createOptions,
            IntPtr eaBuffer,
            uint eaLength);

        [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)]
        public static extern int NtOpenFile(
            out  IntPtr handle,
            System.IO.FileAccess access,
            ref OBJECT_ATTRIBUTES objectAttributes,
            out IO_STATUS_BLOCK ioStatus,
            System.IO.FileShare share,
            uint openOptions
            );

        [StructLayout(LayoutKind.Sequential, Pack = 0)]
        public struct OBJECT_ATTRIBUTES
        {
            public Int32 Length;
            public IntPtr RootDirectory;
            public IntPtr ObjectName;
            public uint Attributes;
            public IntPtr SecurityDescriptor;
            public IntPtr SecurityQualityOfService;

        }

        [StructLayout(LayoutKind.Sequential, Pack = 0)]
        public struct IO_STATUS_BLOCK
        {
            public uint status;
            public IntPtr information;
        }

回答1:

ObjectName is a pointer to a UNICODE_STRING struct. A managed equivalent looks like this:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct UNICODE_STRING
{
    public ushort Length;
    public ushort MaximumLength;
    [MarshalAs(UnmanagedType.LPWStr)]
    public String Buffer;
}

You need to use marshalling to get a managed copy of the struct.

var us = Marshal.PtrToStructure<UNICODE_STRING>(objectAttributes.ObjectName);

Once you have the managed struct you can access the Buffer field to get the name of the object.