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 ?