Clone of Akilla's acserver @ https://github.com/deregtd/ACServer

ApiHook.h 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // ApiHook.h
  2. // Declaration and implementation of functions for hooking APIs in DLLs
  3. #ifndef __APIHOOK_H
  4. #define __APIHOOK_H
  5. enum eAddressing
  6. {
  7. eByName,
  8. eByOrdinal
  9. };
  10. struct cHookDescriptor
  11. {
  12. eAddressing m_addr;
  13. LPCTSTR m_szModule,
  14. m_szFunction;
  15. DWORD m_dwOrdinal,
  16. m_pNewFunction,
  17. m_pOldFunction;
  18. };
  19. // Functions to aid hooking
  20. #define MakePtr( cast, ptr, AddValue ) (cast)( (DWORD)(ptr)+(DWORD)(AddValue))
  21. PIMAGE_IMPORT_DESCRIPTOR getNamedImportDescriptor( HMODULE hModule, LPCSTR szImportMod )
  22. {
  23. PIMAGE_DOS_HEADER pDOSHeader = reinterpret_cast< PIMAGE_DOS_HEADER >( hModule );
  24. // Get the PE header.
  25. PIMAGE_NT_HEADERS pNTHeader = MakePtr( PIMAGE_NT_HEADERS, pDOSHeader, pDOSHeader->e_lfanew );
  26. // If there is no imports section, leave now.
  27. if( pNTHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ].VirtualAddress == NULL )
  28. return NULL;
  29. // Get the pointer to the imports section.
  30. PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr ( PIMAGE_IMPORT_DESCRIPTOR, pDOSHeader,
  31. pNTHeader->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ].VirtualAddress );
  32. // Loop through the import module descriptors looking for the
  33. // module whose name matches szImportMod.
  34. while( pImportDesc->Name != NULL )
  35. {
  36. PSTR szCurrMod = MakePtr( PSTR, pDOSHeader, pImportDesc->Name );
  37. if( stricmp( szCurrMod, szImportMod ) == 0 )
  38. // Found it.
  39. break;
  40. // Look at the next one.
  41. pImportDesc ++;
  42. }
  43. // If the name is NULL, then the module is not imported.
  44. if ( pImportDesc->Name == NULL )
  45. return ( NULL ) ;
  46. // All OK, Jumpmaster!
  47. return pImportDesc;
  48. }
  49. bool hookFunctions( cHookDescriptor *pHook, DWORD nCount, bool bHook )
  50. {
  51. HMODULE hProcess = ::GetModuleHandle( NULL );
  52. for( cHookDescriptor *i = pHook; i != pHook + nCount; ++ i )
  53. {
  54. // Get the specific import descriptor.
  55. PIMAGE_IMPORT_DESCRIPTOR pImportDesc = getNamedImportDescriptor( hProcess, i->m_szModule );
  56. if ( pImportDesc == NULL )
  57. continue;
  58. // Get the original thunk information for this DLL. I cannot use
  59. // the thunk information stored in the pImportDesc->FirstThunk
  60. // because the that is the array that the loader has already
  61. // bashed to fix up all the imports. This pointer gives us acess
  62. // to the function names.
  63. PIMAGE_THUNK_DATA pOrigThunk = MakePtr( PIMAGE_THUNK_DATA, hProcess, pImportDesc->OriginalFirstThunk );
  64. // Get the array pointed to by the pImportDesc->FirstThunk. This is
  65. // where I will do the actual bash.
  66. PIMAGE_THUNK_DATA pRealThunk = MakePtr( PIMAGE_THUNK_DATA, hProcess, pImportDesc->FirstThunk );
  67. // Loop through and look for the one that matches the name.
  68. for( ; pOrigThunk->u1.Function != NULL; ++ pOrigThunk, ++ pRealThunk )
  69. {
  70. if( i->m_addr == eByName )
  71. {
  72. if( pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG )
  73. // Only look at those that are imported by name, not ordinal.
  74. continue;
  75. // Look get the name of this imported function.
  76. PIMAGE_IMPORT_BY_NAME pByName = MakePtr( PIMAGE_IMPORT_BY_NAME, hProcess, pOrigThunk->u1.AddressOfData );
  77. // If the name starts with NULL, then just skip out now.
  78. if( pByName->Name[ 0 ] == '\0' )
  79. continue;
  80. if ( ::_tcsicmp( reinterpret_cast< char * >( pByName->Name ), i->m_szFunction ) != 0 )
  81. // This name dosen't match
  82. continue;
  83. }
  84. else
  85. {
  86. if( !( pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG ) )
  87. // The import must be by ordinal
  88. continue;
  89. if( ( pOrigThunk->u1.Ordinal & ~IMAGE_ORDINAL_FLAG ) != i->m_dwOrdinal )
  90. // Ordinal does not match
  91. continue;
  92. }
  93. // I found it. Now I need to change the protection to
  94. // writable before I do the blast. Note that I am now
  95. // blasting into the real thunk area!
  96. MEMORY_BASIC_INFORMATION mbi_thunk;
  97. ::VirtualQuery( pRealThunk, &mbi_thunk, sizeof ( MEMORY_BASIC_INFORMATION ) );
  98. ::VirtualProtect( mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect );
  99. // Save the original address if requested.
  100. if( bHook )
  101. {
  102. i->m_pOldFunction = (ULONG) pRealThunk->u1.Function;
  103. pRealThunk->u1.Function = (ULONG *) i->m_pNewFunction;
  104. }
  105. else if( i->m_pOldFunction != NULL )
  106. pRealThunk->u1.Function = (ULONG *) i->m_pOldFunction;
  107. DWORD dwOldProtect;
  108. ::VirtualProtect( mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect );
  109. break;
  110. }
  111. }
  112. return true;
  113. }
  114. #endif