Hernan has added a new feature to the code offered in our article SQL Server Interception and SQL Injection Attack Prevention. Now it is possible to cancel queries as well as watch them. The code sample uses the Deviare Interception Engine’s call-skipping feature to abort the execution of the CSQLSource::Execute function. The code distribution includes a customized Deviare database for adding the definition of CSQLSource::Execute function parameters. To use this feature you must invoke the application with the “-a” switch in the command line.

We also improved console debugging output and fixed errors that came up when exiting sqlservr.exe. If the developer has a Deviare license, it can be added as a license.txt file in the application directory to disable the splash window.

If you liked this article, you might also like:

  1. Capturing Unencrypted HTTPS Requests and Responses (As Seen at BlackHat USA 2013)
  2. Recording Direct3D Video Games and Calculating Frames per Second
  3. Controlling the Speed of YouTube Videos

Resources

  1. Open Web Application Security Project

Today Manuel Fernandez is presenting HookME at Black Hat USA Arsenal 2013. HookME is software designed for intercepting communications which uses the Nektra Deviare Engine for binary instrumentation. HookME can intercept unencrypted HTTPS web traffic.

Many different proxy servers are used to intercept HTTP traffic. Fiddler is the most popular one for generic purposes. Burp Proxy is the leader for security testing. The issue with intercepting HTTPS traffic is that the proxy application acts as the man in the middle for HTTPS requests and the certificates being presented to the client are not the real ones. For example, intercepting network traffic to Amazon leads to a warning in the browser because the certificate being presented is unsigned. Another issue is that it is time consuming to set up the keys and certificates needed to configure the proxy.

Instead of using a proxy, you can intercept the EncryptMessage function in the Secure32.dll as HookME does:

You can also intercept the WinINet.dll before data is encrypted in the HTTPS request, and after data is decrypted in the HTTPS response. This way, no change is made to any certificate and no man in the middle attack is simulated. These concepts are a starting point for more sophisticated web debugging tools. You can start by intercepting synchronous functions in WinINet.DLL such as InternetOpen, HttpOpenRequest, HttpSendRequest, HttpAddRequestHeaders, InternetReadFile, InternetWriteFile, and their “Ex” functions counterpart.

 

If you liked this article, you might also like:

Related Products

Further Reading

  1. Sniffing the Unsniffable

What is your computer’s maximum render quality, resolution, and frames per second for Battlefield 3? Hard core gamers are eager to show off their expensive, tuned setup at its full potential. Overclocked processors and computers cooled with liquid hydrogen are lovely parts of the gaming folklore. The source code below instruments Direct3D 9 applications to calculate frames per second and capture video. It produces an AVI container using the video codec of your choice. It is also possible to capture game screenshots in the 32-bit BMP format by pressing “delete”. The shortcuts for starting and stopping video recording are “F11” and “F12”.

The code we have provided starts the target process in suspended mode, and hooks the Direct3DCreate9 entry point in the d3d9.dll.

Once we catch a Direct3DCreate9 call, we can use the returned IDirect3D9 pointer to index the VTable. Since the call occurred in the target process address space, we cannot use the pointer directly in our host application; however, we can use Deviare’s functions to read the hooked process memory (starting with the IDirect3D9 pointer) to get the method addresses. This is a very interesting and useful technique which avoids sending address data from the intercepted process to the host processes [1].

We use the object’s VTable address to get the address of the CreateDevice method and hook the device creation with INktSpyMgr::CreateHookForAddress and INktSpyMgr::AddCustomHandler. Note that the resulting events INktSpyMgr will trigger both local hook handlers in the SpyMgr host process, and remote hook handlers in the intercepted process. The local handler creates hooks for IDirect3DDevice9::EndScene, IDirect3DDevice9::Reset, IDirect3DDevice9::Present, and IDirect3DDevice9::Release using the remote VTable indexing technique. The remote handler initializes the FPS display, keyboard hook, font, and sprite resources for the console.

All hooking is done from the Deviare host executable. The plugin that resides in the target process address space does not create additional Deviare hooks. Once all needed IDirect3DDevice9 methods are successfully hooked, we can focus on implementing video capture and FPS display.

In order to implement video capture and FPS display we must define the behavior of the pre-call and post-call handlers for the Present, EndScene, and Reset methods.

In the pre-call to Present, we peek at the first item in the keyboard command queue to check for user actions. If it is the “take screenshot” command, we read the current display device backbuffer and save it to disk. If the first item is the “start recording” command, we create and setup the AVI Video file and it’s streams. If it is the “stop recording” command, we simply close the AVI file if it was recording frames. If video recording has been enabled, we add a frame to the current AVI file and update the frame per second statistics. The number of frames recorded depends on the specified frame rate (the default is 15).

