Hooking COM interfaces

Sep 28, 2010 at 8:00 PM

Just FYI, a class that queries COM interfaces, so you can hook the function. Use it like this:

            COMclassQuery.COMclassQuery.COMclassInfo cci = new COMclassQuery.COMclassQuery.COMclassInfo(typeof(AVRSimulatorMKIILib.SimulatorMKIIClass), typeof(AVRSimulatorMKIILib.IAvrTarget), "Run");
            COMclassQuery.COMclassQuery.Query(cci);

            hookSimMk2Run = EasyHook.LocalHook.Create(cci.FunctionPointer, new Delegate_SimulatorMKIIClass_IAvrTarget_Run(SimulatorMKIIClass_IAvrTarget_Run), null);
            hookSimMk2Run.ThreadACL.SetExclusiveACL(new Int32[] { });

Sometimes rpcrt4.dll hijacks (puts proxies into) the vtable, so we need to go little deeper^^

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Reflection;

namespace COMclassQuery
{
    [ComImport()]
    [Guid("00000001-0000-0000-C000-000000000046")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IClassFactory
    {
        [return: MarshalAs(UnmanagedType.I4)]
        [PreserveSig]
        int CreateInstance(
            [In, MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
            ref Guid riid,
            [Out, MarshalAs(UnmanagedType.Interface)] out object obj);

        [return: MarshalAs(UnmanagedType.I4)]
        [PreserveSig]
        int LockServer(
            [In] bool fLock);
    }

    [ComImport()]
    [Guid("B196B28F-BAB4-101A-B69C-00AA00341D07")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IClassFactory2
    {
        [return: MarshalAs(UnmanagedType.Interface)]
        Object CreateInstance(
          [In, MarshalAs(UnmanagedType.Interface)] Object unused,
          [In, MarshalAs(UnmanagedType.LPStruct)] Guid iid);

        void LockServer(Int32 fLock);

        IntPtr GetLicInfo(); // TODO : an enum called LICINFO

        [return: MarshalAs(UnmanagedType.BStr)]
        String RequestLicKey(
          [In, MarshalAs(UnmanagedType.U4)] int reserved);

        [return: MarshalAs(UnmanagedType.Interface)]
        Object CreateInstanceLic(
          [In, MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
          [In, MarshalAs(UnmanagedType.Interface)] object pUnkReserved,
          [In, MarshalAs(UnmanagedType.LPStruct)] Guid iid,
          [In, MarshalAs(UnmanagedType.BStr)] string bstrKey);
    }

    class COMclassQuery
    {
        public class COMclassInfo : IDisposable
        {
            public COMclassInfo(Type classtype, Type interfacetype, string functionname)
            {
                m_ClassType = classtype;
                m_InterfaceType = interfacetype;
                m_MethodInfo = m_InterfaceType.GetMethod(functionname); 
            }

            Type m_ClassType;
            public Type ClassType { get { return m_ClassType; } }

            Type m_InterfaceType;
            public Type InterfaceType { get { return m_InterfaceType; } }

            MethodInfo m_MethodInfo;
            public string Functionname { get { return m_MethodInfo.Name; } }
            public MethodInfo Method { get { return m_MethodInfo; } }

            internal IntPtr m_FunctionPointer = IntPtr.Zero;
            public IntPtr FunctionPointer { get { return m_FunctionPointer; } }

            internal System.Diagnostics.ProcessModule m_ModuleHandle;
            public System.Diagnostics.ProcessModule LibraryOfFunction
            {
                get
                {
                    if (m_ModuleHandle == null)
                    {
                        DetermineModuleHandle();
                    }
                    return m_ModuleHandle;
                }
            }

            internal void DetermineModuleHandle()
            {
                System.Diagnostics.Process pr = System.Diagnostics.Process.GetCurrentProcess();
                foreach (System.Diagnostics.ProcessModule pm in pr.Modules)
                {
                    if (m_FunctionPointer.ToInt64() >= pm.BaseAddress.ToInt64() &&
                        m_FunctionPointer.ToInt64() <= pm.BaseAddress.ToInt64() + pm.ModuleMemorySize)
                    {
                        m_ModuleHandle = pm;
                        return;
                    }
                }
                m_ModuleHandle = null;
            }

            public void Dispose()
            {
                Marshal.Release(m_FunctionPointer);
            }
        }

        private delegate int DllGetClassObjectDelegate(ref Guid ClassId, ref Guid InterfaceId, [Out, MarshalAs(UnmanagedType.Interface)] out object ppunk);

        public static unsafe void Query(COMclassInfo cci)
        {
            Guid classguid = cci.ClassType.GUID;
            Guid interfguid = cci.InterfaceType.GUID;
            Guid classfactoryguid = typeof(IClassFactory).GUID;
            Guid classfactory2guid = typeof(IClassFactory2).GUID;
            object classinstance = null;

#if false
            // create an instance via .NET built-in functionality
            // vtable might be hijacked by rpcrt4.dll
            classinstance = cci.ClassType.InvokeMember("", BindingFlags.CreateInstance, null, null, null);
#endif
#if false
            // create via ole-convenience-function
            // vtable might be hijacked by rpcrt4.dll
            OLE32.CoCreateInstance(ref classguid, null, 1 + 4, ref interfguid, out classinstance);
#endif
#if false
            // create via ole-functions
            // vtable might be hijacked by rpcrt4.dll
            try
            {
                if (classinstance == null)
                {
                    object classfactoryO;
                    OLE32.CoGetClassObject(ref classguid, 1 + 4, 0, ref classfactoryguid, out classfactoryO);
                    IClassFactory classfactory = (IClassFactory)classfactoryO;
                    classfactory.CreateInstance(null, ref interfguid, out classinstance);
                    Marshal.FinalReleaseComObject(classfactory);
                }
            }
            catch { }
            try
            {
                if (classinstance == null)
                {
                    object classfactoryO;
                    OLE32.CoGetClassObject(ref classguid, 1 + 4, 0, ref classfactoryguid, out classfactoryO);
                    IClassFactory2 classfactory = (IClassFactory2)classfactoryO;
                    classinstance = classfactory.CreateInstance(null, interfguid);
                    Marshal.FinalReleaseComObject(classfactory);
                }
            }
            catch { }
            if (classinstance == null)
            {
                // Error...
            }
#endif
#if true
            // create via raw dll functions
            // no chance for other people to hijack the vtable
            try
            {
                if (classinstance == null)
                {
                    do
                    {
                        Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey("CLSID\\{" + classguid.ToString() + "}\\InprocServer32");
                        if (rk == null)
                            break;
                        string classdllname = rk.GetValue(null).ToString();
                        int libH = KERNEL32.LoadLibrary(classdllname);
                        if (libH == 0)
                            break;
                        int factoryFunc = KERNEL32.GetProcAddress(libH, "DllGetClassObject");
                        if (factoryFunc == 0)
                            break;
                        DllGetClassObjectDelegate factoryDel = (DllGetClassObjectDelegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(factoryFunc), typeof(DllGetClassObjectDelegate));
                        object classfactoryO;
                        factoryDel(ref classguid, ref classfactoryguid, out classfactoryO);
                        if (classfactoryO == null)
                            break;
                        IClassFactory classfactory = (IClassFactory)classfactoryO;
                        classfactory.CreateInstance(null, ref interfguid, out classinstance);
                        Marshal.FinalReleaseComObject(classfactory);
                    } while (false);
                }
            }
            catch { }
            try
            {
                if (classinstance == null)
                {
                    do
                    {
                        Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey("CLSID\\{" + classguid.ToString() + "}\\InprocServer32");
                        if (rk == null)
                            break;
                        string classdllname = rk.GetValue(null).ToString();
                        int libH = KERNEL32.LoadLibrary(classdllname);
                        if (libH == 0)
                            break;
                        int factoryFunc = KERNEL32.GetProcAddress(libH, "DllGetClassObject");
                        if (factoryFunc == 0)
                            break;
                        DllGetClassObjectDelegate factoryDel = (DllGetClassObjectDelegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(factoryFunc), typeof(DllGetClassObjectDelegate));
                        object classfactoryO;
                        factoryDel(ref classguid, ref classfactory2guid, out classfactoryO);
                        if (classfactoryO == null)
                            break;
                        IClassFactory2 classfactory = (IClassFactory2)classfactoryO;
                        classinstance = classfactory.CreateInstance(null, interfguid);
                        Marshal.FinalReleaseComObject(classfactory);
                    } while (false);
                }
            }
            catch { }
            if (classinstance == null)
            {
                // Error...
            }
#endif

            IntPtr interfaceIntPtr = Marshal.GetComInterfaceForObject(classinstance, cci.InterfaceType);
            int*** interfaceRawPtr = (int***)interfaceIntPtr.ToPointer();
            // get vtable
            int** vTable = *interfaceRawPtr;
            // get com-slot-number (vtable-index) of function X
            // get function-address from vtable
            int mi_vto = Marshal.GetComSlotForMethodInfo(cci.Method);
            int* faddr = vTable[mi_vto];
            cci.m_FunctionPointer = new IntPtr(faddr);
            // release intptr
            Marshal.Release(interfaceIntPtr);
            Marshal.FinalReleaseComObject(classinstance);
        }
    }

}

Oct 8, 2010 at 2:32 PM

Thanks.

When I have time I'll look this through and see if it can be provided with EH somehow,
if this is ok for you. Please let me know :)

Oct 8, 2010 at 5:15 PM

I am not claiming any copyrights, if you mean that. I posted it in the public domain :)

Oct 18, 2013 at 5:28 PM
Edited Oct 18, 2013 at 5:57 PM
@tleibnitz, this is amazingly useful, thank you very much! @SimonAllaeys, will this make it to the EasyHook codebase?

(And I do know this thread is 3 years old, but it is still pertinent)

One question for either of you (or anyone who can answer) - I'm trying to hook FileExists() in Scripting.FileSystemObject on a 32-bit machine.

I did the injection using
COMclassQuery.COMclassQuery.COMclassInfo cci = new COMclassQuery.COMclassQuery.COMclassInfo(
  typeof(Scripting.FileSystemObjectClass),
  typeof(Scripting.IFileSystem3),
  "FileExists");

COMclassQuery.COMclassQuery.Query(cci);

//Get the old function:
_FileExists = (DFileExists)Marshal.GetDelegateForFunctionPointer, typeof(DFileExists));

FileExistsHook = LocalHook.Create(
  cci.FunctionPointer,
  new DFileExists(FileExists_Hook),
  null);
When I set a breakpoint in FileExists_Hook, it gets triggered, but the parameter (string FileName) is gibberish. Calling the original function with that parameter causes a crash. I have tried different calling conventions in the UnmanagedFunctionPointer attribute of DFileExists, I've tried different character sets, and I've even tried marshalling the parameter manually by making the parameter an IntPtr and using the Marshal.PtrToString* functions. None of this has helped.. Any ideas?
Oct 18, 2013 at 8:00 PM
Edited Oct 18, 2013 at 8:39 PM
Update: What is actually being passed to the FileExists method is a pointer to the vftable for the FileSystem interface. Why would this be happening?

Update 2: Never mind-- found my answer here:
A COM interface pointer is a pointer to a structure that consists of just a vtable. The vtable is a structure that contains a bunch of function pointers. Each function in the list takes that interface pointer (p) as its first parameter ("this").
Oct 18, 2013 at 8:58 PM
Edited Oct 18, 2013 at 9:06 PM
Got it working. Hopefully this will help someone else who is only half-educated about COM, like me.

I had to declare my unmanaged delegate like so:
[UnmanagedFunctionPointer(
  CallingConvention.StdCall,
  CharSet=CharSet.Unicode,
  SetLastError=false)]
  [return: MarshalAs(UnmanagedType.U4)]
  delegate uint DFileExists(
    [In] IntPtr vtablePtr,
    [In] string fileSpec,
    [Out, MarshalAs(UnmanagedType.VariantBool)] out bool pfExists);
Marked as answer by spazzarama on 2/6/2014 at 7:53 PM
Apr 13, 2014 at 3:35 PM
Edited Apr 13, 2014 at 3:42 PM
I'm sorry, but a lot over the past time.

Using your code IFileOperation CopyItems hook but KERNEL32 does not exist in the current context error.

I've written it
        public static class KERNEL32
        {
            [DllImport("kernel32.dll")]
            public static extern int LoadLibrary(
                System.String lpFileName
                );

