Leaked source code of windows server 2003
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.

1427 lines
38 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. apidll.cpp
  5. Abstract:
  6. This file implements the non-architecture specific
  7. code for the api monitor trojan/support dll.
  8. Author:
  9. Wesley Witt (wesw) 28-June-1995
  10. Environment:
  11. User Mode
  12. --*/
  13. #include "apidllp.h"
  14. #include <tchar.h>
  15. #pragma hdrstop
  16. typedef struct _BUF_INFO {
  17. LPSTR BufferHead;
  18. LPSTR Buffer;
  19. } BUF_INFO, *PBUF_INFO;
  20. PVOID MemPtr;
  21. PDLL_INFO DllList;
  22. HANDLE hLogFile;
  23. PGETCURRENTTHREADID pGetCurrentThreadId;
  24. PUCHAR ThunksBase;
  25. PUCHAR Thunks;
  26. BOOL RunningOnNT;
  27. BOOL StaticLink;
  28. ULONG_PTR LoadLibraryA_Addr;
  29. ULONG_PTR LoadLibraryW_Addr;
  30. ULONG_PTR FreeLibrary_Addr;
  31. ULONG_PTR GetProcAddress_Addr;
  32. HANDLE ApiTraceMutex;
  33. HANDLE ApiMemMutex;
  34. PTRACE_BUFFER TraceBuffer;
  35. DWORD ThreadCnt;
  36. DLL_INFO WndProcDllInfo;
  37. BOOL printNow = 0;
  38. extern "C" {
  39. LPDWORD ApiCounter;
  40. LPDWORD ApiTraceEnabled;
  41. LPDWORD ApiTimingEnabled;
  42. LPDWORD FastCounterAvail;
  43. LPDWORD ApiOffset;
  44. LPDWORD ApiStrings;
  45. LPDWORD ApiCount;
  46. LPDWORD WndProcEnabled;
  47. LPDWORD WndProcCount;
  48. LPDWORD WndProcOffset;
  49. DWORD TlsReEnter;
  50. DWORD TlsStack;
  51. DWORD ThunkOverhead;
  52. DWORD ThunkCallOverhead;
  53. PTLSGETVALUE pTlsGetValue;
  54. PTLSSETVALUE pTlsSetValue;
  55. PGETLASTERROR pGetLastError;
  56. PSETLASTERROR pSetLastError;
  57. PVIRTUALALLOC pVirtualAlloc;
  58. PQUERYPERFORMANCECOUNTER pQueryPerformanceCounter;
  59. }
  60. extern API_MASTER_TABLE ApiTables[];
  61. BOOL ReDirectIat(VOID);
  62. BOOL ProcessDllLoad(VOID);
  63. PUCHAR CreateApiThunk(ULONG_PTR,PUCHAR,PDLL_INFO,PAPI_INFO);
  64. BOOL ProcessApiTable(PDLL_INFO DllInfo);
  65. VOID CreateWndProcApi(LPCSTR lpszClassName, WNDPROC *pWndProc);
  66. VOID CalibrateThunk();
  67. VOID Calib1Func(VOID);
  68. VOID Calib2Func(VOID);
  69. VOID (*Calib1Thunk)();
  70. VOID (*Calib2Thunk)();
  71. extern "C" void
  72. __cdecl
  73. dprintf(
  74. char *format,
  75. ...
  76. )
  77. /*++
  78. Routine Description:
  79. Prints a debug string to the API monitor.
  80. Arguments:
  81. format - printf() format string
  82. ... - Variable data
  83. Return Value:
  84. None.
  85. --*/
  86. {
  87. char buf[1024];
  88. va_list arg_ptr;
  89. va_start(arg_ptr, format);
  90. pTlsSetValue( TlsReEnter, (LPVOID) 1 );
  91. _vsnprintf(buf, sizeof(buf), format, arg_ptr);
  92. OutputDebugString( buf );
  93. pTlsSetValue( TlsReEnter, (LPVOID) 0 );
  94. return;
  95. }
  96. extern "C" {
  97. DWORD
  98. ApiDllEntry(
  99. HINSTANCE hInstance,
  100. DWORD Reason,
  101. LPVOID Context
  102. )
  103. /*++
  104. Routine Description:
  105. DLL initialization function.
  106. Arguments:
  107. hInstance - Instance handle
  108. Reason - Reason for the entrypoint being called
  109. Context - Context record
  110. Return Value:
  111. TRUE - Initialization succeeded
  112. FALSE - Initialization failed
  113. --*/
  114. {
  115. if (Reason == DLL_PROCESS_ATTACH) {
  116. return ProcessDllLoad();
  117. }
  118. if (Reason == DLL_THREAD_ATTACH) {
  119. pTlsSetValue( TlsReEnter, (LPVOID) 1 );
  120. PTHREAD_STACK Stack = (PTHREAD_STACK) pVirtualAlloc( NULL, sizeof(THREAD_STACK), MEM_COMMIT, PAGE_READWRITE );
  121. if (!Stack) {
  122. return FALSE;
  123. }
  124. Stack->ThreadNum = ++ThreadCnt;
  125. // Start at 2nd entry so that there is always a parent frame
  126. Stack->Pointer = (DWORD_PTR)&Stack->Body[FRAME_SIZE];
  127. pTlsSetValue( TlsReEnter, (LPVOID) 0 );
  128. pTlsSetValue( TlsStack, Stack );
  129. return TRUE;
  130. }
  131. if (Reason == DLL_THREAD_DETACH) {
  132. return TRUE;
  133. }
  134. if (Reason == DLL_PROCESS_DETACH) {
  135. return TRUE;
  136. }
  137. return TRUE;
  138. }
  139. } //extern "C"
  140. PDLL_INFO
  141. AddDllToList(
  142. ULONG DllAddr,
  143. LPSTR DllName,
  144. ULONG DllSize
  145. )
  146. {
  147. //
  148. // look for the dll entry in the list
  149. //
  150. for (ULONG i=0; i<MAX_DLLS; i++) {
  151. if (DllList[i].BaseAddress == DllAddr) {
  152. return &DllList[i];
  153. }
  154. }
  155. //
  156. // this check should be unnecessary
  157. // the debugger side (apimon.exe) takes
  158. // care of adding the dlls to the list when
  159. // it gets a module load from the debug
  160. // subsystem. this code is here only so
  161. // a test program that is not a debugger
  162. // will work properly.
  163. //
  164. for (i=0; i<MAX_DLLS; i++) {
  165. if (DllList[i].BaseAddress == 0) {
  166. DllList[i].BaseAddress = DllAddr;
  167. strcpy( DllList[i].Name, DllName );
  168. DllList[i].Size = DllSize;
  169. return &DllList[i];
  170. }
  171. }
  172. //
  173. // we could not find a dll in the list that matched
  174. // and we could not add it because the list is
  175. // is full. we're hosed.
  176. //
  177. return NULL;
  178. }
  179. BOOL
  180. ProcessDllLoad(
  181. VOID
  182. )
  183. /*++
  184. Routine Description:
  185. Sets up the API thunks for the process that this dll
  186. is loaded into.
  187. Arguments:
  188. None.
  189. Return Value:
  190. TRUE - Success
  191. FALSE - Failure
  192. --*/
  193. {
  194. ULONG i;
  195. ULONG cnt;
  196. HANDLE hMap;
  197. //
  198. // see if we are running on NT
  199. // this is necessary because APIMON implements some
  200. // features that are NOT available on WIN95
  201. //
  202. OSVERSIONINFO OsVersionInfo;
  203. OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo);
  204. GetVersionEx( &OsVersionInfo );
  205. RunningOnNT = OsVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT;
  206. TlsReEnter = TlsAlloc();
  207. if (TlsReEnter == TLS_OUT_OF_INDEXES) {
  208. return FALSE;
  209. }
  210. TlsStack = TlsAlloc();
  211. if (TlsStack == TLS_OUT_OF_INDEXES) {
  212. return FALSE;
  213. }
  214. HMODULE hMod = GetModuleHandle( KERNEL32 );
  215. if (!hMod) {
  216. return FALSE;
  217. }
  218. pGetCurrentThreadId = (PGETCURRENTTHREADID) GetProcAddress( hMod, "GetCurrentThreadId" );
  219. if (!pGetCurrentThreadId) {
  220. return FALSE;
  221. }
  222. pGetLastError = (PGETLASTERROR) GetProcAddress( hMod, "GetLastError" );
  223. if (!pGetLastError) {
  224. return FALSE;
  225. }
  226. pSetLastError = (PSETLASTERROR) GetProcAddress( hMod, "SetLastError" );
  227. if (!pSetLastError) {
  228. return FALSE;
  229. }
  230. pQueryPerformanceCounter = (PQUERYPERFORMANCECOUNTER) GetProcAddress( hMod, "QueryPerformanceCounter" );
  231. if (!pQueryPerformanceCounter) {
  232. return FALSE;
  233. }
  234. pTlsGetValue = (PTLSGETVALUE) GetProcAddress( hMod, "TlsGetValue" );
  235. if (!pTlsGetValue) {
  236. return FALSE;
  237. }
  238. pTlsSetValue = (PTLSSETVALUE) GetProcAddress( hMod, "TlsSetValue" );
  239. if (!pTlsSetValue) {
  240. return FALSE;
  241. }
  242. pVirtualAlloc = (PVIRTUALALLOC) GetProcAddress( hMod, "VirtualAlloc" );
  243. if (!pVirtualAlloc) {
  244. return FALSE;
  245. }
  246. Thunks = (PUCHAR)VirtualAlloc( NULL, THUNK_SIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
  247. if (!Thunks) {
  248. return FALSE;
  249. }
  250. ThunksBase = Thunks;
  251. PTHREAD_STACK Stack = (PTHREAD_STACK) pVirtualAlloc( NULL, sizeof(THREAD_STACK), MEM_COMMIT, PAGE_READWRITE );
  252. if (!Stack) {
  253. return FALSE;
  254. }
  255. Stack->ThreadNum = ++ThreadCnt;
  256. // Start at 2nd entry so that there is always a parent frame
  257. Stack->Pointer = (DWORD_PTR)&Stack->Body[FRAME_SIZE];
  258. pTlsSetValue( TlsReEnter, (LPVOID) 0 );
  259. pTlsSetValue( TlsStack, Stack );
  260. hMap = OpenFileMapping(
  261. FILE_MAP_WRITE,
  262. FALSE,
  263. "ApiWatch"
  264. );
  265. if (!hMap) {
  266. return FALSE;
  267. }
  268. MemPtr = (PUCHAR)MapViewOfFile(
  269. hMap,
  270. FILE_MAP_WRITE,
  271. 0,
  272. 0,
  273. 0
  274. );
  275. if (!MemPtr) {
  276. return FALSE;
  277. }
  278. ApiCounter = (LPDWORD) MemPtr + 0;
  279. ApiTraceEnabled = (LPDWORD) MemPtr + 1;
  280. ApiTimingEnabled = (LPDWORD) MemPtr + 2;
  281. FastCounterAvail = (LPDWORD) MemPtr + 3;
  282. ApiOffset = (LPDWORD) MemPtr + 4;
  283. ApiStrings = (LPDWORD) MemPtr + 5;
  284. ApiCount = (LPDWORD) MemPtr + 6;
  285. WndProcEnabled = (LPDWORD) MemPtr + 7;
  286. WndProcCount = (LPDWORD) MemPtr + 8;
  287. WndProcOffset = (LPDWORD) MemPtr + 9;
  288. DllList = (PDLL_INFO) ((LPDWORD)MemPtr + 10);
  289. //
  290. // open the shared memory region for the api trace buffer
  291. //
  292. hMap = OpenFileMapping(
  293. FILE_MAP_WRITE,
  294. FALSE,
  295. "ApiTrace"
  296. );
  297. if (!hMap) {
  298. return FALSE;
  299. }
  300. TraceBuffer = (PTRACE_BUFFER)MapViewOfFile(
  301. hMap,
  302. FILE_MAP_WRITE,
  303. 0,
  304. 0,
  305. 0
  306. );
  307. if (!TraceBuffer) {
  308. return FALSE;
  309. }
  310. ApiTraceMutex = OpenMutex( SYNCHRONIZE, FALSE, "ApiTraceMutex" );
  311. if (!ApiTraceMutex) {
  312. return FALSE;
  313. }
  314. ApiMemMutex = OpenMutex( SYNCHRONIZE, FALSE, "ApiMemMutex" );
  315. if (!ApiMemMutex) {
  316. return FALSE;
  317. }
  318. // Initialize dummy window proc Dll
  319. // (Only need the fields accesed by thunk and thunk creation)
  320. strcpy(WndProcDllInfo.Name, WNDPROCDLL);
  321. WndProcDllInfo.Enabled = TRUE;
  322. CalibrateThunk();
  323. ReDirectIat();
  324. // Disable close handle exceptions
  325. if (RunningOnNT) {
  326. NtCurrentPeb()->NtGlobalFlag &= ~FLG_ENABLE_CLOSE_EXCEPTIONS;
  327. }
  328. return TRUE;
  329. }
  330. PUCHAR
  331. ProcessThunk(
  332. ULONG_PTR ThunkAddr,
  333. ULONG_PTR IatAddr,
  334. PUCHAR Text
  335. )
  336. {
  337. PDLL_INFO DllInfo;
  338. for (ULONG k=0; k<MAX_DLLS; k++) {
  339. DllInfo = &DllList[k];
  340. if (ThunkAddr >= DllInfo->BaseAddress &&
  341. ThunkAddr < DllInfo->BaseAddress+DllInfo->Size) {
  342. break;
  343. }
  344. }
  345. if (k == MAX_DLLS) {
  346. return Text;
  347. }
  348. PIMAGE_DOS_HEADER dh = (PIMAGE_DOS_HEADER)DllInfo->BaseAddress;
  349. PIMAGE_NT_HEADERS nh = (PIMAGE_NT_HEADERS)(dh->e_lfanew + DllInfo->BaseAddress);
  350. PIMAGE_SECTION_HEADER SectionHdrs = IMAGE_FIRST_SECTION( nh );
  351. BOOL IsCode = FALSE;
  352. for (ULONG l=0; l<nh->FileHeader.NumberOfSections; l++) {
  353. if (ThunkAddr-DllInfo->BaseAddress >= SectionHdrs[l].VirtualAddress &&
  354. ThunkAddr-DllInfo->BaseAddress < SectionHdrs[l].VirtualAddress+SectionHdrs[l].SizeOfRawData) {
  355. if (SectionHdrs[l].Characteristics & IMAGE_SCN_MEM_EXECUTE) {
  356. IsCode = TRUE;
  357. break;
  358. }
  359. break;
  360. }
  361. }
  362. if (!IsCode) {
  363. return Text;
  364. }
  365. PAPI_INFO ApiInfo = (PAPI_INFO)(DllInfo->ApiOffset + (ULONG_PTR)DllList);
  366. for (l=0; l<DllInfo->ApiCount; l++) {
  367. if (ApiInfo[l].Address == ThunkAddr) {
  368. return CreateApiThunk( IatAddr, Text, DllInfo, &ApiInfo[l] );
  369. }
  370. }
  371. return Text;
  372. }
  373. PUCHAR
  374. ProcessUnBoundImage(
  375. PDLL_INFO DllInfo,
  376. PUCHAR Text
  377. )
  378. {
  379. PIMAGE_DOS_HEADER dh = (PIMAGE_DOS_HEADER)DllInfo->BaseAddress;
  380. if (dh->e_magic != IMAGE_DOS_SIGNATURE) {
  381. return Text;
  382. }
  383. PIMAGE_NT_HEADERS nh = (PIMAGE_NT_HEADERS)(dh->e_lfanew + DllInfo->BaseAddress);
  384. PIMAGE_SECTION_HEADER SectionHdrs = IMAGE_FIRST_SECTION( nh );
  385. ULONG Address = nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  386. ULONG i;
  387. for (i=0; i<nh->FileHeader.NumberOfSections; i++) {
  388. if (Address >= SectionHdrs[i].VirtualAddress &&
  389. Address < SectionHdrs[i].VirtualAddress+SectionHdrs[i].SizeOfRawData) {
  390. break;
  391. }
  392. }
  393. if (i == nh->FileHeader.NumberOfSections) {
  394. return Text;
  395. }
  396. ULONG_PTR SeekPos = DllInfo->BaseAddress +
  397. nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  398. ULONG PageProt;
  399. ULONG ThunkProt;
  400. ULONG_PTR ImportStart = SeekPos;
  401. PUCHAR TextStart = Text;
  402. VirtualProtect(
  403. (PVOID)ImportStart,
  404. nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size,
  405. PAGE_READWRITE,
  406. &PageProt
  407. );
  408. while( TRUE ) {
  409. PIMAGE_IMPORT_DESCRIPTOR desc = (PIMAGE_IMPORT_DESCRIPTOR)SeekPos;
  410. SeekPos += sizeof(IMAGE_IMPORT_DESCRIPTOR);
  411. if ((desc->Characteristics == 0) && (desc->Name == 0) && (desc->FirstThunk == 0)) {
  412. //
  413. // End of import descriptors
  414. //
  415. break;
  416. }
  417. ULONG_PTR *ThunkAddr = (ULONG_PTR *)((ULONG)desc->FirstThunk + DllInfo->BaseAddress);
  418. while( *ThunkAddr ) {
  419. #ifdef _X86_
  420. if (RunningOnNT) {
  421. Text = ProcessThunk(*ThunkAddr, (ULONG_PTR)ThunkAddr, Text );
  422. } else {
  423. Text = ProcessThunk(*(PULONG)(*ThunkAddr + 1), (ULONG)ThunkAddr, Text );
  424. }
  425. #else
  426. Text = ProcessThunk(*ThunkAddr, (ULONG_PTR)ThunkAddr, Text );
  427. #endif
  428. ThunkAddr += 1;
  429. }
  430. }
  431. VirtualProtect(
  432. (PVOID)ImportStart,
  433. nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size,
  434. PageProt,
  435. &PageProt
  436. );
  437. FlushInstructionCache(
  438. GetCurrentProcess(),
  439. (PVOID)DllInfo->BaseAddress,
  440. DllInfo->Size
  441. );
  442. FlushInstructionCache(
  443. GetCurrentProcess(),
  444. (PVOID)TextStart,
  445. (DWORD)(Text-TextStart)
  446. );
  447. return Text;
  448. }
  449. PUCHAR
  450. ProcessBoundImage(
  451. PDLL_INFO DllInfo,
  452. PUCHAR Text,
  453. PULONG IatBase,
  454. ULONG IatCnt
  455. )
  456. {
  457. ULONG j;
  458. ULONG PageProt;
  459. ULONG ThunkProt;
  460. PUCHAR TextStart = Text;
  461. VirtualProtect(
  462. IatBase,
  463. IatCnt*4,
  464. PAGE_READWRITE,
  465. &PageProt
  466. );
  467. //
  468. // process the iat entries
  469. //
  470. for (j=0; j<IatCnt; j++) {
  471. if (IatBase[j]) {
  472. #ifdef _X86_
  473. if (RunningOnNT) {
  474. Text = ProcessThunk( IatBase[j], (ULONG_PTR)&IatBase[j], Text );
  475. } else {
  476. Text = ProcessThunk(*(PULONG)(IatBase[j] + 1), (ULONG)&IatBase[j], Text );
  477. }
  478. #else
  479. Text = ProcessThunk( IatBase[j], (ULONG_PTR)&IatBase[j], Text );
  480. #endif
  481. }
  482. }
  483. VirtualProtect(
  484. IatBase,
  485. IatCnt*4,
  486. PageProt,
  487. &PageProt
  488. );
  489. FlushInstructionCache(
  490. GetCurrentProcess(),
  491. (PVOID)DllInfo->BaseAddress,
  492. DllInfo->Size
  493. );
  494. FlushInstructionCache(
  495. GetCurrentProcess(),
  496. (PVOID)TextStart,
  497. (DWORD)(Text-TextStart)
  498. );
  499. return Text;
  500. }
  501. BOOL
  502. ReDirectIat(
  503. VOID
  504. )
  505. {
  506. ULONG i;
  507. PUCHAR Text = Thunks;
  508. for (i=0; i<MAX_DLLS; i++) {
  509. PDLL_INFO DllInfo = &DllList[i];
  510. if (!DllInfo->BaseAddress) {
  511. break;
  512. }
  513. if ((DllInfo->Snapped) || (DllInfo->Unloaded)) {
  514. continue;
  515. }
  516. PIMAGE_DOS_HEADER dh = (PIMAGE_DOS_HEADER)DllInfo->BaseAddress;
  517. PULONG IatBase = NULL;
  518. ULONG IatCnt = 0;
  519. if (dh->e_magic == IMAGE_DOS_SIGNATURE) {
  520. PIMAGE_NT_HEADERS nh = (PIMAGE_NT_HEADERS)(dh->e_lfanew + DllInfo->BaseAddress);
  521. if (nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) {
  522. IatBase = (PULONG)(DllInfo->BaseAddress +
  523. nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress);
  524. IatCnt = nh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size / 4;
  525. }
  526. } else {
  527. continue;
  528. }
  529. if (!IatBase) {
  530. Text = ProcessUnBoundImage( DllInfo, Text );
  531. } else {
  532. Text = ProcessBoundImage( DllInfo, Text, IatBase, IatCnt );
  533. }
  534. DllInfo->Snapped = TRUE;
  535. ProcessApiTable( DllInfo );
  536. }
  537. Thunks = Text;
  538. return TRUE;
  539. }
  540. extern "C" {
  541. VOID
  542. HandleDynamicDllLoadA(
  543. ULONG_PTR DllAddress,
  544. LPSTR DllName
  545. )
  546. {
  547. if ( (!DllAddress) || (_stricmp(DllName,TROJANDLL)==0) ) {
  548. return;
  549. }
  550. ReDirectIat();
  551. }
  552. VOID
  553. HandleDynamicDllLoadW(
  554. ULONG_PTR DllAddress,
  555. LPWSTR DllName
  556. )
  557. {
  558. CHAR AsciiBuf[512];
  559. ZeroMemory( AsciiBuf, sizeof(AsciiBuf) );
  560. WideCharToMultiByte(
  561. CP_ACP,
  562. 0,
  563. DllName,
  564. wcslen(DllName),
  565. AsciiBuf,
  566. sizeof(AsciiBuf),
  567. NULL,
  568. NULL
  569. );
  570. if (!strlen(AsciiBuf)) {
  571. return;
  572. }
  573. HandleDynamicDllLoadA( DllAddress, AsciiBuf );
  574. }
  575. VOID
  576. HandleRegisterClassA(
  577. WNDCLASSA *pWndClass
  578. )
  579. {
  580. if (!*WndProcEnabled)
  581. return;
  582. // Don't deal with call procedure handles or special addresses
  583. #ifdef _WIN64
  584. if (HIWORD((((DWORD_PTR)pWndClass->lpfnWndProc) >> 32)) == 0xFFFF)
  585. #else
  586. if (HIWORD(pWndClass->lpfnWndProc) == 0xFFFF)
  587. #endif
  588. return;
  589. if ((ULONG_PTR)(pWndClass->lpfnWndProc) & 0x80000000) {
  590. return;
  591. }
  592. pTlsSetValue( TlsReEnter, (LPVOID) 1 );
  593. if ((ULONG_PTR)pWndClass->lpszClassName < 0x10000) {
  594. CreateWndProcApi("<Atom>", &pWndClass->lpfnWndProc);
  595. } else {
  596. CreateWndProcApi( pWndClass->lpszClassName, &pWndClass->lpfnWndProc );
  597. }
  598. pTlsSetValue( TlsReEnter, (LPVOID) 0 );
  599. }
  600. VOID HandleRegisterClassW(
  601. WNDCLASSW *pWndClass
  602. )
  603. {
  604. CHAR AsciiBuf[128];
  605. if (!*WndProcEnabled)
  606. return;
  607. // Don't deal with call procedure handles or special addresses
  608. #ifdef _WIN64
  609. if ((HIWORD((((DWORD_PTR)pWndClass->lpfnWndProc) >> 32)) == 0xFFFF) ||
  610. #else
  611. if (( HIWORD(pWndClass->lpfnWndProc) == 0xFFFF) ||
  612. #endif
  613. ((ULONG_PTR)(pWndClass->lpfnWndProc) & 0x80000000) ) {
  614. return;
  615. }
  616. if ((ULONG_PTR)pWndClass->lpszClassName < 0x10000) {
  617. CreateWndProcApi( "<Atom>", &pWndClass->lpfnWndProc );
  618. return;
  619. }
  620. pTlsSetValue( TlsReEnter, (LPVOID) 1 );
  621. ZeroMemory( AsciiBuf, sizeof(AsciiBuf) );
  622. WideCharToMultiByte(
  623. CP_ACP,
  624. 0,
  625. pWndClass->lpszClassName,
  626. wcslen(pWndClass->lpszClassName),
  627. AsciiBuf,
  628. sizeof(AsciiBuf),
  629. NULL,
  630. NULL
  631. );
  632. pTlsSetValue( TlsReEnter, (LPVOID) 0 );
  633. if (!strlen(AsciiBuf)) {
  634. return;
  635. }
  636. CreateWndProcApi( AsciiBuf, &pWndClass->lpfnWndProc );
  637. }
  638. LONG_PTR
  639. HandleSetWindowLong(
  640. HWND hWindow,
  641. LONG lOffset,
  642. LPARAM lValue
  643. )
  644. {
  645. if (!*WndProcEnabled || (lOffset != GWLP_WNDPROC))
  646. return lValue;
  647. // Don't handle special addresses
  648. #ifdef _WIN64
  649. if ((HIWORD((lValue >> 32)) == 0xFFFF) ||
  650. #else
  651. if ( (HIWORD(lValue) == 0xFFFF) ||
  652. #endif
  653. ((ULONG_PTR)lValue & 0x80000000) ) {
  654. return lValue;
  655. }
  656. CreateWndProcApi( "Subclass", (WNDPROC*)&lValue );
  657. return lValue;
  658. }
  659. VOID
  660. HandleDynamicDllFree(
  661. ULONG_PTR DllAddress
  662. )
  663. {
  664. for (ULONG i=0; i<MAX_DLLS; i++) {
  665. if (DllList[i].BaseAddress == DllAddress) {
  666. DllList[i].Unloaded = TRUE;
  667. // DllList[i].Enabled = FALSE; Leave enable in case it's reloaded
  668. DllList[i].Snapped = FALSE;
  669. break;
  670. }
  671. }
  672. }
  673. ULONG_PTR
  674. HandleGetProcAddress(
  675. ULONG_PTR ProcAddress
  676. )
  677. {
  678. if (ProcAddress == NULL)
  679. return NULL;
  680. Thunks = ProcessThunk(ProcAddress, (ULONG_PTR)&ProcAddress, Thunks);
  681. return ProcAddress;
  682. }
  683. } // extern "C"
  684. VOID
  685. CreateWndProcApi(
  686. LPCSTR lpszClassName,
  687. WNDPROC *pWndProc
  688. )
  689. {
  690. PAPI_INFO ApiInfo;
  691. DWORD i;
  692. PUCHAR NewThunks;
  693. CHAR debugBuf[256];
  694. // Don't re-thunk one of our own thunks
  695. if (ThunksBase <= (PUCHAR)*pWndProc && (PUCHAR)*pWndProc < Thunks)
  696. return;
  697. pTlsSetValue( TlsReEnter, (LPVOID) 1 );
  698. // Get exclusive access to API memory
  699. WaitForSingleObject( ApiMemMutex, INFINITE );
  700. // Check for existing thunk for this window proc
  701. ApiInfo = (PAPI_INFO)(*WndProcOffset + (ULONG_PTR)DllList);
  702. for (i=0; i<*WndProcCount; i++,ApiInfo++) {
  703. if (ApiInfo->Address == (ULONG_PTR)*pWndProc) {
  704. *pWndProc = (WNDPROC)ApiInfo->ThunkAddress;
  705. ReleaseMutex(ApiMemMutex);
  706. pTlsSetValue( TlsReEnter, (LPVOID) 0 );
  707. return;
  708. }
  709. }
  710. // Allocate an API Info slot
  711. if (*ApiCount < MAX_APIS) {
  712. *WndProcOffset -= sizeof(API_INFO);
  713. *WndProcCount += 1;
  714. *ApiCount += 1;
  715. ApiInfo = (PAPI_INFO)(*WndProcOffset + (ULONG_PTR)DllList);
  716. ApiInfo->Name = *ApiStrings;
  717. strcpy( (LPSTR)((LPSTR)MemPtr + *ApiStrings), lpszClassName );
  718. *ApiStrings += (strlen(lpszClassName) + 1);
  719. }
  720. else {
  721. ApiInfo = NULL;
  722. }
  723. if (ApiInfo != NULL) {
  724. ApiInfo->Count = 0;
  725. ApiInfo->NestCount = 0;
  726. ApiInfo->Time = 0;
  727. ApiInfo->CalleeTime = 0;
  728. ApiInfo->ThunkAddress = 0;
  729. ApiInfo->Address = (ULONG_PTR)*pWndProc;
  730. ApiInfo->DllOffset = 0;
  731. ApiInfo->HardFault = 0;
  732. ApiInfo->SoftFault = 0;
  733. ApiInfo->CodeFault = 0;
  734. ApiInfo->DataFault = 0;
  735. NewThunks = CreateMachApiThunk( (PULONG_PTR)pWndProc, Thunks, &WndProcDllInfo, ApiInfo );
  736. FlushInstructionCache( GetCurrentProcess(), (PVOID)Thunks, (DWORD)(NewThunks - Thunks));
  737. Thunks = NewThunks;
  738. }
  739. ReleaseMutex( ApiMemMutex );
  740. pTlsSetValue( TlsReEnter, (LPVOID) 0 );
  741. }
  742. BOOL
  743. ProcessApiTable(
  744. PDLL_INFO DllInfo
  745. )
  746. {
  747. ULONG i,j;
  748. PAPI_MASTER_TABLE ApiMaster = NULL;
  749. i = 0;
  750. while( ApiTables[i].Name ) {
  751. if (_stricmp( ApiTables[i].Name, DllInfo->Name ) == 0) {
  752. ApiMaster = &ApiTables[i];
  753. break;
  754. }
  755. i += 1;
  756. }
  757. if (!ApiMaster) {
  758. return FALSE;
  759. }
  760. if (ApiMaster->Processed) {
  761. return TRUE;
  762. }
  763. i = 0;
  764. PAPI_TABLE ApiTable = ApiMaster->ApiTable;
  765. PAPI_INFO ApiInfo = (PAPI_INFO)(DllInfo->ApiOffset + (ULONG_PTR)DllList);
  766. while( ApiTable[i].Name ) {
  767. for (j=0; j<DllInfo->ApiCount; j++) {
  768. if (strcmp( ApiTable[i].Name, (LPSTR)MemPtr+ApiInfo[j].Name ) == 0) {
  769. ApiInfo[j].ApiTable = &ApiTable[i];
  770. ApiInfo[j].ApiTableIndex = i + 1;
  771. break;
  772. }
  773. }
  774. i += 1;
  775. }
  776. ApiMaster->Processed = TRUE;
  777. return TRUE;
  778. }
  779. PUCHAR
  780. CreateApiThunk(
  781. ULONG_PTR IatAddr,
  782. PUCHAR Text,
  783. PDLL_INFO DllInfo,
  784. PAPI_INFO ApiInfo
  785. )
  786. {
  787. CHAR debugBuf[256];
  788. #if DBG
  789. _stprintf(debugBuf, "CreateApiThunk: %s:%s\n",DllInfo->Name, (LPSTR)MemPtr + ApiInfo->Name);
  790. OutputDebugString(debugBuf);
  791. #endif
  792. LPSTR Name = (LPSTR)MemPtr+ApiInfo->Name;
  793. if ((strcmp(Name,"FlushInstructionCache")==0) ||
  794. (strcmp(Name,"NtFlushInstructionCache")==0) ||
  795. (strcmp(Name,"ZwFlushInstructionCache")==0) ||
  796. (strcmp(Name,"VirtualProtect")==0) ||
  797. (strcmp(Name,"VirtualProtectEx")==0) ||
  798. (strcmp(Name,"NtProtectVirtualMemory")==0) ||
  799. (strcmp(Name,"ZwProtectVirtualMemory")==0) ||
  800. (strcmp(Name,"QueryPerformanceCounter")==0) ||
  801. (strcmp(Name,"NtQueryPerformanceCounter")==0) ||
  802. (strcmp(Name,"ZwQueryPerformanceCounter")==0) ||
  803. (strcmp(Name,"NtCallbackReturn")==0) ||
  804. (strcmp(Name,"ZwCallbackReturn")==0) ||
  805. (strcmp(Name,"_chkstk")==0) ||
  806. (strcmp(Name,"_alloca_probe")==0) ||
  807. (strcmp(Name,"GetLastError")==0) ||
  808. (strcmp(Name,"SetLastError")==0) ||
  809. (strcmp(Name,"_setjmp")==0) ||
  810. (strcmp(Name,"_setjmp3")==0) ||
  811. (strcmp(Name,"longjmp")==0) ||
  812. (strcmp(Name,"_longjmpex")==0) ||
  813. (strcmp(Name,"TlsGetValue")==0) ||
  814. (strcmp(Name,"TlsSetValue")==0) ||
  815. (strncmp(Name,"_Ots",4)==0)) {
  816. return Text;
  817. }
  818. PUCHAR stat = CreateMachApiThunk( (PULONG_PTR)IatAddr, Text, DllInfo, ApiInfo );
  819. return stat;
  820. }
  821. LPSTR
  822. UnDname(
  823. LPSTR sym,
  824. LPSTR undecsym,
  825. DWORD bufsize
  826. )
  827. {
  828. if (*sym != '?') {
  829. return sym;
  830. }
  831. if (UnDecorateSymbolName( sym,
  832. undecsym,
  833. bufsize,
  834. UNDNAME_COMPLETE |
  835. UNDNAME_NO_LEADING_UNDERSCORES |
  836. UNDNAME_NO_MS_KEYWORDS |
  837. UNDNAME_NO_FUNCTION_RETURNS |
  838. UNDNAME_NO_ALLOCATION_MODEL |
  839. UNDNAME_NO_ALLOCATION_LANGUAGE |
  840. UNDNAME_NO_MS_THISTYPE |
  841. UNDNAME_NO_CV_THISTYPE |
  842. UNDNAME_NO_THISTYPE |
  843. UNDNAME_NO_ACCESS_SPECIFIERS |
  844. UNDNAME_NO_THROW_SIGNATURES |
  845. UNDNAME_NO_MEMBER_TYPE |
  846. UNDNAME_NO_RETURN_UDT_MODEL |
  847. UNDNAME_NO_ARGUMENTS |
  848. UNDNAME_NO_SPECIAL_SYMS |
  849. UNDNAME_NAME_ONLY )) {
  850. return undecsym;
  851. }
  852. return sym;
  853. }
  854. extern "C" ULONG
  855. GetApiInfo(
  856. PAPI_INFO *ApiInfo,
  857. PDLL_INFO *DllInfo,
  858. PULONG ApiFlag,
  859. ULONG Address
  860. )
  861. {
  862. ULONG i;
  863. ULONG rval;
  864. LONG High;
  865. LONG Low;
  866. LONG Middle;
  867. PAPI_INFO ai;
  868. *ApiInfo = NULL;
  869. *DllInfo = NULL;
  870. *ApiFlag = APITYPE_NORMAL;
  871. #if defined(_M_IX86)
  872. //
  873. // the call instruction use to call penter
  874. // is 5 bytes long
  875. //
  876. Address -= 5;
  877. rval = 1;
  878. #elif defined(_M_MRX000)
  879. //
  880. // search for the beginning of the prologue
  881. //
  882. PULONG Instr = (PULONG) (Address - 4);
  883. i = 0;
  884. rval = 0;
  885. while( i < 16 ) {
  886. //
  887. // the opcode for the addiu instruction is 9
  888. //
  889. if ((*Instr >> 16) == 0xafbf) {
  890. //
  891. // find the return address
  892. //
  893. rval = *Instr & 0xffff;
  894. break;
  895. }
  896. Instr -= 1;
  897. i += 1;
  898. }
  899. if (i == 16 || rval == 0) {
  900. return 0;
  901. }
  902. #elif defined(_M_ALPHA)
  903. rval = 1;
  904. #elif defined(_M_PPC)
  905. //
  906. // On PPC, the penter call sequence looks like this:
  907. //
  908. // mflr r0
  909. // stwu sp,-0x40(sp)
  910. // bl ..penter
  911. //
  912. // So the function entry point is the return address - 12.
  913. //
  914. // (We really should do a function table lookup here, so
  915. // we're not dependent on the sequence...)
  916. //
  917. Address -= 12;
  918. rval = 1;
  919. #else
  920. #error( "unknown target machine" );
  921. #endif
  922. for (i=0; i<MAX_DLLS; i++) {
  923. if (Address >= DllList[i].BaseAddress &&
  924. Address < DllList[i].BaseAddress + DllList[i].Size) {
  925. *DllInfo = &DllList[i];
  926. break;
  927. }
  928. }
  929. if (!*DllInfo) {
  930. return 0;
  931. }
  932. ai = (PAPI_INFO)((*DllInfo)->ApiOffset + (ULONG_PTR)DllList);
  933. Low = 0;
  934. High = (*DllInfo)->ApiCount - 1;
  935. while (High >= Low) {
  936. Middle = (Low + High) >> 1;
  937. if (Address < ai[Middle].Address) {
  938. High = Middle - 1;
  939. } else if (Address > ai[Middle].Address) {
  940. Low = Middle + 1;
  941. } else {
  942. *ApiInfo = &ai[Middle];
  943. break;
  944. }
  945. }
  946. if (!*ApiInfo) {
  947. return 0;
  948. }
  949. if (Address == LoadLibraryA_Addr) {
  950. *ApiFlag = APITYPE_LOADLIBRARYA;
  951. } else if (Address == LoadLibraryW_Addr) {
  952. *ApiFlag = APITYPE_LOADLIBRARYW;
  953. } else if (Address == FreeLibrary_Addr) {
  954. *ApiFlag = APITYPE_FREELIBRARY;
  955. } else if (Address == GetProcAddress_Addr) {
  956. *ApiFlag = APITYPE_GETPROCADDRESS;
  957. }
  958. return rval;
  959. }
  960. extern "C" VOID
  961. ApiTrace(
  962. PAPI_INFO ApiInfo,
  963. ULONG_PTR Arg[MAX_TRACE_ARGS],
  964. ULONG ReturnValue,
  965. ULONG Caller,
  966. DWORDLONG EnterTime,
  967. DWORDLONG Duration,
  968. ULONG LastError
  969. )
  970. {
  971. PTRACE_ENTRY TraceEntry;
  972. ULONG TraceEntryLen;
  973. PTHREAD_STACK ThreadStack;
  974. LPSTR TraceString;
  975. LPSTR TraceLimit;
  976. CHAR debugBuf[128];
  977. ULONG_PTR len;
  978. DWORD *dwPtr;
  979. ULONG i;
  980. ULONG ArgCount;
  981. __try {
  982. pTlsSetValue( TlsReEnter, (LPVOID) 1 );
  983. WaitForSingleObject( ApiTraceMutex, INFINITE );
  984. // if trace buffer has room for another entry
  985. if ( TraceBuffer->Offset + sizeof(TRACE_ENTRY) < TraceBuffer->Size ) {
  986. TraceEntry = (PTRACE_ENTRY)((PCHAR)TraceBuffer->Entry + TraceBuffer->Offset);
  987. TraceEntry->Address = ApiInfo->Address;
  988. TraceEntry->ReturnValue = ReturnValue;
  989. TraceEntry->Caller = Caller;
  990. TraceEntry->LastError = LastError;
  991. TraceEntry->ApiTableIndex = ApiInfo->ApiTableIndex;
  992. TraceEntry->EnterTime = EnterTime;
  993. TraceEntry->Duration = Duration;
  994. ArgCount = (ApiInfo->ApiTable && ApiInfo->ApiTable->ArgCount) ?
  995. ApiInfo->ApiTable->ArgCount : DFLT_TRACE_ARGS;
  996. for (i=0; i<ArgCount; i++)
  997. TraceEntry->Args[i] = Arg[i];
  998. ThreadStack = (PTHREAD_STACK)pTlsGetValue(TlsStack);
  999. TraceEntry->ThreadNum = ThreadStack->ThreadNum;
  1000. TraceEntry->Level = (DWORD)((ThreadStack->Pointer - (DWORD_PTR)ThreadStack->Body))
  1001. / FRAME_SIZE - 1;
  1002. TraceEntryLen = sizeof(TRACE_ENTRY);
  1003. if (ApiInfo->ApiTable && ApiInfo->ApiTable->ArgCount) {
  1004. PAPI_TABLE ApiTable = ApiInfo->ApiTable;
  1005. TraceString = (LPSTR)TraceEntry + sizeof(TRACE_ENTRY);
  1006. TraceLimit = (LPSTR)TraceBuffer->Entry + TraceBuffer->Size;
  1007. for (i=0; i<ApiTable->ArgCount; i++) {
  1008. switch( LOWORD(ApiTable->ArgType[i]) ) {
  1009. case T_DWORD:
  1010. break;
  1011. case T_DWORDPTR:
  1012. if (TraceEntry->Args[i]) {
  1013. TraceEntry->Args[i] = *(DWORD*)(TraceEntry->Args[i] + HIWORD(ApiTable->ArgType[i]));
  1014. }
  1015. break;
  1016. case T_DLONGPTR:
  1017. // Warning - this type wipes out the following arg to save a DWORDLONG
  1018. if (TraceEntry->Args[i]) {
  1019. dwPtr = (DWORD*) (TraceEntry->Args[i] + HIWORD(ApiTable->ArgType[i]));
  1020. TraceEntry->Args[i] = dwPtr[0];
  1021. TraceEntry->Args[i+1] = dwPtr[1];
  1022. }
  1023. break;
  1024. case T_LPSTRC:
  1025. case T_LPSTR:
  1026. //
  1027. // go read the string
  1028. //
  1029. {
  1030. if (HIWORD(TraceEntry->Args[i]) == 0)
  1031. len = 0;
  1032. else if (ApiTable->ArgType[i] == T_LPSTRC)
  1033. len = TraceEntry->Args[i+1];
  1034. else {
  1035. TraceEntry->Args[i] += HIWORD(ApiTable->ArgType[i]);
  1036. len = strlen( (LPSTR) TraceEntry->Args[i] );
  1037. }
  1038. if ( TraceString + len >= TraceLimit )
  1039. len = 0;
  1040. if (len)
  1041. memcpy(TraceString, (LPSTR)TraceEntry->Args[i], len);
  1042. TraceString[len] = 0;
  1043. TraceString += Align(sizeof(WCHAR), (len + 1));
  1044. }
  1045. break;
  1046. case T_LPWSTRC:
  1047. case T_LPWSTR:
  1048. //
  1049. // go read the string
  1050. //
  1051. {
  1052. if (HIWORD(TraceEntry->Args[i]) == 0)
  1053. len = 0;
  1054. else if (ApiTable->ArgType[i] == T_LPSTRC)
  1055. len = TraceEntry->Args[i+1];
  1056. else {
  1057. TraceEntry->Args[i] += HIWORD(ApiTable->ArgType[i]);
  1058. len = (wcslen( (LPWSTR) TraceEntry->Args[i] ));
  1059. }
  1060. if ( TraceString + len * sizeof(WCHAR) >= TraceLimit )
  1061. len = 0;
  1062. if (len)
  1063. memcpy( (LPWSTR)TraceString, (LPWSTR) TraceEntry->Args[i], len * sizeof(WCHAR) );
  1064. ((LPWSTR)TraceString)[len] = 0;
  1065. TraceString += (len + 1) * sizeof(WCHAR);
  1066. }
  1067. break;
  1068. case T_UNISTR:
  1069. case T_OBJNAME:
  1070. //
  1071. // go read the string
  1072. //
  1073. {
  1074. PUNICODE_STRING pustr;
  1075. if (ApiTable->ArgType[i] == T_OBJNAME)
  1076. pustr = ((POBJECT_ATTRIBUTES)TraceEntry->Args[i])->ObjectName;
  1077. else
  1078. pustr = (PUNICODE_STRING)TraceEntry->Args[i];
  1079. len = pustr->Length + sizeof(WCHAR);
  1080. if (pustr != NULL && TraceString + len < TraceLimit) {
  1081. wcsncpy( (LPWSTR)TraceString, pustr->Buffer, pustr->Length/sizeof(WCHAR));
  1082. ((LPWSTR)TraceString)[pustr->Length/sizeof(WCHAR)] = 0;
  1083. }
  1084. else {
  1085. len = sizeof(WCHAR);
  1086. ((LPWSTR)TraceString)[0] = 0;
  1087. }
  1088. TraceString += len;
  1089. }
  1090. break;
  1091. }
  1092. }
  1093. // align overall entry length to DWORDLONG
  1094. TraceEntryLen = (DWORD)(Align(sizeof(DWORDLONG), TraceString - (LPSTR)TraceEntry));
  1095. }
  1096. TraceBuffer->Count += 1;
  1097. TraceEntry->SizeOfStruct = TraceEntryLen;
  1098. TraceBuffer->Offset += TraceEntryLen;
  1099. }
  1100. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  1101. ;
  1102. }
  1103. ReleaseMutex( ApiTraceMutex );
  1104. pTlsSetValue( TlsReEnter, (LPVOID) 0 );
  1105. }
  1106. VOID
  1107. CalibrateThunk(
  1108. VOID
  1109. )
  1110. {
  1111. int i;
  1112. DLL_INFO CalibDllInfo;
  1113. API_INFO Calib1ApiInfo,Calib2ApiInfo;
  1114. PUCHAR NewThunks;
  1115. ULONGLONG MinTime;
  1116. CHAR debugbuf[128];
  1117. // Setup calibration Dll
  1118. strcpy(CalibDllInfo.Name, "Calib");
  1119. CalibDllInfo.Enabled = TRUE;
  1120. // Setup calibration Api
  1121. Calib1ApiInfo.Count = 0;
  1122. Calib1ApiInfo.NestCount = 0;
  1123. Calib1ApiInfo.Time = 0;
  1124. Calib1ApiInfo.CalleeTime = 0;
  1125. Calib1ApiInfo.ThunkAddress = 0;
  1126. Calib1ApiInfo.TraceEnabled = 0;
  1127. Calib1ApiInfo.Address = (ULONG_PTR)Calib1Func;
  1128. Calib2ApiInfo.Count = 0;
  1129. Calib2ApiInfo.NestCount = 0;
  1130. Calib2ApiInfo.Time = 0;
  1131. Calib2ApiInfo.CalleeTime = 0;
  1132. Calib2ApiInfo.ThunkAddress = 0;
  1133. Calib2ApiInfo.TraceEnabled = 0;
  1134. Calib2ApiInfo.Address = (ULONG_PTR)Calib2Func;
  1135. // Create thunks
  1136. NewThunks = CreateMachApiThunk( (PULONG_PTR)&Calib1Thunk, Thunks, &CalibDllInfo, &Calib1ApiInfo );
  1137. NewThunks = CreateMachApiThunk( (PULONG_PTR)&Calib2Thunk, NewThunks, &CalibDllInfo, &Calib2ApiInfo );
  1138. FlushInstructionCache( GetCurrentProcess(), (PVOID)Thunks, (DWORD)(NewThunks - Thunks));
  1139. Thunks = NewThunks;
  1140. ThunkOverhead = 0;
  1141. ThunkCallOverhead = 0;
  1142. // Call the calibration function via the thunk
  1143. MinTime = 1000000;
  1144. for (i=0; i<1000; i++) {
  1145. Calib1ApiInfo.Time = 0;
  1146. (*Calib1Thunk)();
  1147. if (Calib1ApiInfo.Time < MinTime)
  1148. MinTime = Calib1ApiInfo.Time;
  1149. }
  1150. // Take min time as the overhead
  1151. ThunkOverhead = (DWORD)MinTime;
  1152. MinTime = 1000000;
  1153. for (i=0; i<1000; i++) {
  1154. Calib2ApiInfo.Time = 0;
  1155. (*Calib2Thunk)();
  1156. if (Calib2ApiInfo.Time < MinTime)
  1157. MinTime = Calib1ApiInfo.Time;
  1158. }
  1159. ThunkCallOverhead = (DWORD)MinTime;
  1160. }
  1161. // Null function for measuring overhead
  1162. VOID
  1163. Calib1Func(
  1164. VOID
  1165. )
  1166. {
  1167. return;
  1168. }
  1169. // Calling function for measuring overhead
  1170. VOID
  1171. Calib2Func(
  1172. VOID
  1173. )
  1174. {
  1175. (*Calib1Thunk)();
  1176. }