Quickstart

Introduction

This quick start will show the basic capabilities for intercepting system calls with Deviare from a C# program. Requeriments are: a proper C# compiler/editor (VisualStudio or SharpDevelop), and Deviare 1.0.2.

Project Setup

Install Deviare Commercial Edition. The referenced version in this document is 1.0.2.

We'll use Deviare with C#. Open a Visual Studio session and create a new C# Windows Forms project. Name it as 'DeviareTest'.

With the project successfully setup, add the Deviare library references to the project. Right-Click on References on the Solution Explorer, Add References.... Navigate to the COM tab, and select the following components:

  • Deviare 1.1 Type Library
  • Deviare Common 1.1 Type Library
  • DeviareDB 1.1 Type Library
  • DeviareParams 1.1 Type Library
  • DeviareTools 1.1 Type Library
Click Ok to add the component references to your C# project.

In the default form of your project, insert a Textbox control from the toolbox that will act as our output window.

Set the following properties to the TextBox:

  • Name: txtOutput
  • Multiline: True
  • Scrollbars: Both
  • Readonly: True
  • Dock: Fill
If you are working with a x64 operating system, change your Build project settings to force compilation to x86 to tell the .NET framework to find the registered Deviare COM classes in the WOW64 registry instead of the 64-bit native registry.

To do this right click on your Project, "Build" tab, and set your platform target to X86.

Hooking Basics

Our task will be to hook CreateFileW system calls. In this case, we'll use a known application such as NOTEPAD.EXE as a target for our example.

Prior to call any Deviare library methods, we need to initialize the SpyMgr class. This can be done in the Form constructor:

_spyMgr = new Deviare.SpyMgr();
_proxy = _spyMgr.CreateHooksEventsProxy() as Deviare.HookEventsProxy;
_proxy.Attach(_spyMgr.Hooks);

_proxy is a variable of type Deviare.HookEventsProxy. Attaching hooks to an event proxy makes possible to execute interception handling code on the UI thread. If proxies are avoided, the function interception handler code will be called in a different thread. This is useful for non-blocking background processing, but for this example we'll output data on every interception call. Nevertheless, an example of calling UI methods without proxies is provided at the end of this document.

Just to be safe, we can use the process enumeration facilities in Deviare to check if Notepad is running, as in the following function:

private bool IsProcessRunning(string procName, out DeviareTools.IProcess proc)
        {
            DeviareTools.IEnumProcesses iEnumProc = _spyMgr.get_Processes(0).Enumerator;
            DeviareTools.IProcess iProc = iEnumProc.Next;

            bool found = false;
            while (iProc != null && !found)
            {
                if (iProc.Name.ToLower() == procName)
                {
                    found = true;
                    break;
                }
                iProc = iEnumProc.Next;
            }

            proc = (found ? iProc : null);
            return found;
        }

Note that this function will return the referenced process, if it's found, through the 3rd out parameter.

This process reference is used in our example to tell Deviare which process we want to target for hooking.

On the Form load event handler, we create a new Deviare.Hook object passing the system call to be intercepted in module!function format to the spyMgr.CreateHook function (_hook_CreateFileW is of type Deviare.Hook):
 
 _hook_CreateFileW = _spyMgr.CreateHook("kernel32.dll!CreateFileW");

When this system call is executed in the context of the target process, we catch it in our proxy event handler, to be able to process it under UI thread, as we mentioned above.

   hk.Properties = (int) DeviareCommonLib.NktHookFlags._call_before;
   proxy.OnFunctionCalled += new Deviare.DHookEvents_OnFunctionCalledEventHandler(_proxy_OnFunctionCalled);

Note the hk.Properties specifying that our handler will be called *before* hooked function execution begins. Other option is _call_after, which calls our handler when the hooked function terminates (right after return), making possible to inspect return values.

Now we specify the processes we want to attach. iproc was returned from the IsProcessRunning function above, and in our example contains information about NOTEPAD process.

    _hook_CreateFileW.Attach(iproc);
    _hook_CreateFileW.Hook();

Finally, Hook::Hook() call initiates interception mechanism for the specified functions, on the specified process and under the configured properties.

Interception Handling

Now let's see how we can implement a simple reporting routine for every CreateFileW call.

 void _proxy_OnFunctionCalled(Deviare.Hook h, DeviareTools.IProcess proc, 
 DeviareParams.ICallInfo callInfo, 
 Deviare.IRemoteCall rCall)
        {
// handling code here...           
        }

As an example, we want to report the CreateFileW function parameters for each interception. It's prototype is:

HANDLE WINAPI CreateFile(
  __in      LPCTSTR lpFileName,
  __in      DWORD dwDesiredAccess,
  __in      DWORD dwShareMode,
  __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __in      DWORD dwCreationDisposition,
  __in      DWORD dwFlagsAndAttributes,
  __in_opt  HANDLE hTemplateFile
);

We can easily traverse those parameters using the callInfo.Params enumerator:

            DeviareParams.IEnumParams enumParms = callInfo.Params.Enumerator;
            DeviareParams.IParam param = enumParms.Next;

            while (param != null)
            {
                /// ... printout parameter data ...                

                param = enumParms.Next;
            }

