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):
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.
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.
This IUnknown interface exposes three methods, which are fundamental to the life cycle of a COM object:
AddRef
Release
QueryInterface
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
).
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:
#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:
- 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.
- 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
+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:
- CoInitializeEx: call needed to allow the calling thread to use the COM library and to create a new apartment for the thread if required.
- CoCreateInstance: creates the “Common Item Dialog” object from its CLSID and has a pointer returned, specifying the Interface ID, to an IFileOpenDialog instance
- Calls the Show method of the same interface, which displays a dialog box to the user
- Call the GetResult method, which returns a pointer to the chosen file, a “Shell Item” object that implements IShellItem.
- Calls the GetDisplayName method of IShellItem and displays a MessageBox
- 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.
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.
TOOLS
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!