COM, OLE and ActiveX Hooking


   - General information
   - Auto Monitoring
      - Auto Monitoring COM Objects Creation File Syntax
      - Auto Monitoring Files Syntax
   - Static Monitoring
      - Static Monitoring Files Syntax
   - Interface Methods Creating Objects (since 5.1 version only)
   - Overriding, or Adding Pre/Post call hooks
   - Hooking Objects Creation And Destruction
   - COM Options
   - COM Tools

   - Advanced information


General information

To enable COM hooking, click the toolbar button on the main window before starting the application to hook.
Next it's recommended to attach application at startup to avoid COM object creation miss
With default options you don't need to do anything else.

Use Monitoring File Builder to automatically generate monitoring files for COM interfaces (Use the "COM Type Library" or "COM All Registered Type Library" options)



COM (and so OLE and ActiveX) hooking is an extension to API hooking.
That means all processes, modules, monitoring and overriding filters work as the classical API way.
Monitoring files too support the same functions and parameter flags.

In all the following we will use "COM", but all applies to COM OLE and ActiveX (OCX)

The first thing you should decide is if you want to use automatic COM hooking or static COM hooking.
  - Automatic hooks (recommanded) spy all objects created with a set of functions (and matching some specified classes according to CLSID filters).
They allow a lazy way for classes supporting IDispatch Interface (you don't need to write a monitoring file).
They allow to use the COM Interaction Tool, which allow you to do call methods of hooked interfaces (as the Remote Call dialog for API). It spyes object life cycle too.
  - Use static hooks only if you exactly know which components you want to hooks, and if you don't want to use the COM Interaction Tool.

In conclusion, auto monitoring gives you more control on COM objects.

To see all COM components available on your computer; convert CLSID or IID to or from their name; or get objects methods addresses (VA, RVA or RAW) use the Com Tools

Auto Monitoring

COM Auto Monitoring can be enabled/disabled thanks to button
It can be done before starting hooking (recommended to not loose first objects creation); or when hooking is already started.

COM auto hooking works spying a set of API.
So the first thing to do is to specify which API creating COM objects you want to hook.
The configuration file (COM options / "File defining hooked function for COM objects creation") comes with some well-known API.
So you just need to adjust it according to your needs.

To avoid to auto hook all created COM objects with specified API, you can use an exclusion or inclusion CLSID filter file (COM options / "Use CLSID filter")

COM options allow you to ask for various reports too : reporting created COM objects, reporting Interfaces with no monitoring files associated...

Monitoring files for COM auto hooking are located in "monitoring files\COM\" directory. (syntax is described here)

If no monitoring file is available for a specified Interface, and Interface support the IDispatch Interface (and according to COM options), IDispatch interface will be parsed automatically. That means even you haven't created a monitoring file for such interface, methods exposed throw IDispatch will be hooked.
Notice : Methods exposed throw IDispatch are methods you can view with an Ole/ActiveX methods browser (also known as tlb browser).

For Interfaces not supporting IDispatch, you sadly still have to create monitoring files (see COM Auto Monitoring files syntax).

When you disable COM Auto Monitoring while hooking is started, you will be asked if you want to unhook already hooked objects.
If you answer "Yes", you won't see COM logs anymore : every non static hooks to COM methods will be destroyed.
By ansewering "No", you will continue to see logs generated by already hooked COM methods, but no new method will be hooked.




Auto Monitoring: COM Objects Creation File Syntax

3 things are necessary to hook a created COM object :
   - the class ID (CLSID) used for creation
   - the interface ID (IID) used for creation
   - the pointer to created object returned by the function

So API definitions must specify where to find these information. The syntax is the following:
DllName|FunctionName|Option1|Option2...|OptionN or
WinApiOverrideDllNameLike|FunctionName|Option1|Option2...|OptionN
where options are

1) Stack size (the stack size needed for all parameters)
   StackSize= / x64StackSize= for all definitions, specify the stack size in bytes of all parameters.

2) CLSID (for all definitions provide information on CLSID)
   CLSIDStackIndex= / x64CLSIDStackIndex= bytes count between stack position (ESP/RSP) and CLSID parameter address
   REFCLSIDStackIndex= / x64REFCLSIDStackIndex= bytes count between stack position (ESP/RSP) and CLSID* parameter address
   CLSIDValue= CLSID string value (used for function not requiring a CLSID parameter)