In this example however it's easier to get the seven CreateFileW parameters since we know the expected types of each parameter. So we can replace the enumeration loop above with:

            string lpFileName = (string)callInfo.Params.get_Item(0).Value;
            uint dwDesiredAccess = (uint)callInfo.Params.get_Item(1).Value;
            uint dwShareMode = (uint)callInfo.Params.get_Item(2).Value;
            uint lpSecAttr = (uint)callInfo.Params.get_Item(3).Value;
            uint dwCreationDisposition = (uint)callInfo.Params.get_Item(4).Value;
            uint dwFlags = (uint)callInfo.Params.get_Item(5).Value;
            uint hTemplate = (uint)callInfo.Params.get_Item(6).Value;
   txtOutput.AppendText(Environment.NewLine);
            txtOutput.AppendText(string.Format("CreateFileW(\"{0}\",{1},{2},{3},{4},{5},0x{6})", lpFileName, 
            dwDesiredAccess, dwShareMode, lpSecAttr, dwCreationDisposition, dwFlags, hTemplate.ToString("x8")));

With this code, we get console output like:

CreateFileW("C:\Documents and Settings\hernan\My Documents\*.txt",2147483648,1,0,3,128,0x00000000)
CreateFileW("C:\Documents and Settings\hernan\My Documents\WOW.txt",2147483648,1,0,3,128,0x00000000)
CreateFileW("C:\Documents and Settings\hernan\My Documents\WOW.txt",2147483648,3,0,3,128,0x00000000)

Further refinement could interpret every parameter and translate them to symbolic constants, for example, with the following snippet:

List<String> lda = new List<string>();
if ( (dwDesiredAccess & 0x10000000) == 0x10000000 ) lda.Add("GENERIC_ALL");
if ( (dwDesiredAccess & 0x40000000) == 0x40000000 ) lda.Add("GENERIC_WRITE");
if ( (dwDesiredAccess & 0x80000000) == 0x80000000 ) lda.Add("GENERIC_READ");
string sDesiredAccess = string.Join("|", lda.ToArray());   

The above output is now:

CreateFileW("C:\Documents and Settings\hernan\My Documents\*.txt",GENERIC_READ,1,0,3,128,0x00000000)
CreateFileW("C:\Documents and Settings\hernan\My Documents\WOW.txt",GENERIC_READ,1,0,3,128,0x00000000)
CreateFileW("C:\Documents and Settings\hernan\My Documents\WOW.txt",GENERIC_READ,3,0,3,128,0x00000000)

Alternative to proxies

We've been using proxies to receive events on the UI thread. It's possible to skip using proxies and asynchronously execute UI code through a proper delegate invocation. The trick is based on the fact that calling Invoke method on a UI control will execute the delegate code in the UI thread.
Instead of adding proxy function handlers, we directly attach a new handler to Deviare.Hook, replacing:

_proxy.OnFunctionCalled += new Deviare.DHooksEvents_OnFunctionCalledEventHandler(_proxy_OnFunctionCalled);

with:

_hook_CreateFileW.OnFunctionCalled += new Deviare.DHookEvents_OnFunctionCalledEventHandler(_hook_CreateFileW_OnFunctionCalled);

The new handler code will call the methods on TextBox (text append) through a delegate:

   void _hook_CreateFileW_OnFunctionCalled(DeviareTools.IProcess proc, DeviareParams.ICallInfo callInfo, Deviare.IRemoteCall rCall)
        {
            string lpFileName = (string)callInfo.Params.get_Item(0).Value;
            uint dwDesiredAccess = (uint)callInfo.Params.get_Item(1).Value;
            uint dwShareMode = (uint)callInfo.Params.get_Item(2).Value;
            uint lpSecAttr = (uint)callInfo.Params.get_Item(3).Value;
            uint dwCreationDisposition = (uint)callInfo.Params.get_Item(4).Value;
            uint dwFlags = (uint)callInfo.Params.get_Item(5).Value;
            uint hTemplate = (uint)callInfo.Params.get_Item(6).Value;

            List<String> lda = new List<string>();
            if ((dwDesiredAccess & 0x10000000) == 0x10000000) lda.Add("GENERIC_ALL");
            if ((dwDesiredAccess & 0x40000000) == 0x40000000) lda.Add("GENERIC_WRITE");
            if ((dwDesiredAccess & 0x80000000) == 0x80000000) lda.Add("GENERIC_READ");
            string sDesiredAccess = string.Join("|", lda.ToArray());

            txtOutput.Invoke(new AppendTextDelegate(AppendText),
               new Object[] {
              string.Format("CreateFileW(\"{0}\",{1},{2},{3},{4},{5},0x{6})", lpFileName, sDesiredAccess, dwShareMode,
                lpSecAttr, dwCreationDisposition, dwFlags, hTemplate.ToString("x8"))});
         
        }

With delegate declaration and code:
private delegate void AppendTextDelegate(string t);

void AppendText(string t)
{
    txtOutput.AppendText(Environment.NewLine);
    txtOutput.AppendText(t);
}

Further research

Feel free to investigate and modify the C++/C# Deviare samples available, along with examining the API documentation provided.

Comments