In the pre-call to EndScene, if the device is not currently in a reset state, we update the console and frame counter text in the display. If it was not changed, it is simply redrawn with the current string buffer.

Pre-call handling of device Reset occurs when windows are resized, video mode changes, or fullscreen mode is entered or exited. If video is being recorded we stop recording since we do not handle multiple resolution streams in our code. Next, we release our font and sprite resources, and our backing display surface. During post-call Reset function handling, we also check for video recording. Additionally, we recreate the surface, sprite and font resources that we released in the Reset pre-call stage.

Possible Improvements

This code can be improved to include instrumentation of DirectX 11 as well as OpenGL applications and games. The technique would be similar. DirectX 10/11 applications use DXGI low-level interfaces to swap back and front buffers and there are no “lost devices”, so instead of hooking EndScene, Present, and Reset, we would hook DXGI library function calls. This approach would be easier than instrumenting DirectX9, since display device handling is simplified.

Audio Capture: Windows Vista and later systems direct all DirectX audio streams through the native AudioSession APIs. The easiest way to capture audio is through the documented loopback audio device. The limitation is that all system audio is captured, not only audio from our desired application.

If audio isolation is required, hooking the IAudioRenderClient interface should copy audio buffer data into user application memory. Audio data could then be transferred to the audio stream in the AVI file.

The key problem of audio capturing is resampling. If the sample rate and bit depth of the AVI stream and audio buffer do not match, the audio must be resampled. Writing a good resampler is not trivial task. Our Audio Recorder SDK provides a basic and fast resampling code.

Code

The AVRecorderTool code is available on github.

Prerequisites

  1. Deviare Instrumentation Engine
  2. Copy Deviare32.db, Deviare64.db, DeviareCOM.dll, DeviareCom64.dll, DvAgent.dll, and DvAgent64.dll to AVRecorderTool\dll

Acknowledgements

Sebastian Wain contributed with the writing of the introduction.

Notes

  1. The Intercepting Direct3D COM Objects and Making Game Walls Invisible article used IPC to send the remote IDirect3D interface address to the host application’s SpyMgr.

Related Products

  1. Deviare Hooking Engine
  2. Deviare In-Process
  3. SpyStudio
  4. Audio Recorder API

If you liked this article, you might also like:

  1. Injecting a DLL in a Modern UI Metro Application
  2. Intercepting Direct3D COM Objects and Making Game Walls Invisible
  3. How to Identify Virtual Table Functions with the VTBL IDA Pro Plugin
  4. Controlling the Speed of YouTube, Flash, HTML5, and Desktop Videos with Deviare Hooks
  5. Logging Printer Activity

Using Deviare to Cheat on Games

This simple Deviare code allows you to to see through game walls. We intercept Direct3D objects and select wireframe mode so the walls are transparent. This code injects a DLL to create an object based on the IDirect3D9 interface and hook the address of the CreateDevice COM method from the virtual table. The hooked CreateDevice COM method receives an object with IDirect3DDevice9 interface, which is used to set the wireframe mode by calling pDeviceInterface->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME). The pDeviceInterface->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID) call reverts to the solid mode. You can switch between the wireframe and the solid modes by using the INSERT and DELETE keys.

Deviare can be used to develop a lot of other game tools. Some ideas to try yourself are:

  • Hooking the random function in the Minesweeper to return zero and solve the game with just one click
  • Retrieving the 3D models from games which encrypt them
  • Implementing an Aimbot

It would be a dangerous thing for the gamer community if streamed online games such as OnLive succeeded. You cannot reverse engineer the cloud. Game cheating has a long early history, it would be bad to cut it. Gamers have been cheating on video games since their invention. It would be a shame to stop the fun.

Prerequisites

  1. Deviare interception engine

Acknowledgments

Code sample written by Douglas from Nektra.

If you like this article, you might also like:

  1. Controlling the Speed of YouTube, Flash, HTML5, and Desktop Videos with Deviare Hooks
  2. Automating Google’s Doodles: 4.9 Second Record on Hurdles
  3. SQL Server Interception and SQL Injection Attack Prevention

Note: we updated the code on August 23, 2013. The new code includes an “abort” feature, discussed in the article Instrumenting Microsoft SQL Server to Abort Dangerous Queries.