3) IID
(not needed if function has a MULTI_QI parameter)
   IIDStackIndex= / x64IIDStackIndex= bytes count between stack position (ESP/RSP) and IID parameter address
   REFIIDStackIndex= / x64REFIIDStackIndex= bytes count between stack position (ESP/RSP) and IID* parameter address
   IIDValue= IID string value (used for function not requiring an IID parameter like DirectDrawCreate)

4) Object(s) pointer(s)

a) For single object creation (like CoCreateInstance)
   ObjectStackIndex= / x64ObjectStackIndex= bytes count between stack position (ESP/RSP) and object pointer parameter address

b) For multiple objects creation (like CoCreateInstanceEx)
   ObjectArrayStackIndex= / x64ObjectArrayStackIndex= bytes count between stack position (ESP/RSP) and MULTI_QI array parameter address
   ObjectArraySizeStackIndex= / x64ObjectArraySizeStackIndex= bytes count between stack position (ESP/RSP) and MULTI_QI array size parameter address

c) If object pointer is the returned value (like Direct3DCreate8) since 5.1 version only
   ObjectIsReturnedValue

x86 and x64 share same configuration files (to avoid to provide same definition twice),
so x86 and x64 information must be present

XXX_StackIndex values are the following :
   x86 : (0 based parameter index) *4
   x64 : RCX,RDX,R8,R9, and ( (0 based param index) - 4 ) *8 for parameters index greater or equal to 4

Examples
1) For CoCreateInstance(REFCLSID rclsid,LPUNKNOWN pUnkOuter,DWORD dwClsContext,REFIID riid,LPVOID * ppv);
   ole32.dll|CoCreateInstance|StackSize=20|REFCLSIDStackIndex=0|REFIIDStackIndex=12|ObjectStackIndex=16|x64StackSize=8|x64REFCLSIDStackIndex=RCX|x64REFIIDStackIndex=R9|x64ObjectStackIndex=0

2) For CoCreateInstanceEx(REFCLSID rclsid,IUnknown* punkOuter,DWORD dwClsCtx,COSERVERINFO* pServerInfo, ULONG cmq, MULTI_QI* pResults);
   ole32.dll|CoCreateInstanceEx|StackSize=24|REFCLSIDStackIndex=0|ObjectArraySizeStackIndex=16|ObjectArrayStackIndex=20|x64StackSize=16|x64REFCLSIDStackIndex=RCX|x64ObjectArraySizeStackIndex=0|x64ObjectArrayStackIndex=8

2) IDirect3D8* Direct3DCreate8(UINT SDKVersion);
   D3d8.dll|Direct3DCreate8|StackSize=4|CLSIDValue={1DD9E8DA-1C77-4d40-B0CF-98FEFDFF9512}|IIDValue={1DD9E8DA-1C77-4d40-B0CF-98FEFDFF9512}|ObjectIsReturnedValue|x64StackSize=0


Notice : When you found no information on CLSID, do not hesitate to put object IID as CLSID, because internally in WinApiOverride, CLSID are only used as a unique identifier to identify objects of the same type (that's we do in the example of Direct3DCreate8)

Notice: A particular treatment is done for CoGetClassObject to hook CreateInstance method of IID_IClassFactory (this function definition is provided in the default COM Object Creation Hooked Functions file that comes with WinAPIOverride binaries)

As methods of interface can create objects too, you may want to hook these created objects. See Interface Methods Creating Objects in this case



Auto Monitoring Files Syntax

Since version 5.1.10, Monitoring File Builder can build COM auto monitoring files automatically from type library, ocx, dll and exe
Use it and you will earn a lot of time !

1) Monitoring files for COM auto hooking must be located in "monitoring files\COM\" directory.

2) Monitoring file name must be "Interface_ID.txt" where Interface_ID is the IID of the interface
By the way for IDispatch auto monitoring file name is "{00020400-0000-0000-C000-000000000046}.txt"

