1

I have downloaded the project 'RemoteFileMonitor' here:

https://github.com/EasyHook/EasyHook-Tutorials/tree/master/Managed/RemoteFileMonitor

This project generate a console log of all files opened by an input process id.

the application run without issue but the log show unexpected result.

I have tested it with different process (notepad included) with the some result:

In short if you open multiple times the some file, the log show it only first time and show more results only for different files.

I need it to monitor in real-time, the file access of an external process, but it is frequently that the process try to open the some file and is important for me this information inside the log.

Here the main part of the original source code:

namespace FileMonitorHook
{

public class InjectionEntryPoint: EasyHook.IEntryPoint
{
    /// <summary>
    /// Reference to the server interface within FileMonitor
    /// </summary>
    ServerInterface _server = null;

    /// <summary>
    /// Message queue of all files accessed
    /// </summary>
    Queue<string> _messageQueue = new Queue<string>();

    /// <summary>
    /// EasyHook requires a constructor that matches <paramref name="context"/> and any additional parameters as provided
    /// in the original call to <see cref="EasyHook.RemoteHooking.Inject(int, EasyHook.InjectionOptions, string, string, object[])"/>.
    /// 
    /// Multiple constructors can exist on the same <see cref="EasyHook.IEntryPoint"/>, providing that each one has a corresponding Run method (e.g. <see cref="Run(EasyHook.RemoteHooking.IContext, string)"/>).
    /// </summary>
    /// <param name="context">The RemoteHooking context</param>
    /// <param name="channelName">The name of the IPC channel</param>
    public InjectionEntryPoint(
        EasyHook.RemoteHooking.IContext context,
        string channelName)
    {
        // Connect to server object using provided channel name
        _server = EasyHook.RemoteHooking.IpcConnectClient<ServerInterface>(channelName);

        // If Ping fails then the Run method will be not be called
        _server.Ping();
    }

    /// <summary>
    /// The main entry point for our logic once injected within the target process. 
    /// This is where the hooks will be created, and a loop will be entered until host process exits.
    /// EasyHook requires a matching Run method for the constructor
    /// </summary>
    /// <param name="context">The RemoteHooking context</param>
    /// <param name="channelName">The name of the IPC channel</param>
    public void Run(
        EasyHook.RemoteHooking.IContext context,
        string channelName)
    {
        // Injection is now complete and the server interface is connected
        _server.IsInstalled(EasyHook.RemoteHooking.GetCurrentProcessId());

        // Install hooks

        // CreateFile https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
        var createFileHook = EasyHook.LocalHook.Create(
            EasyHook.LocalHook.GetProcAddress("kernel32.dll", "CreateFileW"),
            new CreateFile_Delegate(CreateFile_Hook),
            this);

        // ReadFile https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx
        var readFileHook = EasyHook.LocalHook.Create(
            EasyHook.LocalHook.GetProcAddress("kernel32.dll", "ReadFile"),
            new ReadFile_Delegate(ReadFile_Hook),
            this);

        // WriteFile https://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx
        var writeFileHook = EasyHook.LocalHook.Create(
            EasyHook.LocalHook.GetProcAddress("kernel32.dll", "WriteFile"),
            new WriteFile_Delegate(WriteFile_Hook),
            this);

        // Activate hooks on all threads except the current thread
        createFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
        readFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
        writeFileHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });

        _server.ReportMessage("CreateFile, ReadFile and WriteFile hooks installed");

        // Wake up the process (required if using RemoteHooking.CreateAndInject)
        EasyHook.RemoteHooking.WakeUpProcess();

        try
        {
            // Loop until FileMonitor closes (i.e. IPC fails)
            while (true)
            {
                System.Threading.Thread.Sleep(500);

                string[] queued = null;

                lock (_messageQueue)
                {
                    queued = _messageQueue.ToArray();
                    _messageQueue.Clear();
                }

                // Send newly monitored file accesses to FileMonitor
                if (queued != null && queued.Length > 0)
                {
                    _server.ReportMessages(queued);
                }
                else
                {
                    _server.Ping();
                }
            }
        }
        catch
        {
            // Ping() or ReportMessages() will raise an exception if host is unreachable
        }

        // Remove hooks
        createFileHook.Dispose();
        readFileHook.Dispose();
        writeFileHook.Dispose();