Our Deviare hooking engine can be used to hook into Microsoft SQL Server 2012 RTM (11.00.2100.60) and 2014 CTP1 queries at the application level. Tools like WireShark use a different approach since they intercept SQL Server traffic at the network level. The key benefits of intercepting queries at the application level are:

  • Since the query is a string you do not need to understand a protocol to retrieve it
  • As the query is received on a single point, you do not have to handle a variety of communication channels between the client and the server
  • It is possible to change the flow of the application and modify or cancel a query

The sample code below dynamically loads SQL Server public debugging symbols and hooks the CSQLSource::Execute function.

In 2013, the most critical security risk is injection. Our code can be used to develop your own monitoring and mitigation tool. With a lot of zero day bugs around it is not possible to depend only on vendors and their hotfixes. The problem is even worse for closed source applications. For example, Microsoft is ending support for Windows XP on April 8, 2014. How can your organization deal with that? The three main options are:

  1. Migrating to a newer operating system
  2. Paying for extra support
  3. Using tools like Deviare to quickly implement custom security sandboxes and security mitigation tools.

Code

The code is available on github.

Prerequisites

  1. Download Deviare and register it for 32-bit or 64-bit
  2. To enable the Deviare engine to use debugging symbols, the following library files are required: symsrv.dll which can be obtained in the Debugging Tools for Windows package and msdiaXX.dll which is part of the DIA SDK, available in Microsoft Visual Studio installation folder, under DIA SDK/bin directory. The Recommended Debugging Tools for Windows version is 6.12.2633 or higher. Tested DIA SDK versions are 9.0 (msdia90.dll), 10.0 (msdia100.dll) and 11.0 (msdia110.dll).
  3. You must copy those 32 and/or 64-bit DLLs to the dllx86 and dllx64 folders in the root directory of the project, depending on which platforms you want to target.
  4. SQL Server SQLSERVR.EXE service platform should match the project architecture for the interception to succeed.

Use

  1. Compile the solution
  2. Make sure sqlservr.exe service is running
  3. Copy all database files from the project’s DB folder into the binary output folder. See below for information about user-defined databases.
  4. Run SQLSvrIntercept.exe from the commandline with Administrator rights. It will load and cache the symbols under c:\symbols and then displays “Ready.”.

Development Notes

If the application will be deployed to a Microsoft Windows environment which does not have Visual Studio 2012, make sure that the runtime C++ dependencies are available. If they are missing:

  1. To deploy the Debug compilation you can copy the library from %ProgramFiles%\Microsoft Visual Studio 11.0\VC\redist\Debug_NonRedist\x64\Microsoft.VC110.DebugCRT to the System32 directory in the destination computer.
  2. To deploy the Release compilation you can copy the %ProgramFiles%\Microsoft Visual Studio 11.0\VC\redist\x64\Microsoft.VC110.CRT to the System32 directory in the destination computer, or install the Visual Studio 2012 Redistributables from http://www.microsoft.com/en-us/download/details.aspx?id=30679

Generating User Defined Databases

Deviare offers many advanced features such as counting, inspecting and dereferencing function parameters, getting parameter types, and intercepted function skip. A database containing the function signature is required for those features to work with a specific function.

The default Deviare databases are enough for most standard operating system libraries.
However, in case you need to intercept a function that is not present in the default databases, Deviare provides a tool to generate user defined databases. This tool is located in the Deviare-DbGenerator distribution. It should be fed with a C header file containing the function and type definitions to add.

A simple example (along with our own CSQLSource::Execute signature definition) is included in the DB folder for this project. The database files were generated from the sample BASE.H file.

Note that databases are not required for basic interception: you can intercept an anonymous function by specifying its address. However databases are required for query blocking, which uses the Deviare’s call-skipping feature.

Acknowledgments

Nektra’s Hernan Di Pietro wrote the SQL Server interception tool. After researching a bit without finding the correct function to hook he asked on Reverse Engineering Q&A where Brendan Dolan-Gavitt kindly pointed him to the correct function, allowing him to complete the project.

Notes

  1. Some of Microsoft’s product versions do not have public debugging symbols, which are required in order to use the SQL Server interception tool
  2. Not tested on SQL Server 2005 or SQL Server 2008

See Also

  1. Injecting a DLL in a Modern UI Metro Application
  2. Logging Printer Activity
  3. How to Identify Virtual Table Functions with the VTBL IDA Pro Plugin

Further Reading

  1. SQLProb: A Proxy-based Architecture towards Preventing SQL Injection Attacks

VTBL is an IDA script which identifies all the virtual tables found in any module of a native process. The virtual tables can be related to a COM or a C++ class. Unlike other tools, ours does not depend on a specific compiler to obtain a virtual table. This makes it an essential tool for reverse engineers.