            [DllImport("kernel32.dll")]
            public static extern int GetProcAddress
            (
                int hModule,
                System.String procName
            );
        }

            COMclassQuery.COMclassQuery.COMclassInfo cci = new COMclassQuery.COMclassQuery.COMclassInfo(
                typeof(IUnknow), typeof(IUnknow.IFileOperation), "CopyItems");
            COMclassQuery.COMclassQuery.Query(cci);
            CopyHook = LocalHook.Create(cci.FunctionPointer,
                new DCopyItems(CopyItems_Hooked), this);
            CopyHook.ThreadACL.SetInclusiveACL(new Int32[] { });
I can not run any kind of

Does anyone have any idea about it?


I tried it but it does not hook
            CoCreateInstanceHook = LocalHook.Create(
                LocalHook.GetProcAddress("ole32.dll", "CoCreateInstance"),
                new DCoCreateInstance(CoCreateInstance_Hooked),
                this);
            CoCreateInstanceHook.ThreadACL.SetInclusiveACL(new Int32[] { 0 });
If you SetExclusiveACL crash explorer.exe

Windows 7 64bit system
Apr 15, 2014 at 7:10 PM
Edited Apr 16, 2014 at 2:43 PM
It's not really clear what you're trying to do. If you're trying to hook into a COM object, try starting with a combination of the code I posted above. Trying to hook into CoCreateInstance won't help unless you want to know when a COM object has been created using that function.

In your first snippet of code you are using "IUnknow" as your class type. If this is meant to be "IUnknown", I don't think this will help you unless you are trying to hook into either AddRef, QueryInterface, or Release. You have to use your COM object's specific class type and COM interface as parameters into the COMClassInfo constructor. I am not sure what COM object "CopyItems" belongs to... but I think you are closer to your answer in your first snippet if your goal is to hook into a COM object.

You may have to [COMImport] your object's interface. Check out this 10-part series on COM for information on how to do that.
Apr 16, 2014 at 5:33 PM
Edited Apr 19, 2014 at 3:58 PM
My purpose hook com objects.


The sample code but does not work here.

I'm sorry I threw google does not fit here

My English very bad (Thanks google translate :))

