Hooking a DirectX/COM Interface

After all the helpful articles I read here, I am glad that I can contribute to a subject that has not yet been covered.

This article features a description on how to hook a DirectX/COM Interface. I used the DirectInput interface as an example of how to hook an interface function. For the basic Windows hook, I referred to an article by Wade Brainerd that describes the API hooking process.

Task

Intercepting a method of a COM Interface requires an extended approach compared to hooking an API call. If the desired DLL is examined, only the create Interface function is actually exported by the DLL. So, how can you hook your desired function?

A COM interface is basically a list of virtual function pointers that are linked together. You merely have to follow the links and modify every node until you finally reach the pointer of the function that you want to replace.

Step 1

As you can see, only the create interface COM functions are visible, so you have to start your hooking chain at the DirectInputCreate function, which returns a COM interface. Here, you have to inject your DLL into the import address table (IAT) of the calling program.

Step 2

If the calling program invokes a DirectInputCreate, your function is called. Also, you receive a pointer to a pointer of a pointer to a virtual function table that is the interface of the direct input.

DECLARE_INTERFACE_(IDirectInputW, IUnknown)
{
   /*** IUnknown methods ***/
   STDMETHOD(QueryInterface)(THIS_ REFIID riid,
                             LPVOID * ppvObj) PURE;
   STDMETHOD_(ULONG,AddRef)(THIS) PURE;
   STDMETHOD_(ULONG,Release)(THIS) PURE;

   /*** IDirectInputW methods ***/
   STDMETHOD(CreateDevice)(THIS_ REFGUID,LPDIRECTINPUTDEVICEW *,
                           LPUNKNOWN) PURE;
   STDMETHOD(EnumDevices)(THIS_ DWORD,LPDIENUMDEVICESCALLBACKW,
                          LPVOID,DWORD) PURE;
   STDMETHOD(GetDeviceStatus)(THIS_ REFGUID) PURE;
   STDMETHOD(RunControlPanel)(THIS_ HWND,DWORD) PURE;
   STDMETHOD(Initialize)(THIS_ HINSTANCE,DWORD) PURE;
};

Step 3

Now, you can create your device with CreateDevice. You again will receive an address to a different virtual function pointer table; it represents the Device.

Pick the method you want to replace and change the virtual function pointer table in the appropriate place to inject your function.

Step 4

Do the actual data manipulation.

Implementation

Step 1

To hook yourself into an API function, you simply can use the Windows API call SetWindowsHookEx. Here, you create a system hook to monitor the starting processes and match them to your desired program. After you have identified your program, you have to compare the import module names with the DLL you want to replace. Because this hook is written for direct input, the entry you want is:

DINPUT8.DLL

To find this entry, you have to loop through the descriptors until you find your DLL.

// Iterate through each import descriptor, and redirect if
// appropriate
   while ( pImportDesc->FirstThunk )
   {
      PSTR pszImportModuleName = MakePtr( PSTR, hModEXE,
                                          pImportDesc->Name);

      if ( lstrcmpi( pszImportModuleName, Hook->Name ) == 0 )
      {
         sprintf(dbBuffer,"Dll Found in module  %s replace it\n",
                 Hook->Name );
         WriteToLog(dbBuffer);
         RedirectIAT( Hook, pImportDesc, (PVOID)hModEXE );
      }

      pImportDesc++;    // Advance to next import descriptor
   }

After you find your entry, you have to remove the write protection from the IAT with

VirtualQuery( pIAT, &mbi, sizeof(mbi) );

to be able to write into the memory. After the memory is open, you have to find your entry by iterating through the IAT.

while ( pIteratingIAT->u1.Function )
{
   void* HookFn = 0;    // Set to either the SFunctionHook or pStubs.

   // import by name
   if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) )
   {
      PIMAGE_IMPORT_BY_NAME pImportName =
         MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr,
                  pINT->u1.AddressOfData );

      // Iterate through the hook functions, searching for this import.
      SFunctionHook* FHook = DLLHook->Functions;
      while ( FHook->Name )
      {
         if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 )
         {
            sprintf(dbBuffer,"Hooked function: %s\n",
                    (char*)pImportName->Name );
            WriteToLog(dbBuffer);
            // Save the old function in the SFunctionHook structure
            // and get the new one.
            FHook->OrigFn = (unsigned long*)pIteratingIAT->u1.Function;
            HookFn = FHook->HookFn;
            break;
         }

         FHook++;
      }

   }
}

Now, you can now replace it with your own.

// Replace the IAT function pointer if we have a hook.
if ( HookFn )
{
   // Cheez-o hack to see if what we're importing is code or data.
   // If it's code, we shouldn't be able to write to it
   if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) )
   {
      pIteratingIAT->u1.Function = (DWORD)HookFn;
   }
   else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
   {
      // Special hack for Win9X, which builds stubs for imported
      // functions in system DLLs (Loaded above 2GB). These stubs are
      // writeable, so we have to explicitly check for this case
      if ( pIteratingIAT->u1.Function > (DWORD)0x80000000 )
         pIteratingIAT->u1.Function = (DWORD)HookFn;
   }
}

The only thing remaining is to restore the memory attributes, as though nothing ever happened.

VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare);

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read