Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

970 lines
25 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. ShimHook.cpp
  5. Abstract:
  6. Strictly Shim hooking routines.
  7. Notes:
  8. None
  9. History:
  10. 11/01/1999 markder Created
  11. 11/11/1999 markder Added comments
  12. 01/10/2000 linstev Format to new style
  13. 03/14/2000 robkenny Changed DPF from eDebugLevelInfo to eDebugLevelSpew
  14. 03/31/2000 robkenny Added our own private versions of malloc/free new/delete
  15. 10/29/2000 markder Added version 2 support
  16. 08/14/2001 robkenny Moved generic routines to ShimLib.cpp
  17. 08/14/2001 robkenny Moved code inside the ShimLib namespace.
  18. --*/
  19. #include "ShimHook.h"
  20. #include "ShimHookMacro.h"
  21. namespace ShimLib
  22. {
  23. HINSTANCE g_hinstDll;
  24. BOOL g_bMultiShim;
  25. PHOOKAPI g_pAPIHooks;
  26. PSHIM_COM_HOOK g_pCOMHooks;
  27. DWORD g_dwAPIHookCount;
  28. DWORD g_dwCOMHookCount;
  29. DWORD g_dwCOMHookBuffer;
  30. DWORD g_dwShimVersion;
  31. CHAR g_szCommandLine[SHIM_COMMAND_LINE_MAX_BUFFER];
  32. /*++
  33. Global variables for COM hook support
  34. The following variables are pointers to the first entry in linked lists that
  35. are maintained by the mechanism in order to properly manage the hooking
  36. process.
  37. There will be one SHIM_IFACE_FN_MAP for every COM interface function pointer
  38. that was overwritten with one of our hooks.
  39. There will be one SHIM_HOOKED_OBJECT entry every COM interface that is handed
  40. out. This is required to differentiate between different classes that expose
  41. the same interface, but one is hooked and one isn't.
  42. --*/
  43. PSHIM_IFACE_FN_MAP g_pIFaceFnMaps;
  44. PSHIM_HOOKED_OBJECT g_pObjectCache;
  45. PLDR_DATA_TABLE_ENTRY g_DllLoadingEntry;
  46. PHOOKAPI GetHookAPIs( IN LPSTR pszCmdLine, IN LPWSTR pwszShim, IN OUT DWORD *pdwHooksCount );
  47. void PatchFunction( PVOID* pVtbl, DWORD dwVtblIndex, PVOID pfnNew );
  48. ULONG COMHook_AddRef( PVOID pThis );
  49. ULONG COMHook_Release( PVOID pThis );
  50. HRESULT COMHook_QueryInterface( PVOID pThis, REFIID iid, PVOID* ppvObject );
  51. HRESULT COMHook_IClassFactory_CreateInstance( PVOID pThis, IUnknown * pUnkOuter, REFIID riid, void ** ppvObject );
  52. VOID HookObject(IN CLSID *pCLSID, IN REFIID riid, OUT LPVOID *ppv, OUT PSHIM_HOOKED_OBJECT pOb, IN BOOL bClassFactory);
  53. void
  54. NotifyShims(
  55. int nReason,
  56. UINT_PTR extraInfo
  57. )
  58. {
  59. switch (nReason) {
  60. case SN_STATIC_DLLS_INITIALIZED:
  61. InitializeHooksEx(SHIM_STATIC_DLLS_INITIALIZED, NULL, NULL, NULL);
  62. break;
  63. case SN_PROCESS_DYING:
  64. InitializeHooksEx(SHIM_PROCESS_DYING, NULL, NULL, NULL);
  65. break;
  66. case SN_DLL_LOADING:
  67. g_DllLoadingEntry = (PLDR_DATA_TABLE_ENTRY)extraInfo;
  68. InitializeHooksEx(SHIM_DLL_LOADING, NULL, NULL, NULL);
  69. break;
  70. }
  71. }
  72. /*++
  73. Function Description:
  74. Called by the shim mechanism. Initializes the global APIHook array and
  75. returns necessary information to the shim mechanism.
  76. Arguments:
  77. IN dwGetProcAddress - Function pointer to GetProcAddress
  78. IN dwLoadLibraryA - Function pointer to LoadLibraryA
  79. IN dwFreeLibrary - Function pointer to FreeLibrary
  80. IN OUT pdwHooksCount - Receive the number of APIHooks in the returned array
  81. Return Value:
  82. Pointer to global HOOKAPI array.
  83. History:
  84. 11/01/1999 markder Created
  85. --*/
  86. PHOOKAPI
  87. GetHookAPIs(
  88. IN LPSTR pszCmdLine,
  89. IN LPWSTR pwszShim,
  90. IN OUT DWORD * pdwHooksCount
  91. )
  92. {
  93. char szModName[MAX_PATH] = "";
  94. char* pszCursor = NULL;
  95. PHOOKAPI pHookAPIs = NULL;
  96. // Initialize file logging for this shim.
  97. GetModuleFileNameA(g_hinstDll, szModName, MAX_PATH);
  98. pszCursor = szModName + lstrlenA(szModName);
  99. while (pszCursor >= szModName && *pszCursor != '\\') {
  100. pszCursor--;
  101. }
  102. InitFileLogSupport(pszCursor + 1);
  103. pHookAPIs = InitializeHooksEx(DLL_PROCESS_ATTACH, pwszShim, pszCmdLine, pdwHooksCount);
  104. DPF("ShimLib", eDbgLevelBase,
  105. "[Shim] %S%s%s%s\n",
  106. pwszShim,
  107. pszCmdLine[0] != '\0' ? "(\"" : "",
  108. pszCmdLine,
  109. pszCmdLine[0] != '\0' ? "\")" : "");
  110. return pHookAPIs;
  111. }
  112. /*++
  113. Function Description:
  114. Adds an entry to the g_IFaceFnMaps linked list.
  115. Arguments:
  116. IN pVtbl - Pointer to an interface vtable to file under
  117. IN pfnNew - Pointer to the new (stub) function
  118. IN pfnOld - Pointer to the old (original) function
  119. Return Value:
  120. None
  121. History:
  122. 11/01/1999 markder Created
  123. --*/
  124. VOID
  125. AddIFaceFnMap(
  126. IN PVOID pVtbl,
  127. IN PVOID pfnNew,
  128. IN PVOID pfnOld
  129. )
  130. {
  131. PSHIM_IFACE_FN_MAP pNewMap = (PSHIM_IFACE_FN_MAP) ShimMalloc( sizeof(SHIM_IFACE_FN_MAP) );
  132. if (pNewMap == NULL)
  133. {
  134. DPF("ShimLib", eDbgLevelError, "[AddIFaceFnMap] Could not allocate space for new SHIM_IFACE_FN_MAP.\n");
  135. return;
  136. }
  137. DPF("ShimLib", eDbgLevelSpew, "[AddIFaceFnMap] pVtbl: 0x%p pfnNew: 0x%p pfnOld: 0x%p\n",
  138. pVtbl,
  139. pfnNew,
  140. pfnOld);
  141. pNewMap->pVtbl = pVtbl;
  142. pNewMap->pfnNew = pfnNew;
  143. pNewMap->pfnOld = pfnOld;
  144. pNewMap->pNext = g_pIFaceFnMaps;
  145. g_pIFaceFnMaps = pNewMap;
  146. }
  147. /*++
  148. Function Description:
  149. Searches the g_pIFaceFnMaps linked list for a match on pVtbl and pfnNew, and
  150. returns the corresponding pfnOld. This is typically called from inside a
  151. stubbed function to determine what original function pointer to call for the
  152. particular vtable that was used by the caller.
  153. It is also used by PatchFunction to determine if a vtable's function pointer
  154. has already been stubbed.
  155. Arguments:
  156. IN pVtbl - Pointer to an interface vtable to file under
  157. IN pfnNew - Pointer to the new (stub) function
  158. IN bThrowExceptionIfNull - Flag that specifies whether it should be
  159. possible to not find the original function in our function
  160. map
  161. Return Value:
  162. Returns the original function pointer
  163. History:
  164. 11/01/1999 markder Created
  165. --*/
  166. PVOID
  167. LookupOriginalCOMFunction(
  168. IN PVOID pVtbl,
  169. IN PVOID pfnNew,
  170. IN BOOL bThrowExceptionIfNull
  171. )
  172. {
  173. PSHIM_IFACE_FN_MAP pMap = g_pIFaceFnMaps;
  174. PVOID pReturn = NULL;
  175. DPF("ShimLib", eDbgLevelSpew, "[LookupOriginalCOMFunction] pVtbl: 0x%p pfnNew: 0x%p ",
  176. pVtbl,
  177. pfnNew);
  178. // Scan the linked list for a match and return if found.
  179. while (pMap)
  180. {
  181. if (pMap->pVtbl == pVtbl && pMap->pfnNew == pfnNew)
  182. {
  183. pReturn = pMap->pfnOld;
  184. break;
  185. }
  186. pMap = (PSHIM_IFACE_FN_MAP) pMap->pNext;
  187. }
  188. DPF("ShimLib", eDbgLevelSpew, " --> Returned: 0x%p\n", pReturn);
  189. if (!pReturn && bThrowExceptionIfNull)
  190. {
  191. // If we have hit this point, there is something seriously wrong.
  192. // Either there is a bug in the AddRef/Release stubs or the app
  193. // obtained an interface pointer in some way that we don't catch.
  194. DPF("ShimLib", eDbgLevelError,"ERROR: Shim COM APIHooking mechanism failed.\n");
  195. APPBreakPoint();
  196. }
  197. return pReturn;
  198. }
  199. /*++
  200. Function Description:
  201. Stores the original function pointer in the function map and overwrites it in
  202. the vtable with the new one.
  203. Arguments:
  204. IN pVtbl - Pointer to an interface vtable to file under
  205. IN dwVtblIndex - The index of the target function within the vtable.
  206. IN pfnNew - Pointer to the new (stub) function
  207. Return Value:
  208. None
  209. History:
  210. 11/01/1999 markder Created
  211. --*/
  212. VOID
  213. PatchFunction(
  214. IN PVOID* pVtbl,
  215. IN DWORD dwVtblIndex,
  216. IN PVOID pfnNew
  217. )
  218. {
  219. DWORD dwOldProtect = 0;
  220. DWORD dwOldProtect2 = 0;
  221. DPF("ShimLib", eDbgLevelSpew, "[PatchFunction] pVtbl: 0x%p, dwVtblIndex: %d, pfnOld: 0x%p, pfnNew: 0x%p\n",
  222. pVtbl,
  223. dwVtblIndex,
  224. pVtbl[dwVtblIndex],
  225. pfnNew);
  226. // if not patched yet
  227. if (!LookupOriginalCOMFunction( pVtbl, pfnNew, FALSE))
  228. {
  229. AddIFaceFnMap( pVtbl, pfnNew, pVtbl[dwVtblIndex]);
  230. // Make the code page writable and overwrite function pointers in vtable
  231. if (VirtualProtect(pVtbl + dwVtblIndex,
  232. sizeof(DWORD),
  233. PAGE_READWRITE,
  234. &dwOldProtect))
  235. {
  236. pVtbl[dwVtblIndex] = pfnNew;
  237. // Return the code page to its original state
  238. VirtualProtect(pVtbl + dwVtblIndex,
  239. sizeof(DWORD),
  240. dwOldProtect,
  241. &dwOldProtect2);
  242. }
  243. }
  244. }
  245. /*++
  246. Function Description:
  247. This stub exists to keep track of an interface's reference count changes.
  248. Note that the bAddRefTrip flag is cleared, which allows
  249. APIHook_QueryInterface to determine whether an AddRef was performed inside
  250. the original QueryInterface function call.
  251. Arguments:
  252. IN pThis - The object's 'this' pointer
  253. Return Value:
  254. Return value is obtained from original function
  255. History:
  256. 11/01/1999 markder Created
  257. --*/
  258. ULONG
  259. APIHook_AddRef(
  260. IN PVOID pThis
  261. )
  262. {
  263. PSHIM_HOOKED_OBJECT pHookedOb = g_pObjectCache;
  264. _pfn_AddRef pfnOld;
  265. ULONG ulReturn;
  266. pfnOld = (_pfn_AddRef) LookupOriginalCOMFunction( *((PVOID*)(pThis)),
  267. APIHook_AddRef,
  268. TRUE);
  269. ulReturn = (*pfnOld)(pThis);
  270. while (pHookedOb)
  271. {
  272. if (pHookedOb->pThis == pThis)
  273. {
  274. pHookedOb->dwRef++;
  275. pHookedOb->bAddRefTrip = FALSE;
  276. DPF("ShimLib", eDbgLevelSpew, "[AddRef] pThis: 0x%p dwRef: %d ulReturn: %d\n",
  277. pThis,
  278. pHookedOb->dwRef,
  279. ulReturn);
  280. break;
  281. }
  282. pHookedOb = (PSHIM_HOOKED_OBJECT) pHookedOb->pNext;
  283. }
  284. return ulReturn;
  285. }
  286. /*++
  287. Function Description:
  288. This stub exists to keep track of an interface's reference count changes.
  289. Arguments:
  290. IN pThis - The object's 'this' pointer
  291. Return Value:
  292. Return value is obtained from original function
  293. History:
  294. 11/01/1999 markder Created
  295. --*/
  296. ULONG
  297. APIHook_Release(
  298. IN PVOID pThis
  299. )
  300. {
  301. PSHIM_HOOKED_OBJECT *ppHookedOb = &g_pObjectCache;
  302. PSHIM_HOOKED_OBJECT pTemp;
  303. _pfn_Release pfnOld;
  304. ULONG ulReturn;
  305. pfnOld = (_pfn_Release) LookupOriginalCOMFunction(*((PVOID*)(pThis)),
  306. APIHook_Release,
  307. TRUE);
  308. ulReturn = (*pfnOld)( pThis );
  309. while ((*ppHookedOb))
  310. {
  311. if ((*ppHookedOb)->pThis == pThis)
  312. {
  313. (*ppHookedOb)->dwRef--;
  314. DPF("ShimLib", eDbgLevelSpew, "[Release] pThis: 0x%p dwRef: %d ulReturn: %d %s\n",
  315. pThis,
  316. (*ppHookedOb)->dwRef,
  317. ulReturn,
  318. ((*ppHookedOb)->dwRef?"":" --> Deleted"));
  319. if (!((*ppHookedOb)->dwRef))
  320. {
  321. pTemp = (*ppHookedOb);
  322. *ppHookedOb = (PSHIM_HOOKED_OBJECT) (*ppHookedOb)->pNext;
  323. ShimFree(pTemp);
  324. }
  325. break;
  326. }
  327. ppHookedOb = (PSHIM_HOOKED_OBJECT*) &((*ppHookedOb)->pNext);
  328. }
  329. return ulReturn;
  330. }
  331. /*++
  332. Function Description:
  333. This stub catches the application attempting to obtain a new interface
  334. pointer to the same object. The function searches the object cache
  335. to obtain a CLSID for the object and, if found, APIHooks all required
  336. functions in the new vtable (via the HookObject call).
  337. Arguments:
  338. IN pThis - The object's 'this' pointer
  339. IN iid - Reference to the identifier of the requested interface
  340. IN ppvObject - Address of output variable that receives the interface
  341. pointer requested in riid.
  342. Return Value:
  343. Return value is obtained from original function
  344. History:
  345. 11/01/1999 markder Created
  346. --*/
  347. HRESULT
  348. APIHook_QueryInterface(
  349. PVOID pThis,
  350. REFIID iid,
  351. PVOID* ppvObject
  352. )
  353. {
  354. HRESULT hrReturn = E_FAIL;
  355. _pfn_QueryInterface pfnOld = NULL;
  356. PSHIM_HOOKED_OBJECT pOb = g_pObjectCache;
  357. pfnOld = (_pfn_QueryInterface) LookupOriginalCOMFunction(
  358. *((PVOID*)pThis),
  359. APIHook_QueryInterface,
  360. TRUE);
  361. while (pOb)
  362. {
  363. if (pOb->pThis == pThis)
  364. {
  365. pOb->bAddRefTrip = TRUE;
  366. break;
  367. }
  368. pOb = (PSHIM_HOOKED_OBJECT) pOb->pNext;
  369. }
  370. if (S_OK == (hrReturn = (*pfnOld) (pThis, iid, ppvObject)))
  371. {
  372. if (pOb)
  373. {
  374. if (pOb->pThis == *((PVOID*)ppvObject))
  375. {
  376. // Same object. Detect whether QueryInterface used IUnknown::AddRef
  377. // or an internal function.
  378. DPF("ShimLib", eDbgLevelSpew,"[HookObject] Existing object%s. pThis: 0x%p\n",
  379. (pOb->bAddRefTrip?" (AddRef'd) ":""),
  380. pOb->pThis);
  381. if (pOb->bAddRefTrip)
  382. {
  383. (pOb->dwRef)++; // AddRef the object
  384. pOb->bAddRefTrip = FALSE;
  385. }
  386. // We are assured that the CLSID for the object will be the same.
  387. HookObject(pOb->pCLSID, iid, ppvObject, pOb, pOb->bClassFactory);
  388. }
  389. else
  390. {
  391. HookObject(pOb->pCLSID, iid, ppvObject, NULL, pOb->bClassFactory);
  392. }
  393. }
  394. }
  395. return hrReturn;
  396. }
  397. /*++
  398. Function Description:
  399. This stub catches the most interesting part of the object creation process:
  400. The actual call to IClassFactory::CreateInstance. Since no CLSID is passed
  401. in to this function, the stub must decide whether to APIHook the object by
  402. looking up the instance of the class factory in the object cache. IF IT
  403. EXISTS IN THE CACHE, that indicates that it creates an object that we wish
  404. to APIHook.
  405. Arguments:
  406. IN pThis - The object's 'this' pointer
  407. IN pUnkOuter - Pointer to whether object is or isn't part of an aggregate
  408. IN riid - Reference to the identifier of the interface
  409. OUT ppvObject - Address of output variable that receives the interface
  410. pointer requested in riid
  411. Return Value:
  412. Return value is obtained from original function
  413. History:
  414. 11/01/1999 markder Created
  415. --*/
  416. HRESULT
  417. APIHook_IClassFactory_CreateInstance(
  418. PVOID pThis,
  419. IUnknown *pUnkOuter,
  420. REFIID riid,
  421. VOID **ppvObject
  422. )
  423. {
  424. HRESULT hrReturn = E_FAIL;
  425. _pfn_CreateInstance pfnOldCreateInst = NULL;
  426. PSHIM_HOOKED_OBJECT pOb = g_pObjectCache;
  427. pfnOldCreateInst = (_pfn_CreateInstance) LookupOriginalCOMFunction(
  428. *((PVOID*)pThis),
  429. APIHook_IClassFactory_CreateInstance,
  430. FALSE);
  431. if (pfnOldCreateInst == NULL) {
  432. DPF("ShimLib", eDbgLevelError, "[CreateInstance] Cannot find CreateInstance\n", pThis);
  433. return E_FAIL;
  434. }
  435. if (S_OK == (hrReturn = (*pfnOldCreateInst)(pThis, pUnkOuter, riid, ppvObject)))
  436. {
  437. while (pOb)
  438. {
  439. if (pOb->pThis == pThis)
  440. {
  441. // This class factory instance creates an object that we APIHook.
  442. DPF("ShimLib", eDbgLevelSpew, "[CreateInstance] Hooking object! pThis: 0x%p\n", pThis);
  443. HookObject(pOb->pCLSID, riid, ppvObject, NULL, FALSE);
  444. break;
  445. }
  446. pOb = (PSHIM_HOOKED_OBJECT) pOb->pNext;
  447. }
  448. }
  449. return hrReturn;
  450. }
  451. VOID
  452. HookCOMInterface(
  453. REFCLSID rclsid,
  454. REFIID riid,
  455. LPVOID * ppv,
  456. BOOL bClassFactory
  457. )
  458. {
  459. DWORD i = 0;
  460. // Determine if we need to hook this object
  461. for (i = 0; i < g_dwCOMHookCount; i++)
  462. {
  463. if (g_pCOMHooks[i].pCLSID &&
  464. IsEqualGUID( (REFCLSID) *(g_pCOMHooks[i].pCLSID), rclsid))
  465. {
  466. // Yes, we are hooking an interface on this object.
  467. HookObject((CLSID*) &rclsid, riid, ppv, NULL, bClassFactory);
  468. break;
  469. }
  470. }
  471. }
  472. /*++
  473. Function Description:
  474. Free memory associated with Hooks and dump info
  475. Arguments:
  476. None
  477. Return Value:
  478. None
  479. History:
  480. 11/01/1999 markder Created
  481. --*/
  482. VOID
  483. DumpCOMHooks()
  484. {
  485. PSHIM_IFACE_FN_MAP pMap = g_pIFaceFnMaps;
  486. PSHIM_HOOKED_OBJECT pHookedOb = g_pObjectCache;
  487. // Dump function map
  488. DPF("ShimLib", eDbgLevelSpew, "\n--- Shim COM Hook Function Map ---\n\n");
  489. while (pMap)
  490. {
  491. DPF("ShimLib", eDbgLevelSpew, "pVtbl: 0x%p pfnNew: 0x%p pfnOld: 0x%p\n",
  492. pMap->pVtbl,
  493. pMap->pfnNew,
  494. pMap->pfnOld);
  495. pMap = (PSHIM_IFACE_FN_MAP) pMap->pNext;
  496. }
  497. // Dump class factory cache
  498. DPF("ShimLib", eDbgLevelSpew, "\n--- Shim Object Cache (SHOULD BE EMPTY!!) ---\n\n");
  499. while (pHookedOb)
  500. {
  501. DPF("ShimLib", eDbgLevelSpew, "pThis: 0x%p dwRef: %d\n",
  502. pHookedOb->pThis,
  503. pHookedOb->dwRef);
  504. pHookedOb = (PSHIM_HOOKED_OBJECT) pHookedOb->pNext;
  505. }
  506. }
  507. /*++
  508. Function Description:
  509. This function adds the object's important info to the object cache and then
  510. patches all required functions. IUnknown is hooked for all objects
  511. regardless.
  512. Arguments:
  513. IN rclsid - CLSID for the class object
  514. IN riid - Reference to the identifier of the interface that communicates
  515. with the class object
  516. OUT ppv - Address of the pThis pointer that uniquely identifies an
  517. instance of the COM interface
  518. OUT pOb - New obj pointer
  519. IN bClassFactory - Is this a class factory call
  520. Return Value:
  521. None
  522. History:
  523. 11/01/1999 markder Created
  524. --*/
  525. VOID
  526. HookObject(
  527. IN CLSID *pCLSID,
  528. IN REFIID riid,
  529. OUT LPVOID *ppv,
  530. OUT PSHIM_HOOKED_OBJECT pOb,
  531. IN BOOL bClassFactory
  532. )
  533. {
  534. // Here's how a COM object looks in memory:
  535. //
  536. // pv - The pointer to the object's interface. In C++ terms, it
  537. // | is sort of like the "this" pointer but objects
  538. // | will hand back different pointers for different interfaces.
  539. // |
  540. // `-> pVtbl - The COM virtual function table pointer. This is the
  541. // | first 32-bit member of the interface structure.
  542. // |
  543. // |-> QueryInterface - First function in the root interface, IUnknown. This
  544. // | function allows calling members to request a different
  545. // | interface that may be implemented by the object.
  546. // |
  547. // |-> AddRef - Increments the reference count for this interface.
  548. // |
  549. // |-> Release - Decrements the reference count for this interface.
  550. // |
  551. // |-> InterfaceFn1 - Beginning of the interface-specific functions.
  552. // |-> InterfaceFn2
  553. // |-> InterfaceFn3
  554. // | .
  555. // | .
  556. // | .
  557. //
  558. // The COM hooking mechanism is interested in the virtual function table pointer, and to get
  559. // it we must dereference the ppv pointer twice.
  560. PVOID *pVtbl = ((PVOID*)(*((PVOID*)(*ppv))));
  561. DWORD i = 0;
  562. if (!pOb)
  563. {
  564. // If pOb is NULL, then the object does not exist in the cache yet.
  565. // Make a new entry for the object.
  566. DPF("ShimLib", eDbgLevelSpew, "[HookObject] New %s! pThis: 0x%p\n",
  567. (bClassFactory?"class factory":"object"),
  568. *ppv);
  569. pOb = (PSHIM_HOOKED_OBJECT) ShimMalloc(sizeof(SHIM_HOOKED_OBJECT));
  570. if( pOb == NULL )
  571. {
  572. DPF("ShimLib", eDbgLevelError, "[HookObject] Could not allocate memory for SHIM_HOOKED_OBJECT.\n");
  573. return;
  574. }
  575. pOb->pCLSID = pCLSID;
  576. pOb->pThis = *ppv;
  577. pOb->dwRef = 1;
  578. pOb->bAddRefTrip = FALSE;
  579. pOb->pNext = g_pObjectCache;
  580. pOb->bClassFactory = bClassFactory;
  581. g_pObjectCache = pOb;
  582. }
  583. // IUnknown must always be hooked since it is possible to get
  584. // a new interface pointer using it, and we need to process each interface
  585. // handed out. We must also keep track of the reference count so that
  586. // we can clean up our interface function map.
  587. PatchFunction(pVtbl, 0, APIHook_QueryInterface);
  588. PatchFunction(pVtbl, 1, APIHook_AddRef);
  589. PatchFunction(pVtbl, 2, APIHook_Release);
  590. if (bClassFactory && IsEqualGUID(IID_IClassFactory, riid))
  591. {
  592. // If we are processing a class factory, all we care about
  593. // hooking is CreateInstance, since it is an API that produces
  594. // the actual object we are interested in.
  595. PatchFunction(pVtbl, 3, APIHook_IClassFactory_CreateInstance);
  596. }
  597. else
  598. {
  599. for (i = 0; i < g_dwCOMHookCount; i++)
  600. {
  601. if (!(g_pCOMHooks[i].pCLSID) || !pCLSID)
  602. {
  603. // A CLSID was not specified -- hook any object that exposes
  604. // the specified interface.
  605. if (IsEqualGUID( (REFIID) *(g_pCOMHooks[i].pIID), riid))
  606. {
  607. PatchFunction(
  608. pVtbl,
  609. g_pCOMHooks[i].dwVtblIndex,
  610. g_pCOMHooks[i].pfnNew);
  611. }
  612. }
  613. else
  614. {
  615. // A CLSID was specified -- hook only interfaces on the
  616. // specified object.
  617. if (IsEqualGUID((REFCLSID) *(g_pCOMHooks[i].pCLSID), *pCLSID) &&
  618. IsEqualGUID((REFIID) *(g_pCOMHooks[i].pIID), riid))
  619. {
  620. PatchFunction(
  621. pVtbl,
  622. g_pCOMHooks[i].dwVtblIndex,
  623. g_pCOMHooks[i].pfnNew);
  624. }
  625. }
  626. }
  627. }
  628. }
  629. BOOL InitHooks(DWORD dwCount)
  630. {
  631. g_dwAPIHookCount = dwCount;
  632. g_pAPIHooks = (PHOOKAPI) ShimMalloc( g_dwAPIHookCount * sizeof(HOOKAPI) );
  633. if (g_pAPIHooks)
  634. {
  635. ZeroMemory(g_pAPIHooks, g_dwAPIHookCount * sizeof(HOOKAPI) );
  636. }
  637. return g_pAPIHooks != NULL;
  638. }
  639. BOOL InitComHooks(DWORD dwCount)
  640. {
  641. //DECLARE_APIHOOK(DDraw.dll, DirectDrawCreate);
  642. //DECLARE_APIHOOK(DDraw.dll, DirectDrawCreateEx);
  643. g_dwCOMHookCount = dwCount;
  644. g_pCOMHooks = (PSHIM_COM_HOOK) ShimMalloc( g_dwCOMHookCount * sizeof(SHIM_COM_HOOK) );
  645. if (g_pCOMHooks)
  646. {
  647. ZeroMemory(g_pCOMHooks, g_dwCOMHookCount * sizeof(SHIM_COM_HOOK) );
  648. }
  649. return g_pCOMHooks != NULL;
  650. }
  651. VOID AddComHook(REFCLSID clsid, REFIID iid, PVOID hook, DWORD vtblndx)
  652. {
  653. if (g_dwCOMHookBuffer <= g_dwCOMHookCount) {
  654. // Buffer is too small, must resize.
  655. DWORD dwNewBuffer = g_dwCOMHookBuffer * 2;
  656. PSHIM_COM_HOOK pNewBuffer = NULL;
  657. if (dwNewBuffer == 0) {
  658. // 50 is the initial allocation, but it should be at least g_dwCOMHookCount
  659. dwNewBuffer = max(50, g_dwCOMHookCount);
  660. }
  661. pNewBuffer = (PSHIM_COM_HOOK) ShimMalloc( sizeof(SHIM_COM_HOOK) * dwNewBuffer );
  662. if (pNewBuffer == NULL) {
  663. DPF("ShimLib", eDbgLevelError,
  664. "[AddComHook] Could not allocate SHIM_COM_HOOK array.");
  665. return;
  666. }
  667. // Copy over original array, then free the old one.
  668. if (g_pCOMHooks != NULL) {
  669. memcpy(pNewBuffer, g_pCOMHooks, sizeof(SHIM_COM_HOOK) * g_dwCOMHookBuffer);
  670. ShimFree(g_pCOMHooks);
  671. }
  672. g_pCOMHooks = pNewBuffer;
  673. g_dwCOMHookBuffer = dwNewBuffer;
  674. }
  675. g_pCOMHooks[g_dwCOMHookCount].pCLSID = (CLSID*) &clsid;
  676. g_pCOMHooks[g_dwCOMHookCount].pIID = (IID*) &iid;
  677. g_pCOMHooks[g_dwCOMHookCount].dwVtblIndex = vtblndx;
  678. g_pCOMHooks[g_dwCOMHookCount].pfnNew = hook;
  679. g_dwCOMHookCount++;
  680. return;
  681. }
  682. }; // end of namespace ShimLib
  683. /*++
  684. Function Description:
  685. Called on process detach with old shim mechanism.
  686. Arguments:
  687. See MSDN
  688. Return Value:
  689. See MSDN
  690. History:
  691. 11/01/1999 markder Created
  692. --*/
  693. BOOL
  694. DllMain(
  695. HINSTANCE hinstDLL,
  696. DWORD fdwReason,
  697. LPVOID /*lpvReserved*/
  698. )
  699. {
  700. using namespace ShimLib;
  701. switch (fdwReason) {
  702. case DLL_PROCESS_ATTACH:
  703. g_hinstDll = hinstDLL;
  704. g_pAPIHooks = NULL;
  705. g_dwAPIHookCount = 0;
  706. g_dwCOMHookCount = 0;
  707. g_dwCOMHookBuffer = 0;
  708. g_pCOMHooks = NULL;
  709. g_pIFaceFnMaps = NULL;
  710. g_pObjectCache = NULL;
  711. g_szCommandLine[0] = '\0';
  712. g_bMultiShim = FALSE;
  713. g_dwShimVersion = 1;
  714. break;
  715. case DLL_PROCESS_DETACH:
  716. if (g_dwCOMHookCount > 0) {
  717. DumpCOMHooks();
  718. }
  719. InitializeHooks(DLL_PROCESS_DETACH);
  720. InitializeHooksEx(DLL_PROCESS_DETACH, NULL, NULL, NULL);
  721. break;
  722. }
  723. return TRUE;
  724. }