        // Finalise cleanup of hooks
        EasyHook.LocalHook.Release();
    }

    /// <summary>
    /// P/Invoke to determine the filename from a file handle
    /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364962(v=vs.85).aspx
    /// </summary>
    /// <param name="hFile"></param>
    /// <param name="lpszFilePath"></param>
    /// <param name="cchFilePath"></param>
    /// <param name="dwFlags"></param>
    /// <returns></returns>
    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint GetFinalPathNameByHandle(IntPtr hFile, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath, uint cchFilePath, uint dwFlags);

    #region CreateFileW Hook

    /// <summary>
    /// The CreateFile delegate, this is needed to create a delegate of our hook function <see cref="CreateFile_Hook(string, uint, uint, IntPtr, uint, uint, IntPtr)"/>.
    /// </summary>
    /// <param name="filename"></param>
    /// <param name="desiredAccess"></param>
    /// <param name="shareMode"></param>
    /// <param name="securityAttributes"></param>
    /// <param name="creationDisposition"></param>
    /// <param name="flagsAndAttributes"></param>
    /// <param name="templateFile"></param>
    /// <returns></returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall,
                CharSet = CharSet.Unicode,
                SetLastError = true)]
    delegate IntPtr CreateFile_Delegate(
                String filename,
                UInt32 desiredAccess,
                UInt32 shareMode,
                IntPtr securityAttributes,
                UInt32 creationDisposition,
                UInt32 flagsAndAttributes,
                IntPtr templateFile);

    /// <summary>
    /// Using P/Invoke to call original method.
    /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
    /// </summary>
    /// <param name="filename"></param>
    /// <param name="desiredAccess"></param>
    /// <param name="shareMode"></param>
    /// <param name="securityAttributes"></param>
    /// <param name="creationDisposition"></param>
    /// <param name="flagsAndAttributes"></param>
    /// <param name="templateFile"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll",
        CharSet = CharSet.Unicode,
        SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    static extern IntPtr CreateFileW(
        String filename,
        UInt32 desiredAccess,
        UInt32 shareMode,
        IntPtr securityAttributes,
        UInt32 creationDisposition,
        UInt32 flagsAndAttributes,
        IntPtr templateFile);

    /// <summary>
    /// The CreateFile hook function. This will be called instead of the original CreateFile once hooked.
    /// </summary>
    /// <param name="filename"></param>
    /// <param name="desiredAccess"></param>
    /// <param name="shareMode"></param>
    /// <param name="securityAttributes"></param>
    /// <param name="creationDisposition"></param>
    /// <param name="flagsAndAttributes"></param>
    /// <param name="templateFile"></param>
    /// <returns></returns>
    IntPtr CreateFile_Hook(
        String filename,
        UInt32 desiredAccess,
        UInt32 shareMode,
        IntPtr securityAttributes,
        UInt32 creationDisposition,
        UInt32 flagsAndAttributes,
        IntPtr templateFile)
    {
        try
        {
            lock (this._messageQueue)
            {
                if (this._messageQueue.Count < 1000)
                {
                    string mode = string.Empty;
                    switch (creationDisposition)
                    {
                        case 1:
                            mode = "CREATE_NEW";
                            break;
                        case 2:
                            mode = "CREATE_ALWAYS";
                            break;
                        case 3:
                            mode = "OPEN_ALWAYS";
                            break;
                        case 4:
                            mode = "OPEN_EXISTING";
                            break;
                        case 5:
                            mode = "TRUNCATE_EXISTING";
                            break;
                    }

                    // Add message to send to FileMonitor
                    this._messageQueue.Enqueue(
                        string.Format("[{0}:{1}]: CREATE ({2}) \"{3}\"",
                        EasyHook.RemoteHooking.GetCurrentProcessId(), EasyHook.RemoteHooking.GetCurrentThreadId()
                        , mode, filename));
                }
            }
        }
        catch
        {
            // swallow exceptions so that any issues caused by this code do not crash target process
        }

        // now call the original API...
        return CreateFileW(
            filename,
            desiredAccess,
            shareMode,
            securityAttributes,
            creationDisposition,
            flagsAndAttributes,
            templateFile);
    }

    #endregion

    #region ReadFile Hook

    /// <summary>
    /// The ReadFile delegate, this is needed to create a delegate of our hook function <see cref="ReadFile_Hook(IntPtr, IntPtr, uint, out uint, IntPtr)"/>.
    /// </summary>
    /// <param name="hFile"></param>
    /// <param name="lpBuffer"></param>
    /// <param name="nNumberOfBytesToRead"></param>
    /// <param name="lpNumberOfBytesRead"></param>
    /// <param name="lpOverlapped"></param>
    /// <returns></returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
    delegate bool ReadFile_Delegate(
        IntPtr hFile,
        IntPtr lpBuffer,
        uint nNumberOfBytesToRead,
        out uint lpNumberOfBytesRead,
        IntPtr lpOverlapped);

    /// <summary>
    /// Using P/Invoke to call the orginal function
    /// </summary>
    /// <param name="hFile"></param>
    /// <param name="lpBuffer"></param>
    /// <param name="nNumberOfBytesToRead"></param>
    /// <param name="lpNumberOfBytesRead"></param>
    /// <param name="lpOverlapped"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    static extern bool ReadFile(
        IntPtr hFile, 
        IntPtr lpBuffer,
        uint nNumberOfBytesToRead, 
        out uint lpNumberOfBytesRead, 
        IntPtr lpOverlapped);

    /// <summary>
    /// The ReadFile hook function. This will be called instead of the original ReadFile once hooked.
    /// </summary>
    /// <param name="hFile"></param>
    /// <param name="lpBuffer"></param>
    /// <param name="nNumberOfBytesToRead"></param>
    /// <param name="lpNumberOfBytesRead"></param>
    /// <param name="lpOverlapped"></param>
    /// <returns></returns>
    bool ReadFile_Hook(
        IntPtr hFile,
        IntPtr lpBuffer,
        uint nNumberOfBytesToRead,
        out uint lpNumberOfBytesRead,
        IntPtr lpOverlapped)
    {
        bool result = false;
        lpNumberOfBytesRead = 0;

        // Call original first so we have a value for lpNumberOfBytesRead
        result = ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, out lpNumberOfBytesRead, lpOverlapped);

        try
        {
            lock (this._messageQueue)
            {
                if (this._messageQueue.Count < 1000)
                {
                    // Retrieve filename from the file handle
                    StringBuilder filename = new StringBuilder(255);
                    GetFinalPathNameByHandle(hFile, filename, 255, 0);

                    // Add message to send to FileMonitor
                    this._messageQueue.Enqueue(
                        string.Format("[{0}:{1}]: READ ({2} bytes) \"{3}\"",
                        EasyHook.RemoteHooking.GetCurrentProcessId(), EasyHook.RemoteHooking.GetCurrentThreadId()
                        , lpNumberOfBytesRead, filename));
                }
            }
        }
        catch
        {
            // swallow exceptions so that any issues caused by this code do not crash target process
        }

        return result;
    }

    #endregion

    #region WriteFile Hook

    /// <summary>
    /// The WriteFile delegate, this is needed to create a delegate of our hook function <see cref="WriteFile_Hook(IntPtr, IntPtr, uint, out uint, IntPtr)"/>.
    /// </summary>
    /// <param name="hFile"></param>
    /// <param name="lpBuffer"></param>
    /// <param name="nNumberOfBytesToWrite"></param>
    /// <param name="lpNumberOfBytesWritten"></param>
    /// <param name="lpOverlapped"></param>
    /// <returns></returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    delegate bool WriteFile_Delegate(
        IntPtr hFile,
        IntPtr lpBuffer,
        uint nNumberOfBytesToWrite,
        out uint lpNumberOfBytesWritten,
        IntPtr lpOverlapped);

    /// <summary>
    /// Using P/Invoke to call original WriteFile method
    /// </summary>
    /// <param name="hFile"></param>
    /// <param name="lpBuffer"></param>
    /// <param name="nNumberOfBytesToWrite"></param>
    /// <param name="lpNumberOfBytesWritten"></param>
    /// <param name="lpOverlapped"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool WriteFile(
        IntPtr hFile,
        IntPtr lpBuffer,
        uint nNumberOfBytesToWrite,
        out uint lpNumberOfBytesWritten,
        IntPtr lpOverlapped);

    /// <summary>
    /// The WriteFile hook function. This will be called instead of the original WriteFile once hooked.
    /// </summary>
    /// <param name="hFile"></param>
    /// <param name="lpBuffer"></param>
    /// <param name="nNumberOfBytesToWrite"></param>
    /// <param name="lpNumberOfBytesWritten"></param>
    /// <param name="lpOverlapped"></param>
    /// <returns></returns>
    bool WriteFile_Hook(
        IntPtr hFile,
        IntPtr lpBuffer,
        uint nNumberOfBytesToWrite,
        out uint lpNumberOfBytesWritten,
        IntPtr lpOverlapped)
    {
        bool result = false;

        // Call original first so we get lpNumberOfBytesWritten
        result = WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, out lpNumberOfBytesWritten, lpOverlapped);

        try
        {
            lock (this._messageQueue)
            {
                if (this._messageQueue.Count < 1000)
                {
                    // Retrieve filename from the file handle
                    StringBuilder filename = new StringBuilder(255);
                    GetFinalPathNameByHandle(hFile, filename, 255, 0);

                    // Add message to send to FileMonitor
                    this._messageQueue.Enqueue(
                        string.Format("[{0}:{1}]: WRITE ({2} bytes) \"{3}\"",
                        EasyHook.RemoteHooking.GetCurrentProcessId(), EasyHook.RemoteHooking.GetCurrentThreadId()
                        , lpNumberOfBytesWritten, filename));
                }
            }
        }
        catch
        {
            // swallow exceptions so that any issues caused by this code do not crash target process
        }

        return result;
    }

    #endregion
}

}

I don't have mutch experience in hook in general, can someone let me known, if is possible fix this ?

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
  • I would test using your own application first so that you are certain of how the APIs are being called. It could be that you are experiencing results of caching or similar? – Justin Stenning Apr 22 '20 at 05:45
  • Hi Justin, thank you for your response, I don't think is a cache issue. here the source code: http://pro-software.it/FileMonitorHook.zip there is a text file with my email. –  Apr 23 '20 at 07:37
  • Compare with api monitor. If the app doesn’t call the api again then a hook won’t help. For reliable file level stuff I prefer file filter drivers. – Justin Stenning Apr 29 '20 at 12:29
  • Your information is useful, do you have some working sample ? –  Apr 30 '20 at 13:27

0 Answers0