In this tutorial we will create a remote file monitor using EasyHook. We will cover how to:

使用EasyHook创建一个全局文件监控程序,包括

 
  1. Inject a managed assembly into an existing target process based on the process Id
  2. 将托管程序集(dll)注入到已存在的进程ID
  3. Inject a managed assembly into a newly created (and suspended) process using path to executable
  4. 将托管程序集(dll)注入到新创建(并且挂起)的进程(通过该进程运行的路径注入)
  5. Create local hooks within the remote process to monitor 3 file operations (CreateFile, ReadFile and WriteFile)
  6. 通过远程程序创建本地hook,完成CreateFile, ReadFile 和WriteFile3种操作
  7. Report file accesses back to the main console application using .NET inter-process communication (IPC)

For this tutorial we will be creating a solution with two projects:

这个教程里,我们要创建两个解决方案

  • FileMonitor: a C# console application; and
  • FileMonitorHook: a C# class library that contains the hook logic and the IPC interface. This assembly is our injection payload.

Step 1: Create the injection payload: FileMonitorHook class library

第1步:创建注入器:FileMonitorHook类库

Create a new C# class library project called FileMonitorHook.

解决方案下新建建一个叫FileMonitorHook的类库

Add the EasyHook NuGet package (right-click on the FileMonitorHook project, Manage NuGet Packages…), search for EasyHook and install.

添加EasyHook.dll的引用(EasyHook32.dll和EasyHook64.dll要跟被引用的EasyHook.dll放在同一目录)

ServerInterface class

Next we will add the interface class that will be used to communicate from the payload within the target process back to the FileMonitor console app we will add shortly. It is important that this class, and any objects it needs to pass back and forth descend from MarshalByRefObject.

然后添加接口类

/// <summary>
/// Provides an interface for communicating from the client (target) to the server (injector)
/// </summary>
public class ServerInterface : MarshalByRefObject
{
public void IsInstalled(int clientPID)
{
Console.WriteLine("FileMonitor has injected FileMonitorHook into process {0}.\r\n", clientPID);
} /// <summary>
/// Output messages to the console.
/// </summary>
/// <param name="clientPID"></param>
/// <param name="fileNames"></param>
public void ReportMessages(int clientPID, string[] messages)
{
for (int i = ; i < messages.Length; i++)
{
Console.WriteLine(messages[i]);
}
} public void ReportMessage(int clientPID, string message)
{
Console.WriteLine(message);
} /// <summary>
/// Report exception
/// </summary>
/// <param name="e"></param>
public void ReportException(Exception e)
{
Console.WriteLine("The target process has reported an error:\r\n" + e.ToString());
} /// <summary>
/// Called to confirm that the IPC channel is still open / host application has not closed
/// </summary>
public void Ping()
{
}
}

InjectionEntryPoint class

Lastly we need to create a public class that implements EasyHook.IEntryPoint so that the EasyHook injection process can call our custom logic once the payload has been injected.

然后,需要创建一个公共类来实现EasyHook.IEntryPoint接口,当内容被注入后,EasyHook就能调用我们自己写的程序

This class will include a constructor that sets up the IPC connection, and a method called Run with the same parameters as the constructor.

这个类必须有一个IPC连接的构造函数,并且签名相同的Run函数

The constructor and Run method must include at least one parameter of type EasyHook.RemoteHooking.IContext, and then any number of additional parameters providing they are serializable.

这两个函数必须有一个EasyHook.RemoteHooking.IContext类型的参数,其它参数都是序列化的

For example, a minimal IEntryPoint that does nothing would need look like this:

例如,一个最小的IEntryPoint如下

public class MySimpleEntryPoint: EasyHook.IEntryPoint
{
public MySimpleEntryPoint(EasyHook.RemoteHooking.IContext context)
{
} public void Run(EasyHook.RemoteHooking.IContext context)
{
}
}

In the above example, once the payload has been injected, the instance of MySimpleEntryPoint will be created, the Run method called, and then immediately afterwards the payload will be unloaded.

一旦内容被注入,会创建一个MySimpleEntryPoint实例,Run函数会被调用,内容立刻被卸载