Definitions are API like.
The only difference is that you have to specify vtbl index instead of dll name.
VTBLIndex=Index| [ReturnType] FuncName( ParamType [paramName] ) [;]
where Index is 0 based index



Examples

  VTBLIndex=0|IUnknown::QueryInterface(IUnknown* pObject,IID* pIid,PVOID* ppInterface)|Out|FailureIfNegativeRet
  VTBLIndex=1|IUnknown::AddRef(IUnknown* pObject);
  VTBLIndex=2|IUnknown::Release(IUnknown* pObject);
  VTBLIndex=3|HRESULT IDispatch::GetTypeInfoCount(IUnknown* pObject,UINT* pctinfo);|Out|FailureIfNegativeRet

Notice: the interface name "IUnknown::" or "IDispatch::" is optionnal.
I find it better because it's avoid confusion with API having the same name; and it gives information on interfaces in logs.


YOU HAVE TO ADD "IUnknown* pObject" as first parameter for all functions which are not static
This comes from C++ methods calling way

Some methods can create COM objects.
To spy these new objects, you need to add a specific option : see COM : Interface Methods creating objects

As you can see in previous examples, you can use all optionnal flags for parameters,
functions direction, breaking and failure values defined for API monitoring files (see API advanced syntax)


As COM use a lot of derived interfaces, a keyword has been introduced to avoid to copy and past base interfaces definitions.
If your interface derived from another one, you can use
BaseIID=IID
where IID is like {00000000-0000-0000-C000-000000000046}

Examples
By the way if you want to include IUnknown
;include IUnknown
BaseIID={00000000-0000-0000-C000-000000000046}


Using this notation, the IDispatch monitoring file can be like following
;include IUnknown
BaseIID={00000000-0000-0000-C000-000000000046}
VTBLIndex=3|HRESULT IDispatch::GetTypeInfoCount(IUnknown* pObject,UINT* pctinfo);|Out|FailureIfNegativeRet
VTBLIndex=4|HRESULT IDispatch::GetTypeInfo(IUnknown* pObject, UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo);|Out|FailureIfNegativeRet
VTBLIndex=5|HRESULT IDispatch::GetIDsOfNames(IUnknown* pObject,REFIID riid,OLECHAR** rgszNames,UINT cNames, LCID lcid, DISPID* rgDispId);|Out|FailureIfNegativeRet
VTBLIndex=6|HRESULT IDispatch::Invoke(IUnknown* pObject,DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,EXCEPINFO* pExcepInfo, UINT* puArgErr);|Out|FailureIfNegativeRet




These includes are transmitted through monitoring files interfaces definitions.
That means, if your interface is derived from IDispatch (which is derived from IUnknown),
as IDispatch monitoring file contains the definition "BaseIID={00000000-0000-0000-C000-000000000046}" for IUnknown,
you only have to add IDispatch monitoring file not the IUnknown one.
That means you only have to write "BaseIID={00020400-0000-0000-C000-000000000046}" to include both IDispatch and IUnknown definitions.


Using "BaseIID=" translate base monitoring file options (activated or not, parameters and function options)
If you don't want such options translation, you have to copy and paste methods definitions of base interfaces into each derived interface.

Example : create a COM monitoring file from Interface definition
Static Monitoring

Notice: COM static monitoring doesn't need COM auto hooking to be enabled.

Static monitoring files can be loaded and unloaded using the same way as API monitoring files.

The synatx is the following


Static Monitoring Files Syntax

COM@CLSID@IIDVTBLIndex={IID}:Index| [ReturnType] FuncName( ParamType [paramName] ) [;]
where CLSID is the class ID of the object, IID is the interface ID, and Index the 0 based VTBL index;

or

COM@CLSID@BaseIID={IID}

where CLSID is the class ID of the object and IID is the interface ID.
In this second case, static hook will hook all functions defined inside the interface monitoring file used for COM auto hooking.

Examples

For single function definition
COM@{C74190B6-8589-11D1-B16A-00C0F0283628}@IIDVTBLIndex={00020400-0000-0000-C000-000000000046}:56|HRESULT AboutBox(IDispatch* pIDispatch);|FailureIfNegativeRet |BreakBeforeAndAfterCall

