Game Accessibility Library logo SourceForge.net Logo
Game Accessibility Suite: CAT/CATIntercept.cpp Source File

CATIntercept.cpp

Go to the documentation of this file.
00001 /// \file  CATIntercept.cpp
00002 /// \brief Function interception base class
00003 /// \ingroup CAT
00004 ///
00005 /// Copyright (c) 2007-2008 by Michael Ellison.
00006 /// See COPYING.txt for the \ref gaslicense License (MIT License).
00007 ///
00008 // $Author: mikeellison $
00009 // $Date: 2008-01-29 07:01:23 -0600 (Tue, 29 Jan 2008) $
00010 // $Revision:   $
00011 // $NoKeywords: $
00012 #include "CATIntercept.h"
00013 #ifdef CAT_CONFIG_WIN32
00014 #include "CATString.h"
00015 
00016 
00017 CATIntercept::CATIntercept()
00018 {
00019     // Make sure we have the rights to tweak protection levels
00020     HANDLE hToken;
00021     TOKEN_PRIVILEGES tkp;
00022     if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
00023     {    
00024         LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid); 
00025         tkp.PrivilegeCount = 1;
00026         tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;   
00027         AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);        
00028         CloseHandle(hToken);
00029     }
00030 }
00031 
00032 CATIntercept::~CATIntercept()
00033 {
00034     RestoreAll();
00035 }
00036 
00037 void CATIntercept::RestoreAll()
00038 {
00039     while (fHooks.size())
00040     {
00041         Restore(fHooks[0]);        
00042     }
00043 }
00044 
00045 CATResult CATIntercept::Intercept( void*        target, 
00046                                    void*        hookFunc, 
00047                                    CATUInt32    stubBytes,
00048                                    CATHOOK*&    newHook,
00049                                    void*        userParam)
00050 {
00051     newHook = 0;
00052     CATHOOK* hookInfo = (CATHOOK*)VirtualAlloc(0,sizeof(CATHOOK),MEM_COMMIT,PAGE_EXECUTE_READWRITE);
00053     
00054     // Fill with NOPs
00055     memset(hookInfo,0x90,sizeof(CATHOOK));
00056     
00057     hookInfo->HookFunc         = hookFunc;
00058     hookInfo->Target           = target;
00059     hookInfo->OrgInstLen       = stubBytes;
00060     hookInfo->UserParam        = userParam;
00061     hookInfo->InterceptObj     = this;
00062 
00063     // Check for jump table - if it is, follow it to real code and hook there instead.
00064     while (*(CATUInt8*)target == 0xe9)
00065     {
00066         target = ((CATUInt8*)target + *(CATUInt32*)((CATUInt8*)target +1)) + 5;
00067         hookInfo->Target = target;
00068     }
00069     
00070     // Add our hookInfo struct to the stack for the hook function
00071     hookInfo->PrePushHook        = 0x68;
00072     hookInfo->PrePushHookAddress = (CATUInt32)(UINT_PTR)hookInfo;
00073 
00074     // Add swap between the hook push on the stack and the return address.    
00075     // xchg eax,[esp]
00076     // xchg eax,[esp+4]
00077     // xchg eax,[esp]
00078     hookInfo->StackSwap1[0] = 0x87240487;
00079     hookInfo->StackSwap1[1] = 0x87042444;
00080     hookInfo->StackSwap2    = 0x2404;
00081 
00082     // Set a jump to go to the hook function from our HookInfo struct.
00083     hookInfo->HookJmp           = 0xe9;
00084     hookInfo->HookJmpLoc        = ( ((CATUInt32)(UINT_PTR)hookFunc)) -
00085                                    (((CATUInt32)(UINT_PTR)hookInfo) + offsetof(CATHOOK,HookJmp) + 5);
00086 
00087 
00088     // Set up jump to go back to host, after stub bytes
00089     hookInfo->PostPatchJump    = 0xe9;
00090     hookInfo->PostPatchJumpLoc = ( ((CATUInt32)(UINT_PTR)target)) -
00091                                  (((CATUInt32)(UINT_PTR)hookInfo) + offsetof(CATHOOK,PostPatchJump) + 5) + stubBytes;
00092 
00093 
00094     // store original bytes
00095     DWORD oldProtect;
00096     if (0 == VirtualProtectEx(GetCurrentProcess(),target,stubBytes,PAGE_EXECUTE_READWRITE,&oldProtect))
00097         return CATRESULT(CAT_ERR_HOOK_PROTECT_FAILED);
00098 
00099     memcpy(hookInfo->OrgInst,target,stubBytes);
00100 
00101     fHooks.push_back(hookInfo);
00102     newHook = hookInfo;  
00103 
00104     // Patch original function to jump to our CATHOOK struct
00105     *((CATUInt8*)target)                  = 0xe9;
00106     *(CATUInt32*)((CATUInt8*)target + 1)  = ((CATUInt32)(UINT_PTR)hookInfo) - (((CATUInt32)(UINT_PTR)target)+5);
00107     
00108     if (0 == VirtualProtectEx(GetCurrentProcess(),target,stubBytes,oldProtect,&oldProtect))
00109         return CATRESULT(CAT_ERR_HOOK_PROTECT_FAILED);
00110 
00111     // Flush cache just in case. 
00112     FlushInstructionCache(GetCurrentProcess(),0,0);
00113 
00114     return CAT_SUCCESS;
00115 }
00116 
00117 CATResult CATIntercept::Restore(CATHOOK*& hookInfo)
00118 {
00119     if (hookInfo == 0)
00120         return CAT_ERR_INVALID_PARAM;
00121 
00122     // Remove hook
00123     DWORD oldProtect;
00124     
00125     if (0 == VirtualProtectEx(GetCurrentProcess(),
00126                               hookInfo->Target,
00127                               hookInfo->OrgInstLen,
00128                               PAGE_EXECUTE_READWRITE,
00129                               &oldProtect))
00130     {
00131         return CATRESULT(CAT_ERR_HOOK_PROTECT_FAILED);
00132     }
00133     
00134     memcpy(hookInfo->Target,hookInfo->OrgInst,hookInfo->OrgInstLen);
00135     
00136     if (0 == VirtualProtectEx(GetCurrentProcess(),
00137                               hookInfo->Target,
00138                               hookInfo->OrgInstLen,
00139                               oldProtect,
00140                               &oldProtect))
00141     {
00142         return CATRESULT(CAT_ERR_HOOK_PROTECT_FAILED);
00143     }
00144     
00145     FlushInstructionCache(GetCurrentProcess(),0,0);
00146 
00147     // Remove from vector and free it
00148     std::vector<CATHOOK*>::iterator iter = fHooks.begin();
00149     while (iter != fHooks.end())
00150     {
00151         if ( (*iter) == hookInfo )
00152         {            
00153             VirtualFree(hookInfo,sizeof(CATHOOK),MEM_RELEASE);
00154             hookInfo = 0;
00155             fHooks.erase(iter);
00156             return CAT_SUCCESS;
00157         }
00158         ++iter;
00159     }    
00160 
00161     return CATRESULT(CAT_ERR_HOOK_NOT_FOUND);
00162 }
00163 
00164 void* CATIntercept::GetFunctionFromVTable(void* objectPtr, CATUInt32 vtableIndex)
00165 {
00166     UINT_PTR* vtable = (UINT_PTR*)*(UINT_PTR*)objectPtr;
00167     void *func = 0;
00168     
00169     // just return 0 on invalid reference.
00170     try
00171     {
00172         func = (void*)vtable[vtableIndex];
00173     }
00174     catch (...)
00175     {
00176         func = 0;
00177     }
00178 
00179     return func;
00180 }
00181 
00182 CATResult CATIntercept::InterceptCOMObject(   void*                         comObject,
00183                                               CATINTERCEPT_COM_TABLE_ENTRY* interceptTable,
00184                                               void*                         userParam)
00185 {
00186     if (interceptTable == 0)
00187         return CATRESULT(CAT_ERR_INVALID_PARAM);
00188 
00189     CATResult result = CAT_SUCCESS;
00190     while (interceptTable->VTableIndex != (CATUInt32)-1)
00191     {
00192         void* func = GetFunctionFromVTable(comObject, interceptTable->VTableIndex);
00193         if (func)
00194         {
00195             CATHOOK* tmpHook = 0;
00196             result = Intercept( func, 
00197                                 interceptTable->HookFunction, 
00198                                 interceptTable->StubLength,                                
00199                                 tmpHook, 
00200                                 userParam);
00201             if (CATFAILED(result))
00202                 return result;
00203         }
00204         interceptTable++;
00205     }
00206 
00207     return CAT_SUCCESS;
00208 }
00209 
00210 // Save the interception data for a COM intercept table to the registry,
00211 // so we don't have to do all the nasty COM creation later.
00212 CATResult CATIntercept::SaveInterceptData (   const CATWChar*               objectName,
00213                                               void*                         comObject,
00214                                               CATINTERCEPT_COM_TABLE_ENTRY* interceptTable,
00215                                               void*                         userParam)
00216 {
00217     if (interceptTable == 0)
00218         return CATRESULT(CAT_ERR_INVALID_PARAM);
00219 
00220     // Create keys to 
00221     HKEY interceptKey = 0;
00222     ::RegCreateKeyEx(HKEY_CURRENT_USER,
00223                      L"SOFTWARE\\GameAccessSuite\\CATIntercept",
00224                      0,0,
00225                      REG_OPTION_NON_VOLATILE,
00226                      KEY_ALL_ACCESS,
00227                      NULL,
00228                      &interceptKey,
00229                      0);
00230 
00231     if (interceptKey == 0)
00232         return CAT_ERROR;
00233     
00234     HKEY objectKey = 0;
00235     ::RegCreateKeyEx(interceptKey,objectName,0,0,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,0,&objectKey,0);
00236 
00237     if (objectKey == 0)
00238     {
00239         ::RegCloseKey(interceptKey);
00240         return CAT_ERROR;
00241     }
00242 
00243 
00244     CATResult result = CAT_SUCCESS;
00245     while (interceptTable->VTableIndex != (CATUInt32)-1)
00246     {
00247         void* func = GetFunctionFromVTable(comObject, interceptTable->VTableIndex);
00248 
00249         CATString vindex = interceptTable->VTableIndex;
00250 
00251         ::RegSetValueEx(objectKey,vindex,0,REG_DWORD,(BYTE*)&func,4);
00252 
00253         interceptTable++;
00254     }
00255 
00256     ::RegCloseKey(objectKey);
00257     ::RegCloseKey(interceptKey);
00258     return CAT_SUCCESS;
00259 }
00260 
00261 /// Load interception data from the registry if it's available
00262 CATResult CATIntercept::LoadAndHook       (   const CATWChar*               objectName,
00263                                               CATINTERCEPT_COM_TABLE_ENTRY* interceptTable,
00264                                               void*                         userParam)
00265 {
00266     if (interceptTable == 0)
00267         return CATRESULT(CAT_ERR_INVALID_PARAM);
00268 
00269     // Create keys to 
00270     HKEY interceptKey = 0;
00271     ::RegCreateKeyEx(HKEY_CURRENT_USER,
00272                      L"SOFTWARE\\GameAccessSuite\\CATIntercept",
00273                      0,0,
00274                      REG_OPTION_NON_VOLATILE,
00275                      KEY_ALL_ACCESS,
00276                      NULL,
00277                      &interceptKey,
00278                      0);
00279 
00280     if (interceptKey == 0)
00281         return CAT_ERROR;
00282     
00283     HKEY objectKey = 0;
00284     ::RegCreateKeyEx(interceptKey,objectName,0,0,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,0,&objectKey,0);
00285 
00286     if (objectKey == 0)
00287     {
00288         ::RegCloseKey(interceptKey);
00289         return CAT_ERROR;
00290     }
00291 
00292     CATINTERCEPT_COM_TABLE_ENTRY* start = interceptTable;
00293     CATResult result = CAT_SUCCESS;
00294     // First, scan for all requested functions. If any aren't there, going to have to do it from scratch.
00295     while (interceptTable->VTableIndex != (CATUInt32)-1)
00296     {
00297         CATString vindex = interceptTable->VTableIndex;
00298         void* func = 0;
00299         DWORD type = 0;
00300         DWORD dataLen = 4;
00301         ::RegQueryValueEx(objectKey,vindex,0,&type,(BYTE*)&func,&dataLen);
00302         
00303         if (func == 0)
00304         {
00305             ::RegCloseKey(objectKey);
00306             ::RegCloseKey(interceptKey);
00307             return CAT_ERROR;
00308         }
00309                     
00310         interceptTable++;
00311     }
00312 
00313     // Got 'em.  Do the hook.
00314     interceptTable = start;
00315     while (interceptTable->VTableIndex != (CATUInt32)-1)
00316     {
00317         CATString vindex = interceptTable->VTableIndex;
00318         void* func = 0;
00319         DWORD type = 0;
00320         DWORD dataLen = 4;
00321         ::RegQueryValueEx(objectKey,vindex,0,&type,(BYTE*)&func,&dataLen);
00322         
00323         if (func)
00324         {
00325             CATHOOK* tmpHook = 0;
00326             result = Intercept( func, 
00327                                 interceptTable->HookFunction, 
00328                                 interceptTable->StubLength,                                
00329                                 tmpHook, 
00330                                 userParam);
00331             if (CATFAILED(result))
00332                 return result;
00333         }
00334                     
00335         interceptTable++;
00336     }
00337 
00338     ::RegCloseKey(objectKey);
00339     ::RegCloseKey(interceptKey);
00340     return CAT_SUCCESS;
00341 }
00342 
00343 
00344 CATResult CATIntercept::InterceptDLL(   HMODULE                       module,
00345                                         CATINTERCEPT_DLL_TABLE_ENTRY* interceptTable,
00346                                         void*                         userParam)
00347 {
00348     if (interceptTable == 0)
00349         return CATRESULT(CAT_ERR_INVALID_PARAM);
00350 
00351     CATResult result = CAT_SUCCESS;
00352     while (interceptTable->FunctionName != 0)
00353     {
00354         void* func = ::GetProcAddress(module,interceptTable->FunctionName);
00355         if (func)
00356         {
00357             CATHOOK* tmpHook = 0;
00358             result = Intercept( func, 
00359                                 interceptTable->HookFunction, 
00360                                 interceptTable->StubLength,                                
00361                                 tmpHook, 
00362                                 userParam);
00363             if (CATFAILED(result))
00364                 return result;
00365         }
00366         interceptTable++;
00367     }
00368 
00369     return CAT_SUCCESS;
00370 }
00371 
00372 #endif //CAT_CONFIG_WIN32

Generated on Mon Feb 11 04:09:44 2008 for Game Accessibility Suite by doxygen 1.5.4