Our file monitor example is going to accomplish the following:

我们的例子实现如下:

    1. Constructor: 构造函数
      1. Connect to the server IPC channel for ServerInterface
      2. ServerInterface连接IPC
      3. Attempt to call the ServerInterface.Ping method we created earlier to confirm the channel is accessible
      4. 尝试用ServerInterface Ping我们能创建的方法来判断能否访问
    2. Run:
      1. Install our hooks for CreateFile, ReadFile and WriteFile. Our hooks will not modify the original behaviour in any way, they will simply add a message to the queue of messages to be returned to the console app.
      2. 为CreateFile, ReadFile and WriteFile增加hook,我们的hook部改变原始表现,只是增加了一个打印信息
      3. Enter an endless loop to communicate the results back to the console app.
      4. 用一个无限循环来打印返回信息
        Important: if using a loop, be sure to add a Thread.Sleep
        so that you do not cause performance problems in the target
        application. If a loop is not used then consider using a signal event
        such as an EventWaitHandle to prevent the Run method exiting prematurely.
      5. 注意:用循环的时候,一定要Sleep,否则影响目标程序性能。如果不再用循环,使用EventWaitHandle来提前结束它
/// <summary>
/// EasyHook will look for a class implementing <see cref="EasyHook.IEntryPoint"/> during injection. This
/// becomes the entry point within the target process after injection is complete.
/// </summary>
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[] { });
readFileHook.ThreadACL.SetExclusiveACL(new Int32[] { });
writeFileHook.ThreadACL.SetExclusiveACL(new Int32[] { }); _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(); string[] queued = null; lock (_messageQueue)
{
queued = _messageQueue.ToArray();
_messageQueue.Clear();
} // Send newly monitored file accesses to FileMonitor
if (queued != null && queued.Length > )
{
_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 < )
{
string mode = string.Empty;
switch (creationDisposition)
{
case :
mode = "CREATE_NEW";
break;
case :
mode = "CREATE_ALWAYS";
break;
case :
mode = "OPEN_ALWAYS";
break;
case :
mode = "OPEN_EXISTING";
break;
case :
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 = ; // 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 < )
{
// Retrieve filename from the file handle
StringBuilder filename = new StringBuilder();
GetFinalPathNameByHandle(hFile, filename, , ); // 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 < )
{
// Retrieve filename from the file handle
StringBuilder filename = new StringBuilder();
GetFinalPathNameByHandle(hFile, filename, , ); // 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
}

Step 2: Create the FileMonitor console app

第2步:创建一个FileMonitor控制台程序

Add a new C# console app project to the solution called FileMonitor.

Add the EasyHook NuGet package (right-click on the FileMonitor project, Manage NuGet Packages…), search for EasyHook and install.

添加EasyHook引用

Add a reference to FileMonitorHook that we added previously so that we can make use of the ServerInterface class we created.

添加FileMonitorHook引用,这样就能使用ServerInterface

Within Program.cs we will create the following two static methods:

在Program.cs 中创建两个静态方法

  • Main: the program entry point where we will perform the injection and then wait for the user to exit; and
  • Main程序进入,执行注入,等待退出
  • ProcessArgs: (to process the command line arguments).
  • ProcessArgs 来处理命令行参数

FileMonitor console app code

Within the console app we will be doing the following:

在控制台程序,我们做如下事情:

  1. Check command line arguments (either a process ID or path to executable - see ProcessArgs method in the source code listed below)
  2. 检查命令行参数(进程id或者exe路径)
  3. Create the IPC server channel using the EasyHook.RemoteHooking.IpcCreateServer<T> helper. The channel name will be generated for us for added security and we will be passing that into the target process during injection.
  4. 创建IPC服务(EasyHook.RemoteHooking.IpcCreateServer<T>),它能安全的注入目标进程
  5. Determine the full path to the payload “FileMonitorHook.dll”
  6. 确定注入内容FileMonitorHook.dll的完成路径
  7. If a target process Id has been provided
  8. 如果提供了进程id
    1. Inject the payload into the target process, passing the IPC channel name as the one and only parameter to send through to the payload as part of the injection process.
    2. 注入内容到目标进程,通过IPC通道名发送注入内容到进程
  9. If a target executable path has been specified
  10. 如果提供了 exe路径
    1. CreateAndInject the payload into the target process. Again the IPC channel name will be passed as the only parameter.
    2. 创建并且注入内容到目标经常,并且IPC通道名会作为唯一参数
class Program
{
static void Main(string[] args)
{
Int32 targetPID = ;
string targetExe = null; // Will contain the name of the IPC server channel
string channelName = null; // Process command line arguments or print instructions and retrieve argument value
ProcessArgs(args, out targetPID, out targetExe); if (targetPID <= && string.IsNullOrEmpty(targetExe))
return; // Create the IPC server using the FileMonitorIPC.ServiceInterface class as a singleton
EasyHook.RemoteHooking.IpcCreateServer<FileMonitorHook.ServerInterface>(ref channelName, System.Runtime.Remoting.WellKnownObjectMode.Singleton); // Get the full path to the assembly we want to inject into the target process
string injectionLibrary = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "FileMonitorHook.dll"); try
{
// Injecting into existing process by Id
if (targetPID > )
{
Console.WriteLine("Attempting to inject into process {0}", targetPID); // inject into existing process
EasyHook.RemoteHooking.Inject(
targetPID, // ID of process to inject into
injectionLibrary, // 32-bit library to inject (if target is 32-bit)
injectionLibrary, // 64-bit library to inject (if target is 64-bit)
channelName // the parameters to pass into injected library
// ...
);
}
// Create a new process and then inject into it
else if (!string.IsNullOrEmpty(targetExe))
{
Console.WriteLine("Attempting to create and inject into {0}", targetExe);
// start and inject into a new process
EasyHook.RemoteHooking.CreateAndInject(
targetExe, // executable to run
"", // command line arguments for target
, // additional process creation flags to pass to CreateProcess
EasyHook.InjectionOptions.DoNotRequireStrongName, // allow injectionLibrary to be unsigned
injectionLibrary, // 32-bit library to inject (if target is 32-bit)
injectionLibrary, // 64-bit library to inject (if target is 64-bit)
out targetPID, // retrieve the newly created process ID
channelName // the parameters to pass into injected library
// ...
);
}
}
catch (Exception e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("There was an error while injecting into target:");
Console.ResetColor();
Console.WriteLine(e.ToString());
} Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine("<Press any key to exit>");
Console.ResetColor();
Console.ReadKey();
} static void ProcessArgs(string[] args, out int targetPID, out string targetExe)
{
targetPID = ;
targetExe = null; // Load any parameters
while ((args.Length != ) || !Int32.TryParse(args[], out targetPID) || !File.Exists(args[]))
{
if (targetPID > )
{
break;
}
if (args.Length != || !File.Exists(args[]))
{
Console.WriteLine("Usage: FileMonitor ProcessID");
Console.WriteLine(" or: FileMonitor PathToExecutable");
Console.WriteLine("");
Console.WriteLine("e.g. : FileMonitor 1234");
Console.WriteLine(" to monitor an existing process with PID 1234");
Console.WriteLine(@" or : FileMonitor ""C:\Windows\Notepad.exe""");
Console.WriteLine(" create new notepad.exe process using RemoteHooking.CreateAndInject");
Console.WriteLine();
Console.WriteLine("Enter a process Id or path to executable");
Console.Write("> "); args = new string[] { Console.ReadLine() }; if (String.IsNullOrEmpty(args[])) return;
}
else
{
targetExe = args[];
break;
}
}
}
}

Example output

Here are some screenshots of the example output from the full solution found in the EasyHook-Tutorials GitHub repository.

下面是运行截图

CreateAndInject into a new notepad.exe process

创建并且新建一个notepad.exe的进程

Inject into an existing notepad.exe process using the process Id

注入刚才创建的notepad程序的进程id

Messages being returned to the console app from the injected payload.

打印注入的内容

问题记录

FileMonitor要添加System.Runtime.Remoting引用

EasyHook Creating a remote file monitor的更多相关文章

  1. TFS Build Error: CSC : fatal error CS0042: Unexpected error creating debug information file 'xxxx.PDB'

    CSC : fatal error CS0042: Unexpected error creating debug information file 'xxxx.PDB' -- 'c:\Builds\ ...

  2. oracle rman catalog--ORA-01580: error creating control backup file

    在测试rman catalog时,错误的设置了snapshot路径,报错 RMAN> show snapshot controlfile name; RMAN configuration par ...

  3. Observer - IO (File Monitor)

    1. 概述 有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 2. ...

  4. Creating a Unique File Name

    If you are working with files or file attachments in PeopleCode, you will typically want to create a ...

  5. 远程文件同步详解(Remote File Sync)

    1. 远程文件同步的常见方式: 1.cron + rsync 优点: 简单 缺点:定时执行,实时性比较差:另外,rsync同步数据时,需要扫描所有文件后进行比对,进行差量传输.如果文件数量达到了百万甚 ...

  6. 实战远程文件同步(Remote File Sync)

    1. 远程文件同步的常见方式: 1.cron + rsync 优点: 简单 缺点:定时执行,实时性比较差:另外,rsync同步数据时,需要扫描所有文件后进行比对,进行差量传输.如果文件数量达到了百万甚 ...

  7. judge remote file exist

    # -*- coding:utf-8 -*- import paramiko,os,sys,time print ''' *****判断远端服务器上的某个文件是否存在***** ''' ip = ra ...

  8. [PHP学习教程 - 文件]002.判断远程文件是否存在(Remote File Exists)

    引言:项目过程当中碰到了类似流程这样的需求,对服务器上的文件进行依次操作,如:检查文件格式->检查文件是否有更新->处理更新->同步其他服务器等等 如果需求的操作是依赖于远程文件是否 ...

  9. Unmanaged Exports not creating a .lib file

    别用VS2017!别用VS2017!别用VS2017!去吧.

随机推荐

  1. 移除数组中指定键(Yii2)

    /** * 移除数组中指定key * @param $data * @param $key * @return array */ public static function removeKey($d ...

  2. 关于ftp无法链接的情况

    首先查看ftp是否安装 systemctl status vsftpd 如果没有先安装 yum install vsftpd 然后启动 systemctl start vsftpd 如果有报错 Job ...

  3. Laravel5如何向闭合函数内传递参数 where function 传参

    如上,怎么将$title 像$query一样,在函数内部使用? $result = UserMenus::with(['menu'=>function($query){ $query->w ...

  4. multi gpu inference with tfserving or keras

    在tfserving上,目测只能在每个gpu上起docker    https://github.com/tensorflow/serving/issues/311#issuecomment-4801 ...

  5. 在同一个方法里,有redis,数据库和api,如何保证方法的事务性或者最终一致性?

    https://segmentfault.com/q/1010000017519179/a-1020000017547192

  6. html中checkbox自定义样式(css版本)

    <span class="choose"><input type="checkbox" class="input_check&quo ...

  7. curl检查访问网页返回的状态码

    urls=('www.baidu.com' 'mm.yellowurl.cn' 'm.yellowurl.cn' 'http://m.yellowurl.cn/product/a.html'); fo ...

  8. 移动端 使用 vConsole调试

    前言 用vue 写移动端代码,有个报名页面 就在iOS 9下出现问题,vue的循环渲染都正常,一开始的数据也能取到.证明不是vue的兼容性问题 但是在用户点击按钮发现不能点击进入跳转 工具 推荐使用  ...

  9. scikit-plot

    安装说明 安装Scikit-plot非常简单,直接用命令: pip install scikit-plot 即可完成安装. 仓库地址: https://github.com/reiinakano/sc ...

  10. GO语言学习笔记3-int与byte类型转换

    1.主机字节序 主机字节序模式有两种,大端数据模式和小端数据模式.在网络编程中应注意这两者的区别,以保证数据处理的正确性.例如,网络的数据是以大端数据模式进行交互,而我们的主机大多数以小端模式处理,如 ...