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