https://drive.google.com/folderview?id=0B_beZobIWlVaZkU1OWI3eHpFR0U&usp=sharing

Thank you for your interest.

Edit:
Asked this question a month before testing and I've read all the threads.
Com object can not hook.
I'm about to go crazy. Please any com objects Could you give an example?

Thank you
Apr 23, 2014 at 10:26 AM
Edited Apr 23, 2014 at 10:27 AM
Tontito,

Hooking COM object methods is very similar to hooking other API methods.

One difference is that often you will need an object reference to find the function addresses. I have done this in the past by hooking VTable function addresses. Examples of how I have done this for a Direct3D Device object can be found here: http://spazzarama.com/2010/03/29/screen-capture-with-direct3d-api-hooks/ and here: http://spazzarama.com/2011/03/14/c-screen-capture-and-overlays-for-direct3d-9-10-and-11-using-api-hooks/

Read through that info, and take a look at the sample code (the most up-to-date code is found in the GitHub repository found in the second link).

Keep in mind that the function addresses you find this way are valid for ALL instances of that COM class. If you then subtract the base address you have a relative address you can use across different applications, i.e. you could determine the VTable addresses in a separate application and then pass this information into your hook if you can't do this within the target application itself.

Other than that, the main thing to remember is that the first parameter will be the COM object instance (i.e. "this").