The script works on all IDA versions. To use it, you must:

  1. Use IDA to disassembly the module you want to analyze.
  2. Load the “VTBL.IDC” script from File -> Script File or by using the ALT F7 shortcut.
  3. VTBL.EXE will be executed.
  4. Select the process you want to analyze.
  5. Enable the suspension of the process if you want to intercept the process from the beginning.
  6. Select the module you want to analyze. It must be the same module you disassemble in step one.
  7. Once the analysis is over, select the virtual table to obtain its cross reference. The tool displays the number of functions the virtual table contains.
  8. Hook the selected virtual table
  9. Close the VTBL.EXE dialog
  10. Both, the disassembled code and the IDA output window will display all processed cross references.

We tried the tool out on Notepad++.exe. See video below. We used open source software so we could compare the results with the original source code.

We ran Notepad++.exe, selected the Notepad++.exe module and waited until all the virtual tables had been analyzed. The tool displays a list of virtual tables with the following format: VTBL_X1_X2_X3, where X1 is the index, X2 the start address, and X3 the function count. We hooked the virtual table with “CD” in the index field.

After the process we closed VTDL.EXE and analyzed the results with IDA Pro.

Prerequisites

  1. Deviare Hooking Engine
  2. Compile the VTBL_Code\Helper\Helperhelper.vcproj
  3. Open the Visual Studio 2010 project
  4. Change the Form1.cs DLL imports to point to the helper.dll and DeviareCOM.dll
  5. Compile the project
  6. Open VLTB.idc and modify the full paths of DeviareTest.exe and CrossRef.dat

Source Code

VTBL is available as vtbl-ida-pro-plugin.

Related Services

  1. Reverse Engineering
  2. Interception and Filter Drivers Services

If you like this article, you might also like:

  1. Instrumenting Direct3D Applications to Capture Video and Calculate FPS
  2. Injecting a DLL in a Modern UI Metro Application
  3. SQL Server Interception and SQL Injection Attack Prevention
  4. Reverse Engineering and The Cloud

In Spy Studio: Solving an Issue on IE6 we troubleshot an issue that happens while virtualizing an Internet Explorer 6 (IE6) layer for Japanese using Symantec Workspace Virtualization. IE6 worked when IE8 was installed as the base operating system but not when the base was IE9. In the following screenshot we show how Spy Studio’s trace compare function analyzes the difference between running IE6 with the IE8 and with the IE9 base on the Windows 7 operating system.

As you can see, this user interface is much more informative than comparing a long list of line-oriented logs.

Imagine that an App-V Package does not work as expected. Can it be solved with the App-V Sequencer alone or do you need more advanced tools? Or what if the IT department tries to simplify the process of application virtualization with VMware ThinApp Factory, while the virtualization team tries to pinpoint an issue with Process Monitor? Comparing long logs is tedious and error prone. How can testing and troubleshooting be improved? These common issues extend to all application virtualization products, including: Symantec’s Workspace Virtualization, Spoon, and Cameyo.

Spy Studio and Deviare can both be used to troubleshoot virtualization. Spy Studio’s graphic console allows you to record application behavior and compare different runs in different environments. You can compare the differences between running a fully virtualized application and running it in a physical environment. Spy Studio complements Process Monitor, and can also import Process Monitor’s logs for comparison. In Comparing Traces with Spy Studio we provide a step by step example of how to compare traces.

Deviare is a more advanced tool which enables application virtualization professionals and QA engineers to build their own custom troubleshooting software.

If you are interested in learning how to develop and customize your own registry monitor visualization tool please take a look at Instrumenting Instrumenting Binary Applications with VBScript and Deviare.

Resources

  1. Application Virtualization Solution Overview and Feature Comparison Matrix
  2. Application Virtualization Troubleshooting and Support
  3. APP-V Troubleshooting … Demystified
  4. VMWare ThinApp Troubleshooting Blog
  5. P2V

Related Services

  1. Reverse Engineering
  2. Interception and Filter Drivers Services
  3. Application Virtualization and Packaging

See Also

  1. Benchmarking IE6 Virtualization: VMWare ThinApp vs. Symantec Workspace Virtualization
  2. Benchmarking Microsoft Office’s PowerPoint Virtualization: Microsoft App-V Vs. Symantec Workspace Virtualization

