Skip to main content

Notice

Please note that most of the software linked on this forum is likely to be safe to use. If you are unsure, feel free to ask in the relevant topics, or send a private message to an administrator or moderator. To help curb the problems of false positives, or in the event that you do find actual malware, you can contribute through the article linked here.
Topic: COM/ActiveX/OLE Help (Read 2740 times) previous topic - next topic
0 Members and 3 Guests are viewing this topic.

COM/ActiveX/OLE Help

Hello. I've been trying unsuccessfully to enable scripting Automation for a component. I believe I have set up most of it, implementing all of the IDispatch and IUnknown interfaces along with the implementation of the available interfaces, the type library and classes and even the registry and the manifest. I cannot get, for example, JSP3 to load it. Whereas JSP3 loads all previously working objects within their respective DLLs (for example, var VUMeter = new ActiveXObject("VUMeter");).

I see that the old version of foo_vis_vumeter.dll from 2013 does this and works. I have it disassembled in Ghidra and can tell that it is only importing/using the following (relevant) functions from Win32: CLSIDFromProgID(), CoInitialize(), CoRegisterClassObject(), CoRevokeClassObject(), CoUnitialize(), IsEqualGUID(), LoadTypeLib(), I've implemented and call those functions where I thought appropriate/equivalent. I've also tried to match the calling parameters for each of these. Other than the type library (TLB) placed under RT_TYPELIB with ID 1 and the use of the functions above, I do not see anything else related to COM/OLE in the disassembly. It also only exports foobar2000_get_interface(), no mention or hint of DllRegisterServer(), DllUnregisterServer(), DllGetClassObject(), DllCanUnloadNow().

Anyone out there have any ideas of what I'm missing or, better yet, a sample implementation of a component I could refer to? Most of the source that I've been able to find implements the other end or seems to assume different contexts. I think I must be missing some fundamental assumption, but unsure what that could be.

Any hints or help would be much appreciated.

Re: COM/ActiveX/OLE Help

Reply #1
If you don't mind using a hacky approach, you can try the following:
1. Use CoRegisterClassObject to register your IClassFactory implementation.
2. Hook CLSIDFromProgID to return your CLSID for the specified ProgID instead of querying the registry or manifest.

Alternatively, you can refer to the implementation in foo_flowin on GitHub.
However, this method might conflict with other plugins that use the same approach. Don't know if there are better alternatives for COM registration-free solutions, I didn’t delve into it further.


Re: COM/ActiveX/OLE Help

Reply #3
Thanks @TT and @ohyeah. I've released the VUMeter component with Automation implemented. The key part I was missing to get it working was the hooking/trampoline. I was getting foiled because even an administrative user does not have enough privileges to modify the registry without elevation.

For the benefit of others, I made a couple of changes relative to the foo_mycom implementation:
  • Used Detours library instead of MinHook as the former supports ARM64.
  • Registered COM in DllMain() instead of initquit() since the foobar2000 component services initialize in unpredictable order. Made this change because I was seeing the script from another component trying to get the ActiveX object before it was registered by my component's initquit().
  • There is a double reference count in the Factory constructor with its AddRef() causing some unfreed memory left over on exit. To fix initialize refCount to zero instead of one like so: MyCOMClassFactory() : refCount(0) {}

Re: COM/ActiveX/OLE Help

Reply #4
Your comment about refcount and DllMain got me curious because reference count is initialized to 1 in all COM example codes everywhere, and using DllMain for such stuff is forbidden by Microsoft's documentation. Are you people certain you know what you are doing?
DllMain best practices
OleInitialize docs, which says directly "do not call OleInitialize or OleUninitialize from the DllMain function".

Re: COM/ActiveX/OLE Help

Reply #5
Scripting components generally run scripts from WM_CREATE of their respective UI elements so initquit::on_init was always going to be too late.

But init_stage_callack should be perfect for this.

Code: [Select]
void init()
{
    // do stuff
}

FB2K_ON_INIT_STAGE(init, init_stages::before_ui_init);

Re: COM/ActiveX/OLE Help

Reply #6
Thanks I'll take a look.

Actually I have issues when loading two user-components that are using the same foo_mycom implementation:
Spoiler (click to show/hide)

-TT

Re: COM/ActiveX/OLE Help

Reply #7
Your comment about refcount and DllMain got me curious because reference count is initialized to 1 in all COM example codes everywhere, and using DllMain for such stuff is forbidden by Microsoft's documentation. Are you people certain you know what you are doing?
DllMain best practices
OleInitialize docs, which says directly "do not call OleInitialize or OleUninitialize from the DllMain function".
I did see the DllMain warning. That is why I called it out above as a deviation. Happily marc2k3 came through with a saner approach :)

The double-count is caused because CComPtr's constructor (atlcomcli.h line 173) calls AddRef(). The two alternatives I know of (and tested):
  • If using the ATL smart pointer class, initialize to 0. This is how CComObjectRootBase is coded.
  • If using a native pointer, initialize to 1 and call Release() before the function returns. This is how many examples implement/use these as Case correctly calls out. Using this second alternative has the downside of needing to release the reference if the function returns prematurely due to some failure in addition to the regular return. In this case if CoRegisterClassObject() fails; note that this function itself calls AddRef() which requires an eventual call to CoRevokeClassObject() to Release().