For hooking a full interface already defined in a COM auto monitoring file
COM@{d45fd2fc-5c6e-11d1-9ec1-00c04fd7081f}@BaseIID={A7B93C91-7B81-11D0-AC5F-00C04FD97575}



Interface Methods creating objects

Not only API can create COM objects. Some methods of interface can do it too like IDirect3D8::CreateDevice.
So you have to have a way to hook these created objects.
The solution is to use a syntax similar to COM Object Creation File Syntax options added after the Auto Monitoring File Syntax

Syntax
VTBLIndex=Index| [ReturnType] FuncName( ParamType [paramName] ) [;] |ObjectCreation(Option1|Option2...|OptionN)
It can be resumed by
AutoMonitoringFileSyntax | ObjectCreation( ComObjectCreationFileSyntax_Options )


You have one more key word than ComObjectCreationFileSyntax which is CurrentObjectCLSID and is used like |CLSIDValue=CurrentObjectCLSID
This allow to add interface to the current CLSID used (can be usefull in some case like for IDispatch::GetTypeInfo)

Examples
Notice : definition must be on a single line, they are on multiple lines here only for presentation

a) For IDirect3D8::CreateDevice, as we don't know CLSID of IDirect3DDevice8, we use IID_IDirect3DDevice8 as CLSID (see COM object creation file syntax notice), and IID_IDirect3DDevice8 value is {7385E5DF-8FE8-41D5-86B6-D7B48547B6CF}
so we have

VTBLIndex=15|HRESULT IDirect3D8::CreateDevice(IUnknown* pObject, UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice8** ppReturnedDeviceInterface);|FailureIfNegativeRet
|ObjectCreation(CLSIDValue={7385E5DF-8FE8-41D5-86B6-D7B48547B6CF}|IIDValue={7385E5DF-8FE8-41D5-86B6-D7B48547B6CF}|ObjectStackIndex=24|x64ObjectStackIndex=16)



b) For IDispatch::GetTypeInfo, we have to use same CLSID as the currently used, and the IID_ITypeInfo value is {00020401-0000-0000-C000-000000000046}
so we have

VTBLIndex=4|HRESULT IDispatch::GetTypeInfo(IUnknown* pObject,UINT iTInfo,LCID lcid,ITypeInfo** ppTInfo);|Out|FailureIfNegativeRet
|ObjectCreation(CLSIDValue=CurrentObjectCLSID|IIDValue={00020401-0000-0000-C000-000000000046}|ObjectStackIndex=12|x64ObjectStackIndex=R9)




Overriding, or Adding Pre/Post call hooks

Notice: COM overriding doesn't need COM auto hooking to be enabled.

Example can be found in directory "Overriding Dll SDK\COM\QueryInterfaceRestriction"


Overriding a COM method can be done using COM@CLSID@IIDVTBLIndex={IID}:Index as dll name, in an overriding, pre or post dll array (see overriding dll)
where CLSID is the class ID of the object, IID is the interface ID, and Index the 0 based VTBL index

By the way to override the AboutBox method of Microsoft TreeView Control 6 SP4 we can do the following

  1. STRUCT_FAKE_API pArrayFakeAPI[]=
  2. {
  3. // reminder: - stdcall AboutBox(IDispatch* pObject) vtbl index=56 <-- definition and vtbl index provided by "Com Tools"/"Show Methods addresses"
  4. // - Com definition : COM@CLSID@IIDVTBLIndex=IID:Index
  5. // Microsoft TreeView Control 6 SP4 (CLSID {C74190B6-8589-11D1-B16A-00C0F0283628})
  6. // IDispatch Interface ({00020400-0000-0000-C000-000000000046})
  7. // AboutBox VTBL Index : 56
  8. {_T("COM@{C74190B6-8589-11D1-B16A-00C0F0283628}@IIDVTBLIndex={00020400-0000-0000-C000-000000000046}:56"),
  9. _T("AboutBox"),
  10. (FARPROC)mAboutBox,
  11. StackSizeOf(IDispatch*),
  12. 0 // FirstBytesCanExecuteAnywhereSize is no use for com
  13. },
  14.  
  15. {_T(""),_T(""),NULL,0,0}// last element for ending loops
  16. };
