Now that we have seen DDE technology, we will have noticed some of its limitations. Let’s compare them with the newcomer COM that we will now examine

(DDE => GREEN; COM => RED):

1. DDE uses window messages for communication, which makes it slower and more complex. COM uses native function calls, which are generally faster.
2. If a window does not respond in time to a DDE message, this can cause a deadlock or infinite wait condition if the DDE server does not implement a way to notify the client of its “deadlock” (via the fBusy member of the WM_DDE_ACK message at 1). COM incorporates error handling mechanisms, and deadlocks are much less frequent.
3. DDE is limited to local window communications, while COM supports local and remote communications (via DCOM).
4. DDE is limited to the exchange of strings and raw data, COM is based on an object-oriented model, with features such as polymorphism, inheritance, and code reuse.

In general, COM, now widely used in the Windows ecosystem, is considered more secure, efficient and versatile than the deprecated DDE.

Let’s see how it works!


COM

Introduction

Let’s read the official MSDN documentation first, then I will give you my own definition:

COM is a platform-independent, distributed, object-oriented system for creating binary software components that can interact. COM is the foundation technology for Microsoft's OLE (compound documents) and ActiveX (Internet-enabled components) technologies."

Simply put, COM provides a standard (component-based) way to write code that is callable by different programming languages (it is language-agnostic) and different programs. All of these, through a well-defined COM API, know how and where to find COM functions and how to call them. It is no accident that it is called COM Application Binary Interface, and now we will see practically why COM is so easy to be called.

COM Component Virtual Table Structure
COM Component Virtual Table Structure

Foundamental Notions

Let’s begin by saying that each COM component provides a service. A COM component practically, as we shall see, is nothing more than a DLL or EXE that implements COM interfaces. Each COM component derives (and must derive) from a base interface called IUnknown, along with one or more other interfaces. For example, the COM object “Common Item Dialog” cascades all the methods and properties of the interfaces from which it is derived, as shown in the figure below.

Inheritance Tree of a COM Component
Inheritance Tree of "Common Item Dialog"

This IUnknown interface exposes three methods, which are fundamental to the life cycle of a COM object:

  • AddRef
  • Release
  • QueryInterface
IUnknown Interface
IUnknown Interface Methods

COM uses an object lifecycle management method called Reference Counting. Once an object is instantiated, its counter is initialized to 1. Each copy of this object increments its counter by 1, and each of its Releases decrements it by 1. When the counter reaches 0, the object is automatically finalized.

AddRef is called when another COM component uses the interface; Release is called when this component should no longer use the interface; QueryInterface allows runtime discovery of other interfaces supported by the COM object. If the interface exists, QueryInterface returns a pointer to its vft; if it does not exist it returns an error code.

CLSID and ProgID

Each COM object has assigned a unique GUID called CLSID, and a “text” version of it called ProgID, which we will see is especially useful in DCOM calls. Each of these is registered in Windows in registry keys in HKEY_CLASSES_ROOT\CLSID (HKLM\Software\CLSID or HKCU\Software\Classes\CLSID).

Word ProgID
Word.Application ProgID

COM: DEVELOPER NOTES

COM Server Types

COM uses a client-server model, where there is a client COM (which can be a compiled application, scripts, VBA code, etc.) and a COM Server, which can be:

#In-Process Server: it is loaded into memory in the client application. They are usually .dll or .ocx
#Local Server: it is a separate application with its own PID. They are usually .exe files.
#Remote Server: they are COM services hosted on machines other than the client COM. They are called procedures found in Remote Servers using DCOM and RPC.

COM Apartments

In addition, all COM objects are divided into groups called “apartments”. There are two types of apartments in COM:

  1. Single-Threaded Apartment (STA): each COM object residing in an STA can be used only by the thread with which it is associated. If another thread wants to access an object in an STA, COM uses “marshalling” to send the request to the correct thread. Windows message loop is used to read and execute requests one at a time.
  2. Multi-Threaded Apartment (MTA): is used for COM objects designed to have multiple concurrent accesses from different threads. Objects in an MTA must be thread-safe. Any thread can access them directly without marshalling. Each COM object belongs to exactly one apartment.

Some of the most used COM Interfaces

#IDispatch: provides support for “late binding,” that is, it allows methods to be called and properties of a COM object to be accessed at runtime. Some of its methods are:
+GetTypeInfo: retrieves information about the type of the object.
+GetIDofNames: each method or property has its own DISPID, which uniquely identifies it.
+Invoke: executes methods or reads properties by their DISPID.

