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.

846 lines
22 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. StackSwap.cpp
  5. Abstract:
  6. Some applications make the assumption that Win32 APIs don't use any stack
  7. space. This stems from the architecture of win9x - whereby many APIs
  8. thunked and therefore had there own stack.
  9. Of course on NT this isn't the case and many APIs are normal user mode
  10. functions that don't even call down to kernel. To make matters worse, some
  11. applications depend on *no* stack usage in a number of other ways, for
  12. example:
  13. 1. Sierra Cart racing keeps a pointer in old stack
  14. 2. Baldur's Gate *double* dereferences pointers in old stack
  15. 3. NFL Blitz keeps it's linked lists on the stack and so simply calling
  16. an API causes corruption
  17. 4. NFL Blitz 2000 runs out of stack space calling CreateFile
  18. 5. Interplay EReg has uninitialized variables on the stack which are
  19. normally zeroed on win9x
  20. This shim is parameterized and takes a list of APIs and the behavior of
  21. each. Behavior is defined as the following:
  22. - No stack is used by this API
  23. 0 - After API is called, old stack will be filled with zeroes
  24. 1 - After API is called, old stack will be filled with valid pointers
  25. 2 - After API is called, old stack will be filled with valid pointers
  26. to pointers
  27. The default is that no stack space is used by the API.
  28. Notes:
  29. This is a general purpose shim.
  30. History:
  31. 05/03/2000 linstev Created
  32. 03/12/2001 robkenny Blessed for DBCS
  33. --*/
  34. #include "precomp.h"
  35. // This module has been given an official blessing to use the str routines.
  36. #include "LegalStr.h"
  37. IMPLEMENT_SHIM_BEGIN(StackSwap)
  38. #include "ShimHookMacro.h"
  39. #include "ShimStack.h"
  40. #include "StackSwap_Exports.h"
  41. #include "StackSwap_Excludes.h"
  42. APIHOOK_ENUM_BEGIN
  43. APIHOOK_ENUM_ENTRY(CreateThread)
  44. APIHOOK_ENUM_ENTRY(TerminateThread)
  45. APIHOOK_ENUM_ENTRY(ExitThread)
  46. APIHOOK_ENUM_END
  47. #define THREAD_VAR Vdm // The TEB variable to overwrite
  48. #define STUB_SIZE 64 // size of stub code in bytes
  49. #define STACK_SIZE 65536 // size of temporary stack
  50. #define STACK_COPY_SIZE 16 // number of dwords to copy from old stack
  51. #define STACK_FILL_SIZE 256 // default number of dwords to fill
  52. #define STACK_GUARD_SIZE 4096 // gaurd page at the top of the stack - must be a multiple of 4096
  53. #define STACK_FILL_NONE -1 // no old stack filling
  54. #define STACK_FILL_ZERO 0 // fill old stack with zeroes
  55. #define STACK_FILL_PTR1 1 // fill old stack with pointers
  56. #define STACK_FILL_PTR2 2 // fill old stack with pointers to pointers
  57. PVOID g_dwZero = 0; // used for pointer to zero
  58. PVOID g_dwPtr = &g_dwPtr; // used for pointer to pointer
  59. PVOID g_arrFill[] =
  60. {
  61. 0,
  62. &g_dwZero,
  63. &g_dwPtr
  64. };
  65. // Store for each hook returned by the parser
  66. struct HOOK
  67. {
  68. char szModule[MAX_PATH]; // Module name
  69. char szFnName[MAX_PATH]; // Function name
  70. PVOID pfnNew; // Pointer to stub
  71. DWORD dwFill; // Stack fill type
  72. DWORD dwFillSize; // Number of dwords to fill
  73. struct HOOK *next;
  74. };
  75. HOOK *g_pHooks = NULL;
  76. HOOK g_AllHooks[] =
  77. {
  78. {"KERNEL32.DLL", "*", NULL, STACK_FILL_NONE},
  79. {"GDI32.DLL", "*", NULL, STACK_FILL_NONE},
  80. {"USER32.DLL", "*", NULL, STACK_FILL_NONE},
  81. {"WINMM.DLL", "*", NULL, STACK_FILL_NONE}
  82. };
  83. DWORD dwStubCount = 0;
  84. // Thread local data
  85. typedef struct _THREAD_DATA
  86. {
  87. PVOID pfnHook; // Address of actual call
  88. PVOID pNewStack; // The new stack
  89. PVOID pOldStack; // The old stack
  90. DWORD dwFill; // Fill method
  91. DWORD dwFillSize; // Number of dwords to fill
  92. ULONG ulCount; // Number of times we've entered
  93. DWORD dwRet; // Return value
  94. DWORD dwEcx, dwEsi, dwEdi; // Tempory storage, since we don't have a stack
  95. } THREAD_DATA;
  96. /*++
  97. This function is called from the stubs. It's purpose is to give the API a new
  98. stack to use. It does this by doing the following:
  99. 1. Copy the original stack to the new stack
  100. 2. Call the original hook
  101. 3. Copy the changed stack back to the original stack
  102. 4. Return control to the original caller
  103. The only tricky things about this routine are that we don't want to use any
  104. stack at all (no push/pop) and we need to calculate how much stack was used
  105. for the parameters - something we don't know because we don't have the proto-
  106. type.
  107. If we really wanted to use push and pop, we could have set up a temporary
  108. stack, but since we only need ecx, esi and edi, there didn't seem to be any
  109. point.
  110. --*/
  111. __declspec(naked)
  112. void
  113. SwapStack()
  114. {
  115. __asm {
  116. inc [eax + THREAD_DATA.ulCount] // increment counter
  117. mov [eax + THREAD_DATA.dwEcx], ecx // backup ecx
  118. mov [eax + THREAD_DATA.dwEsi], esi // backup esi
  119. mov [eax + THREAD_DATA.dwEdi], edi // backup edi
  120. mov ecx, [esp] // retrieve 'Hook' from Stub()
  121. mov [eax + THREAD_DATA.pfnHook], ecx // which we got from the call
  122. add esp, 4 // move the stack up to the return address
  123. mov dword ptr [esp], offset SwapBack // fill in our new return address
  124. lea edi, [esp + 4] // dst = new stack
  125. mov esi, [eax + THREAD_DATA.pOldStack] // src = old stack
  126. add esi, 4 // note the +4s since the first dword is the return address
  127. cld // clear direction flag
  128. mov ecx, STACK_COPY_SIZE - 1 // copy off STACK_COPY_SIZE - 1 bytes
  129. rep movsd // do the copy
  130. mov ecx, [eax + THREAD_DATA.dwEcx] // restore ecx
  131. mov esi, [eax + THREAD_DATA.dwEsi] // restore esi
  132. mov edi, [eax + THREAD_DATA.dwEdi] // restore edi
  133. jmp [eax + THREAD_DATA.pfnHook] // jump back into the stub to do the actual
  134. SwapBack:
  135. mov [esp - 4], eax // unfortunately this is the only way to store the return
  136. mov eax, fs:[0x18] // get the TEB
  137. mov eax, [eax + TEB.THREAD_VAR] // get our thread local pointer
  138. mov [eax + THREAD_DATA.dwEcx], ecx // backup ecx
  139. mov [eax + THREAD_DATA.dwEsi], esi // backup esi
  140. mov [eax + THREAD_DATA.dwEdi], edi // backup edi
  141. mov ecx, [esp - 4] // get return value
  142. mov [eax + THREAD_DATA.dwRet], ecx // store return value for later
  143. mov ecx, esp // this is where we find out how many parameters were passed
  144. sub ecx, [eax + THREAD_DATA.pNewStack] // on the stack - so we get the difference in ecx
  145. mov edi, [eax + THREAD_DATA.pOldStack] // original stack
  146. mov esi, [edi] // read the real return address
  147. add edi, ecx // move the stack up, so we don't copy unnecessay stack
  148. mov [edi - 4], esi // put the return address into edi-4: this is the only time we
  149. // use app stack space at all
  150. mov esp, edi
  151. mov ecx, [eax + THREAD_DATA.dwFill] // test how we're going to fill
  152. cmp ecx, STACK_FILL_NONE
  153. jz FillDone
  154. mov esi, [ecx*4 + g_arrFill] // value to fill with
  155. lea edi, [esp - 8] // we're going to fill backwards, so esp-8 will skip the return address
  156. mov ecx, [eax + THREAD_DATA.dwFillSize] // number of dwords to fill with
  157. FillStack:
  158. mov [edi], esi // store the value
  159. sub edi, 4
  160. dec ecx
  161. jnz FillStack
  162. FillDone:
  163. mov ecx, [eax + THREAD_DATA.dwEcx] // restore ecx
  164. mov esi, [eax + THREAD_DATA.dwEsi] // restore esi
  165. mov edi, [eax + THREAD_DATA.dwEdi] // restore edi
  166. dec [eax + THREAD_DATA.ulCount] // decrement counter
  167. mov eax, [eax + THREAD_DATA.dwRet] // get the return value
  168. jmp dword ptr [esp - 4] // return to original caller
  169. }
  170. }
  171. //
  172. // We need the stub to do a far call to SwapStack since the stub will move, but
  173. // I can't seem to force the far call without this method
  174. //
  175. DWORD_PTR g_pfnStackSwap = (DWORD_PTR)SwapStack;
  176. /*++
  177. This is the stub function that is called by every API. It is copied from here
  178. to blocks of executable memory and the calls and fill types are written to
  179. hard coded addresses within it.
  180. The instuctions:
  181. mov [eax + THREAD_DATA.dwFill], 0xFFFFFFFF is replaced by
  182. mov [eax + THREAD_DATA.dwFill], FILL_TYPE
  183. mov [eax + THREAD_DATA.dwFillSize], 0xFFFFFFFF is replaced by
  184. mov [eax + THREAD_DATA.dwFill], FILL_SIZE
  185. and
  186. call g_pfnStackSwap is replaced by
  187. call g_pAPIHooks[api].pfnOld
  188. --*/
  189. __declspec(naked)
  190. void
  191. Stub()
  192. {
  193. __asm {
  194. mov eax, fs:[0x18] // get the TEB
  195. mov eax, [eax + TEB.THREAD_VAR] // get our thread local pointer
  196. or eax, eax // our pointer is gone
  197. jz Hook // exit gracefully
  198. cmp [eax + THREAD_DATA.ulCount], 0 // have we already swapped the stack
  199. jnz Hook
  200. mov [eax + THREAD_DATA.dwFill], 0xFFFFFFFF // the 0xFFFFFFFF will be replaced by the fill type
  201. mov [eax + THREAD_DATA.dwFillSize], 0xFFFFFFFF // the 0xFFFFFFFF will be replaced by the fill size
  202. mov [eax + THREAD_DATA.pOldStack], esp // backup the old stack
  203. mov esp, [eax + THREAD_DATA.pNewStack] // swap the stack
  204. call g_pfnStackSwap // call into the stack swapping code
  205. Hook:
  206. jmp [g_pHooks] // jump to the hook
  207. }
  208. }
  209. /*++
  210. Create a new stack
  211. --*/
  212. THREAD_DATA *
  213. AllocStack()
  214. {
  215. LPVOID p = VirtualAlloc(
  216. 0,
  217. sizeof(THREAD_DATA) + STACK_SIZE + STACK_GUARD_SIZE,
  218. MEM_COMMIT,
  219. PAGE_READWRITE);
  220. if (p)
  221. {
  222. DWORD dwOld;
  223. if (!VirtualProtect(p, STACK_GUARD_SIZE, PAGE_READONLY | PAGE_GUARD, &dwOld))
  224. {
  225. DPFN( eDbgLevelError, "Failed to place Gaurd page at the top of the stack");
  226. }
  227. THREAD_DATA *pTD = (THREAD_DATA *)((DWORD_PTR)p + STACK_SIZE + STACK_GUARD_SIZE);
  228. pTD->pNewStack = (LPVOID)((DWORD_PTR)pTD - STACK_COPY_SIZE * 4);
  229. return pTD;
  230. }
  231. else
  232. {
  233. DPFN( eDbgLevelError, "Failed to allocate new stack");
  234. return NULL;
  235. }
  236. }
  237. /*++
  238. Free the stack
  239. --*/
  240. BOOL
  241. FreeStack(THREAD_DATA *pTD)
  242. {
  243. BOOL bRet = FALSE;
  244. if (pTD)
  245. {
  246. LPVOID p = (LPVOID)((DWORD_PTR)pTD - STACK_SIZE - STACK_GUARD_SIZE);
  247. bRet = VirtualFree(p, 0, MEM_RELEASE);
  248. }
  249. if (!bRet)
  250. {
  251. DPFN( eDbgLevelError, "Failed to free a stack");
  252. }
  253. return bRet;
  254. }
  255. /*++
  256. Hook CreateThread so we can add our stuff to the TEB.
  257. --*/
  258. HANDLE
  259. APIHOOK(CreateThread)(
  260. LPSECURITY_ATTRIBUTES lpThreadAttributes,
  261. DWORD dwStackSize,
  262. LPTHREAD_START_ROUTINE lpStartAddress,
  263. LPVOID lpParameter,
  264. DWORD dwCreationFlags,
  265. LPDWORD lpThreadId
  266. )
  267. {
  268. HANDLE hRet;
  269. DWORD dwFlags = dwCreationFlags;
  270. NEW_STACK();
  271. hRet = ORIGINAL_API(CreateThread)(
  272. lpThreadAttributes,
  273. dwStackSize,
  274. lpStartAddress,
  275. lpParameter,
  276. dwCreationFlags | CREATE_SUSPENDED,
  277. lpThreadId);
  278. if (hRet)
  279. {
  280. THREAD_BASIC_INFORMATION tbi;
  281. NTSTATUS Status;
  282. Status = NtQueryInformationThread(
  283. hRet,
  284. ThreadBasicInformation,
  285. &tbi,
  286. sizeof(tbi),
  287. NULL);
  288. if ((NT_SUCCESS(Status)) && (tbi.TebBaseAddress))
  289. {
  290. tbi.TebBaseAddress->THREAD_VAR = AllocStack();
  291. }
  292. if (!(dwFlags & CREATE_SUSPENDED))
  293. {
  294. ResumeThread(hRet);
  295. }
  296. }
  297. OLD_STACK();
  298. return hRet;
  299. }
  300. /*++
  301. Hook TerminateThread so we can clean up the thread local data.
  302. --*/
  303. BOOL
  304. APIHOOK(TerminateThread)(
  305. HANDLE hThread,
  306. DWORD dwExitCode
  307. )
  308. {
  309. THREAD_BASIC_INFORMATION tbi;
  310. NTSTATUS Status;
  311. BOOL bRet;
  312. THREAD_DATA *pTD = NULL;
  313. Status = NtQueryInformationThread(
  314. hThread,
  315. ThreadBasicInformation,
  316. &tbi,
  317. sizeof(tbi),
  318. NULL);
  319. if ((NT_SUCCESS(Status)) && (tbi.TebBaseAddress))
  320. {
  321. pTD = (THREAD_DATA *)(tbi.TebBaseAddress->THREAD_VAR);
  322. }
  323. bRet = ORIGINAL_API(TerminateThread)(hThread, dwExitCode);
  324. FreeStack(pTD);
  325. return bRet;
  326. }
  327. /*++
  328. Hook ExitThread so we can clean up the thread local data.
  329. --*/
  330. VOID
  331. APIHOOK(ExitThread)(
  332. DWORD dwExitCode
  333. )
  334. {
  335. NTSTATUS Status;
  336. THREAD_BASIC_INFORMATION tbi;
  337. HANDLE hThread = GetCurrentThread();
  338. Status = NtQueryInformationThread(
  339. hThread,
  340. ThreadBasicInformation,
  341. &tbi,
  342. sizeof(tbi),
  343. NULL);
  344. if ((NT_SUCCESS(Status)) && (tbi.TebBaseAddress))
  345. {
  346. THREAD_DATA *pTD = (THREAD_DATA *)tbi.TebBaseAddress->THREAD_VAR;
  347. // Make sure we don't free it if we're using it
  348. if (pTD && (pTD->ulCount == 0))
  349. {
  350. FreeStack(pTD);
  351. }
  352. }
  353. ORIGINAL_API(ExitThread)(dwExitCode);
  354. }
  355. /*++
  356. Add the specified hook to the linked list - this accepts wildcards.
  357. --*/
  358. VOID
  359. AddHooks(HOOK *pHook)
  360. {
  361. if (strstr(pHook->szFnName, "*") == 0)
  362. {
  363. // Now that we have a hook (not a wild card), we need to make sure it's
  364. // ok to add it to the list. There are some calls that cannot be shimmed
  365. for (int i=0; i<sizeof(Excludes)/sizeof(FNEXCLUDE); i++)
  366. {
  367. if ((_stricmp(pHook->szModule, (LPSTR)Excludes[i].pszModule) == 0) &&
  368. (strcmp(pHook->szFnName, (LPSTR)Excludes[i].pszFnName) == 0))
  369. {
  370. DPFN( eDbgLevelInfo,"Ignoring %s!%s", Excludes[i].pszModule, Excludes[i].pszFnName);
  371. return;
  372. }
  373. }
  374. // The hook passes, so add it to the list.
  375. HOOK *pH = (HOOK *) malloc(sizeof(HOOK));
  376. if (pH)
  377. {
  378. MoveMemory(pH, pHook, sizeof(HOOK));
  379. pH->next = g_pHooks;
  380. g_pHooks = pH;
  381. }
  382. return;
  383. }
  384. // Here we have to look through the exports
  385. LOADED_IMAGE image;
  386. if (!LoadModule(pHook->szModule, &image))
  387. {
  388. DPFN( eDbgLevelError, "Module %s not found", pHook->szModule);
  389. return;
  390. }
  391. EXPORT_ENUM exports;
  392. CHAR szFnName[MAX_PATH];
  393. strcpy(szFnName, pHook->szFnName);
  394. DWORD dwLen = (DWORD)((DWORD_PTR)strstr(pHook->szFnName, "*") - (DWORD_PTR)&pHook->szFnName);
  395. // Enumerate the exports for this module
  396. BOOL bMore = EnumFirstExport(&image, &exports);
  397. while (bMore)
  398. {
  399. if ((dwLen == 0) ||
  400. (strncmp(exports.ExportFunction, szFnName, dwLen) == 0))
  401. {
  402. // We have a match
  403. strcpy(pHook->szFnName, exports.ExportFunction);
  404. AddHooks(pHook);
  405. }
  406. bMore = EnumNextExport(&exports);
  407. }
  408. // Done with this module
  409. UnloadModule(&image);
  410. }
  411. /*++
  412. Parse the command line for APIs to fix stack problems with:
  413. USER32.DLL!GetDC:0; KERNEL32.DLL!CreateFile*
  414. The :X is to define behaviour - so:
  415. :0 fill old stack with zeroes
  416. :1 fill old stack with pointers
  417. :2 fill old stack with pointers to pointers
  418. --*/
  419. DWORD
  420. ParseCommandLineA(
  421. LPCSTR lpCommandLine
  422. )
  423. {
  424. char seps[] = " :,\t;!";
  425. char *token = NULL;
  426. HOOK *pHook = NULL;
  427. DWORD dwState = 0;
  428. HOOK hook;
  429. // Since strtok modifies the string, we need to copy it
  430. LPSTR szCommandLine = (LPSTR) malloc(strlen(lpCommandLine) + 1);
  431. if (!szCommandLine) goto Exit;
  432. strcpy(szCommandLine, lpCommandLine);
  433. //
  434. // See if we need to do all modules
  435. //
  436. if ((strcmp(szCommandLine, "") == 0) || (strcmp(szCommandLine, "*") == 0))
  437. {
  438. for (int i=0; i<sizeof(g_AllHooks)/sizeof(HOOK); i++)
  439. {
  440. AddHooks(&g_AllHooks[i]);
  441. }
  442. goto Exit;
  443. }
  444. //
  445. // Run the string, looking for exception names
  446. //
  447. token = _strtok(szCommandLine, seps);
  448. while (token)
  449. {
  450. switch (dwState)
  451. {
  452. case 2: // handle the :X[(fill size)] case
  453. dwState = 0;
  454. if (token[0] && ((token[1] == '\0') || (token[1] == '(')))
  455. {
  456. switch (token[0])
  457. {
  458. case '0':
  459. hook.dwFill = STACK_FILL_ZERO;
  460. break;
  461. case '1':
  462. hook.dwFill = STACK_FILL_PTR1;
  463. break;
  464. case '2':
  465. hook.dwFill = STACK_FILL_PTR2;
  466. break;
  467. default:
  468. hook.dwFill = STACK_FILL_ZERO;
  469. }
  470. if (token[1] == '(')
  471. {
  472. token+=2; // advance to the beginning of the fill size
  473. token[strlen(token)-1] = '\0'; // null terminate
  474. hook.dwFillSize = atol(token) >> 2; // get fill size in dwords
  475. if (hook.dwFillSize == 0)
  476. {
  477. hook.dwFillSize = STACK_FILL_SIZE;
  478. }
  479. }
  480. // We must be done, so add this hook
  481. AddHooks(&hook);
  482. break;
  483. }
  484. AddHooks(&hook);
  485. case 0: // add a new API module name
  486. ZeroMemory(&hook, sizeof(HOOK));
  487. strcpy(hook.szModule, token);
  488. hook.dwFill = STACK_FILL_NONE;
  489. hook.dwFillSize = STACK_FILL_SIZE;
  490. dwState++;
  491. break;
  492. case 1: // add a new API function name
  493. dwState++;
  494. if (strlen(hook.szModule) == 0)
  495. {
  496. DPFN( eDbgLevelError, "Parse error with token %s", token);
  497. goto Exit;
  498. }
  499. strcpy(hook.szFnName, token);
  500. break;
  501. }
  502. // Get the next token
  503. token = _strtok(NULL, seps);
  504. }
  505. if (dwState == 2)
  506. {
  507. AddHooks(&hook);
  508. }
  509. Exit:
  510. if (szCommandLine)
  511. {
  512. free(szCommandLine);
  513. }
  514. if (!g_pHooks)
  515. {
  516. DPFN( eDbgLevelError, "No hooks added");
  517. return 0;
  518. }
  519. //
  520. // Dump results of command line parse
  521. //
  522. DPFN( eDbgLevelInfo, "--------------------------------------------");
  523. DPFN( eDbgLevelInfo, " Stack Swapping the following APIs: ");
  524. DPFN( eDbgLevelInfo, "--------------------------------------------");
  525. DWORD dwCount = 0;
  526. pHook = g_pHooks;
  527. while (pHook)
  528. {
  529. DPFN( eDbgLevelInfo, "%s!%s: Fill=%d, Size=%d", pHook->szModule, pHook->szFnName, pHook->dwFill, pHook->dwFillSize*4);
  530. dwCount++;
  531. pHook = pHook->next;
  532. }
  533. DPFN( eDbgLevelInfo, "--------------------------------------------");
  534. return dwCount;
  535. }
  536. /*++
  537. Builds the stubs for the hooked APIs
  538. --*/
  539. DWORD
  540. BuildStubs()
  541. {
  542. // Count the stubs
  543. DWORD dwCount = 0;
  544. HOOK *pHook = g_pHooks;
  545. while (pHook)
  546. {
  547. dwCount++;
  548. pHook = pHook->next;
  549. }
  550. // Create the stubs
  551. LPBYTE pStub = (LPBYTE) VirtualAlloc(
  552. 0,
  553. STUB_SIZE * dwCount,
  554. MEM_COMMIT,
  555. PAGE_EXECUTE_READWRITE);
  556. if (!pStub)
  557. {
  558. DPFN( eDbgLevelError, "Could not allocate memory for stubs");
  559. return 0;
  560. }
  561. pHook = g_pHooks;
  562. PHOOKAPI pAPIHook = &g_pAPIHooks[APIHOOK_Count];
  563. while (pHook)
  564. {
  565. MoveMemory(pStub, Stub, STUB_SIZE);
  566. LPDWORD p;
  567. p = (LPDWORD)((DWORD_PTR)pStub + 0x19); // fill in the fill type
  568. *p = pHook->dwFill;
  569. p = (LPDWORD)((DWORD_PTR)pStub + 0x19+7); // fill in the fill size
  570. *p = pHook->dwFillSize;
  571. p = (LPDWORD)((DWORD_PTR)pStub + 0x2b+7); // fill in the hook
  572. *p = (DWORD_PTR)&pAPIHook->pfnOld;
  573. ZeroMemory(pAPIHook, sizeof(HOOKAPI));
  574. pAPIHook->pszModule = pHook->szModule;
  575. pAPIHook->pszFunctionName = pHook->szFnName;
  576. pAPIHook->pfnNew = pStub;
  577. DPFN( eDbgLevelSpew, "%08lx %s!%s", pStub, pHook->szModule, pHook->szFnName);
  578. pStub += STUB_SIZE;
  579. pAPIHook++;
  580. pHook = pHook->next;
  581. }
  582. return dwCount;
  583. }
  584. /*++
  585. Free the stub list allocated by ParseCommandLineA
  586. --*/
  587. VOID
  588. FreeStubs()
  589. {
  590. HOOK *pHook = g_pHooks;
  591. while (pHook)
  592. {
  593. pHook = pHook->next;
  594. free(g_pHooks);
  595. g_pHooks = pHook;
  596. }
  597. }
  598. /*++
  599. Register hooked functions
  600. --*/
  601. BOOL
  602. NOTIFY_FUNCTION(DWORD fdwReason)
  603. {
  604. if (fdwReason == DLL_PROCESS_ATTACH)
  605. {
  606. // Run the command line to check for hooks - returns number found
  607. dwStubCount = ParseCommandLineA(COMMAND_LINE);
  608. if (dwStubCount)
  609. {
  610. //
  611. // Increase the hook structure size.
  612. //
  613. g_pAPIHooks = (PHOOKAPI) realloc(g_pAPIHooks,
  614. sizeof(HOOKAPI) * (APIHOOK_Count + dwStubCount));
  615. if (!g_pAPIHooks)
  616. {
  617. DPFN( eDbgLevelError, "Failed to re-allocate hooks");
  618. return FALSE;
  619. }
  620. }
  621. INIT_STACK(1024 * 128, 32);
  622. NtCurrentTeb()->THREAD_VAR = AllocStack();
  623. BuildStubs();
  624. }
  625. else if (fdwReason == DLL_PROCESS_DETACH)
  626. {
  627. // Ignore cleanup
  628. // FreeStubs();
  629. }
  630. return TRUE;
  631. }
  632. HOOK_BEGIN
  633. CALL_NOTIFY_FUNCTION
  634. APIHOOK_ENTRY(KERNEL32.DLL, CreateThread)
  635. APIHOOK_ENTRY(KERNEL32.DLL, TerminateThread)
  636. APIHOOK_ENTRY(KERNEL32.DLL, ExitThread)
  637. if (fdwReason == DLL_PROCESS_ATTACH)
  638. {
  639. // Write out the new size
  640. *pdwHookCount = APIHOOK_Count + dwStubCount;
  641. }
  642. HOOK_END
  643. IMPLEMENT_SHIM_END