The Deviare Interception Engine includes a feature that allows developers to add plugins to hooks. When a hooked API is called, it will raise the OnFunctionCalled method of all attached plugins to allow the reading and writing of parameters, and passing custom parameters to the INktSpyMgr object. Since the method is called in the context of the hooked application, the plugin will have local access to all process data.

We use COM as the bridge between the plugin code and the internal engine to support plugin development in C/C++ and any other .NET language. To use COM, the programmer must either register DeviareCOM libraries using RegSvr32 or use Registration Free COM.

The second method is best as it avoids registering components on the computer and asking for elevated privileges to accomplish the task. However in order to enable RegFree-COM, you will need to access the hooked executable manifest file, which is usually embedded in the application, and modify it, which may infringe copyright and have undesirable side effects. In addition, you cannot attach a manifest file to Deviare’s agent module: it will simply be ignored because the OS loader only parses executable manifests, and only on startup.

When a hooked api is called, an INktHookCallInfoPlugin object is created and passed to the plugins to let them know the context of the api call. But, how did we solve the management of COM objects without registering COM classes nor using a manifest file? Here is where Activation Context API comes in.

Activation context API lets us create, activate and manage activation contexts in the same way Windows loader does. First we embed our manifest file as a resource inside DvAgent.dll. Once the agent is attached to the hooked process, it creates an activation context using the data of the embedded resource.

When a hooked API is called, we must:

  1. Activate our custom context.
  2. Create the INktHookCallInfoPlugin object.
  3. Call the plugins callbacks (letting them manage all DeviareCOM objects as they want)
  4. Deactivate the custom context and continue execution.

Our benchmark shows that performance penalty in activating/deactivating contexts is minimal. If you need additional help using registration free COM please do not hesitate to ask in our forum.

Related Services

  1. Reverse Engineering
  2. Interception and Filter Drivers Services
  3. Application Virtualization and Packaging

See Also

  1. Improving Deviare Hooking Performance with Custom Hooks

More Binary Instrumentation Alternatives

Deviare now has custom hooks to improve hooking performance. With custom hooks, two “OnFunctionCall” events are triggered: one in the SpyMgr process, and the other within the agent. The custom hook can send data, or custom parameters. to the SpyMgr process. The custom Deviare modules running in the agent have access to all process data, including file and registry handles. Since custom hooks access local process data, it is now possible to get the full key path of a registry HKEY value and transmit the resulting string as a custom parameter instead of doing a “DuplicateHandle” call in the SpyMgr process. You can also combine asynchronous hooking with custom hooks to keep the “OnFunctionCall” event in SpyMgr from blocking the flow of the application.

The Deviare Hooking Engine aims to simplify the “hooking experience” for people who are outside the Windows internals, x86/x64 Assembly, and reverse engineering fields, there is usually a penalty when you want to abstract something complex like API function hooking in a developer friendly COM component like Deviare. Our custom hooks are designed to meet different needs.

Another interesting advantage of using custom hooks is that you can mix different programming languages. For example, you can add a custom hook in C++ but notify the SpyMgr in C#, or vice versa.

The sample code below monitors and intercepts file mappings via the function MapViewOfFile on notepad.exe 32 and 64 bit. The custom hook analyzes the buffer in search of a specific string posing as malware. The read is cancelled if that string is found in the buffer. The sample code has two modes to illustrate the difference between only using SpyMgr, and relying on custom hooks: the displayed combo switches from one mode to the other. Custom hooks are also essential for video and audio processing. If you are interested in bootstrapping a project that requires audio hooking, do not hesitate to look at our Audio Recorder API. It will save you a lot of time and money. This code also hooks both 32 and 64 bit process from the same (“Any CPU”) assembly.

Code

The code is available on github.

Prerequisites

  1. Install the latest Deviare version
  2. Register 32 and 64 bit COM DLLs (although registration-free COM can be used in custom hooks too)
  3. Download the source code
  4. Copy Nektra.Deviare2.dll to the Lib directory inside the project
  5. Build the Debug configuration
  6. Run Notepad
  7. Run the hooking console

 

One of the top uses of our Windows Live Mail API is integrating a vendor antivirus to the Windows Live Mail desktop client. For example, Trend Micro’s Titanium Security Solutions uses it to integrate with Windows Live Mail and recognize viruses. The C# code sample below uses the nClam library to interface with an open source project: Clam AntiVirus.

While integrating Windows Live Mail API with an antivirus engine helps to mitigate risks from e-mails there are obviously many other sources of security risks. Exploits came in many disguises: they can take advantage of vulnerabilities of web browsers or of network services running on your machine. The Deviare Interception Engine can unmask them. It intercepts network APIs and can be used to discover and deactivate malware at the network level, not just within a specific application.