Hope this helps.

J
Apr 23, 2014 at 10:34 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Apr 24, 2014 at 10:42 AM
Edited Apr 24, 2014 at 10:57 AM
I guess I'm a bit stupid. I do not understand without examples.

I did not understand a thing from the sample you sent.
I'm very new to the truth on this issue but FilemonInject sample was very clear and easy to.

I solved in this way are

MoveFileWithProgressW
CopyFileExW
CreateProcessW (and inject new process)
OpenProcess
TerminateProcess
ExitWindowsEx

I'm writing program with delphi CyberCafe Software from Turkey

www.tredcafe.com (No advertising and free)

I'm trying to add usb protection. Windows XP ok (MoveFileWithProgressW and CopyFileExW)

Windows Vista and above using IFileOperation CopyItems.

I'm trying for more than 1 month.
I could not not be.

actually can be done but paid with madCodeHook.
programs not paid and do not earn money so I'm trying to do with free solution EasyHook
http://stuani.blogspot.com.tr/

I will continue to try, but right now I can not figure.
I will write here the answer to solve in the future hopefully.
I want to thank everyone who is interested.

Sorry my English very bad.
May 7, 2014 at 10:46 AM
Dear Tonino

I have read your source code.
Please change folowing command
CoCreateInstanceHook.ThreadACL.SetInclusiveACL(new Int32[] { 0 });
To:
CoCreateInstanceHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
May 9, 2014 at 12:17 PM
Edited May 9, 2014 at 1:58 PM
Hi lecanhminh,