replacing method with the following stupid function

  1. HRESULT __stdcall mAboutBox(IDispatch* pIDispatch)
  2. {
  3. UNREFERENCED_PARAMETER(pIDispatch);
  4. MessageBox(NULL,_T("I don't remember :D"),_T("AboutBox [Overrided]"),MB_OK|MB_TOPMOST|MB_ICONINFORMATION);
  5. return S_OK;
  6. }

All overriding arrays (faking, pre and post method call) can be used with COM.


A full COM overriding dll source code is available in "Overriding Dll SDK\COM\QueryInterfaceRestriction\" folder



Hooking Objects Creation And Destruction

Notice: COM objects creation hooking doesn't need COM auto hooking to be enabled.

Example can be found in directory "Overriding Dll SDK\COM\COMObjectCreationSpy"

To hook COM object creation, you only have to create an overriding dll exporting the following function
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Export COMObjectCreationCallBack function only if you want to monitor COM object creation
  3. // parameters :
  4. // in : CLSID* pClsid : pointer to CLSID used for object creation
  5. // IID* pIid : pointer to IID used for object creation
  6. // PVOID pObject : pointer to newly created com object
  7. // PRE_POST_API_CALL_HOOK_INFOS* pHookInfos : struct defined in ExportedStructs.h
  8. // return : tells to continue or stop callback chain for this object creation
  9. // TRUE to continue to report this object creation in other COMObjectCreationCallBack functions of other overriding dll
  10. // FALSE to stop reporting this object creation in other COMObjectCreationCallBack functions of other overriding dll
  11. ///////////////////////////////////////////////////////////////////////////////
  12. extern "C" __declspec(dllexport)
  13. BOOL __stdcall COMObjectCreationCallBack(CLSID* pClsid,IID* pIid,PVOID pObject,PRE_POST_API_CALL_HOOK_INFOS* pHookInfos)
  14. {
  15. // your filters for pClsid
  16. // your filters for pIid
  17.  
  18. // you can change object properties or call object methods here
  19.  
  20. MessageBox(0,_T("Com Object Created"),_T("Information"),MB_OK|MB_TOPMOST|MB_ICONINFORMATION);
  21.  
  22. return TRUE;
  23. }

COM object creation hooking use the same configuration file as COM auto hooking (see Auto Monitoring COM Objects Creation File Syntax);
that means only COM objects created with specified APIs are reported.


The parameter 4, PRE_POST_API_CALL_HOOK_INFOS* pHookInfos, gives you some information like:
   - the return address of API having created object,
   - the handle of module having done call
   - if current call match current overriding modules filters


To hook COM object creation, you only have to create an overriding dll exporting the following function

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Export COMObjectDeletionCallBack function only if you want to monitor COM object destruction
  3. // parameters :
  4. // in : CLSID* pClsid : pointer to CLSID used for object creation
  5. // PVOID pIUnknownInterfaceAssociatedToObject : pointer to the IUnknown interface of the com object
  6. // PVOID pInterfaceReturnedAtObjectCreation : pointer to the interface returned at com object creation (the PVOID pObject value provided in COMObjectCreationCallBack)
  7. // PRE_POST_API_CALL_HOOK_INFOS* pHookInfos : struct defined in ExportedStructs.h see definition for help
  8. // return : tells to continue or stop callback chain for this object creation
  9. // TRUE to continue to report this object deletion in other COMObjectDeletionCallBack functions of other overriding dll
  10. // FALSE to stop reporting this object deletion in other COMObjectDeletionCallBack functions of other overriding dll
  11. ///////////////////////////////////////////////////////////////////////////////
  12. extern "C" __declspec(dllexport)
  13. BOOL __stdcall COMObjectDeletionCallBack(CLSID* pClsid,PVOID pIUnknownInterfaceAssociatedToObject,PVOID pInterfaceReturnedAtObjectCreation,PRE_POST_API_CALL_HOOK_INFOS* pHookInfos)
  14. {
  15.  
  16. MessageBox(0,_T("Com Object Destroyed"),_T("Information"),MB_OK|MB_TOPMOST|MB_ICONINFORMATION);
  17.  
  18. return TRUE;
  19. }