971 lines
25 KiB
C++
971 lines
25 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ShimHook.cpp
|
|
|
|
Abstract:
|
|
|
|
Strictly Shim hooking routines.
|
|
|
|
Notes:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
11/11/1999 markder Added comments
|
|
01/10/2000 linstev Format to new style
|
|
03/14/2000 robkenny Changed DPF from eDebugLevelInfo to eDebugLevelSpew
|
|
03/31/2000 robkenny Added our own private versions of malloc/free new/delete
|
|
10/29/2000 markder Added version 2 support
|
|
08/14/2001 robkenny Moved generic routines to ShimLib.cpp
|
|
08/14/2001 robkenny Moved code inside the ShimLib namespace.
|
|
|
|
--*/
|
|
|
|
#include "ShimHook.h"
|
|
#include "ShimHookMacro.h"
|
|
|
|
namespace ShimLib
|
|
{
|
|
|
|
HINSTANCE g_hinstDll;
|
|
BOOL g_bMultiShim;
|
|
PHOOKAPI g_pAPIHooks;
|
|
PSHIM_COM_HOOK g_pCOMHooks;
|
|
DWORD g_dwAPIHookCount;
|
|
DWORD g_dwCOMHookCount;
|
|
DWORD g_dwCOMHookBuffer;
|
|
DWORD g_dwShimVersion;
|
|
CHAR g_szCommandLine[SHIM_COMMAND_LINE_MAX_BUFFER];
|
|
|
|
/*++
|
|
|
|
Global variables for COM hook support
|
|
|
|
The following variables are pointers to the first entry in linked lists that
|
|
are maintained by the mechanism in order to properly manage the hooking
|
|
process.
|
|
|
|
There will be one SHIM_IFACE_FN_MAP for every COM interface function pointer
|
|
that was overwritten with one of our hooks.
|
|
|
|
There will be one SHIM_HOOKED_OBJECT entry every COM interface that is handed
|
|
out. This is required to differentiate between different classes that expose
|
|
the same interface, but one is hooked and one isn't.
|
|
|
|
--*/
|
|
PSHIM_IFACE_FN_MAP g_pIFaceFnMaps;
|
|
PSHIM_HOOKED_OBJECT g_pObjectCache;
|
|
PLDR_DATA_TABLE_ENTRY g_DllLoadingEntry;
|
|
|
|
|
|
|
|
PHOOKAPI GetHookAPIs( IN LPSTR pszCmdLine, IN LPWSTR pwszShim, IN OUT DWORD *pdwHooksCount );
|
|
void PatchFunction( PVOID* pVtbl, DWORD dwVtblIndex, PVOID pfnNew );
|
|
ULONG COMHook_AddRef( PVOID pThis );
|
|
ULONG COMHook_Release( PVOID pThis );
|
|
HRESULT COMHook_QueryInterface( PVOID pThis, REFIID iid, PVOID* ppvObject );
|
|
HRESULT COMHook_IClassFactory_CreateInstance( PVOID pThis, IUnknown * pUnkOuter, REFIID riid, void ** ppvObject );
|
|
VOID HookObject(IN CLSID *pCLSID, IN REFIID riid, OUT LPVOID *ppv, OUT PSHIM_HOOKED_OBJECT pOb, IN BOOL bClassFactory);
|
|
|
|
|
|
|
|
void
|
|
NotifyShims(
|
|
int nReason,
|
|
UINT_PTR extraInfo
|
|
)
|
|
{
|
|
switch (nReason) {
|
|
case SN_STATIC_DLLS_INITIALIZED:
|
|
InitializeHooksEx(SHIM_STATIC_DLLS_INITIALIZED, NULL, NULL, NULL);
|
|
break;
|
|
case SN_PROCESS_DYING:
|
|
InitializeHooksEx(SHIM_PROCESS_DYING, NULL, NULL, NULL);
|
|
break;
|
|
case SN_DLL_LOADING:
|
|
|
|
g_DllLoadingEntry = (PLDR_DATA_TABLE_ENTRY)extraInfo;
|
|
|
|
InitializeHooksEx(SHIM_DLL_LOADING, NULL, NULL, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Called by the shim mechanism. Initializes the global APIHook array and
|
|
returns necessary information to the shim mechanism.
|
|
|
|
Arguments:
|
|
|
|
IN dwGetProcAddress - Function pointer to GetProcAddress
|
|
IN dwLoadLibraryA - Function pointer to LoadLibraryA
|
|
IN dwFreeLibrary - Function pointer to FreeLibrary
|
|
IN OUT pdwHooksCount - Receive the number of APIHooks in the returned array
|
|
|
|
Return Value:
|
|
|
|
Pointer to global HOOKAPI array.
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
PHOOKAPI
|
|
GetHookAPIs(
|
|
IN LPSTR pszCmdLine,
|
|
IN LPWSTR pwszShim,
|
|
IN OUT DWORD * pdwHooksCount
|
|
)
|
|
{
|
|
char szModName[MAX_PATH] = "";
|
|
char* pszCursor = NULL;
|
|
PHOOKAPI pHookAPIs = NULL;
|
|
|
|
// Initialize file logging for this shim.
|
|
|
|
GetModuleFileNameA(g_hinstDll, szModName, MAX_PATH);
|
|
|
|
pszCursor = szModName + lstrlenA(szModName);
|
|
|
|
while (pszCursor >= szModName && *pszCursor != '\\') {
|
|
pszCursor--;
|
|
}
|
|
|
|
InitFileLogSupport(pszCursor + 1);
|
|
|
|
pHookAPIs = InitializeHooksEx(DLL_PROCESS_ATTACH, pwszShim, pszCmdLine, pdwHooksCount);
|
|
|
|
DPF("ShimLib", eDbgLevelBase,
|
|
"[Shim] %S%s%s%s\n",
|
|
pwszShim,
|
|
pszCmdLine[0] != '\0' ? "(\"" : "",
|
|
pszCmdLine,
|
|
pszCmdLine[0] != '\0' ? "\")" : "");
|
|
|
|
return pHookAPIs;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Adds an entry to the g_IFaceFnMaps linked list.
|
|
|
|
Arguments:
|
|
|
|
IN pVtbl - Pointer to an interface vtable to file under
|
|
IN pfnNew - Pointer to the new (stub) function
|
|
IN pfnOld - Pointer to the old (original) function
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
AddIFaceFnMap(
|
|
IN PVOID pVtbl,
|
|
IN PVOID pfnNew,
|
|
IN PVOID pfnOld
|
|
)
|
|
{
|
|
PSHIM_IFACE_FN_MAP pNewMap = (PSHIM_IFACE_FN_MAP) ShimMalloc( sizeof(SHIM_IFACE_FN_MAP) );
|
|
|
|
if (pNewMap == NULL)
|
|
{
|
|
DPF("ShimLib", eDbgLevelError, "[AddIFaceFnMap] Could not allocate space for new SHIM_IFACE_FN_MAP.\n");
|
|
return;
|
|
}
|
|
|
|
DPF("ShimLib", eDbgLevelSpew, "[AddIFaceFnMap] pVtbl: 0x%p pfnNew: 0x%p pfnOld: 0x%p\n",
|
|
pVtbl,
|
|
pfnNew,
|
|
pfnOld);
|
|
|
|
pNewMap->pVtbl = pVtbl;
|
|
pNewMap->pfnNew = pfnNew;
|
|
pNewMap->pfnOld = pfnOld;
|
|
|
|
pNewMap->pNext = g_pIFaceFnMaps;
|
|
g_pIFaceFnMaps = pNewMap;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Searches the g_pIFaceFnMaps linked list for a match on pVtbl and pfnNew, and
|
|
returns the corresponding pfnOld. This is typically called from inside a
|
|
stubbed function to determine what original function pointer to call for the
|
|
particular vtable that was used by the caller.
|
|
|
|
It is also used by PatchFunction to determine if a vtable's function pointer
|
|
has already been stubbed.
|
|
|
|
Arguments:
|
|
|
|
IN pVtbl - Pointer to an interface vtable to file under
|
|
IN pfnNew - Pointer to the new (stub) function
|
|
IN bThrowExceptionIfNull - Flag that specifies whether it should be
|
|
possible to not find the original function in our function
|
|
map
|
|
|
|
Return Value:
|
|
|
|
Returns the original function pointer
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
PVOID
|
|
LookupOriginalCOMFunction(
|
|
IN PVOID pVtbl,
|
|
IN PVOID pfnNew,
|
|
IN BOOL bThrowExceptionIfNull
|
|
)
|
|
{
|
|
PSHIM_IFACE_FN_MAP pMap = g_pIFaceFnMaps;
|
|
PVOID pReturn = NULL;
|
|
|
|
DPF("ShimLib", eDbgLevelSpew, "[LookupOriginalCOMFunction] pVtbl: 0x%p pfnNew: 0x%p ",
|
|
pVtbl,
|
|
pfnNew);
|
|
|
|
// Scan the linked list for a match and return if found.
|
|
while (pMap)
|
|
{
|
|
if (pMap->pVtbl == pVtbl && pMap->pfnNew == pfnNew)
|
|
{
|
|
pReturn = pMap->pfnOld;
|
|
break;
|
|
}
|
|
|
|
pMap = (PSHIM_IFACE_FN_MAP) pMap->pNext;
|
|
}
|
|
|
|
DPF("ShimLib", eDbgLevelSpew, " --> Returned: 0x%p\n", pReturn);
|
|
|
|
if (!pReturn && bThrowExceptionIfNull)
|
|
{
|
|
// If we have hit this point, there is something seriously wrong.
|
|
// Either there is a bug in the AddRef/Release stubs or the app
|
|
// obtained an interface pointer in some way that we don't catch.
|
|
DPF("ShimLib", eDbgLevelError,"ERROR: Shim COM APIHooking mechanism failed.\n");
|
|
APPBreakPoint();
|
|
}
|
|
|
|
return pReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Stores the original function pointer in the function map and overwrites it in
|
|
the vtable with the new one.
|
|
|
|
Arguments:
|
|
|
|
IN pVtbl - Pointer to an interface vtable to file under
|
|
IN dwVtblIndex - The index of the target function within the vtable.
|
|
IN pfnNew - Pointer to the new (stub) function
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
PatchFunction(
|
|
IN PVOID* pVtbl,
|
|
IN DWORD dwVtblIndex,
|
|
IN PVOID pfnNew
|
|
)
|
|
{
|
|
DWORD dwOldProtect = 0;
|
|
DWORD dwOldProtect2 = 0;
|
|
|
|
DPF("ShimLib", eDbgLevelSpew, "[PatchFunction] pVtbl: 0x%p, dwVtblIndex: %d, pfnOld: 0x%p, pfnNew: 0x%p\n",
|
|
pVtbl,
|
|
dwVtblIndex,
|
|
pVtbl[dwVtblIndex],
|
|
pfnNew);
|
|
|
|
// if not patched yet
|
|
if (!LookupOriginalCOMFunction( pVtbl, pfnNew, FALSE))
|
|
{
|
|
AddIFaceFnMap( pVtbl, pfnNew, pVtbl[dwVtblIndex]);
|
|
|
|
// Make the code page writable and overwrite function pointers in vtable
|
|
if (VirtualProtect(pVtbl + dwVtblIndex,
|
|
sizeof(DWORD),
|
|
PAGE_READWRITE,
|
|
&dwOldProtect))
|
|
{
|
|
pVtbl[dwVtblIndex] = pfnNew;
|
|
|
|
// Return the code page to its original state
|
|
VirtualProtect(pVtbl + dwVtblIndex,
|
|
sizeof(DWORD),
|
|
dwOldProtect,
|
|
&dwOldProtect2);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This stub exists to keep track of an interface's reference count changes.
|
|
Note that the bAddRefTrip flag is cleared, which allows
|
|
APIHook_QueryInterface to determine whether an AddRef was performed inside
|
|
the original QueryInterface function call.
|
|
|
|
Arguments:
|
|
|
|
IN pThis - The object's 'this' pointer
|
|
|
|
Return Value:
|
|
|
|
Return value is obtained from original function
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
APIHook_AddRef(
|
|
IN PVOID pThis
|
|
)
|
|
{
|
|
PSHIM_HOOKED_OBJECT pHookedOb = g_pObjectCache;
|
|
_pfn_AddRef pfnOld;
|
|
ULONG ulReturn;
|
|
|
|
pfnOld = (_pfn_AddRef) LookupOriginalCOMFunction( *((PVOID*)(pThis)),
|
|
APIHook_AddRef,
|
|
TRUE);
|
|
|
|
ulReturn = (*pfnOld)(pThis);
|
|
|
|
while (pHookedOb)
|
|
{
|
|
if (pHookedOb->pThis == pThis)
|
|
{
|
|
pHookedOb->dwRef++;
|
|
pHookedOb->bAddRefTrip = FALSE;
|
|
DPF("ShimLib", eDbgLevelSpew, "[AddRef] pThis: 0x%p dwRef: %d ulReturn: %d\n",
|
|
pThis,
|
|
pHookedOb->dwRef,
|
|
ulReturn);
|
|
break;
|
|
}
|
|
|
|
pHookedOb = (PSHIM_HOOKED_OBJECT) pHookedOb->pNext;
|
|
}
|
|
|
|
return ulReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This stub exists to keep track of an interface's reference count changes.
|
|
|
|
Arguments:
|
|
|
|
IN pThis - The object's 'this' pointer
|
|
|
|
Return Value:
|
|
|
|
Return value is obtained from original function
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
ULONG
|
|
APIHook_Release(
|
|
IN PVOID pThis
|
|
)
|
|
{
|
|
PSHIM_HOOKED_OBJECT *ppHookedOb = &g_pObjectCache;
|
|
PSHIM_HOOKED_OBJECT pTemp;
|
|
_pfn_Release pfnOld;
|
|
ULONG ulReturn;
|
|
|
|
pfnOld = (_pfn_Release) LookupOriginalCOMFunction(*((PVOID*)(pThis)),
|
|
APIHook_Release,
|
|
TRUE);
|
|
|
|
ulReturn = (*pfnOld)( pThis );
|
|
|
|
while ((*ppHookedOb))
|
|
{
|
|
if ((*ppHookedOb)->pThis == pThis)
|
|
{
|
|
(*ppHookedOb)->dwRef--;
|
|
|
|
DPF("ShimLib", eDbgLevelSpew, "[Release] pThis: 0x%p dwRef: %d ulReturn: %d %s\n",
|
|
pThis,
|
|
(*ppHookedOb)->dwRef,
|
|
ulReturn,
|
|
((*ppHookedOb)->dwRef?"":" --> Deleted"));
|
|
|
|
if (!((*ppHookedOb)->dwRef))
|
|
{
|
|
pTemp = (*ppHookedOb);
|
|
*ppHookedOb = (PSHIM_HOOKED_OBJECT) (*ppHookedOb)->pNext;
|
|
ShimFree(pTemp);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
ppHookedOb = (PSHIM_HOOKED_OBJECT*) &((*ppHookedOb)->pNext);
|
|
}
|
|
|
|
return ulReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This stub catches the application attempting to obtain a new interface
|
|
pointer to the same object. The function searches the object cache
|
|
to obtain a CLSID for the object and, if found, APIHooks all required
|
|
functions in the new vtable (via the HookObject call).
|
|
|
|
Arguments:
|
|
|
|
IN pThis - The object's 'this' pointer
|
|
IN iid - Reference to the identifier of the requested interface
|
|
IN ppvObject - Address of output variable that receives the interface
|
|
pointer requested in riid.
|
|
|
|
Return Value:
|
|
|
|
Return value is obtained from original function
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
APIHook_QueryInterface(
|
|
PVOID pThis,
|
|
REFIID iid,
|
|
PVOID* ppvObject
|
|
)
|
|
{
|
|
HRESULT hrReturn = E_FAIL;
|
|
_pfn_QueryInterface pfnOld = NULL;
|
|
PSHIM_HOOKED_OBJECT pOb = g_pObjectCache;
|
|
|
|
pfnOld = (_pfn_QueryInterface) LookupOriginalCOMFunction(
|
|
*((PVOID*)pThis),
|
|
APIHook_QueryInterface,
|
|
TRUE);
|
|
|
|
while (pOb)
|
|
{
|
|
if (pOb->pThis == pThis)
|
|
{
|
|
pOb->bAddRefTrip = TRUE;
|
|
break;
|
|
}
|
|
pOb = (PSHIM_HOOKED_OBJECT) pOb->pNext;
|
|
}
|
|
|
|
if (S_OK == (hrReturn = (*pfnOld) (pThis, iid, ppvObject)))
|
|
{
|
|
if (pOb)
|
|
{
|
|
if (pOb->pThis == *((PVOID*)ppvObject))
|
|
{
|
|
// Same object. Detect whether QueryInterface used IUnknown::AddRef
|
|
// or an internal function.
|
|
DPF("ShimLib", eDbgLevelSpew,"[HookObject] Existing object%s. pThis: 0x%p\n",
|
|
(pOb->bAddRefTrip?" (AddRef'd) ":""),
|
|
pOb->pThis);
|
|
|
|
if (pOb->bAddRefTrip)
|
|
{
|
|
(pOb->dwRef)++; // AddRef the object
|
|
pOb->bAddRefTrip = FALSE;
|
|
}
|
|
|
|
// We are assured that the CLSID for the object will be the same.
|
|
HookObject(pOb->pCLSID, iid, ppvObject, pOb, pOb->bClassFactory);
|
|
}
|
|
else
|
|
{
|
|
HookObject(pOb->pCLSID, iid, ppvObject, NULL, pOb->bClassFactory);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hrReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This stub catches the most interesting part of the object creation process:
|
|
The actual call to IClassFactory::CreateInstance. Since no CLSID is passed
|
|
in to this function, the stub must decide whether to APIHook the object by
|
|
looking up the instance of the class factory in the object cache. IF IT
|
|
EXISTS IN THE CACHE, that indicates that it creates an object that we wish
|
|
to APIHook.
|
|
|
|
Arguments:
|
|
|
|
IN pThis - The object's 'this' pointer
|
|
IN pUnkOuter - Pointer to whether object is or isn't part of an aggregate
|
|
IN riid - Reference to the identifier of the interface
|
|
OUT ppvObject - Address of output variable that receives the interface
|
|
pointer requested in riid
|
|
|
|
Return Value:
|
|
|
|
Return value is obtained from original function
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
APIHook_IClassFactory_CreateInstance(
|
|
PVOID pThis,
|
|
IUnknown *pUnkOuter,
|
|
REFIID riid,
|
|
VOID **ppvObject
|
|
)
|
|
{
|
|
HRESULT hrReturn = E_FAIL;
|
|
_pfn_CreateInstance pfnOldCreateInst = NULL;
|
|
PSHIM_HOOKED_OBJECT pOb = g_pObjectCache;
|
|
|
|
pfnOldCreateInst = (_pfn_CreateInstance) LookupOriginalCOMFunction(
|
|
*((PVOID*)pThis),
|
|
APIHook_IClassFactory_CreateInstance,
|
|
FALSE);
|
|
|
|
if (pfnOldCreateInst == NULL) {
|
|
DPF("ShimLib", eDbgLevelError, "[CreateInstance] Cannot find CreateInstance\n", pThis);
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (S_OK == (hrReturn = (*pfnOldCreateInst)(pThis, pUnkOuter, riid, ppvObject)))
|
|
{
|
|
while (pOb)
|
|
{
|
|
if (pOb->pThis == pThis)
|
|
{
|
|
// This class factory instance creates an object that we APIHook.
|
|
DPF("ShimLib", eDbgLevelSpew, "[CreateInstance] Hooking object! pThis: 0x%p\n", pThis);
|
|
HookObject(pOb->pCLSID, riid, ppvObject, NULL, FALSE);
|
|
break;
|
|
}
|
|
|
|
pOb = (PSHIM_HOOKED_OBJECT) pOb->pNext;
|
|
}
|
|
}
|
|
|
|
return hrReturn;
|
|
}
|
|
|
|
|
|
VOID
|
|
HookCOMInterface(
|
|
REFCLSID rclsid,
|
|
REFIID riid,
|
|
LPVOID * ppv,
|
|
BOOL bClassFactory
|
|
)
|
|
{
|
|
DWORD i = 0;
|
|
|
|
// Determine if we need to hook this object
|
|
for (i = 0; i < g_dwCOMHookCount; i++)
|
|
{
|
|
if (g_pCOMHooks[i].pCLSID &&
|
|
IsEqualGUID( (REFCLSID) *(g_pCOMHooks[i].pCLSID), rclsid))
|
|
{
|
|
// Yes, we are hooking an interface on this object.
|
|
HookObject((CLSID*) &rclsid, riid, ppv, NULL, bClassFactory);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Free memory associated with Hooks and dump info
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
DumpCOMHooks()
|
|
{
|
|
PSHIM_IFACE_FN_MAP pMap = g_pIFaceFnMaps;
|
|
PSHIM_HOOKED_OBJECT pHookedOb = g_pObjectCache;
|
|
|
|
// Dump function map
|
|
DPF("ShimLib", eDbgLevelSpew, "\n--- Shim COM Hook Function Map ---\n\n");
|
|
|
|
while (pMap)
|
|
{
|
|
DPF("ShimLib", eDbgLevelSpew, "pVtbl: 0x%p pfnNew: 0x%p pfnOld: 0x%p\n",
|
|
pMap->pVtbl,
|
|
pMap->pfnNew,
|
|
pMap->pfnOld);
|
|
|
|
pMap = (PSHIM_IFACE_FN_MAP) pMap->pNext;
|
|
}
|
|
|
|
// Dump class factory cache
|
|
DPF("ShimLib", eDbgLevelSpew, "\n--- Shim Object Cache (SHOULD BE EMPTY!!) ---\n\n");
|
|
|
|
while (pHookedOb)
|
|
{
|
|
DPF("ShimLib", eDbgLevelSpew, "pThis: 0x%p dwRef: %d\n",
|
|
pHookedOb->pThis,
|
|
pHookedOb->dwRef);
|
|
|
|
pHookedOb = (PSHIM_HOOKED_OBJECT) pHookedOb->pNext;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
This function adds the object's important info to the object cache and then
|
|
patches all required functions. IUnknown is hooked for all objects
|
|
regardless.
|
|
|
|
Arguments:
|
|
|
|
IN rclsid - CLSID for the class object
|
|
IN riid - Reference to the identifier of the interface that communicates
|
|
with the class object
|
|
OUT ppv - Address of the pThis pointer that uniquely identifies an
|
|
instance of the COM interface
|
|
OUT pOb - New obj pointer
|
|
IN bClassFactory - Is this a class factory call
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
HookObject(
|
|
IN CLSID *pCLSID,
|
|
IN REFIID riid,
|
|
OUT LPVOID *ppv,
|
|
OUT PSHIM_HOOKED_OBJECT pOb,
|
|
IN BOOL bClassFactory
|
|
)
|
|
{
|
|
// Here's how a COM object looks in memory:
|
|
//
|
|
// pv - The pointer to the object's interface. In C++ terms, it
|
|
// | is sort of like the "this" pointer but objects
|
|
// | will hand back different pointers for different interfaces.
|
|
// |
|
|
// `-> pVtbl - The COM virtual function table pointer. This is the
|
|
// | first 32-bit member of the interface structure.
|
|
// |
|
|
// |-> QueryInterface - First function in the root interface, IUnknown. This
|
|
// | function allows calling members to request a different
|
|
// | interface that may be implemented by the object.
|
|
// |
|
|
// |-> AddRef - Increments the reference count for this interface.
|
|
// |
|
|
// |-> Release - Decrements the reference count for this interface.
|
|
// |
|
|
// |-> InterfaceFn1 - Beginning of the interface-specific functions.
|
|
// |-> InterfaceFn2
|
|
// |-> InterfaceFn3
|
|
// | .
|
|
// | .
|
|
// | .
|
|
//
|
|
|
|
// The COM hooking mechanism is interested in the virtual function table pointer, and to get
|
|
// it we must dereference the ppv pointer twice.
|
|
PVOID *pVtbl = ((PVOID*)(*((PVOID*)(*ppv))));
|
|
|
|
DWORD i = 0;
|
|
|
|
if (!pOb)
|
|
{
|
|
// If pOb is NULL, then the object does not exist in the cache yet.
|
|
// Make a new entry for the object.
|
|
|
|
DPF("ShimLib", eDbgLevelSpew, "[HookObject] New %s! pThis: 0x%p\n",
|
|
(bClassFactory?"class factory":"object"),
|
|
*ppv);
|
|
|
|
pOb = (PSHIM_HOOKED_OBJECT) ShimMalloc(sizeof(SHIM_HOOKED_OBJECT));
|
|
|
|
if( pOb == NULL )
|
|
{
|
|
DPF("ShimLib", eDbgLevelError, "[HookObject] Could not allocate memory for SHIM_HOOKED_OBJECT.\n");
|
|
return;
|
|
}
|
|
|
|
pOb->pCLSID = pCLSID;
|
|
pOb->pThis = *ppv;
|
|
pOb->dwRef = 1;
|
|
pOb->bAddRefTrip = FALSE;
|
|
pOb->pNext = g_pObjectCache;
|
|
pOb->bClassFactory = bClassFactory;
|
|
|
|
g_pObjectCache = pOb;
|
|
}
|
|
|
|
// IUnknown must always be hooked since it is possible to get
|
|
// a new interface pointer using it, and we need to process each interface
|
|
// handed out. We must also keep track of the reference count so that
|
|
// we can clean up our interface function map.
|
|
|
|
PatchFunction(pVtbl, 0, APIHook_QueryInterface);
|
|
PatchFunction(pVtbl, 1, APIHook_AddRef);
|
|
PatchFunction(pVtbl, 2, APIHook_Release);
|
|
|
|
if (bClassFactory && IsEqualGUID(IID_IClassFactory, riid))
|
|
{
|
|
// If we are processing a class factory, all we care about
|
|
// hooking is CreateInstance, since it is an API that produces
|
|
// the actual object we are interested in.
|
|
PatchFunction(pVtbl, 3, APIHook_IClassFactory_CreateInstance);
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < g_dwCOMHookCount; i++)
|
|
{
|
|
if (!(g_pCOMHooks[i].pCLSID) || !pCLSID)
|
|
{
|
|
// A CLSID was not specified -- hook any object that exposes
|
|
// the specified interface.
|
|
if (IsEqualGUID( (REFIID) *(g_pCOMHooks[i].pIID), riid))
|
|
{
|
|
PatchFunction(
|
|
pVtbl,
|
|
g_pCOMHooks[i].dwVtblIndex,
|
|
g_pCOMHooks[i].pfnNew);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// A CLSID was specified -- hook only interfaces on the
|
|
// specified object.
|
|
if (IsEqualGUID((REFCLSID) *(g_pCOMHooks[i].pCLSID), *pCLSID) &&
|
|
IsEqualGUID((REFIID) *(g_pCOMHooks[i].pIID), riid))
|
|
{
|
|
PatchFunction(
|
|
pVtbl,
|
|
g_pCOMHooks[i].dwVtblIndex,
|
|
g_pCOMHooks[i].pfnNew);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL InitHooks(DWORD dwCount)
|
|
{
|
|
g_dwAPIHookCount = dwCount;
|
|
g_pAPIHooks = (PHOOKAPI) ShimMalloc( g_dwAPIHookCount * sizeof(HOOKAPI) );
|
|
if (g_pAPIHooks)
|
|
{
|
|
ZeroMemory(g_pAPIHooks, g_dwAPIHookCount * sizeof(HOOKAPI) );
|
|
}
|
|
|
|
return g_pAPIHooks != NULL;
|
|
}
|
|
|
|
BOOL InitComHooks(DWORD dwCount)
|
|
{
|
|
//DECLARE_APIHOOK(DDraw.dll, DirectDrawCreate);
|
|
//DECLARE_APIHOOK(DDraw.dll, DirectDrawCreateEx);
|
|
|
|
g_dwCOMHookCount = dwCount;
|
|
g_pCOMHooks = (PSHIM_COM_HOOK) ShimMalloc( g_dwCOMHookCount * sizeof(SHIM_COM_HOOK) );
|
|
if (g_pCOMHooks)
|
|
{
|
|
ZeroMemory(g_pCOMHooks, g_dwCOMHookCount * sizeof(SHIM_COM_HOOK) );
|
|
}
|
|
|
|
return g_pCOMHooks != NULL;
|
|
|
|
}
|
|
|
|
VOID AddComHook(REFCLSID clsid, REFIID iid, PVOID hook, DWORD vtblndx)
|
|
{
|
|
if (g_dwCOMHookBuffer <= g_dwCOMHookCount) {
|
|
|
|
// Buffer is too small, must resize.
|
|
DWORD dwNewBuffer = g_dwCOMHookBuffer * 2;
|
|
PSHIM_COM_HOOK pNewBuffer = NULL;
|
|
|
|
if (dwNewBuffer == 0) {
|
|
// 50 is the initial allocation, but it should be at least g_dwCOMHookCount
|
|
dwNewBuffer = max(50, g_dwCOMHookCount);
|
|
}
|
|
|
|
pNewBuffer = (PSHIM_COM_HOOK) ShimMalloc( sizeof(SHIM_COM_HOOK) * dwNewBuffer );
|
|
|
|
if (pNewBuffer == NULL) {
|
|
DPF("ShimLib", eDbgLevelError,
|
|
"[AddComHook] Could not allocate SHIM_COM_HOOK array.");
|
|
return;
|
|
}
|
|
|
|
// Copy over original array, then free the old one.
|
|
|
|
if (g_pCOMHooks != NULL) {
|
|
memcpy(pNewBuffer, g_pCOMHooks, sizeof(SHIM_COM_HOOK) * g_dwCOMHookBuffer);
|
|
ShimFree(g_pCOMHooks);
|
|
}
|
|
|
|
g_pCOMHooks = pNewBuffer;
|
|
g_dwCOMHookBuffer = dwNewBuffer;
|
|
}
|
|
|
|
g_pCOMHooks[g_dwCOMHookCount].pCLSID = (CLSID*) &clsid;
|
|
g_pCOMHooks[g_dwCOMHookCount].pIID = (IID*) &iid;
|
|
g_pCOMHooks[g_dwCOMHookCount].dwVtblIndex = vtblndx;
|
|
g_pCOMHooks[g_dwCOMHookCount].pfnNew = hook;
|
|
|
|
g_dwCOMHookCount++;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
}; // end of namespace ShimLib
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Called on process detach with old shim mechanism.
|
|
|
|
Arguments:
|
|
|
|
See MSDN
|
|
|
|
Return Value:
|
|
|
|
See MSDN
|
|
|
|
History:
|
|
|
|
11/01/1999 markder Created
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
DllMain(
|
|
HINSTANCE hinstDLL,
|
|
DWORD fdwReason,
|
|
LPVOID /*lpvReserved*/
|
|
)
|
|
{
|
|
using namespace ShimLib;
|
|
|
|
switch (fdwReason) {
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
g_hinstDll = hinstDLL;
|
|
g_pAPIHooks = NULL;
|
|
g_dwAPIHookCount = 0;
|
|
g_dwCOMHookCount = 0;
|
|
g_dwCOMHookBuffer = 0;
|
|
g_pCOMHooks = NULL;
|
|
g_pIFaceFnMaps = NULL;
|
|
g_pObjectCache = NULL;
|
|
g_szCommandLine[0] = '\0';
|
|
g_bMultiShim = FALSE;
|
|
g_dwShimVersion = 1;
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
if (g_dwCOMHookCount > 0) {
|
|
DumpCOMHooks();
|
|
}
|
|
|
|
InitializeHooks(DLL_PROCESS_DETACH);
|
|
InitializeHooksEx(DLL_PROCESS_DETACH, NULL, NULL, NULL);
|
|
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|