The code can be modified to work with commercial antivirus engines such as those by Symantec, Trend Micro, McAfee, Kaspersky, F-Secure, ESET, AVG, Microsoft, Bitdefender, and GFI. Since the list is long and each of those APIs has different idiosyncrasies, companies such as OPSWAT have developed a unified API. The various “Malware Analysis Online Services” can also be easily integrated to a custom solution.

Code

Prerequisites

  1. Visual Studio 2010
  2. Request a trial for Windows Live Mail API here
  3. Download ClamAV here and VC redistributables if needed
  4. Extract the Clam Anti Virus binaries to a specific directory and create an empty folder called DB there
  5. Run CMD as Administrator, go to the ClamAV directory and run freshclam.exe
  6. Install the ClamAV service by running: clamd –install
  7. Install the ClamAV update service by running: freshclam –install
  8. Download nClam from here and add/modify it as a reference in the project
  9. Get the Windows Live Mail API Anti-Virus example from github

Main.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Win32;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using NktWLMailApi;
using NktWLMailApiInit;
using NktWLMailStore;
using nClam;

namespace Antivirus
{
    public class Main
    {
        static string pluginsKey = "Software\Nektra\WLMailApi\Plugins";
        static string pluginValueName = "AntivirusDemo";
        static string pluginValueData = "Antivirus.Main";
        private NktWLMailApiInit.WLMailApiInit wlmailApiInit;
        private NktWLMailApi.WLMailApi wlmailApiCore;
        private NktWLMailStore.FolderManager folderManager;
        private NktWLMailStore.MailAccountManager accountManager;
        private Utils utils;
        private nClam.ClamClient clamClient;
        private const string CLAM_HOST = "localhost";
        private const int CLAM_PORT = 3310;

        [ComRegisterFunctionAttribute]
        public static void RegisterFunction(Type t)
        {
            RegistryKey key = Registry.LocalMachine.OpenSubKey(pluginsKey, true);
            if (key == null)
            {
                key = Registry.LocalMachine.CreateSubKey(pluginsKey);
                if (key == null)
                {
                    System.Windows.Forms.MessageBox.Show("Error registering component");

                    return;
                }
            }

            key.SetValue(pluginValueName, pluginValueData);
            key.Close();
        }

        [ComUnregisterFunctionAttribute]
        public static void UnregisterFunction(Type t)
        {
            RegistryKey key = Registry.LocalMachine.OpenSubKey(pluginsKey, true);
            if (key == null)
            {
                return;
            }

            key.DeleteValue(pluginValueName, false);
            key.Close();
        }

        public Main()
        {
            wlmailApiInit = new NktWLMailApiInit.WLMailApiInit();
            wlmailApiInit.OnInit += new NktWLMailApiInit.IWLMailApiInitEvents_OnInitEventHandler(wlmailApiInit_OnInit);
            wlmailApiInit.OnShutdown += new NktWLMailApiInit.IWLMailApiInitEvents_OnShutdownEventHandler(wlmailApiInit_OnShutdown);

        }

