• Main Page
  • Classes
  • Files
  • File List

HookDLL.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2012 Bolton Software Ltd.
00004  * Copyright (C) 2011 Chris Schoeneman
00005  * 
00006  * This package is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * found in the file COPYING that should have accompanied this file.
00009  * 
00010  * This package is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 /*--------------------------------------------------------------------------------------------------------
00020     Original comment:
00021 
00022     APIHIJACK.CPP - Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000.
00023     http://msdn.microsoft.com/library/periodic/period00/hood0200.htm
00024     Adapted by Wade Brainerd, wadeb@wadeb.com
00025 --------------------------------------------------------------------------------------------------------*/
00026 
00027 #define WIN32_LEAN_AND_MEAN
00028 #include <windows.h>
00029 #include <stdio.h>
00030 #include "HookDLL.h"
00031 
00032 #include <sstream>
00033 std::stringstream _hookDllLogStream;
00034 #define LOG(s) \
00035     _hookDllLogStream.str(""); \
00036     _hookDllLogStream << "Synergy HookDLL: " << s << std::endl; \
00037     OutputDebugString( _hookDllLogStream.str().c_str() );
00038 
00039 //===========================================================================
00040 // Called from the DLPD_IAT_STUB stubs.  Increments "count" field of the stub
00041 
00042 void __cdecl DefaultHook( PVOID dummy )
00043 {
00044     // asm only supported on 32-bit
00045 #ifdef _M_IX86
00046     __asm   pushad  // Save all general purpose registers
00047 
00048     // Get return address, then subtract 5 (size of a CALL X instruction)
00049     // The result points at a DLPD_IAT_STUB
00050 
00051     // pointer math!  &dummy-1 really subtracts sizeof(PVOID)
00052     PDWORD pRetAddr = (PDWORD)(&dummy - 1);
00053 
00054     DLPD_IAT_STUB * pDLPDStub = (DLPD_IAT_STUB *)(*pRetAddr - 5);
00055 
00056     pDLPDStub->count++;
00057 
00058     #if 0
00059     // Remove the above conditional to get a cheezy API trace from
00060     // the loader process.  It's slow!
00061     if ( !IMAGE_SNAP_BY_ORDINAL( pDLPDStub->pszNameOrOrdinal) )
00062     {
00063         LOG( "Called hooked function: " );
00064         LOG( (PSTR)pDLPDStub->pszNameOrOrdinal );
00065     }
00066     #endif
00067 
00068     __asm   popad   // Restore all general purpose registers
00069 #endif
00070 }
00071 
00072 // This function must be __cdecl!!!
00073 void __cdecl DelayLoadProfileDLL_UpdateCount( PVOID dummy );
00074 
00075 PIMAGE_IMPORT_DESCRIPTOR g_pFirstImportDesc;
00076 
00077 //===========================================================================
00078 // Given an HMODULE, returns a pointer to the PE header
00079 
00080 PIMAGE_NT_HEADERS PEHeaderFromHModule(HMODULE hModule)
00081 {
00082     PIMAGE_NT_HEADERS pNTHeader = 0;
00083     
00084     __try
00085     {
00086         if ( PIMAGE_DOS_HEADER(hModule)->e_magic != IMAGE_DOS_SIGNATURE )
00087             __leave;
00088 
00089         pNTHeader = PIMAGE_NT_HEADERS(PBYTE(hModule)
00090                     + PIMAGE_DOS_HEADER(hModule)->e_lfanew);
00091         
00092         if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
00093             pNTHeader = 0;
00094     }
00095     __except( EXCEPTION_EXECUTE_HANDLER )
00096     {       
00097     }
00098 
00099     return pNTHeader;
00100 }
00101 
00102 //===========================================================================
00103 // Builds stubs for and redirects the IAT for one DLL (pImportDesc)
00104 
00105 bool RedirectIAT( SDLLHook* DLLHook, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, PVOID pBaseLoadAddr )
00106 {
00107     PIMAGE_THUNK_DATA pIAT;     // Ptr to import address table
00108     PIMAGE_THUNK_DATA pINT;     // Ptr to import names table
00109     PIMAGE_THUNK_DATA pIteratingIAT;
00110 
00111     // Figure out which OS platform we're on
00112     OSVERSIONINFO osvi; 
00113     osvi.dwOSVersionInfoSize = sizeof(osvi);
00114     GetVersionEx( &osvi );
00115 
00116     // If no import names table, we can't redirect this, so bail
00117     if ( pImportDesc->OriginalFirstThunk == 0 )
00118     {
00119         LOG( "no IAT available." );
00120         return false;
00121     }
00122 
00123     pIAT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->FirstThunk );
00124     pINT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->OriginalFirstThunk );
00125 
00126     // Count how many entries there are in this IAT.  Array is 0 terminated
00127     pIteratingIAT = pIAT;
00128     unsigned cFuncs = 0;
00129     while ( pIteratingIAT->u1.Function )
00130     {
00131         cFuncs++;
00132         pIteratingIAT++;
00133     }
00134 
00135     if ( cFuncs == 0 )  // If no imported functions, we're done!
00136     {
00137         LOG( "no imported functions" );
00138         return false;
00139     }
00140 
00141     // These next few lines ensure that we'll be able to modify the IAT,
00142     // which is often in a read-only section in the EXE.
00143     DWORD flOldProtect, flNewProtect, flDontCare;
00144     MEMORY_BASIC_INFORMATION mbi;
00145     
00146     // Get the current protection attributes                            
00147     VirtualQuery( pIAT, &mbi, sizeof(mbi) );
00148     
00149     // remove ReadOnly and ExecuteRead attributes, add on ReadWrite flag
00150     flNewProtect = mbi.Protect;
00151     flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ);
00152     flNewProtect |= (PAGE_READWRITE);
00153     
00154     if ( !VirtualProtect(   pIAT, sizeof(PVOID) * cFuncs,
00155                             flNewProtect, &flOldProtect) )
00156     {
00157         LOG( "could not remove ReadOnly and ExecuteRead attributes, or add ReadWrite flag" );
00158         return false;
00159     }
00160 
00161     // If the Default hook is enabled, build an array of redirection stubs in the processes memory.
00162     DLPD_IAT_STUB * pStubs = 0;
00163     if ( DLLHook->UseDefault )
00164     {
00165         // Allocate memory for the redirection stubs.  Make one extra stub at the
00166         // end to be a sentinel
00167         pStubs = new DLPD_IAT_STUB[ cFuncs + 1];
00168         if ( !pStubs )
00169         {
00170             LOG( "could not allocate memory for redirection stubs" );
00171             return false;
00172         }
00173     }
00174 
00175     // Scan through the IAT, completing the stubs and redirecting the IAT
00176     // entries to point to the stubs
00177     pIteratingIAT = pIAT;
00178 
00179     while ( pIteratingIAT->u1.Function )
00180     {
00181         void* HookFn = 0;  // Set to either the SFunctionHook or pStubs.
00182 
00183         if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) )  // import by name
00184         {
00185             PIMAGE_IMPORT_BY_NAME pImportName = MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr, pINT->u1.AddressOfData );
00186 
00187             LOG( "checking function with name: " << pImportName->Name );
00188 
00189             // Iterate through the hook functions, searching for this import.
00190             SFunctionHook* FHook = DLLHook->Functions;
00191             while ( FHook->Name )
00192             {
00193                 if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 )
00194                 {
00195                     // Save the old function in the SFunctionHook structure and get the new one.
00196                     FHook->OrigFn = (void*)pIteratingIAT->u1.Function;
00197                     HookFn = FHook->HookFn;
00198 
00199                     LOG( "hooked function: " << pImportName->Name );
00200 
00201                     break;
00202                 }
00203 
00204                 FHook++;
00205             }
00206 
00207             // If the default function is enabled, store the name for the user.
00208             if ( DLLHook->UseDefault )
00209                 pStubs->pszNameOrOrdinal = (DWORD)&pImportName->Name;
00210         }
00211         else // added comparison for ordinal
00212         {
00213             LOG( "checking function at ordinal: " << pINT->u1.Ordinal );
00214 
00215             SFunctionHook* FHook = DLLHook->Functions;
00216             while ( FHook->Name )
00217             {
00218                 if ( (DWORD)FHook->Name == pINT->u1.Ordinal )
00219                 {
00220                     // Save the old function in the SFunctionHook structure and get the new one.
00221                     FHook->OrigFn = (void*)pIteratingIAT->u1.Function;
00222                     HookFn = FHook->HookFn;
00223 
00224                     LOG( "hooked ordinal: " << pINT->u1.Ordinal );
00225 
00226                     break;
00227                 }
00228                 FHook++;
00229             }
00230             // If the default function is enabled, store the ordinal for the user.
00231             if ( DLLHook->UseDefault )
00232                 pStubs->pszNameOrOrdinal = (DWORD)pINT->u1.Ordinal;
00233         }
00234 
00235         // If the default function is enabled, fill in the fields to the stub code.
00236         if ( DLLHook->UseDefault )
00237         {
00238             pStubs->data_call = (DWORD)(PDWORD)DLLHook->DefaultFn
00239                                 - (DWORD)(PDWORD)&pStubs->instr_JMP;
00240             pStubs->data_JMP = *(PDWORD)pIteratingIAT - (DWORD)(PDWORD)&pStubs->count;
00241 
00242             // If it wasn't manually hooked, use the Stub function.
00243             if ( !HookFn )
00244                 HookFn = (void*)pStubs;
00245         }
00246 
00247         // Replace the IAT function pointer if we have a hook.
00248         if ( HookFn )
00249         {
00250             // Cheez-o hack to see if what we're importing is code or data.
00251             // If it's code, we shouldn't be able to write to it
00252             if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) )
00253             {
00254                 pIteratingIAT->u1.Function = (DWORD)(PDWORD)HookFn;
00255             }
00256             else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
00257             {
00258                 // Special hack for Win9X, which builds stubs for imported
00259                 // functions in system DLLs (Loaded above 2GB).  These stubs are
00260                 // writeable, so we have to explicitly check for this case
00261                 if ( pIteratingIAT->u1.Function > (DWORD)(PDWORD)0x80000000 )
00262                     pIteratingIAT->u1.Function = (DWORD)(PDWORD)HookFn;
00263             }
00264         }
00265 
00266         if ( DLLHook->UseDefault )
00267             pStubs++;           // Advance to next stub
00268 
00269         pIteratingIAT++;    // Advance to next IAT entry
00270         pINT++;             // Advance to next INT entry
00271     }
00272 
00273     if ( DLLHook->UseDefault )
00274         pStubs->pszNameOrOrdinal = 0;   // Final stub is a sentinel
00275 
00276     // Put the page attributes back the way they were.
00277     VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare);
00278     
00279     return true;
00280 }
00281 
00282 //===========================================================================
00283 // Top level routine to find the EXE's imports, and redirect them
00284 bool HookAPICalls( SDLLHook* hook )
00285 {
00286     if ( !hook )
00287     {
00288         LOG("no hook");
00289         return false;
00290     }
00291 
00292     HMODULE hModEXE = GetModuleHandle( 0 );
00293 
00294     PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule( hModEXE );
00295     
00296     if ( !pExeNTHdr )
00297     {
00298         LOG("no PE header");
00299         return false;
00300     }
00301 
00302     DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory
00303                         [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
00304     if ( !importRVA )
00305     {
00306         LOG("no virtual address for image directory entry import");
00307         return false;
00308     }
00309 
00310     // Convert imports RVA to a usable pointer
00311     PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr( PIMAGE_IMPORT_DESCRIPTOR,
00312                                                     hModEXE, importRVA );
00313 
00314     // Save off imports address in a global for later use
00315     g_pFirstImportDesc = pImportDesc;   
00316 
00317     // Iterate through each import descriptor, and redirect if appropriate
00318     while ( pImportDesc->FirstThunk )
00319     {
00320         PSTR pszImportModuleName = MakePtr( PSTR, hModEXE, pImportDesc->Name);
00321 
00322         if ( lstrcmpi( pszImportModuleName, hook->Name ) == 0 )
00323         {
00324             LOG( "found " << hook->Name << " in process" );
00325 
00326             if ( RedirectIAT( hook, pImportDesc, (PVOID)hModEXE ) )
00327             {
00328                 LOG( "redirected IAT ok" );
00329                 return true;
00330             }
00331             else
00332             {
00333                 LOG( "failed to redirect IAT" );
00334             }
00335         }
00336         
00337         pImportDesc++;  // Advance to next import descriptor
00338     }
00339 
00340     return false;
00341 }
00342 

Generated on Tue Jun 18 2013 00:00:06 for Synergy by  doxygen 1.7.1