Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access COM vtable from C#

Tags:

c#

interop

com

Is there any way in C# to access a COM object's virtual method table in order to get a function's address?

like image 250
lfalin Avatar asked Aug 17 '10 16:08

lfalin


People also ask

What is a vtable in C?

In computer programming, a virtual method table (VMT), virtual function table, virtual call table, dispatch table, vtable, or vftable is a mechanism used in a programming language to support dynamic dispatch (or run-time method binding).

How is vtable implemented C++?

To implement virtual functions, C++ uses a special form of late binding known as the virtual table. The virtual table is a lookup table of functions used to resolve function calls in a dynamic/late binding manner.

What is stored in vtable?

The vtable contains an entry for each virtual function accessible by the class and stores a pointer to its definition. Only the most specific function definition callable by the class is stored in the vtable.

What is vtable and VPTR?

# Vtable is created by compiler at compile time. # VPTR is a hidden pointer created by compiler implicitly. # If base class pointer pointing to a function which is not available in base class then it will generate error over there. # Memory of Vtable and VPTR gets allocated inside the process address space.


2 Answers

After a lot of searching and piecing together different partial solutions, I figured out how to do it.

First you need to define the COM coclass for the object you're trying to access:

[ComImport, Guid("..."), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface ISomeCOMInterface
{
   // Define interface methods here, using PInvoke conversion between types
}

Next you need to instantiate the COM object. There are a couple of ways to do that. Since I was interested in DirectSound, I used:

[DllImport("dsound.dll", EntryPoint = "DirectSoundCreate", ...]
static extern void DirectSoundCreate(IntPtr GUID, [Out, MarshalAs(UnmanagedType.Interface)] out IDirectSound directSound, IntPtr pUnkOuter);

IDirectSound directSound;
DirectSoundCreate(IntPtr.Zero, out directSound, IntPtr.Zero);

Since I now had my COM object, I could then use Hans' suggestion of Marshal.GetComInterfaceForObject():

IntPtr comPtr = Marshal.GetComInterfaceForObject(directSound, typeof(IDirectSound));
IntPtr vTable = Marshal.ReadIntPtr(comPtr);

As an added bonus, you can then iterate through the vtable functions like this:

int start = Marshal.GetStartComSlot(typeof(IDirectSound));
int end = Marshal.GetEndComSlot(typeof(IDirectSound));

ComMemberType mType = 0;
for (int i = start; i <= end; i++)
{
    System.Reflection.MemberInfo mi = Marshal.GetMethodInfoForComSlot(typeof(IDirectSound), i, ref mType);
    Console.WriteLine("Method {0} at address 0x{1:X}", mi.Name, Marshal.ReadIntPtr(vTable, i * Marshal.SizeOf(typeof(IntPtr))).ToInt64());
}

Extra Reading / References:

  • http://msdn.microsoft.com/en-us/library/aa645736(VS.71).aspx [Info on COM coclass declarations]
  • http://www.codeproject.com/KB/COM/com_in_c1.aspx [Info on VTables and COM in general]
  • http://naudio.codeplex.com/ [Info specific to the DirectSound COM Interfaces]
like image 62
lfalin Avatar answered Oct 24 '22 04:10

lfalin


You can't dig the native interface pointer out of the RCW. But you can call Marshal.GetComInterfaceForObject(), should be good as long as you ask for the right interface. From there, you can Marshal.ReadIntPtr() to get the v-table entries. Offset 0 is QueryInterface, 4 is AddRef, 8 is Release, 12 is the first interface method, etc. Double that for x64 code. Marshal.GetComSlotForMethodInfo() is an option.

Actually calling the method requires Marshal.GetDelegateForFunctionPointer(), you need to declare the delegate with the exact signature of the COM interface method. That won't be the signature it has when you call this method normally, it is the [PreserveSig] signature. In other words, a function that returns HRESULT and a ref argument if it returns a value.

Opportunities to bomb your program are plentiful.


After update: you need Marshal.GetFunctionPointerForDelegate and Marshal.WriteIntPtr to patch the v-table slot entry.

like image 2
Hans Passant Avatar answered Oct 24 '22 05:10

Hans Passant