I've tried it before SetExclusiveACL.

But Run project Windows 7 64bit and hook Explorer.exe, Explorer.exe crash.

Thank you for your interest.
May 14, 2014 at 3:35 AM
Edited May 14, 2014 at 3:52 AM
Dear Tonino

I have not try with Windows 7 64bit but when run windows 7 32bit.
I can only hook to CoCreateInstance function.
const string IFileOperation_GUID = "3ad05575-8857-4850-9277-11b85bdb8e09";
public LocalHook CopyItemsHook = null;
public LocalHook CoCreateInstanceHook = null;
public static IntPtr GetVTblAddressesItem(IntPtr pointer, int methodsIndex)
        {
            IntPtr vTable = Marshal.ReadIntPtr(pointer);            
            return Marshal.ReadIntPtr(vTable, methodsIndex * IntPtr.Size);
        }
    private DCopyItems dCopyItems;
    private IntPtr originalCopyItem;
    public void Run(RemoteHooking.IContext context, string channelName)
    {
        try
        {
            CoCreateInstanceHook = LocalHook.Create(
            InTargetProc: LocalHook.GetProcAddress("ole32.dll", "CoCreateInstance"),
            InNewProc: new DCoCreateInstance(CoCreateInstance_Hooked),
            InCallback: this);
            CoCreateInstanceHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
            if (CoCreateInstanceHook != null) {
                OutputDebugString("CoCreateInstanceHook= Create Suceess Full");
            }
                           dCopyItems = new DCopyItems(CopyItems_Hooked);
                     }catch (Exception e)
        {
            OutputDebugString("Error: " + e.StackTrace);
            _interface.ReportException(e);
            return;
        }

        RemoteHooking.WakeUpProcess();
        _interface.IsInstalled(RemoteHooking.GetCurrentProcessId());

        keepItInMemory();
        freeResources();

    }
