1 /* $Id: ncbi_win_hook.cpp 617976 2020-10-08 18:28:58Z grichenk $ 2 * =========================================================================== 3 * 4 * PUBLIC DOMAIN NOTICE 5 * National Center for Biotechnology Information 6 * 7 * This software/database is a "United States Government Work" under the 8 * terms of the United States Copyright Act. It was written as part of 9 * the author's official duties as a United States Government employee and 10 * thus cannot be copyrighted. This software/database is freely available 11 * to the public for use. The National Library of Medicine and the U.S. 12 * Government have not placed any restriction on its use or reproduction. 13 * 14 * Although all reasonable efforts have been taken to ensure the accuracy 15 * and reliability of the software and data, the NLM and the U.S. 16 * Government do not and cannot warrant the performance or results that 17 * may be obtained by using this software or data. The NLM and the U.S. 18 * Government disclaim all warranties, express or implied, including 19 * warranties of performance, merchantability or fitness for any particular 20 * purpose. 21 * 22 * Please cite the author in any work or product based on this material. 23 * 24 * =========================================================================== 25 * 26 * Author: Sergey Sikorskiy 27 * 28 * File Description: Windows DLL function hooking 29 * 30 */ 31 32 #include <ncbi_pch.hpp> 33 #include <dbapi/error_codes.hpp> 34 #include <corelib/ncbiapp.hpp> 35 36 #if defined(NCBI_OS_MSWIN) 37 38 #include "ncbi_win_hook.hpp" 39 #include <algorithm> 40 41 #include <WinVer.h> 42 #include <dbghelp.h> 43 44 #pragma comment(lib, "DbgHelp.lib") 45 46 #pragma warning( push ) 47 #pragma warning( disable : 4191 ) // unsafe conversion from ... 48 49 #define NCBI_USE_ERRCODE_X Dbapi_DrvrWinHook 50 51 BEGIN_NCBI_SCOPE 52 53 // Microsoft does not define MODULEENTRY32A and PROCESSENTRY32A 54 // so, we define here our own mapping to ANSI variants 55 56 typedef struct tagMODULEENTRY32 MODULEENTRY32_A; 57 typedef MODULEENTRY32_A * PMODULEENTRY32_A; 58 typedef MODULEENTRY32_A * LPMODULEENTRY32_A; 59 60 typedef struct tagPROCESSENTRY32 PROCESSENTRY32_A; 61 typedef PROCESSENTRY32_A * PPROCESSENTRY32_A; 62 typedef PROCESSENTRY32_A * LPPROCESSENTRY32_A; 63 64 namespace NWinHook 65 { 66 67 //////////////////////////////////////////////////////////////////////////// 68 /// 69 70 class CModuleInstance 71 { 72 public: 73 CModuleInstance(char *pszName, HMODULE hModule); 74 ~CModuleInstance(void); 75 76 void AddModule(CModuleInstance* pModuleInstance); 77 void ReleaseModules(void); 78 79 /// Returns Full path and filename of the executable file for the process or DLL 80 char* GetName(void) const; 81 /// Sets Full path and filename of the executable file for the process or DLL 82 void SetName(char *pszName); 83 /// Returns module handle 84 HMODULE GetHandle(void) const; 85 void SetHandle(HMODULE handle); 86 /// Returns only the filename of the executable file for the process or DLL 87 char* GetBaseName(void) const; 88 89 private: 90 char* m_pszName; 91 HMODULE m_ModuleHandle; 92 93 protected: 94 typedef vector<CModuleInstance*> TInternalList; 95 96 TInternalList m_pInternalList; 97 }; 98 99 //////////////////////////////////////////////////////////////////////////// 100 /// 101 class CExeModuleInstance; 102 103 class CLibHandler 104 { 105 public: 106 CLibHandler(void); 107 virtual ~CLibHandler(void); 108 109 virtual BOOL PopulateModules(CModuleInstance* pProcess) = 0; 110 virtual BOOL PopulateProcess(DWORD dwProcessId, BOOL bPopulateModules) = 0; GetExeModuleInstance(void) const111 CExeModuleInstance* GetExeModuleInstance(void) const { 112 return m_pProcess.get(); 113 } 114 115 protected: 116 unique_ptr<CExeModuleInstance> m_pProcess; 117 }; 118 119 //////////////////////////////////////////////////////////////////////////// 120 typedef BOOL (WINAPI * FEnumProcesses)(DWORD * lpidProcess, 121 DWORD cb, 122 DWORD * cbNeeded 123 ); 124 125 typedef BOOL (WINAPI * FEnumProcessModules)(HANDLE hProcess, 126 HMODULE *lphModule, 127 DWORD cb, 128 LPDWORD lpcbNeeded 129 ); 130 131 typedef DWORD (WINAPI * FGetModuleFileNameExA)(HANDLE hProcess, 132 HMODULE hModule, 133 LPSTR lpFilename, 134 DWORD nSize 135 ); 136 137 138 139 //////////////////////////////////////////////////////////////////////////// 140 /// class CPsapiHandler 141 /// 142 class CPsapiHandler : public CLibHandler 143 { 144 public: 145 CPsapiHandler(void); 146 virtual ~CPsapiHandler(void); 147 148 BOOL Initialize(void); 149 void Finalize(void); 150 virtual BOOL PopulateModules(CModuleInstance* pProcess); 151 virtual BOOL PopulateProcess(DWORD dwProcessId, BOOL bPopulateModules); 152 153 private: 154 HMODULE m_hModPSAPI; 155 FEnumProcesses m_pfnEnumProcesses; 156 FEnumProcessModules m_pfnEnumProcessModules; 157 FGetModuleFileNameExA m_pfnGetModuleFileNameExA; 158 }; 159 160 161 //////////////////////////////////////////////////////////////////////////// 162 // typedefs for ToolHelp32 functions 163 // 164 typedef HANDLE (WINAPI * FCreateToolHelp32Snapshot) (DWORD dwFlags, 165 DWORD th32ProcessID 166 ); 167 168 typedef BOOL (WINAPI * FProcess32First) (HANDLE hSnapshot, 169 LPPROCESSENTRY32_A lppe 170 ); 171 172 typedef BOOL (WINAPI * FProcess32Next) (HANDLE hSnapshot, 173 LPPROCESSENTRY32_A lppe 174 ); 175 176 typedef BOOL (WINAPI * FModule32First) (HANDLE hSnapshot, 177 LPMODULEENTRY32_A lpme 178 ); 179 180 typedef BOOL (WINAPI * FModule32Next) (HANDLE hSnapshot, 181 LPMODULEENTRY32_A lpme 182 ); 183 184 185 186 //////////////////////////////////////////////////////////////////////////// 187 /// class CToolhelpHandler 188 /// 189 class CToolhelpHandler : public CLibHandler 190 { 191 public: 192 CToolhelpHandler(void); 193 virtual ~CToolhelpHandler(void); 194 195 BOOL Initialize(void); 196 virtual BOOL PopulateModules(CModuleInstance* pProcess); 197 virtual BOOL PopulateProcess(DWORD dwProcessId, BOOL bPopulateModules); 198 199 private: 200 BOOL ModuleFirst(HANDLE hSnapshot, PMODULEENTRY32_A pme) const; 201 BOOL ModuleNext(HANDLE hSnapshot, PMODULEENTRY32_A pme) const; 202 BOOL ProcessFirst(HANDLE hSnapshot, PROCESSENTRY32_A* pe32) const; 203 BOOL ProcessNext(HANDLE hSnapshot, PROCESSENTRY32_A* pe32) const; 204 205 // ToolHelp function pointers 206 FCreateToolHelp32Snapshot m_pfnCreateToolhelp32Snapshot; 207 FProcess32First m_pfnProcess32First; 208 FProcess32Next m_pfnProcess32Next; 209 FModule32First m_pfnModule32First; 210 FModule32Next m_pfnModule32Next; 211 }; 212 213 214 //////////////////////////////////////////////////////////////////////////// 215 /// The taskManager dynamically decides whether to use ToolHelp 216 /// library or PSAPI 217 /// This is a proxy class to redirect calls to a handler ... 218 /// 219 class CTaskManager 220 { 221 public: 222 CTaskManager(void); 223 ~CTaskManager(void); 224 225 BOOL PopulateProcess(DWORD dwProcessId, BOOL bPopulateModules) const; 226 CExeModuleInstance* GetProcess(void) const; 227 228 private: 229 CLibHandler *m_pLibHandler; 230 }; 231 232 233 //////////////////////////////////////////////////////////////////////////// 234 /// class CHookedFunction 235 /// 236 class CHookedFunction : public CObject 237 { 238 public: 239 CHookedFunction(PCSTR pszCalleeModName, 240 PCSTR pszFuncName, 241 PROC pfnOrig, 242 PROC pfnHook 243 ); 244 ~CHookedFunction(void); 245 246 public: 247 HMODULE GetCalleeModHandle(void) const; 248 PCSTR GetCalleeModName(void) const; 249 PCSTR GetFuncName(void) const; 250 PROC GetPfnHook(void) const; 251 PROC GetPfnOrig(void) const; 252 /// Set up a new hook function 253 BOOL HookImport(void); 254 /// Restore the original API handler 255 BOOL UnHookImport(void); 256 /// Replace the address of the function in the IAT of a specific module 257 BOOL ReplaceInOneModule(bool bHookOrRestore, 258 PCSTR pszCalleeModName, 259 PROC pfnCurrent, 260 PROC pfnNew, 261 HMODULE hmodCaller 262 ); 263 /// Indicates whether the hooked function is mandatory one 264 BOOL IsMandatory(void); 265 266 private: 267 typedef set<HMODULE> TModuleSet; 268 269 BOOL m_bHooked; 270 HMODULE m_CalleeModHandle; 271 char m_szCalleeModName[MAX_PATH]; 272 char m_szFuncName[MAX_PATH]; 273 PROC m_pfnOrig; 274 PROC m_pfnHook; 275 /// Maximum private memory address 276 static PVOID sm_pvMaxAppAddr; 277 /// Set of hoocked modules 278 TModuleSet m_HookedModuleSet; 279 280 /// Perform actual replacing of function pointers 281 BOOL DoHook(bool bHookOrRestore, 282 PROC pfnCurrent, 283 PROC pfnNew 284 ); 285 286 /// Replace the address of a imported function entry in all modules 287 BOOL ReplaceInAllModules(bool bHookOrRestore, 288 PCSTR pszCalleeModName, 289 PROC pfnCurrent, 290 PROC pfnNew 291 ); 292 }; 293 294 295 /////////////////////////////////////////////////////////////////////////// 296 typedef HMODULE (WINAPI *FLoadLibraryA)(LPCSTR lpLibFileName); 297 typedef HMODULE (WINAPI *FLoadLibraryW)(LPCWSTR lpLibFileName); 298 typedef HMODULE (WINAPI *FLoadLibraryExA)(LPCSTR lpLibFileName, 299 HANDLE hFile, 300 DWORD dwFlags 301 ); 302 typedef HMODULE (WINAPI *FLoadLibraryExW)(LPCWSTR lpLibFileName, 303 HANDLE hFile, 304 DWORD dwFlags 305 ); 306 typedef FARPROC (WINAPI *FGetProcAddress)(HMODULE hModule, 307 LPCSTR lpProcName 308 ); 309 typedef VOID (WINAPI *FExitProcess)(UINT uExitCode); 310 311 //////////////////////////////////////////////////////////////////////////// 312 // Version of the function, which was found at initialization time ... 313 static FGetProcAddress g_FGetProcAddress = reinterpret_cast<FGetProcAddress> 314 (::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "GetProcAddress")); 315 // Version of the function, which was found at initialization time ... 316 static FLoadLibraryA g_LoadLibraryA = reinterpret_cast<FLoadLibraryA> 317 (::GetProcAddress(::GetModuleHandleA("kernel32.dll"), "LoadLibraryA")); 318 319 //////////////////////////////////////////////////////////////////////////// 320 class CKernell32 321 { 322 public: 323 CKernell32(); 324 ~CKernell32(); 325 326 public: 327 // Version of the function developed by Microsoft ... 328 static HMODULE WINAPI LoadLibraryA(LPCSTR lpLibFileName); 329 // Version of the function developed by Microsoft ... 330 static HMODULE WINAPI LoadLibraryW(LPCWSTR lpLibFileName); 331 // Version of the function developed by Microsoft ... 332 static HMODULE WINAPI LoadLibraryExA(LPCSTR lpLibFileName, 333 HANDLE hFile, 334 DWORD dwFlags 335 ); 336 // Version of the function developed by Microsoft ... 337 static HMODULE WINAPI LoadLibraryExW(LPCWSTR lpLibFileName, 338 HANDLE hFile, 339 DWORD dwFlags 340 ); 341 // Version of the function developed by Microsoft ... 342 static FARPROC WINAPI GetProcAddress(HMODULE hModule, 343 LPCSTR lpProcName 344 ); 345 // Version of the function, which was found at initialization time ... 346 static VOID WINAPI ExitProcess(UINT uExitCode); 347 348 private: 349 bool IsPatched(const void* addr); 350 DWORD GetRVAFromExportSection( 351 HMODULE hmodOriginal, 352 PSTR pszFuncName 353 ); 354 355 private: 356 HMODULE m_ModuleKenell32; 357 PIMAGE_NT_HEADERS m_nt_header; 358 unsigned long long m_ImageStart; 359 unsigned long long m_ImageEnd; 360 static FLoadLibraryA sm_FLoadLibraryA; 361 static FLoadLibraryW sm_FLoadLibraryW; 362 static FLoadLibraryExA sm_FLoadLibraryExA; 363 static FLoadLibraryExW sm_FLoadLibraryExW; 364 static FGetProcAddress sm_FGetProcAddress; 365 static FExitProcess sm_FExitProcess; 366 }; 367 368 FLoadLibraryA CKernell32::sm_FLoadLibraryA = NULL; 369 FLoadLibraryW CKernell32::sm_FLoadLibraryW = NULL; 370 FLoadLibraryExA CKernell32::sm_FLoadLibraryExA = NULL; 371 FLoadLibraryExW CKernell32::sm_FLoadLibraryExW = NULL; 372 FGetProcAddress CKernell32::sm_FGetProcAddress = NULL; 373 FExitProcess CKernell32::sm_FExitProcess = NULL; 374 CKernell32()375 CKernell32::CKernell32() 376 { 377 m_ModuleKenell32 = ::GetModuleHandleA("kernel32.dll"); 378 379 // Retrieve original procedure address ... 380 // 381 sm_FGetProcAddress = reinterpret_cast<FGetProcAddress> 382 (::GetProcAddress(m_ModuleKenell32, "GetProcAddress")); 383 sm_FLoadLibraryA = reinterpret_cast<FLoadLibraryA> 384 (::GetProcAddress(m_ModuleKenell32, "LoadLibraryA")); 385 sm_FLoadLibraryW = reinterpret_cast<FLoadLibraryW> 386 (::GetProcAddress(m_ModuleKenell32, "LoadLibraryW")); 387 sm_FLoadLibraryExA = reinterpret_cast<FLoadLibraryExA> 388 (::GetProcAddress(m_ModuleKenell32, "LoadLibraryExA")); 389 sm_FLoadLibraryExW = reinterpret_cast<FLoadLibraryExW> 390 (::GetProcAddress(m_ModuleKenell32, "LoadLibraryExW")); 391 sm_FExitProcess = reinterpret_cast<FExitProcess> 392 (::GetProcAddress(m_ModuleKenell32, "ExitProcess")); 393 394 m_nt_header = ::ImageNtHeader(m_ModuleKenell32); 395 396 if (!m_nt_header) { 397 return; 398 } 399 400 m_ImageStart = m_nt_header->OptionalHeader.ImageBase; 401 m_ImageEnd = m_ImageStart + m_nt_header->OptionalHeader.SizeOfImage; 402 403 unsigned long long mod_addr = (uintptr_t)m_ModuleKenell32; 404 405 // There is a trick below ... 406 // If the import section is already patched, we can get procedure address 407 // from the export section ... 408 // 409 sm_FGetProcAddress = IsPatched(sm_FGetProcAddress) ? 410 reinterpret_cast<FGetProcAddress>( 411 GetRVAFromExportSection(m_ModuleKenell32, "GetProcAddress") + 412 mod_addr 413 ) : 414 sm_FGetProcAddress; 415 sm_FLoadLibraryA = IsPatched(sm_FLoadLibraryA) ? 416 reinterpret_cast<FLoadLibraryA>( 417 GetRVAFromExportSection(m_ModuleKenell32, "LoadLibraryA") + 418 mod_addr 419 ) : 420 sm_FLoadLibraryA; 421 sm_FLoadLibraryW = IsPatched(sm_FLoadLibraryW) ? 422 reinterpret_cast<FLoadLibraryW>( 423 GetRVAFromExportSection(m_ModuleKenell32, "LoadLibraryW") + 424 mod_addr 425 ) : 426 sm_FLoadLibraryW; 427 sm_FLoadLibraryExA = IsPatched(sm_FLoadLibraryExA) ? 428 reinterpret_cast<FLoadLibraryExA>( 429 GetRVAFromExportSection(m_ModuleKenell32, "LoadLibraryExA") + 430 mod_addr 431 ) : 432 sm_FLoadLibraryExA; 433 sm_FLoadLibraryExW = IsPatched(sm_FLoadLibraryExW) ? 434 reinterpret_cast<FLoadLibraryExW>( 435 GetRVAFromExportSection(m_ModuleKenell32, "LoadLibraryExW") + 436 mod_addr 437 ) : 438 sm_FLoadLibraryExW; 439 // sm_FExitProcess = IsPatched(sm_FExitProcess) ? 440 // reinterpret_cast<FExitProcess>( 441 // GetRVAFromExportSection(m_ModuleKenell32, "ExitProcess") + 442 // mod_addr 443 // ) : 444 // sm_FExitProcess; 445 } 446 ~CKernell32()447 CKernell32::~CKernell32() 448 { 449 } 450 IsPatched(const void * addr)451 bool CKernell32::IsPatched(const void* addr) 452 { 453 if ((unsigned long long)addr >= m_ImageStart && 454 (unsigned long long)addr < m_ImageEnd) { 455 return false; 456 } 457 458 return true; 459 } 460 LoadLibraryA(LPCSTR lpLibFileName)461 HMODULE WINAPI CKernell32::LoadLibraryA(LPCSTR lpLibFileName) 462 { 463 return sm_FLoadLibraryA(lpLibFileName); 464 } 465 LoadLibraryW(LPCWSTR lpLibFileName)466 HMODULE WINAPI CKernell32::LoadLibraryW(LPCWSTR lpLibFileName) 467 { 468 return sm_FLoadLibraryW(lpLibFileName); 469 } 470 LoadLibraryExA(LPCSTR lpLibFileName,HANDLE hFile,DWORD dwFlags)471 HMODULE WINAPI CKernell32::LoadLibraryExA(LPCSTR lpLibFileName, 472 HANDLE hFile, 473 DWORD dwFlags 474 ) 475 { 476 return sm_FLoadLibraryExA(lpLibFileName, hFile, dwFlags); 477 } 478 LoadLibraryExW(LPCWSTR lpLibFileName,HANDLE hFile,DWORD dwFlags)479 HMODULE WINAPI CKernell32::LoadLibraryExW(LPCWSTR lpLibFileName, 480 HANDLE hFile, 481 DWORD dwFlags 482 ) 483 { 484 return sm_FLoadLibraryExW(lpLibFileName, hFile, dwFlags); 485 } 486 GetProcAddress(HMODULE hModule,LPCSTR lpProcName)487 FARPROC WINAPI CKernell32::GetProcAddress(HMODULE hModule, 488 LPCSTR lpProcName 489 ) 490 { 491 return sm_FGetProcAddress(hModule, lpProcName); 492 } 493 ExitProcess(UINT uExitCode)494 VOID WINAPI CKernell32::ExitProcess(UINT uExitCode) 495 { 496 sm_FExitProcess(uExitCode); 497 } 498 499 500 CKernell32 g_Kernell32; 501 502 //////////////////////////////////////////////////////////////////////////// 503 const char* GetErrCodeString(void) const504 CWinHookException::GetErrCodeString(void) const 505 { 506 switch (GetErrCode()) { 507 case eDbghelp: return "eDbghelp"; 508 case eDisabled: return "eDisabled"; 509 default: return CException::GetErrCodeString(); 510 } 511 } 512 513 //////////////////////////////////////////////////////////////////////////// 514 /// class CPEi386 515 /// 516 class CPEi386 { 517 private: 518 CPEi386(void); 519 ~CPEi386(void) throw(); 520 521 public: 522 static CPEi386& GetInstance(void); 523 524 PVOID GetIAT(HMODULE base, int section) const; 525 526 private: 527 typedef PVOID (WINAPI *FImageDirectoryEntryToData) ( 528 PVOID Base, 529 BOOLEAN MappedAsImage, 530 USHORT DirectoryEntry, 531 PULONG Size 532 ); 533 534 HMODULE m_ModDbghelp; 535 FImageDirectoryEntryToData m_ImageDirectoryEntryToData; 536 537 friend class CSafeStatic_Allocator<CPEi386>; 538 }; 539 540 //////////////////////////////////////////////////////////////////////////// 541 /// class CExeModuleInstance 542 /// 543 /// Represents exactly one loaded EXE module 544 /// 545 class CExeModuleInstance : public CModuleInstance { 546 public: 547 CExeModuleInstance(CLibHandler* pLibHandler, 548 char* pszName, 549 HMODULE hModule, 550 DWORD dwProcessId 551 ); 552 ~CExeModuleInstance(void); 553 554 /// Returns process id 555 DWORD GetProcessId(void) const; 556 BOOL PopulateModules(void); 557 size_t GetModuleCount(void) const; 558 CModuleInstance* GetModuleByIndex(size_t dwIndex) const; 559 560 private: 561 DWORD m_dwProcessId; 562 CLibHandler* m_pLibHandler; 563 }; 564 565 //////////////////////////////////////////////////////////////////////////// IsToolHelpSupported(void)566 static BOOL IsToolHelpSupported(void) 567 { 568 BOOL bResult(FALSE); 569 HMODULE hModToolHelp; 570 PROC pfnCreateToolhelp32Snapshot; 571 572 hModToolHelp = g_LoadLibraryA("KERNEL32.DLL"); 573 574 if (hModToolHelp != NULL) { 575 pfnCreateToolhelp32Snapshot = ::GetProcAddress( 576 hModToolHelp, 577 "CreateToolhelp32Snapshot" 578 ); 579 580 bResult = (pfnCreateToolhelp32Snapshot != NULL); 581 582 ::FreeLibrary(hModToolHelp); 583 } 584 585 return bResult; 586 } 587 588 IsPsapiSupported(void)589 static BOOL IsPsapiSupported(void) 590 { 591 BOOL bResult = FALSE; 592 HMODULE hModPSAPI = NULL; 593 594 hModPSAPI = g_LoadLibraryA("PSAPI.DLL"); 595 bResult = (hModPSAPI != NULL); 596 if (bResult) { 597 ::FreeLibrary(hModPSAPI); 598 } 599 600 return bResult; 601 } 602 ModuleFromAddress(PVOID pv)603 static HMODULE ModuleFromAddress(PVOID pv) 604 { 605 MEMORY_BASIC_INFORMATION mbi; 606 607 return ((::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0) 608 ? (HMODULE) mbi.AllocationBase : NULL); 609 } 610 611 612 //////////////////////////////////////////////////////////////////////////// GetRVAFromExportSection(HMODULE hmodOriginal,PSTR pszFuncName)613 DWORD CKernell32::GetRVAFromExportSection( 614 HMODULE hmodOriginal, 615 PSTR pszFuncName 616 ) 617 { 618 DWORD rva = 0; 619 620 // Get the address of the module's export section 621 PIMAGE_EXPORT_DIRECTORY pExportDir = 622 static_cast<PIMAGE_EXPORT_DIRECTORY> 623 (CPEi386::GetInstance().GetIAT(hmodOriginal, 624 IMAGE_DIRECTORY_ENTRY_EXPORT)); 625 626 // Does this module has export section ? 627 if (pExportDir == NULL) { 628 return rva; 629 } 630 631 // Get the name of the DLL 632 PSTR pszDllName = reinterpret_cast<PSTR>( 633 static_cast<uintptr_t>(pExportDir->Name) + 634 reinterpret_cast<uintptr_t>(hmodOriginal) 635 ); 636 // Get the starting ordinal value. By default is 1, but 637 // is not required to be so 638 DWORD dwFuncNumber = pExportDir->Base; 639 // The number of entries in the EAT 640 size_t dwNumberOfExported = pExportDir->NumberOfFunctions; 641 // Get the address of the ENT 642 PDWORD pdwFunctions = 643 reinterpret_cast<PDWORD>( 644 static_cast<uintptr_t>(pExportDir->AddressOfFunctions) + 645 reinterpret_cast<uintptr_t>(hmodOriginal)); 646 647 // Get the export ordinal table 648 PWORD pwOrdinals = 649 reinterpret_cast<PWORD>( 650 static_cast<uintptr_t>(pExportDir->AddressOfNameOrdinals) + 651 reinterpret_cast<uintptr_t>(hmodOriginal)); 652 653 // Get the address of the array with all names 654 PDWORD pszFuncNames = 655 reinterpret_cast<PDWORD>( 656 static_cast<uintptr_t>(pExportDir->AddressOfNames) + 657 reinterpret_cast<uintptr_t>(hmodOriginal)); 658 659 PSTR pszExpFunName; 660 661 // Walk through all of the entries and try to locate the 662 // one we are looking for 663 for (size_t i = 0; i < dwNumberOfExported; ++i, ++pdwFunctions) { 664 DWORD entryPointRVA = *pdwFunctions; 665 if (entryPointRVA == 0) { 666 // Skip over gaps in exported function 667 // ordinals (the entrypoint is 0 for 668 // these functions). 669 continue; 670 } 671 672 // See if this function has an associated name exported for it. 673 for (unsigned j = 0; j < pExportDir->NumberOfNames; ++j) { 674 // Note that pwOrdinals[x] return values starting form 0.. (not from 1) 675 if (pwOrdinals[j] == i) { 676 pszExpFunName = reinterpret_cast<PSTR>( 677 static_cast<uintptr_t>(pszFuncNames[j]) + 678 reinterpret_cast<uintptr_t>(hmodOriginal)); 679 // Is this the same ordinal value ? 680 // Notice that we need to add 1 to pwOrdinals[j] to get actual 681 // number 682 if ((pszExpFunName != NULL) && 683 (strcmp(pszExpFunName, pszFuncName) == 0) 684 ) { 685 rva = entryPointRVA; 686 return rva; 687 } 688 } 689 } 690 } 691 692 // This function is not in the caller's import section 693 return rva; 694 } 695 696 697 //////////////////////////////////////////////////////////////////////////// 698 typedef struct { 699 char szCalleeModName[MAX_PATH]; 700 char szFuncName[MAX_PATH]; 701 } API_FUNC_ID; 702 703 const API_FUNC_ID MANDATORY_API_FUNCS[] = 704 { 705 {"Kernel32.dll", "LoadLibraryA"}, 706 {"Kernel32.dll", "LoadLibraryW"}, 707 {"Kernel32.dll", "LoadLibraryExA"}, 708 {"Kernel32.dll", "LoadLibraryExW"}, 709 {"Kernel32.dll", "GetProcAddress"} 710 }; 711 712 // This macro evaluates to the number of elements in MANDATORY_API_FUNCS 713 #define NUMBER_OF_MANDATORY_API_FUNCS (sizeof(MANDATORY_API_FUNCS) / \ 714 sizeof(MANDATORY_API_FUNCS[0])) 715 716 //////////////////////////////////////////////////////////////////////////// CLibHandler(void)717 CLibHandler::CLibHandler(void) 718 { 719 } 720 ~CLibHandler(void)721 CLibHandler::~CLibHandler(void) 722 { 723 } 724 725 //////////////////////////////////////////////////////////////////////////// CModuleInstance(char * pszName,HMODULE hModule)726 CModuleInstance::CModuleInstance(char *pszName, HMODULE hModule): 727 m_pszName(NULL), 728 m_ModuleHandle(hModule) 729 { 730 SetName(pszName); 731 } 732 ~CModuleInstance(void)733 CModuleInstance::~CModuleInstance(void) 734 { 735 try { 736 ReleaseModules(); 737 738 delete [] m_pszName; 739 } 740 NCBI_CATCH_ALL_X( 5, NCBI_CURRENT_FUNCTION ) 741 } 742 743 AddModule(CModuleInstance * pModuleInstance)744 void CModuleInstance::AddModule(CModuleInstance* pModuleInstance) 745 { 746 m_pInternalList.push_back(pModuleInstance); 747 } 748 ReleaseModules(void)749 void CModuleInstance::ReleaseModules(void) 750 { 751 ITERATE(TInternalList, it, m_pInternalList) { 752 delete *it; 753 } 754 m_pInternalList.clear(); 755 } 756 GetName(void) const757 char* CModuleInstance::GetName(void) const 758 { 759 return (m_pszName); 760 } 761 GetBaseName(void) const762 char* CModuleInstance::GetBaseName(void) const 763 { 764 char *pdest; 765 int ch = '\\'; 766 // Search backward 767 pdest = strrchr(m_pszName, ch); 768 if (pdest != NULL) { 769 return (&pdest[1]); 770 } 771 else { 772 return (m_pszName); 773 } 774 } 775 SetName(char * pszName)776 void CModuleInstance::SetName(char *pszName) 777 { 778 delete [] m_pszName; 779 780 if ((pszName != NULL) && (strlen(pszName))) { 781 m_pszName = new char[strlen(pszName) + 1]; 782 strcpy(m_pszName, pszName); 783 } 784 else { 785 m_pszName = new char[strlen("\0") + 1]; 786 strcpy(m_pszName, "\0"); 787 } 788 789 } 790 GetHandle(void) const791 HMODULE CModuleInstance::GetHandle(void) const 792 { 793 return m_ModuleHandle; 794 } 795 SetHandle(HMODULE handle)796 void CModuleInstance::SetHandle(HMODULE handle) 797 { 798 m_ModuleHandle = handle; 799 } 800 801 802 //////////////////////////////////////////////////////////////////////////// CExeModuleInstance(CLibHandler * pLibHandler,char * pszName,HMODULE hModule,DWORD dwProcessId)803 CExeModuleInstance::CExeModuleInstance(CLibHandler* pLibHandler, 804 char* pszName, 805 HMODULE hModule, 806 DWORD dwProcessId 807 ): 808 CModuleInstance(pszName, hModule), 809 m_pLibHandler(pLibHandler), 810 m_dwProcessId(dwProcessId) 811 { 812 813 } 814 ~CExeModuleInstance(void)815 CExeModuleInstance::~CExeModuleInstance(void) 816 { 817 818 } 819 GetProcessId(void) const820 DWORD CExeModuleInstance::GetProcessId(void) const 821 { 822 return (m_dwProcessId); 823 } 824 PopulateModules(void)825 BOOL CExeModuleInstance::PopulateModules(void) 826 { 827 _ASSERT(m_pLibHandler); 828 return (m_pLibHandler->PopulateModules(this)); 829 } 830 831 GetModuleCount(void) const832 size_t CExeModuleInstance::GetModuleCount(void) const 833 { 834 return (m_pInternalList.size()); 835 } 836 GetModuleByIndex(size_t dwIndex) const837 CModuleInstance* CExeModuleInstance::GetModuleByIndex(size_t dwIndex) const 838 { 839 if (m_pInternalList.size() <= dwIndex) { 840 return (NULL); 841 } 842 843 return (m_pInternalList[dwIndex]); 844 } 845 846 //////////////////////////////////////////////////////////////////////////// CPsapiHandler(void)847 CPsapiHandler::CPsapiHandler(void): 848 m_hModPSAPI(NULL), 849 m_pfnEnumProcesses(NULL), 850 m_pfnEnumProcessModules(NULL), 851 m_pfnGetModuleFileNameExA(NULL) 852 { 853 854 } 855 ~CPsapiHandler(void)856 CPsapiHandler::~CPsapiHandler(void) 857 { 858 try { 859 Finalize(); 860 } 861 NCBI_CATCH_ALL_X( 6, NCBI_CURRENT_FUNCTION ) 862 } 863 Initialize(void)864 BOOL CPsapiHandler::Initialize(void) 865 { 866 BOOL bResult = FALSE; 867 // 868 // Get to the 3 functions in PSAPI.DLL dynamically. We can't 869 // be sure that PSAPI.DLL has been installed 870 // 871 if (m_hModPSAPI == NULL) { 872 m_hModPSAPI = g_LoadLibraryA("PSAPI.DLL"); 873 } 874 875 if (m_hModPSAPI != NULL) { 876 // ::GetProcAddress cannot be used here !!! 877 m_pfnEnumProcesses = 878 (FEnumProcesses) 879 g_FGetProcAddress(m_hModPSAPI,"EnumProcesses"); 880 881 // ::GetProcAddress cannot be used here !!! 882 m_pfnEnumProcessModules = 883 (FEnumProcessModules) 884 g_FGetProcAddress(m_hModPSAPI, "EnumProcessModules"); 885 886 // ::GetProcAddress cannot be used here !!! 887 m_pfnGetModuleFileNameExA = 888 (FGetModuleFileNameExA) 889 g_FGetProcAddress(m_hModPSAPI, "GetModuleFileNameExA"); 890 891 } 892 893 bResult 894 = m_pfnEnumProcesses 895 && m_pfnEnumProcessModules 896 && m_pfnGetModuleFileNameExA; 897 898 return bResult; 899 } 900 Finalize(void)901 void CPsapiHandler::Finalize(void) 902 { 903 if (m_hModPSAPI != NULL) { 904 ::FreeLibrary(m_hModPSAPI); 905 } 906 } 907 PopulateModules(CModuleInstance * pProcess)908 BOOL CPsapiHandler::PopulateModules(CModuleInstance* pProcess) 909 { 910 BOOL bResult = TRUE; 911 CModuleInstance *pDllModuleInstance = NULL; 912 913 if (Initialize() == TRUE) { 914 DWORD pidArray[1024]; 915 DWORD cbNeeded; 916 DWORD nProcesses; 917 // EnumProcesses returns an array with process IDs 918 if (m_pfnEnumProcesses(pidArray, sizeof(pidArray), &cbNeeded)) { 919 // Determine number of processes 920 nProcesses = cbNeeded / sizeof(DWORD); 921 // Release the container 922 pProcess->ReleaseModules(); 923 924 for (DWORD i = 0; i < nProcesses; i++) { 925 HMODULE hModuleArray[1024]; 926 HANDLE hProcess; 927 DWORD pid = pidArray[i]; 928 DWORD nModules; 929 // Let's open the process 930 hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | 931 PROCESS_VM_READ, 932 FALSE, pid); 933 934 if (!hProcess) { 935 continue; 936 } 937 938 if (static_cast<CExeModuleInstance*>(pProcess)->GetProcessId() != pid) { 939 ::CloseHandle(hProcess); 940 continue; 941 } 942 943 // EnumProcessModules function retrieves a handle for 944 // each module in the specified process. 945 if (!m_pfnEnumProcessModules(hProcess, 946 hModuleArray, 947 sizeof(hModuleArray), 948 &cbNeeded)) { 949 ::CloseHandle(hProcess); 950 continue; 951 } 952 953 // Calculate number of modules in the process 954 nModules = cbNeeded / sizeof(hModuleArray[0]); 955 956 for (DWORD j = 0; j < nModules; j++) { 957 HMODULE hModule = hModuleArray[j]; 958 char szModuleName[MAX_PATH]; 959 960 m_pfnGetModuleFileNameExA(hProcess, 961 hModule, 962 szModuleName, 963 sizeof(szModuleName) 964 ); 965 966 if (0 == j) { // First module is the EXE. 967 // Do nothing. 968 } 969 else { // Not the first module. It's a DLL 970 pDllModuleInstance = 971 new CModuleInstance(szModuleName, 972 hModule 973 ); 974 pProcess->AddModule(pDllModuleInstance); 975 } 976 } 977 ::CloseHandle(hProcess); // We're done with this process handle 978 } 979 bResult = TRUE; 980 } 981 else { 982 bResult = FALSE; 983 } 984 } 985 else { 986 bResult = FALSE; 987 } 988 return bResult; 989 } 990 991 PopulateProcess(DWORD dwProcessId,BOOL bPopulateModules)992 BOOL CPsapiHandler::PopulateProcess(DWORD dwProcessId, 993 BOOL bPopulateModules) 994 { 995 BOOL bResult = TRUE; 996 CExeModuleInstance* pProcessInfo; 997 998 if (Initialize() == TRUE) { 999 HMODULE hModuleArray[1024]; 1000 HANDLE hProcess; 1001 DWORD nModules; 1002 DWORD cbNeeded; 1003 hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | 1004 PROCESS_VM_READ, 1005 FALSE, 1006 dwProcessId 1007 ); 1008 if (hProcess) { 1009 if (!m_pfnEnumProcessModules(hProcess, 1010 hModuleArray, 1011 sizeof(hModuleArray), 1012 &cbNeeded 1013 )) { 1014 ::CloseHandle(hProcess); 1015 } 1016 else { 1017 // Calculate number of modules in the process 1018 nModules = cbNeeded / sizeof(hModuleArray[0]); 1019 1020 for (DWORD j = 0; j < nModules; j++) { 1021 HMODULE hModule = hModuleArray[j]; 1022 char szModuleName[MAX_PATH]; 1023 1024 m_pfnGetModuleFileNameExA(hProcess, 1025 hModule, 1026 szModuleName, 1027 sizeof(szModuleName) 1028 ); 1029 1030 if (j == 0) { // First module is the EXE. Just add it to the map 1031 pProcessInfo = new CExeModuleInstance(this, 1032 szModuleName, 1033 hModule, 1034 dwProcessId 1035 ); 1036 m_pProcess.reset(pProcessInfo); 1037 1038 if (bPopulateModules) { 1039 pProcessInfo->PopulateModules(); 1040 } 1041 1042 break; 1043 } 1044 } 1045 ::CloseHandle(hProcess); 1046 } 1047 } 1048 } 1049 else { 1050 bResult = FALSE; 1051 } 1052 return bResult; 1053 } 1054 1055 //////////////////////////////////////////////////////////////////////////// CToolhelpHandler(void)1056 CToolhelpHandler::CToolhelpHandler(void) 1057 { 1058 } 1059 ~CToolhelpHandler(void)1060 CToolhelpHandler::~CToolhelpHandler(void) 1061 { 1062 } 1063 1064 Initialize(void)1065 BOOL CToolhelpHandler::Initialize(void) 1066 { 1067 BOOL bResult = FALSE; 1068 HINSTANCE hInstLib; 1069 1070 hInstLib = g_LoadLibraryA("Kernel32.DLL"); 1071 if (hInstLib != NULL) { 1072 // We must link to these functions of Kernel32.DLL explicitly. Otherwise 1073 // a module using this code would fail to load under Windows NT, which does not 1074 // have the Toolhelp32 functions in the Kernel32. 1075 m_pfnCreateToolhelp32Snapshot = 1076 (FCreateToolHelp32Snapshot) 1077 ::GetProcAddress(hInstLib, "CreateToolhelp32Snapshot"); 1078 m_pfnProcess32First = (FProcess32First) 1079 ::GetProcAddress(hInstLib, "Process32First"); 1080 m_pfnProcess32Next = (FProcess32Next) 1081 ::GetProcAddress(hInstLib, "Process32Next"); 1082 m_pfnModule32First = (FModule32First) 1083 ::GetProcAddress(hInstLib, "Module32First"); 1084 m_pfnModule32Next = (FModule32Next) 1085 ::GetProcAddress(hInstLib, "Module32Next"); 1086 1087 ::FreeLibrary( hInstLib ); 1088 1089 bResult = m_pfnCreateToolhelp32Snapshot && 1090 m_pfnProcess32First && 1091 m_pfnProcess32Next && 1092 m_pfnModule32First && 1093 m_pfnModule32Next; 1094 } 1095 1096 return bResult; 1097 } 1098 PopulateModules(CModuleInstance * pProcess)1099 BOOL CToolhelpHandler::PopulateModules(CModuleInstance* pProcess) 1100 { 1101 BOOL bResult = TRUE; 1102 CModuleInstance *pDllModuleInstance = NULL; 1103 HANDLE hSnapshot = INVALID_HANDLE_VALUE; 1104 1105 hSnapshot = m_pfnCreateToolhelp32Snapshot( 1106 TH32CS_SNAPMODULE, 1107 static_cast<CExeModuleInstance*>(pProcess)->GetProcessId()); 1108 1109 MODULEENTRY32_A me = { sizeof(me)}; 1110 1111 for (BOOL bOk = ModuleFirst(hSnapshot, &me); bOk; bOk = ModuleNext(hSnapshot, &me)) { 1112 // We don't need to add to the list the process itself. 1113 // The module list should keep references to DLLs only 1114 if (my_stricmp(pProcess->GetBaseName(), me.szModule) != 0) { 1115 pDllModuleInstance = new CModuleInstance(me.szExePath, me.hModule); 1116 pProcess->AddModule(pDllModuleInstance); 1117 } 1118 else { 1119 // However, we should fix up the module of the EXE, because 1120 // th32ModuleID member has meaning only to the tool help functions 1121 // and it is not usable by Win32 API elements. 1122 pProcess->SetHandle( me.hModule ); 1123 } 1124 } 1125 1126 if (hSnapshot != INVALID_HANDLE_VALUE) { 1127 ::CloseHandle(hSnapshot); 1128 } 1129 1130 return bResult; 1131 } 1132 ModuleFirst(HANDLE hSnapshot,PMODULEENTRY32_A pme) const1133 BOOL CToolhelpHandler::ModuleFirst(HANDLE hSnapshot, PMODULEENTRY32_A pme) const 1134 { 1135 return (m_pfnModule32First(hSnapshot, pme)); 1136 } 1137 ModuleNext(HANDLE hSnapshot,PMODULEENTRY32_A pme) const1138 BOOL CToolhelpHandler::ModuleNext(HANDLE hSnapshot, PMODULEENTRY32_A pme) const 1139 { 1140 return (m_pfnModule32Next(hSnapshot, pme)); 1141 } 1142 ProcessFirst(HANDLE hSnapshot,PROCESSENTRY32_A * pe32) const1143 BOOL CToolhelpHandler::ProcessFirst(HANDLE hSnapshot, PROCESSENTRY32_A* pe32) const 1144 { 1145 return (m_pfnProcess32First(hSnapshot, pe32)); 1146 } 1147 ProcessNext(HANDLE hSnapshot,PROCESSENTRY32_A * pe32) const1148 BOOL CToolhelpHandler::ProcessNext(HANDLE hSnapshot, PROCESSENTRY32_A* pe32) const 1149 { 1150 return (m_pfnProcess32Next(hSnapshot, pe32)); 1151 } 1152 PopulateProcess(DWORD dwProcessId,BOOL bPopulateModules)1153 BOOL CToolhelpHandler::PopulateProcess(DWORD dwProcessId, BOOL bPopulateModules) 1154 { 1155 BOOL bResult = FALSE; 1156 CExeModuleInstance* pProcessInfo; 1157 HANDLE hSnapshot = INVALID_HANDLE_VALUE; 1158 1159 if (Initialize() == TRUE) { 1160 hSnapshot = m_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, dwProcessId); 1161 1162 PROCESSENTRY32_A pe32 = { sizeof(pe32)}; 1163 1164 for (BOOL bOk = ProcessFirst(hSnapshot, &pe32); 1165 bOk; 1166 bOk = ProcessNext(hSnapshot, &pe32)) { 1167 if ((dwProcessId != NULL) && (dwProcessId != pe32.th32ProcessID)) { 1168 continue; 1169 } 1170 1171 pProcessInfo = 1172 new CExeModuleInstance(this, 1173 pe32.szExeFile, 1174 NULL, 1175 pe32.th32ProcessID 1176 ); 1177 m_pProcess.reset(pProcessInfo); 1178 if (bPopulateModules) { 1179 pProcessInfo->PopulateModules(); 1180 } 1181 1182 if (dwProcessId != NULL) { 1183 break; 1184 } 1185 } 1186 1187 if (hSnapshot != INVALID_HANDLE_VALUE) { 1188 ::CloseHandle(hSnapshot); 1189 } 1190 1191 bResult = TRUE; 1192 } 1193 1194 return bResult; 1195 } 1196 1197 //////////////////////////////////////////////////////////////////////////// CTaskManager()1198 CTaskManager::CTaskManager(): 1199 m_pLibHandler(NULL) 1200 { 1201 if (IsPsapiSupported()) { 1202 m_pLibHandler = new CPsapiHandler; 1203 } 1204 else { 1205 if (IsToolHelpSupported()) 1206 m_pLibHandler = new CToolhelpHandler; 1207 } 1208 } 1209 ~CTaskManager(void)1210 CTaskManager::~CTaskManager(void) 1211 { 1212 try { 1213 delete m_pLibHandler; 1214 } 1215 NCBI_CATCH_ALL_X( 7, NCBI_CURRENT_FUNCTION ) 1216 } 1217 PopulateProcess(DWORD dwProcessId,BOOL bPopulateModules) const1218 BOOL CTaskManager::PopulateProcess(DWORD dwProcessId, 1219 BOOL bPopulateModules) const 1220 { 1221 _ASSERT(m_pLibHandler); 1222 return (m_pLibHandler->PopulateProcess(dwProcessId, bPopulateModules)); 1223 } 1224 1225 GetProcess(void) const1226 CExeModuleInstance* CTaskManager::GetProcess(void) const 1227 { 1228 _ASSERT(m_pLibHandler); 1229 return (m_pLibHandler->GetExeModuleInstance()); 1230 } 1231 1232 1233 1234 void UnHookAllFuncs(void)1235 CHookedFunctions::UnHookAllFuncs(void) 1236 { 1237 ITERATE(TModuleList, mod_it, m_ModuleList) { 1238 ITERATE(TFunctionList, it, mod_it->second) { 1239 CRef<CHookedFunction> pHook; 1240 1241 pHook = it->second; 1242 BOOL result = pHook->UnHookImport(); 1243 1244 if (result == FALSE) { 1245 ERR_POST_X(4, Warning << pHook->GetFuncName() << 1246 " wasn't unhooked in " << NCBI_CURRENT_FUNCTION); 1247 } 1248 } 1249 } 1250 1251 m_ModuleList.clear(); 1252 m_ModuleNameList.clear(); 1253 } 1254 1255 /////////////////////////////////////////////////////////////////////////////// 1256 // 1257 // The highest private memory address (used for Windows 9x only) 1258 // 1259 PVOID CHookedFunction::sm_pvMaxAppAddr = NULL; 1260 // 1261 // The PUSH opcode on x86 platforms 1262 // 1263 const BYTE cPushOpCode = 0x68; 1264 CHookedFunction(PCSTR pszCalleeModName,PCSTR pszFuncName,PROC pfnOrig,PROC pfnHook)1265 CHookedFunction::CHookedFunction(PCSTR pszCalleeModName, 1266 PCSTR pszFuncName, 1267 PROC pfnOrig, 1268 PROC pfnHook 1269 ) : 1270 m_bHooked(FALSE), 1271 m_CalleeModHandle(NULL), 1272 m_pfnOrig(pfnOrig), 1273 m_pfnHook(pfnHook) 1274 { 1275 strcpy(m_szCalleeModName, pszCalleeModName); 1276 strcpy(m_szFuncName, pszFuncName); 1277 1278 m_CalleeModHandle = ::GetModuleHandleA(m_szCalleeModName); 1279 1280 if (sm_pvMaxAppAddr == NULL) { 1281 // Functions with address above lpMaximumApplicationAddress require 1282 // special processing (Windows 9x only) 1283 SYSTEM_INFO si; 1284 GetSystemInfo(&si); 1285 sm_pvMaxAppAddr = si.lpMaximumApplicationAddress; 1286 } 1287 1288 if (m_pfnOrig > sm_pvMaxAppAddr) { 1289 // The address is in a shared DLL; the address needs fixing up 1290 PBYTE pb = (PBYTE) m_pfnOrig; 1291 if (pb[0] == cPushOpCode) { 1292 // Skip over the PUSH op code and grab the real address 1293 PVOID pv = * (PVOID*) &pb[1]; 1294 m_pfnOrig = (PROC) pv; 1295 } 1296 } 1297 } 1298 1299 ~CHookedFunction(void)1300 CHookedFunction::~CHookedFunction(void) 1301 { 1302 try { 1303 BOOL result = UnHookImport(); 1304 1305 if (result == FALSE) { 1306 ERR_POST_X(4, Warning << 1307 "Import is not unhooked in " << NCBI_CURRENT_FUNCTION); 1308 } 1309 } 1310 NCBI_CATCH_ALL_X( 8, NCBI_CURRENT_FUNCTION ) 1311 } 1312 GetCalleeModHandle(void) const1313 HMODULE CHookedFunction::GetCalleeModHandle(void) const 1314 { 1315 return m_CalleeModHandle; 1316 } 1317 GetCalleeModName(void) const1318 PCSTR CHookedFunction::GetCalleeModName(void) const 1319 { 1320 return (const_cast<PCSTR>(m_szCalleeModName)); 1321 } 1322 GetFuncName(void) const1323 PCSTR CHookedFunction::GetFuncName(void) const 1324 { 1325 return const_cast<PCSTR>(m_szFuncName); 1326 } 1327 GetPfnHook(void) const1328 PROC CHookedFunction::GetPfnHook(void) const 1329 { 1330 return m_pfnHook; 1331 } 1332 GetPfnOrig(void) const1333 PROC CHookedFunction::GetPfnOrig(void) const 1334 { 1335 return m_pfnOrig; 1336 } 1337 HookImport(void)1338 BOOL CHookedFunction::HookImport(void) 1339 { 1340 m_bHooked = DoHook(TRUE, m_pfnOrig, m_pfnHook); 1341 1342 return m_bHooked; 1343 } 1344 UnHookImport(void)1345 BOOL CHookedFunction::UnHookImport(void) 1346 { 1347 if (m_bHooked) { 1348 m_bHooked = !DoHook(FALSE, m_pfnHook, m_pfnOrig); 1349 } 1350 1351 return (!m_bHooked); 1352 } 1353 ReplaceInAllModules(bool bHookOrRestore,PCSTR pszCalleeModName,PROC pfnCurrent,PROC pfnNew)1354 BOOL CHookedFunction::ReplaceInAllModules(bool bHookOrRestore, 1355 PCSTR pszCalleeModName, 1356 PROC pfnCurrent, 1357 PROC pfnNew 1358 ) 1359 { 1360 BOOL bResult = FALSE; 1361 1362 if ((pfnCurrent != NULL) && (pfnNew != NULL)) { 1363 BOOL bReplace = FALSE; 1364 CExeModuleInstance *pProcess = NULL; 1365 CTaskManager taskManager; 1366 CModuleInstance *pModule; 1367 1368 // Retrieves information about current process and modules. 1369 // The taskManager dynamically decides whether to use ToolHelp 1370 // library or PSAPI 1371 taskManager.PopulateProcess(::GetCurrentProcessId(), TRUE); 1372 pProcess = taskManager.GetProcess(); 1373 if (pProcess != NULL) { 1374 // Enumerates all modules loaded by (pProcess) process 1375 for (size_t i = 0; i < pProcess->GetModuleCount(); ++i) { 1376 pModule = pProcess->GetModuleByIndex(i); 1377 bReplace = (pModule->GetHandle() != 1378 ModuleFromAddress(CKernell32::LoadLibraryA)); 1379 1380 // We don't hook functions in our own modules 1381 if (bReplace) { 1382 // Hook this function in this module 1383 bResult = ReplaceInOneModule(bHookOrRestore, 1384 pszCalleeModName, 1385 pfnCurrent, 1386 pfnNew, 1387 pModule->GetHandle() 1388 ) || bResult; 1389 } 1390 } 1391 1392 // Hook this function in the executable as well 1393 bResult = ReplaceInOneModule(bHookOrRestore, 1394 pszCalleeModName, 1395 pfnCurrent, 1396 pfnNew, 1397 pProcess->GetHandle() 1398 ) || bResult; 1399 } 1400 } 1401 return bResult; 1402 } 1403 1404 ReplaceInOneModule(bool bHookOrRestore,PCSTR pszCalleeModName,PROC pfnCurrent,PROC pfnNew,HMODULE hmodCaller)1405 BOOL CHookedFunction::ReplaceInOneModule(bool bHookOrRestore, 1406 PCSTR pszCalleeModName, 1407 PROC pfnCurrent, 1408 PROC pfnNew, 1409 HMODULE hmodCaller 1410 ) 1411 { 1412 BOOL bResult = FALSE; 1413 1414 if (bHookOrRestore == false) { 1415 // We are restoring hoock ... 1416 TModuleSet::const_iterator it = m_HookedModuleSet.find(hmodCaller); 1417 1418 if (it == m_HookedModuleSet.end()) { 1419 // Hook wasn't set in this module ... 1420 // That is OK. 1421 return TRUE; 1422 } 1423 } 1424 1425 // Get the address of the module's import section 1426 PIMAGE_IMPORT_DESCRIPTOR pImportDesc = 1427 static_cast<PIMAGE_IMPORT_DESCRIPTOR> 1428 (CPEi386::GetInstance().GetIAT(hmodCaller, 1429 IMAGE_DIRECTORY_ENTRY_IMPORT)); 1430 1431 // Does this module has import section ? 1432 if (pImportDesc == NULL) { 1433 // There is no import section, but that is OK. 1434 bResult = TRUE; 1435 return bResult; 1436 } 1437 1438 // Loop through all descriptors and 1439 // find the import descriptor containing references to callee's functions 1440 // Get import descriptor for a given pszCalleeModName (callee's module name) 1441 while (pImportDesc->Name) { 1442 PSTR pszModName = (PSTR)((PBYTE) hmodCaller + pImportDesc->Name); 1443 if (my_stricmp(pszModName, pszCalleeModName) == 0) { 1444 break; // Found 1445 } 1446 pImportDesc++; 1447 } 1448 1449 // Does this module import any functions from this callee ? 1450 if (pImportDesc->Name == 0) { 1451 // This module doesn't import anything from "pszCalleeModName". 1452 // No problem. We can live with that. 1453 bResult = TRUE; 1454 return bResult; 1455 } 1456 1457 1458 // We have import descriptor. Let's get a function. 1459 1460 1461 // Get caller's IAT 1462 PIMAGE_THUNK_DATA pThunk = 1463 (PIMAGE_THUNK_DATA)( (PBYTE) hmodCaller + pImportDesc->FirstThunk ); 1464 1465 // Replace current function address with new one 1466 while (pThunk->u1.Function) { 1467 // Get the address of the function address 1468 PROC* ppfn = (PROC*) &pThunk->u1.Function; 1469 // Is this the function we're looking for? 1470 // !!! It can be hoocked by others ... !!! 1471 BOOL bFound = (*ppfn == pfnCurrent); 1472 // Is this Windows 9x 1473 if (!bFound && (*ppfn > sm_pvMaxAppAddr)) { 1474 PBYTE pbInFunc = (PBYTE) *ppfn; 1475 1476 // Is this a wrapper (debug thunk) represented by PUSH instruction? 1477 if (pbInFunc[0] == cPushOpCode) { 1478 ppfn = (PROC*) &pbInFunc[1]; 1479 // Is this the function we're looking for? 1480 bFound = (*ppfn == pfnCurrent); 1481 } 1482 } 1483 1484 if (bFound) { 1485 DWORD dwOldProtect; 1486 // In order to provide writable access to this part of the 1487 // memory we need to change the memory protection 1488 if (::VirtualProtect(ppfn, 1489 sizeof(*ppfn), 1490 PAGE_READWRITE, 1491 &dwOldProtect) == FALSE 1492 ) { 1493 return bResult; 1494 } 1495 1496 // Hook the function. 1497 *ppfn = *pfnNew; 1498 1499 // Restore the protection back 1500 DWORD dwDummy; 1501 ::VirtualProtect(ppfn, 1502 sizeof(*ppfn), 1503 dwOldProtect, 1504 &dwDummy 1505 ); 1506 1507 bResult = TRUE; 1508 1509 if (bHookOrRestore) { 1510 m_HookedModuleSet.insert(hmodCaller); 1511 } else { 1512 m_HookedModuleSet.erase(hmodCaller); 1513 } 1514 1515 break; 1516 } 1517 1518 pThunk++; 1519 } 1520 1521 // This function is not in the caller's import section 1522 return bResult; 1523 } 1524 DoHook(bool bHookOrRestore,PROC pfnCurrent,PROC pfnNew)1525 BOOL CHookedFunction::DoHook(bool bHookOrRestore, 1526 PROC pfnCurrent, 1527 PROC pfnNew 1528 ) 1529 { 1530 // Hook this function in all currently loaded modules 1531 return (ReplaceInAllModules(bHookOrRestore, 1532 m_szCalleeModName, 1533 pfnCurrent, 1534 pfnNew 1535 )); 1536 } 1537 1538 // Indicates whether the hooked function is mandatory one IsMandatory(void)1539 BOOL CHookedFunction::IsMandatory(void) 1540 { 1541 BOOL bResult = FALSE; 1542 API_FUNC_ID apiFuncId; 1543 for (int i = 0; i < NUMBER_OF_MANDATORY_API_FUNCS; ++i) { 1544 apiFuncId = MANDATORY_API_FUNCS[i]; 1545 if ((my_stricmp(apiFuncId.szCalleeModName, m_szCalleeModName) == 0) && 1546 (my_stricmp(apiFuncId.szFuncName, m_szFuncName) == 0)) { 1547 bResult = TRUE; 1548 break; 1549 } 1550 } 1551 1552 return bResult; 1553 } 1554 1555 //////////////////////////////////////////////////////////////////////////// CHookedFunctions(void)1556 CHookedFunctions::CHookedFunctions(void) 1557 { 1558 } 1559 ~CHookedFunctions(void)1560 CHookedFunctions::~CHookedFunctions(void) 1561 { 1562 } 1563 1564 //////////////////////////////////////////////////////////////////////////// ExtractModuleFileName(char * pszFullFileName)1565 static BOOL ExtractModuleFileName(char* pszFullFileName) 1566 { 1567 BOOL bResult = FALSE; 1568 1569 if (::IsBadReadPtr(pszFullFileName, MAX_PATH) != TRUE) { 1570 char *pdest; 1571 int ch = '\\'; 1572 1573 // Search backward 1574 pdest = strrchr(pszFullFileName, ch); 1575 if (pdest != NULL) 1576 strcpy(pszFullFileName, &pdest[1]); 1577 1578 bResult = TRUE; 1579 } 1580 1581 return bResult; 1582 } 1583 1584 //////////////////////////////////////////////////////////////////////////// x_GetFunctionNameFromExportSection(HMODULE hmodOriginal,DWORD dwFuncOrdinalNum,PSTR pszFuncName) const1585 BOOL CHookedFunctions::x_GetFunctionNameFromExportSection( 1586 HMODULE hmodOriginal, 1587 DWORD dwFuncOrdinalNum, 1588 PSTR pszFuncName 1589 ) const 1590 { 1591 BOOL bResult = FALSE; 1592 // Make sure we return a valid string (atleast an empty one) 1593 strcpy(pszFuncName, "\0"); 1594 1595 // Get the address of the module's export section 1596 PIMAGE_EXPORT_DIRECTORY pExportDir = 1597 static_cast<PIMAGE_EXPORT_DIRECTORY> 1598 (CPEi386::GetInstance().GetIAT(hmodOriginal, 1599 IMAGE_DIRECTORY_ENTRY_EXPORT)); 1600 1601 // Does this module has export section ? 1602 if (pExportDir == NULL) { 1603 return bResult; 1604 } 1605 1606 // Get the name of the DLL 1607 PSTR pszDllName = reinterpret_cast<PSTR>( 1608 static_cast<uintptr_t>(pExportDir->Name) + 1609 reinterpret_cast<uintptr_t>(hmodOriginal) 1610 ); 1611 // Get the starting ordinal value. By default is 1, but 1612 // is not required to be so 1613 DWORD dwFuncNumber = pExportDir->Base; 1614 // The number of entries in the EAT 1615 size_t dwNumberOfExported = pExportDir->NumberOfFunctions; 1616 // Get the address of the ENT 1617 PDWORD pdwFunctions = 1618 reinterpret_cast<PDWORD>( 1619 static_cast<uintptr_t>(pExportDir->AddressOfFunctions) + 1620 reinterpret_cast<uintptr_t>(hmodOriginal)); 1621 // Get the export ordinal table 1622 PWORD pwOrdinals = 1623 reinterpret_cast<PWORD>( 1624 static_cast<uintptr_t>(pExportDir->AddressOfNameOrdinals) + 1625 reinterpret_cast<uintptr_t>(hmodOriginal)); 1626 // Get the address of the array with all names 1627 PDWORD pszFuncNames = 1628 reinterpret_cast<PDWORD>( 1629 static_cast<uintptr_t>(pExportDir->AddressOfNames) + 1630 reinterpret_cast<uintptr_t>(hmodOriginal)); 1631 1632 PSTR pszExpFunName; 1633 1634 // Walk through all of the entries and try to locate the 1635 // one we are looking for 1636 for (size_t i = 0; i < dwNumberOfExported; ++i, ++pdwFunctions) { 1637 DWORD entryPointRVA = *pdwFunctions; 1638 if (entryPointRVA == 0) { 1639 // Skip over gaps in exported function 1640 // ordinals (the entrypoint is 0 for 1641 // these functions). 1642 continue; 1643 } 1644 1645 // See if this function has an associated name exported for it. 1646 for (unsigned j = 0; j < pExportDir->NumberOfNames; ++j) { 1647 // Note that pwOrdinals[x] return values starting form 0.. (not from 1) 1648 if (pwOrdinals[j] == i) { 1649 pszExpFunName = reinterpret_cast<PSTR>( 1650 static_cast<uintptr_t>(pszFuncNames[j]) + 1651 reinterpret_cast<uintptr_t>(hmodOriginal)); 1652 // Is this the same ordinal value ? 1653 // Notice that we need to add 1 to pwOrdinals[j] to get actual 1654 // number 1655 if (dwFuncOrdinalNum == pwOrdinals[j] + 1) { 1656 if ((pszExpFunName != NULL) && (strlen(pszExpFunName) > 0)) { 1657 strcpy(pszFuncName, pszExpFunName); 1658 } 1659 1660 return bResult; 1661 } 1662 } 1663 } 1664 } 1665 1666 // This function is not in the caller's import section 1667 return bResult; 1668 } 1669 x_GetFunctionNameByOrdinal(PCSTR pszCalleeModName,DWORD dwFuncOrdinalNum,PSTR pszFuncName) const1670 void CHookedFunctions::x_GetFunctionNameByOrdinal( 1671 PCSTR pszCalleeModName, 1672 DWORD dwFuncOrdinalNum, 1673 PSTR pszFuncName 1674 ) const 1675 { 1676 HMODULE hmodOriginal = ::GetModuleHandleA(pszCalleeModName); 1677 1678 // Take the name from the export section of the DLL 1679 x_GetFunctionNameFromExportSection( 1680 hmodOriginal, 1681 dwFuncOrdinalNum, 1682 pszFuncName 1683 ); 1684 } 1685 x_GetFunctionNameByOrdinal(HMODULE hmodOriginal,DWORD dwFuncOrdinalNum,PSTR pszFuncName) const1686 void CHookedFunctions::x_GetFunctionNameByOrdinal( 1687 HMODULE hmodOriginal, 1688 DWORD dwFuncOrdinalNum, 1689 PSTR pszFuncName 1690 ) const 1691 { 1692 // Take the name from the export section of the DLL 1693 x_GetFunctionNameFromExportSection( 1694 hmodOriginal, 1695 dwFuncOrdinalNum, 1696 pszFuncName 1697 ); 1698 } 1699 1700 1701 CRef<CHookedFunction> GetHookedFunction(PCSTR pszCalleeModName,PCSTR pszFuncName) const1702 CHookedFunctions::GetHookedFunction(PCSTR pszCalleeModName, 1703 PCSTR pszFuncName) const 1704 { 1705 CRef<CHookedFunction> pHook; 1706 char szFuncName[MAX_PATH]; 1707 1708 // Prevent accessing invalid pointers and examine values 1709 // for APIs exported by ordinal 1710 if ((pszFuncName) && 1711 (reinterpret_cast<uintptr_t>(pszFuncName) > 0xFFFF) && 1712 strlen(pszFuncName)) { 1713 strcpy(szFuncName, pszFuncName); 1714 } 1715 else { 1716 // It is safe to cast a pointer to DWORD here because it is not a 1717 // pointer, it is an ordinal number of a function. 1718 x_GetFunctionNameByOrdinal( 1719 pszCalleeModName, 1720 static_cast<DWORD>( 1721 reinterpret_cast<uintptr_t>(pszFuncName)), 1722 szFuncName 1723 ); 1724 } 1725 1726 // Search in the map only if we have found the name of the requested 1727 // function 1728 if (strlen(szFuncName) > 0) { 1729 // Get a module by name ... 1730 TModuleNameList::const_iterator mn_it = 1731 m_ModuleNameList.find(pszCalleeModName); 1732 1733 if (mn_it != m_ModuleNameList.end()) { 1734 const TFunctionList& fn_list = mn_it->second; 1735 1736 // Get the function by name ... 1737 TFunctionList::const_iterator fn_it = fn_list.find(szFuncName); 1738 if (fn_it != fn_list.end()) { 1739 pHook = fn_it->second; 1740 } 1741 } 1742 } 1743 1744 return (pHook); 1745 } 1746 1747 1748 CRef<CHookedFunction> GetHookedFunction(HMODULE hmodOriginal,PCSTR pszFuncName) const1749 CHookedFunctions::GetHookedFunction(HMODULE hmodOriginal, 1750 PCSTR pszFuncName) const 1751 { 1752 CRef<CHookedFunction> pHook; 1753 char szFuncName[MAX_PATH]; 1754 1755 // Prevent accessing invalid pointers and examine values 1756 // for APIs exported by ordinal 1757 if ((pszFuncName) && 1758 (reinterpret_cast<uintptr_t>(pszFuncName) > 0xFFFF) && 1759 strlen(pszFuncName)) { 1760 strcpy(szFuncName, pszFuncName); 1761 } 1762 else { 1763 // It is safe to cast a pointer to DWORD here because it is not a 1764 // pointer, it is an ordinal number of a function. 1765 x_GetFunctionNameByOrdinal( 1766 hmodOriginal, 1767 static_cast<DWORD>( 1768 reinterpret_cast<uintptr_t>(pszFuncName)), 1769 szFuncName 1770 ); 1771 } 1772 1773 // Search in the map only if we have found the name of the requested 1774 // function 1775 if (strlen(szFuncName) > 0) { 1776 // Get a module by name ... 1777 TModuleList::const_iterator mod_it = 1778 m_ModuleList.find(hmodOriginal); 1779 1780 if (mod_it != m_ModuleList.end()) { 1781 // This module was hooked at least once. 1782 // Let's check if a function was hoocked in this module. 1783 const TFunctionList& fn_list = mod_it->second; 1784 1785 // Get the function by name ... 1786 TFunctionList::const_iterator fn_it = fn_list.find(szFuncName); 1787 if (fn_it != fn_list.end()) { 1788 pHook = fn_it->second; 1789 } 1790 } 1791 } 1792 1793 return (pHook); 1794 } 1795 1796 AddHook(const CRef<CHookedFunction> pHook)1797 BOOL CHookedFunctions::AddHook(const CRef<CHookedFunction> pHook) 1798 { 1799 BOOL bResult = FALSE; 1800 if (pHook != NULL) { 1801 m_ModuleNameList[pHook->GetCalleeModName()][pHook->GetFuncName()] = 1802 pHook; 1803 m_ModuleList[pHook->GetCalleeModHandle()][pHook->GetFuncName()] = 1804 pHook; 1805 1806 bResult = TRUE; 1807 } 1808 return bResult; 1809 } 1810 RemoveHook(const CRef<CHookedFunction> pHook)1811 BOOL CHookedFunctions::RemoveHook(const CRef<CHookedFunction> pHook) 1812 { 1813 BOOL bResult = FALSE; 1814 try { 1815 if (pHook != NULL) { 1816 // Remove from m_ModuleNameList ... 1817 TModuleNameList::iterator mn_it = 1818 m_ModuleNameList.find(pHook->GetCalleeModName()); 1819 1820 if (mn_it != m_ModuleNameList.end()) { 1821 TFunctionList& fn_list = mn_it->second; 1822 TFunctionList::iterator fn_it = 1823 fn_list.find(pHook->GetFuncName()); 1824 1825 if (fn_it != fn_list.end()) { 1826 fn_list.erase(fn_it); 1827 } 1828 } 1829 1830 // Remove from m_ModuleList ... 1831 TModuleList::iterator mod_it = 1832 m_ModuleList.find(pHook->GetCalleeModHandle()); 1833 1834 if (mod_it != m_ModuleList.end()) { 1835 TFunctionList& fn_list = mod_it->second; 1836 TFunctionList::iterator fn_it = 1837 fn_list.find(pHook->GetFuncName()); 1838 1839 if (fn_it != fn_list.end()) { 1840 // An element is already deleted ... 1841 fn_list.erase(fn_it); 1842 } 1843 } 1844 1845 bResult = TRUE; 1846 } 1847 } 1848 catch (...) { 1849 bResult = FALSE; 1850 } 1851 1852 return bResult; 1853 } 1854 1855 //////////////////////////////////////////////////////////////////////////// CApiHookMgr()1856 CApiHookMgr::CApiHookMgr() : 1857 m_bSystemFuncsHooked(FALSE) 1858 { 1859 // A static variable below is used to check for enabling of tracing 1860 // when CNcbiApplication is not available any more. 1861 static bool enabled_from_registry = true; 1862 1863 CNcbiApplication* app = CNcbiApplication::Instance(); 1864 1865 if (app) { 1866 // Get current registry ... 1867 const IRegistry& registry = app->GetConfig(); 1868 1869 if (!registry.GetBool("NCBI_WIN_HOOK", "ENABLED", true)) { 1870 enabled_from_registry = false; 1871 NCBI_THROW(CWinHookException, 1872 eDisabled | Retriable(eRetriable_No), 1873 "Windows API hooking is disabled from registry."); 1874 } 1875 else { 1876 enabled_from_registry = true; 1877 } 1878 } else if (!enabled_from_registry) { 1879 NCBI_THROW(CWinHookException, 1880 eDisabled | Retriable(eRetriable_No), 1881 "Windows API hooking is disabled from registry."); 1882 } 1883 1884 x_HookSystemFuncs(); 1885 } 1886 ~CApiHookMgr(void)1887 CApiHookMgr::~CApiHookMgr(void) 1888 { 1889 try { 1890 x_UnHookAllFuncs(); 1891 } 1892 NCBI_CATCH_ALL_X( 3, NCBI_CURRENT_FUNCTION ) 1893 } 1894 1895 void operator =(const CApiHookMgr &)1896 CApiHookMgr::operator =(const CApiHookMgr&) 1897 { 1898 } 1899 1900 CApiHookMgr& GetInstance(void)1901 CApiHookMgr::GetInstance(void) 1902 { 1903 static CSafeStatic<CApiHookMgr> instance; 1904 1905 return (instance.Get()); 1906 } 1907 x_HookSystemFuncs(void)1908 BOOL CApiHookMgr::x_HookSystemFuncs(void) 1909 { 1910 BOOL bResult; 1911 1912 if (m_bSystemFuncsHooked != TRUE) { 1913 { 1914 bResult = HookImport("Kernel32.dll", 1915 "LoadLibraryA", 1916 (PROC) CApiHookMgr::MyLoadLibraryA 1917 ); 1918 1919 if (bResult == FALSE) { 1920 ERR_POST_X(4, Warning 1921 << "LoadLibraryA is not hooked in " 1922 << NCBI_CURRENT_FUNCTION 1923 ); 1924 } 1925 } 1926 1927 { 1928 bResult = HookImport("Kernel32.dll", 1929 "LoadLibraryW", 1930 (PROC) CApiHookMgr::MyLoadLibraryW 1931 ) || bResult; 1932 1933 if (bResult == FALSE) { 1934 ERR_POST_X(4, Warning 1935 << "LoadLibraryW is not hooked in " 1936 << NCBI_CURRENT_FUNCTION 1937 ); 1938 } 1939 } 1940 1941 { 1942 bResult = HookImport("Kernel32.dll", 1943 "LoadLibraryExA", 1944 (PROC) CApiHookMgr::MyLoadLibraryExA 1945 ) || bResult; 1946 1947 if (bResult == FALSE) { 1948 ERR_POST_X(4, Warning 1949 << "LoadLibraryExA is not hooked in " 1950 << NCBI_CURRENT_FUNCTION 1951 ); 1952 } 1953 } 1954 1955 { 1956 bResult = HookImport("Kernel32.dll", 1957 "LoadLibraryExW", 1958 (PROC) CApiHookMgr::MyLoadLibraryExW 1959 ) || bResult; 1960 1961 if (bResult == FALSE) { 1962 ERR_POST_X(4, Warning 1963 << "LoadLibraryExW is not hooked in " 1964 << NCBI_CURRENT_FUNCTION 1965 ); 1966 } 1967 } 1968 1969 { 1970 bResult = HookImport("Kernel32.dll", 1971 "GetProcAddress", 1972 (PROC) CApiHookMgr::MyGetProcAddress 1973 ) || bResult; 1974 1975 if (bResult == FALSE) { 1976 ERR_POST_X(4, Warning 1977 << "GetProcAddress is not hooked in" 1978 << NCBI_CURRENT_FUNCTION 1979 ); 1980 } 1981 } 1982 1983 m_bSystemFuncsHooked = bResult; 1984 } 1985 1986 return m_bSystemFuncsHooked; 1987 } 1988 x_UnHookAllFuncs(void)1989 void CApiHookMgr::x_UnHookAllFuncs(void) 1990 { 1991 m_pHookedFunctions.UnHookAllFuncs(); 1992 m_bSystemFuncsHooked = FALSE; 1993 } 1994 1995 // Indicates whether there is hooked function HaveHookedFunctions(void) const1996 bool CApiHookMgr::HaveHookedFunctions(void) const 1997 { 1998 // CHookedFunctions is not thread-safe ... 1999 CFastMutexGuard guard(m_Mutex); 2000 2001 return m_pHookedFunctions.HaveHookedFunctions(); 2002 } 2003 2004 CRef<CHookedFunction> GetHookedFunction(HMODULE hmod,PCSTR pszFuncName) const2005 CApiHookMgr::GetHookedFunction(HMODULE hmod, 2006 PCSTR pszFuncName 2007 ) const 2008 { 2009 // CHookedFunctions is not thread-safe ... 2010 CFastMutexGuard guard(m_Mutex); 2011 2012 return m_pHookedFunctions.GetHookedFunction(hmod, pszFuncName); 2013 } 2014 2015 // Hook up an API function HookImport(PCSTR pszCalleeModName,PCSTR pszFuncName,PROC pfnHook)2016 BOOL CApiHookMgr::HookImport(PCSTR pszCalleeModName, 2017 PCSTR pszFuncName, 2018 PROC pfnHook 2019 ) 2020 { 2021 CFastMutexGuard guard(m_Mutex); 2022 2023 BOOL bResult = FALSE; 2024 PROC pfnOrig = NULL; 2025 2026 if (!m_pHookedFunctions.GetHookedFunction( 2027 pszCalleeModName, 2028 pszFuncName 2029 )) { 2030 2031 pfnOrig = xs_GetProcAddressWindows( 2032 ::GetModuleHandleA(pszCalleeModName), 2033 pszFuncName 2034 ); 2035 2036 // It's possible that the requested module is not loaded yet 2037 // so lets try to load it. 2038 if (pfnOrig == NULL) { 2039 HMODULE hmod = g_LoadLibraryA(pszCalleeModName); 2040 2041 if (NULL != hmod) { 2042 pfnOrig = xs_GetProcAddressWindows( 2043 ::GetModuleHandleA(pszCalleeModName), 2044 pszFuncName 2045 ); 2046 } 2047 } 2048 2049 if (pfnOrig != NULL) { 2050 bResult = x_AddHook( 2051 pszCalleeModName, 2052 pszFuncName, 2053 pfnOrig, 2054 pfnHook 2055 ); 2056 } 2057 } 2058 2059 return bResult; 2060 } 2061 2062 // Restores original API function address in IAT UnHookImport(PCSTR pszCalleeModName,PCSTR pszFuncName)2063 BOOL CApiHookMgr::UnHookImport(PCSTR pszCalleeModName, 2064 PCSTR pszFuncName 2065 ) 2066 { 2067 CFastMutexGuard guard(m_Mutex); 2068 2069 BOOL bResult = x_RemoveHook(pszCalleeModName, pszFuncName); 2070 2071 return bResult; 2072 } 2073 2074 // Add a hook to the internally supported container x_AddHook(PCSTR pszCalleeModName,PCSTR pszFuncName,PROC pfnOrig,PROC pfnHook)2075 BOOL CApiHookMgr::x_AddHook(PCSTR pszCalleeModName, 2076 PCSTR pszFuncName, 2077 PROC pfnOrig, 2078 PROC pfnHook 2079 ) 2080 { 2081 BOOL bResult = FALSE; 2082 2083 if (!m_pHookedFunctions.GetHookedFunction(pszCalleeModName, 2084 pszFuncName 2085 ) 2086 ) 2087 { 2088 // Function wasn't hoocked in pszCalleeModName yet ... 2089 // Let's do that. 2090 2091 CRef<CHookedFunction> pHook( 2092 new CHookedFunction(pszCalleeModName, 2093 pszFuncName, 2094 pfnOrig, 2095 pfnHook 2096 ) 2097 ); 2098 2099 // We must create the hook and insert it in the container 2100 BOOL result = pHook->HookImport(); 2101 2102 if (result == FALSE) { 2103 ERR_POST_X(4, Warning << pszFuncName 2104 << " is not hooked in " 2105 << NCBI_CURRENT_FUNCTION 2106 ); 2107 } else { 2108 bResult = m_pHookedFunctions.AddHook(pHook); 2109 } 2110 } 2111 2112 return bResult; 2113 } 2114 2115 // Remove a hook from the internally supported container x_RemoveHook(PCSTR pszCalleeModName,PCSTR pszFuncName)2116 BOOL CApiHookMgr::x_RemoveHook(PCSTR pszCalleeModName, 2117 PCSTR pszFuncName 2118 ) 2119 { 2120 BOOL bResult = FALSE; 2121 CRef<CHookedFunction> pHook; 2122 2123 pHook = m_pHookedFunctions.GetHookedFunction(pszCalleeModName, 2124 pszFuncName 2125 ); 2126 if (pHook != NULL) { 2127 bResult = pHook->UnHookImport(); 2128 if (bResult) { 2129 bResult = m_pHookedFunctions.RemoveHook( pHook ); 2130 } 2131 } 2132 2133 return bResult; 2134 } 2135 2136 2137 // Used when a DLL is newly loaded after hooking a function HackModuleOnLoad(HMODULE hmod,DWORD dwFlags)2138 void WINAPI CApiHookMgr::HackModuleOnLoad(HMODULE hmod, DWORD dwFlags) 2139 { 2140 // If a new module is loaded, just hook it 2141 if ((hmod != NULL) && ((dwFlags & LOAD_LIBRARY_AS_DATAFILE) == 0)) 2142 { 2143 CFastMutexGuard guard(m_Mutex); 2144 2145 // Strange logic below ... 2146 // Should be fixed !!! 2147 ITERATE(CHookedFunctions::TModuleNameList, 2148 mn_it, 2149 m_pHookedFunctions.m_ModuleNameList) 2150 { 2151 2152 const CHookedFunctions::TFunctionList& fn_list = mn_it->second; 2153 2154 ITERATE(CHookedFunctions::TFunctionList, 2155 it, 2156 fn_list) 2157 { 2158 2159 CRef<CHookedFunction> pHook(it->second); 2160 2161 pHook->ReplaceInOneModule( 2162 TRUE, 2163 pHook->GetCalleeModName(), 2164 pHook->GetPfnOrig(), 2165 pHook->GetPfnHook(), 2166 hmod 2167 ); 2168 } 2169 } 2170 } 2171 } 2172 MyLoadLibraryA(PCSTR pszModuleName)2173 HMODULE WINAPI CApiHookMgr::MyLoadLibraryA(PCSTR pszModuleName) 2174 { 2175 HMODULE hmod = NULL; 2176 2177 try { 2178 hmod = CKernell32::LoadLibraryA(pszModuleName); 2179 GetInstance().HackModuleOnLoad(hmod, 0); 2180 } catch (...) { 2181 return NULL; 2182 } 2183 2184 return hmod; 2185 } 2186 MyLoadLibraryW(PCWSTR pszModuleName)2187 HMODULE WINAPI CApiHookMgr::MyLoadLibraryW(PCWSTR pszModuleName) 2188 { 2189 HMODULE hmod = NULL; 2190 2191 try { 2192 hmod = CKernell32::LoadLibraryW(pszModuleName); 2193 GetInstance().HackModuleOnLoad(hmod, 0); 2194 } catch (...) { 2195 return NULL; 2196 } 2197 2198 return hmod; 2199 } 2200 MyLoadLibraryExA(PCSTR pszModuleName,HANDLE hFile,DWORD dwFlags)2201 HMODULE WINAPI CApiHookMgr::MyLoadLibraryExA(PCSTR pszModuleName, 2202 HANDLE hFile, 2203 DWORD dwFlags) 2204 { 2205 HMODULE hmod = NULL; 2206 2207 try { 2208 hmod = CKernell32::LoadLibraryExA(pszModuleName, 2209 hFile, 2210 dwFlags); 2211 GetInstance().HackModuleOnLoad(hmod, 0); 2212 } catch (...) { 2213 return NULL; 2214 } 2215 2216 return hmod; 2217 } 2218 MyLoadLibraryExW(PCWSTR pszModuleName,HANDLE hFile,DWORD dwFlags)2219 HMODULE WINAPI CApiHookMgr::MyLoadLibraryExW(PCWSTR pszModuleName, 2220 HANDLE hFile, 2221 DWORD dwFlags) 2222 { 2223 HMODULE hmod = NULL; 2224 2225 try { 2226 hmod = CKernell32::LoadLibraryExW(pszModuleName, 2227 hFile, 2228 dwFlags); 2229 GetInstance().HackModuleOnLoad(hmod, 0); 2230 } catch (...) { 2231 return NULL; 2232 } 2233 2234 return hmod; 2235 } 2236 MyGetProcAddress(HMODULE hmod,PCSTR pszProcName)2237 FARPROC WINAPI CApiHookMgr::MyGetProcAddress(HMODULE hmod, 2238 PCSTR pszProcName) 2239 { 2240 FARPROC pfn = NULL; 2241 2242 try { 2243 // Attempt to locate if the function has been hijacked 2244 CRef<CHookedFunction> pFuncHook = 2245 GetInstance().GetHookedFunction(hmod, 2246 pszProcName 2247 ); 2248 2249 if (pFuncHook != NULL) { 2250 // The address to return matches an address we want to hook 2251 // Return the hook function address instead 2252 pfn = pFuncHook->GetPfnHook(); 2253 } else { 2254 // Get the original address of the function 2255 pfn = xs_GetProcAddressWindows(hmod, pszProcName); 2256 } 2257 } catch (...) { 2258 return NULL; 2259 } 2260 2261 return pfn; 2262 } 2263 xs_GetProcAddressWindows(HMODULE hmod,PCSTR pszProcName)2264 FARPROC WINAPI CApiHookMgr::xs_GetProcAddressWindows( 2265 HMODULE hmod, 2266 PCSTR pszProcName 2267 ) 2268 { 2269 // return CKernell32::GetProcAddress(hmod, pszProcName); 2270 return (g_FGetProcAddress(hmod, pszProcName)); 2271 } 2272 2273 //////////////////////////////////////////////////////////////////////////// CPEi386(void)2274 CPEi386::CPEi386(void) : 2275 m_ModDbghelp(NULL), 2276 m_ImageDirectoryEntryToData(NULL) 2277 { 2278 m_ModDbghelp = g_LoadLibraryA("DBGHELP.DLL"); 2279 2280 if (m_ModDbghelp != NULL) { 2281 m_ImageDirectoryEntryToData = 2282 reinterpret_cast<FImageDirectoryEntryToData>( 2283 ::GetProcAddress(m_ModDbghelp, 2284 "ImageDirectoryEntryToData" 2285 )); 2286 if (!m_ImageDirectoryEntryToData ) { 2287 NCBI_THROW(CWinHookException, 2288 eDbghelp | Retriable(eRetriable_No), 2289 "Dbghelp.dll does not have " 2290 "ImageDirectoryEntryToData symbol"); 2291 } 2292 } else { 2293 NCBI_THROW(CWinHookException, 2294 eDbghelp | Retriable(eRetriable_No), 2295 "Dbghelp.dll not found"); 2296 } 2297 } 2298 ~CPEi386(void)2299 CPEi386::~CPEi386(void) throw() 2300 { 2301 try { 2302 if (m_ModDbghelp) { 2303 ::FreeLibrary(m_ModDbghelp); 2304 } 2305 } 2306 NCBI_CATCH_ALL_X( 2, NCBI_CURRENT_FUNCTION ) 2307 } 2308 GetInstance(void)2309 CPEi386& CPEi386::GetInstance(void) 2310 { 2311 static CSafeStatic<CPEi386> instance( 2312 CSafeStaticLifeSpan::eLifeSpan_Longest); 2313 2314 return (instance.Get()); 2315 } 2316 GetIAT(HMODULE base,int section) const2317 PVOID CPEi386::GetIAT(HMODULE base, int section) const 2318 { 2319 ULONG ulSize(0); 2320 2321 return m_ImageDirectoryEntryToData(base, 2322 TRUE, 2323 section, 2324 &ulSize 2325 ); 2326 } 2327 2328 2329 static bool s_AppExited = false; 2330 2331 2332 //////////////////////////////////////////////////////////////////////////// COnExitProcess(void)2333 COnExitProcess::COnExitProcess(void) 2334 : m_Hooked(false) 2335 { 2336 if (s_AppExited) 2337 return; 2338 2339 BOOL result = CApiHookMgr::GetInstance().HookImport( 2340 "Kernel32.DLL", 2341 "ExitProcess", 2342 reinterpret_cast<PROC>(COnExitProcess::xs_ExitProcess) 2343 ); 2344 2345 m_Hooked = (result == TRUE); 2346 2347 if (!m_Hooked) { 2348 ERR_POST_X(4, Warning 2349 << "ExitProcess is not hooked in " 2350 << NCBI_CURRENT_FUNCTION 2351 ); 2352 } 2353 } 2354 ~COnExitProcess(void)2355 COnExitProcess::~COnExitProcess(void) 2356 { 2357 try { 2358 ClearAll(); 2359 } 2360 NCBI_CATCH_ALL_X( 9, NCBI_CURRENT_FUNCTION ) 2361 2362 s_AppExited = true; 2363 } 2364 2365 COnExitProcess& Instance(void)2366 COnExitProcess::Instance(void) 2367 { 2368 static CSafeStatic<COnExitProcess> instance; 2369 2370 return instance.Get(); 2371 } 2372 2373 bool Add(TFunct funct)2374 COnExitProcess::Add(TFunct funct) 2375 { 2376 if (m_Hooked) { 2377 // Do not register functions if we cannot run them at right time. 2378 CFastMutexGuard mg(m_Mutex); 2379 2380 TRegistry::iterator it = find( 2381 m_Registry.begin(), 2382 m_Registry.end(), 2383 funct 2384 ); 2385 2386 if (it == m_Registry.end()) { 2387 m_Registry.push_back(funct); 2388 } 2389 2390 return true; 2391 } 2392 2393 return false; 2394 } 2395 2396 void Remove(TFunct funct)2397 COnExitProcess::Remove(TFunct funct) 2398 { 2399 CFastMutexGuard mg(m_Mutex); 2400 2401 TRegistry::iterator it = find( 2402 m_Registry.begin(), 2403 m_Registry.end(), 2404 funct 2405 ); 2406 2407 if (it != m_Registry.end()) { 2408 m_Registry.erase(it); 2409 } 2410 } 2411 2412 void ClearAll(void)2413 COnExitProcess::ClearAll(void) 2414 { 2415 if (!m_Registry.empty()) { 2416 CFastMutexGuard mg(m_Mutex); 2417 2418 if (!m_Registry.empty()) { 2419 // Run all functions ... 2420 ITERATE(TRegistry, it, m_Registry) { 2421 (*it)(); 2422 } 2423 2424 m_Registry.clear(); 2425 } 2426 } 2427 } 2428 xs_ExitProcess(UINT uExitCode)2429 void WINAPI COnExitProcess::xs_ExitProcess(UINT uExitCode) 2430 { 2431 COnExitProcess::Instance().ClearAll(); 2432 2433 CKernell32::ExitProcess(uExitCode); 2434 } 2435 2436 my_stricmp(const char * left,const char * right)2437 int my_stricmp(const char* left, const char* right) 2438 { 2439 for (; *left != 0 && *right != 0; ++left, ++right) { 2440 char cl = *left; 2441 char cr = *right; 2442 if (cl >= 'A' && cl <= 'Z') 2443 cl += 'a' - 'A'; 2444 if (cr >= 'A' && cr <= 'Z') 2445 cr += 'a' - 'A'; 2446 if (cl < cr) 2447 return -1; 2448 if (cl > cr) 2449 return 1; 2450 } 2451 if (*right == 0) { 2452 if (*left == 0) 2453 return 0; 2454 else 2455 return 1; 2456 } 2457 return -1; 2458 } 2459 2460 } 2461 2462 END_NCBI_SCOPE 2463 2464 #pragma warning( pop ) 2465 2466 #endif 2467 2468