        void wlmailApiInit_OnShutdown()
        {
            wlmailApiCore = null;
            wlmailApiInit.OnInit -= new NktWLMailApiInit.IWLMailApiInitEvents_OnInitEventHandler(wlmailApiInit_OnInit);
            wlmailApiInit.OnShutdown -= new NktWLMailApiInit.IWLMailApiInitEvents_OnShutdownEventHandler(wlmailApiInit_OnShutdown);

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        void wlmailApiInit_OnInit()
        {
            wlmailApiCore = new NktWLMailApi.WLMailApi();
            folderManager = new NktWLMailStore.FolderManager();
            accountManager = new NktWLMailStore.MailAccountManager();

            utils = new Utils(wlmailApiCore, folderManager, accountManager);

            this.clamClient = new ClamClient(CLAM_HOST, CLAM_PORT);

            this.wlmailApiCore.OnDatabaseChange += new NktWLMailApi.IWLMailApiEvents_OnDatabaseChangeEventHandler(wlmailApiCore_OnDatabaseChange);
        }

        void wlmailApiCore_OnDatabaseChange(NktWLMailApi.tagDATABASE_TRANSACTION dt, ulong folderId, ulong objId, ulong newParentId)
        {
            var folder = folderManager.GetFolder(folderId);

            if (utils.IsFromQuickViews(folder) || !ShouldBeHandled(dt))
            {
                utils.ReleaseComObject(folder);
                return;
            }

            var message = folder.GetMessage((int)objId);

            switch (dt)
            {
                case tagDATABASE_TRANSACTION.NKT_TR_INSERT_MESSAGE:
                    if (folder.IsSent() == 0 && folder.IsOutbox() == 0)
                    {
                        List<string&rt; virusList = MessageAttachmentsContainViruses(message);

                        if (virusList.Count &rt; 0)
                        {
                            string virusNames = "";
                            foreach (string virusName in virusList)
                            {
                                if (virusNames != "")
                                    virusNames += "," + virusName;
                                else
                                    virusNames = virusName;
                            }
                            MessageBox.Show(string.Format("The message contains the following viruses: {0}", virusNames));
                        }
                    }
                    break;
            }
            utils.ReleaseComObject(message);
            utils.ReleaseComObject(folder);
        }

        private string ClamAntiVirusScanning(string filename) {
            var scanResult = this.clamClient.ScanFileOnServer("c:\users\Admin\Desktop\Temp");

            switch (scanResult.Result)
            {
                case ClamScanResults.Clean:
                    return null;
                    break;

                case ClamScanResults.VirusDetected:
                    return scanResult.InfectedFiles.First().VirusName;
                    break;

                case ClamScanResults.Error:
                    return null;
                    break;
            }

            return null;

        }

        private string GetTemporaryDirectory() // http://stackoverflow.com/questions/278439/creating-a-temporary-directory-in-windows
        {
            string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
            Directory.CreateDirectory(tempDirectory);
            return tempDirectory;
        }

        private List<string&rt; MessageAttachmentsContainViruses(NktWLMailStore.Message message)
        {
            List<string&rt; virusList = new List<string&rt;();

            var attachment = message.GetFirstAttachment();
            string temporaryDirectory = this.GetTemporaryDirectory();

            while (attachment != 0)
            {
                string fullpath = Path.Combine(temporaryDirectory, message.GetFilename(attachment).ToLower() + Path.GetRandomFileName());

                if (message.SaveBodyToFile(attachment, fullpath, 0) == 0)
                {
                    utils.ShowMsgBox("An error occurred while saving message attachments.");

                    break;
                }

                string scanResultString = ClamAntiVirusScanning(fullpath);
                if (scanResultString != null)
                {
                    virusList.Add(scanResultString);
                }

                attachment = message.GetNextAttachment();
            }

            utils.ReleaseComObject(message);

            return virusList;
        }        

        private bool ShouldBeHandled(tagDATABASE_TRANSACTION dt)
        {
            return (dt == tagDATABASE_TRANSACTION.NKT_TR_INSERT_MESSAGE ||
                    dt == tagDATABASE_TRANSACTION.NKT_TR_DELETE_MESSAGE ||
                    dt == tagDATABASE_TRANSACTION.NKT_TR_RENAME_FOLDER ||
                    dt == tagDATABASE_TRANSACTION.NKT_TR_DELETE_FOLDER);
        }
    }

    // Do not pay attention to this class.
    public class WindowWrapper : IWin32Window
    {
        public WindowWrapper(IntPtr handle)
        {
            _hwnd = handle;
        }

        public IntPtr Handle
        {
            get { return _hwnd; }
        }

        private readonly IntPtr _hwnd;
    }
}

Utils.cs

using System;
using System.Globalization;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NktWLMailApi;
using NktWLMailApiInit;
using NktWLMailStore;
using System.Windows.Forms;
using Message = NktWLMailStore.Message;

namespace Antivirus
{
    public class Utils
    {
        private WLMailApi _wlmailApiCore;
        private FolderManager _folderManager;
        private MailAccountManager _accountManager;

        private const ulong QuickViewsFolderID = 7;

        public Utils(WLMailApi wlmailApiCore, FolderManager folderManager, MailAccountManager accountManager)
        {
            _wlmailApiCore = wlmailApiCore;
            _folderManager = folderManager;
            _accountManager = accountManager;
        }

        public void ShowMsgBox(string text)
        {
            ShowMsgBoxHwnd(_wlmailApiCore.GetMainWindow(), text);
        }

        public void ShowMsgBox(IntPtr ownerWindow, string text)
        {
            ShowMsgBoxHwnd((int)ownerWindow, text);
        }