```
    private UInt32 CoCreateInstance_Hooked(
        [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
       IntPtr pUnkOuter, UInt32 dwClsContext, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
       out IntPtr ppv)
    {
        UInt32 result = CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, out ppv);

        if (rclsid.ToString().Equals( IFileOperation_GUID))
        {
            originalCopyItem = GetVTblAddressesItem(ppv, 17);
            OutputDebugString("ppv = " + ppv.ToInt32());
            if (CopyItemsHook != null) {
                CopyItemsHook.Dispose();
            }
            CopyItemsHook = LocalHook.Create(originalCopyItem, dCopyItems, this);
            if (CopyItemsHook != null)
            {
                OutputDebugString("CopyItemsHook= Create Suceess Full");
            }
            CopyItemsHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });         
        }
        return result;
    }

but after that CopyItems_Hooked is not call
          private uint CopyItems_Hooked(
        [MarshalAs(UnmanagedType.Interface)] object punkItems,
        IShellItem psiDestinationFolder)
    {
        OutputDebugString("CopyItems_Hooked");
        return 0;
    }
I think easy hook is not good for this..
I have change to use MinHook with VC++.
and success to hook RenameItems function
Please reference to this link if you need
http://www.jjask.com/366332/idroptarget-drop-method-hooking
I also thanks for your question and your post souce code.
Sep 2, 2014 at 8:59 AM
Hi,

thanks at all for the library and the helper class, it's great. As far as I understand, the helperclass must be able to instantiate the interface in order to hook it.

In my special case, I'd like to hook some IAudioClient functions. However, you would get this interface by picking up the IMMDeviceEnumerator (you can use com-import on that), calling the "GetDefaultAudioEndpoint"-function (or two others), which returns IMMDevice, and then you would need to call IMMDevice::Activate with IID_IAudioClient to actually receive an instance to the IAudioClient for the endpoint.

I'm able to successfully pass the IMMDeviceEnumerator, and I can hook the "GetDefaultAudioEndpoint"-function. Now I'm wondering how to move on. Can I hook the MMDevice Interface, after I got an instance to that? And can I than hook the IAudioClient? I'm a little bit lost here atm.
COMclassQuery.COMclassInfo cOMclassInfo = new COMclassQuery.COMclassInfo(typeof(_MMDeviceEnumerator), typeof(IMMDeviceEnumerator), "GetDefaultAudioEndpoint");
COMclassQuery.Query(cOMclassInfo);
LocalHook localHook = LocalHook.Create(cOMclassInfo.FunctionPointer, new Main.DGetDefaultAudioEndpoint(Main.GetDefaultAudioEndpointH), null);
this happens in the run method of the injected dll. Then, the callback gets called, and I can obtain an Instance to the IMMDevice interface. But how would i be able to hook the IAudioClient I get from the MMDevice?
Sep 3, 2014 at 8:34 AM
Ok,

I kept on digging into the code. I don't know if that's the easiest way, but I'm hooking "down the chain". So I hook the MMDevice's "activate" method and save the IAudioClient instance. I'll then hook into the Initialize. GetBuffer and ReleaseBuffer methods. However, the Initialization routine is called quite often fast in a row, and I'm wondering why.
private static int AudioClientInitialize(int vtablePtr, AUDCLNT_SHAREMODE shareMode, uint streamFlags, ulong bufferDuration, ulong devicePeriod, IntPtr format, Guid audioSessionId = default(Guid))
{
    try
    {
        //Do funny stuff here
        
        Marshal.ThrowExceptionForHR(lastAudioClient.Initialize(shareMode, streamFlags, bufferDuration, devicePeriod, format, audioSessionId));
    }
    catch (Exception ex)
    {
        //shit happened
    }
    return 0;
}