#IPersist: provides methods to save and load the state of an object.
+GetClassID: obtains the CLSID of an object.

#IStream: provides methods to read, write and manage sequential data streams.
+Read: reads n bytes from a stream.
+Write: writes n bytes to a stream.
+Seek: moves the stream pointer to a specified location.

#IClassFactory: allows the creation of COM objects.
+CreateInstance: creates an instance of a COM object.
+LockServer: prevents the server from being dumped from memory.

#IMoniker: allows referencing and then linking other COM objects.

Example of a COM Interface in C++

Let’s take an official example from Microsoft documentation:
https://learn.microsoft.com/en-us/windows/win32/learnwin32/example–the-open-dialog-box

#include <windows.h>
#include <shobjidl.h> 

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | 
        COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        IFileOpenDialog *pFileOpen;

        // Create the FileOpenDialog object.
        hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, 
                IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

        if (SUCCEEDED(hr))
        {
            // Show the Open dialog box.
            hr = pFileOpen->Show(NULL);

            // Get the file name from the dialog box.
            if (SUCCEEDED(hr))
            {
                IShellItem *pItem;
                hr = pFileOpen->GetResult(&pItem);
                if (SUCCEEDED(hr))
                {
                    PWSTR pszFilePath;
                    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

                    // Display the file name to the user.
                    if (SUCCEEDED(hr))
                    {
                        MessageBoxW(NULL, pszFilePath, L"File Path", MB_OK);
                        CoTaskMemFree(pszFilePath);
                    }
                    pItem->Release();
                }
            }
            pFileOpen->Release();
        }
        CoUninitialize();
    }
    return 0;
}

Let me explain step by step:

  1. CoInitializeEx: call needed to allow the calling thread to use the COM library and to create a new apartment for the thread if required.
  2. CoCreateInstance: creates the “Common Item Dialog” object from its CLSID and has a pointer returned, specifying the Interface ID, to an IFileOpenDialog instance
  3. Calls the Show method of the same interface, which displays a dialog box to the user
  4. Call the GetResult method, which returns a pointer to the chosen file, a “Shell Item” object that implements IShellItem.
  5. Calls the GetDisplayName method of IShellItem and displays a MessageBox
  6. Call CoUninitialize to finalize the COM library.

Recall that every COM resource and pointer should always be freed when no longer used, either by Release or CoTaskMemFree. CoTaskMemFree represents a sort of wrapper for free and delete operations, since COM was born to be “language-agnostic.”

COM & Registry Editor

Each COM object and interface, i.e., each CLSID and ProgID and their respective IIDs are all maintained in the Windows registry, as the images below demonstrate.

COM InProcServer32
COM InProcServer32
COM LocalServer32
COM LocalServer32
COM Microsoft Forms 2.0 TabStrip
COM Microsoft Forms 2.0 TabStrip
COM TypeLib
COM TypeLib
COM VersionIndependentProgID
COM VersionIndependentProgID

TypeLib

A “type library” (.tlb) is a binary file that stores information about an object’s COM and DCOM properties and methods, in a form accessible by other applications at runtime. Through the type library, the methods and properties exposed by an object are determined. Definitions are in a format called IDL, Interface Definition Language.

HHCtrl TLB
HHCtrl TLB

TOOLS

OleViewDotNet v1.16

REFERENCES

https://learn.microsoft.com/it-it/windows/win32/learnwin32/error-handling-in-com
Error handling in COM: gives an idea of COM error codes.
https://learn.microsoft.com/it-it/windows/win32/learnwin32/module-2–using-com-in-your-windows-program
Windows COM Overview
https://www.cs.umd.edu/~pugh/com/
Analysis of the COM world: University of Maryland
https://hackyboiz.github.io/2024/11/24/ogu123/COM_Object/
Tutorial COM: “hackyboiz”
https://www.dia.uniroma3.it/autom/Reti_e_Sistemi_Automazione/PDF/COM%20%26%20DCOM.pdf
PPTX UniRoma COM
https://nolongerset.com/com-server-types/
In-Process, Local & Remote Servers in COM
https://bohops.com/2018/06/28/abusing-com-registry-structure-clsid-localserver32-inprocserver32/
Attacking COM
https://mohamed-fakroud.gitbook.io/red-teamings-dojo/windows-internals/playing-around-com-objects-part-1
Red Teaming’s Dojo perspective


We have seen, although without any practical declination, how the COM ecosystem works. In the next blog post we will look in detail at other terminologies often used in these contexts, such as OLE and ActiveX. See you next time!



I new mates!