        private static void ShowMsgBoxHwnd(int hwnd, string text)
        {
            MessageBox.Show(new WindowWrapper((IntPtr)hwnd), text, @"WLMailApi", MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
        }

        public Message GetFirstSelectedMessage()
        {
            var folder = GetCurrentFolder();

            var messageID = _wlmailApiCore.GetFirstSelectedMessageID();

            var message = folder.GetMessage(messageID);

            ReleaseComObject(folder);

            return message.GetID() == -1 ? null : message;
        }

        public Message GetNextSelectedMessage()
        {
            var folder = GetCurrentFolder();

            var messageID = _wlmailApiCore.GetNextSelectedMessageID();

            var message = folder.GetMessage(messageID);

            ReleaseComObject(folder);

            return message.GetID() == -1 ? null : message;
        }

        public Folder GetCurrentFolder()
        {
            var folderId = (ulong)_wlmailApiCore.GetSelectedFolderID();
            return _folderManager.GetFolder(folderId);
        }

        public bool IsFromQuickViews(Folder folder)
        {
            var rootFolder = _folderManager.GetFolder((int)tagSPECIALFOLDERID.NKT_FOLDER_ROOT);
            var rootFolderId = rootFolder.GetID();
            ReleaseComObject(rootFolder);
            rootFolder = null;

            var childrenIds = GetChildrenIds((int)rootFolderId);

            var quickViewsFoldersIds = new List<int&rt;();

            foreach (var childId in childrenIds)
            {
                var child = _folderManager.GetFolder((ulong)childId);
                if (child.GetID() == QuickViewsFolderID)
                {
                    quickViewsFoldersIds = GetChildrenIds(childId);
                    ReleaseComObject(child);
                    break;
                }
                ReleaseComObject(child);
            }

            return quickViewsFoldersIds.Contains((int)folder.GetID());
        }

        private List<int&rt; GetChildrenIds(int folderId)
        {
            var folder = _folderManager.GetFolder((ulong)folderId);

            var childFolder = folder.GetFirstChild();

            var childrenIds = new List<int&rt;();

            while (childFolder != null)
            {
                childrenIds.Add((int)childFolder.GetID());
                ReleaseComObject(childFolder);
                childFolder = folder.GetNextChild();
            }

            childFolder = null;

            ReleaseComObject(folder);
            folder = null;

            return childrenIds;
        }

        public int CommitIfNotInQuickViews(Message msg)
        {
            var folder = _folderManager.GetFolder(msg.GetFolderID());

            if (IsFromQuickViews(folder))
            {
                ShowMsgBox("You can't save nor modify a message " +
                           "in a Quick Views folder.n");
                return 0;
            }

            ReleaseComObject(folder);
            folder = null;

            return msg.Commit();

        }

        public int MoveIfNotInQuickViews(Message msg, int destFolderId)
        {
            var folder = _folderManager.GetFolder(msg.GetFolderID());

            if (IsFromQuickViews(folder))
            {
                ShowMsgBox("You can't move a message you selected " +
                            "from a Quick Views folder.n");
                return 0;
            }

            ReleaseComObject(folder);
            folder = null;

            return _folderManager.MoveMessage(msg.GetFolderID(), (ulong)destFolderId, msg.GetID());
        }

        public void CleanWLMailApiReferences()
        {
            _wlmailApiCore = null;
            _folderManager = null;
            _accountManager = null;
        }

        public int ReleaseComObject(object obj)
        {
            var remainingRefs = -1;

            if (obj != null)
            {
                remainingRefs = Marshal.ReleaseComObject(obj);
                obj = null;
            }

            return remainingRefs;
        }

        public class RegexUtils
        {
            static bool _invalid;

            public static bool IsValidEmail(string strIn)
            {
                _invalid = false;
                if (String.IsNullOrEmpty(strIn))
                    return false;

                // Use IdnMapping class to convert Unicode domain names.
                strIn = Regex.Replace(strIn, @"(@)(.+)$", DomainMapper);
                if (_invalid)
                    return false;

                // Return true if strIn is in valid e-mail format.
                return Regex.IsMatch(strIn,
                       @"^(?("")(""[^""]+?""@)|(([0-9a-z]((.(?!.))|[-!#$%&'*+/=?^`{}|~w])*)(?<=[0-9a-z])@))" +
                       @"(?([)([(d{1,3}.){3}d{1,3}])|(([0-9a-z][-w]*[0-9a-z]*.)+[a-z0-9]{2,17}))$",
                       RegexOptions.IgnoreCase);
            }

            private static string DomainMapper(Match match)
            {
                // IdnMapping class with default property values.
                var idn = new IdnMapping();

                string domainName = match.Groups[2].Value;
                try
                {
                    domainName = idn.GetAscii(domainName);
                }
                catch (ArgumentException)
                {
                    _invalid = true;
                }
                return match.Groups[1].Value + domainName;
            }
        }

    }
}