| 11 | cycrow | 1 | /*--------------------------------------------------------------------------------------------------------
 | 
        
           |  |  | 2 |     APIHIJACK.CPP - Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000.
 | 
        
           |  |  | 3 |     http://msdn.microsoft.com/library/periodic/period00/hood0200.htm
 | 
        
           |  |  | 4 |     Adapted by Wade Brainerd, wadeb@wadeb.com
 | 
        
           |  |  | 5 | --------------------------------------------------------------------------------------------------------*/
 | 
        
           |  |  | 6 | #define WIN32_LEAN_AND_MEAN
 | 
        
           |  |  | 7 | #include <windows.h>
 | 
        
           |  |  | 8 | #include <stdio.h>
 | 
        
           |  |  | 9 | #include "apihijack.h"
 | 
        
           |  |  | 10 |   | 
        
           |  |  | 11 | //===========================================================================
 | 
        
           |  |  | 12 | // Called from the DLPD_IAT_STUB stubs.  Increments "count" field of the stub
 | 
        
           |  |  | 13 |   | 
        
           |  |  | 14 | void __cdecl DefaultHook( PVOID dummy )
 | 
        
           |  |  | 15 | {
 | 
        
           |  |  | 16 |     __asm   pushad  // Save all general purpose registers
 | 
        
           |  |  | 17 |   | 
        
           |  |  | 18 |     // Get return address, then subtract 5 (size of a CALL X instruction)
 | 
        
           |  |  | 19 |     // The result points at a DLPD_IAT_STUB
 | 
        
           |  |  | 20 |   | 
        
           |  |  | 21 |     // pointer math!  &dummy-1 really subtracts sizeof(PVOID)
 | 
        
           |  |  | 22 |     PDWORD pRetAddr = (PDWORD)(&dummy - 1);
 | 
        
           |  |  | 23 |   | 
        
           |  |  | 24 |     DLPD_IAT_STUB * pDLPDStub = (DLPD_IAT_STUB *)(*pRetAddr - 5);
 | 
        
           |  |  | 25 |   | 
        
           |  |  | 26 |     pDLPDStub->count++;
 | 
        
           |  |  | 27 |   | 
        
           |  |  | 28 |     #if 0
 | 
        
           |  |  | 29 |     // Remove the above conditional to get a cheezy API trace from
 | 
        
           |  |  | 30 |     // the loader process.  It's slow!
 | 
        
           |  |  | 31 |     if ( !IMAGE_SNAP_BY_ORDINAL( pDLPDStub->pszNameOrOrdinal) )
 | 
        
           |  |  | 32 |     {
 | 
        
           |  |  | 33 |         OutputDebugString( "Called hooked function: " );
 | 
        
           |  |  | 34 |         OutputDebugString( (PSTR)pDLPDStub->pszNameOrOrdinal );
 | 
        
           |  |  | 35 |         OutputDebugString( "\n" );
 | 
        
           |  |  | 36 |     }
 | 
        
           |  |  | 37 |     #endif
 | 
        
           |  |  | 38 |   | 
        
           |  |  | 39 |     __asm   popad   // Restore all general purpose registers
 | 
        
           |  |  | 40 | }
 | 
        
           |  |  | 41 |   | 
        
           |  |  | 42 | // This function must be __cdecl!!!
 | 
        
           |  |  | 43 | void __cdecl DelayLoadProfileDLL_UpdateCount( PVOID dummy );
 | 
        
           |  |  | 44 |   | 
        
           |  |  | 45 | PIMAGE_IMPORT_DESCRIPTOR g_pFirstImportDesc;
 | 
        
           |  |  | 46 |   | 
        
           |  |  | 47 | //===========================================================================
 | 
        
           |  |  | 48 | // Given an HMODULE, returns a pointer to the PE header
 | 
        
           |  |  | 49 |   | 
        
           |  |  | 50 | PIMAGE_NT_HEADERS PEHeaderFromHModule(HMODULE hModule)
 | 
        
           |  |  | 51 | {
 | 
        
           |  |  | 52 |     PIMAGE_NT_HEADERS pNTHeader = 0;
 | 
        
           |  |  | 53 |   | 
        
           |  |  | 54 |     __try
 | 
        
           |  |  | 55 |     {
 | 
        
           |  |  | 56 |         if ( PIMAGE_DOS_HEADER(hModule)->e_magic != IMAGE_DOS_SIGNATURE )
 | 
        
           |  |  | 57 |             __leave;
 | 
        
           |  |  | 58 |   | 
        
           |  |  | 59 |         pNTHeader = PIMAGE_NT_HEADERS(PBYTE(hModule)
 | 
        
           |  |  | 60 |                     + PIMAGE_DOS_HEADER(hModule)->e_lfanew);
 | 
        
           |  |  | 61 |   | 
        
           |  |  | 62 |         if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
 | 
        
           |  |  | 63 |             pNTHeader = 0;
 | 
        
           |  |  | 64 |     }
 | 
        
           |  |  | 65 |     __except( EXCEPTION_EXECUTE_HANDLER )
 | 
        
           |  |  | 66 |     {       
 | 
        
           |  |  | 67 |     }
 | 
        
           |  |  | 68 |   | 
        
           |  |  | 69 |     return pNTHeader;
 | 
        
           |  |  | 70 | }
 | 
        
           |  |  | 71 |   | 
        
           |  |  | 72 | //===========================================================================
 | 
        
           |  |  | 73 | // Builds stubs for and redirects the IAT for one DLL (pImportDesc)
 | 
        
           |  |  | 74 |   | 
        
           |  |  | 75 | bool RedirectIAT( SDLLHook* DLLHook, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, PVOID pBaseLoadAddr )
 | 
        
           |  |  | 76 | {
 | 
        
           |  |  | 77 |     PIMAGE_THUNK_DATA pIAT;     // Ptr to import address table
 | 
        
           |  |  | 78 |     PIMAGE_THUNK_DATA pINT;     // Ptr to import names table
 | 
        
           |  |  | 79 |     PIMAGE_THUNK_DATA pIteratingIAT;
 | 
        
           |  |  | 80 |   | 
        
           |  |  | 81 |     // Figure out which OS platform we're on
 | 
        
           |  |  | 82 |     OSVERSIONINFO osvi; 
 | 
        
           |  |  | 83 |     osvi.dwOSVersionInfoSize = sizeof(osvi);
 | 
        
           |  |  | 84 |     GetVersionEx( &osvi );
 | 
        
           |  |  | 85 |   | 
        
           |  |  | 86 |     // If no import names table, we can't redirect this, so bail
 | 
        
           |  |  | 87 |     if ( pImportDesc->OriginalFirstThunk == 0 )
 | 
        
           |  |  | 88 |         return false;
 | 
        
           |  |  | 89 |   | 
        
           |  |  | 90 |     pIAT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->FirstThunk );
 | 
        
           |  |  | 91 |     pINT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->OriginalFirstThunk );
 | 
        
           |  |  | 92 |   | 
        
           |  |  | 93 |     // Count how many entries there are in this IAT.  Array is 0 terminated
 | 
        
           |  |  | 94 |     pIteratingIAT = pIAT;
 | 
        
           |  |  | 95 |     unsigned cFuncs = 0;
 | 
        
           |  |  | 96 |     while ( pIteratingIAT->u1.Function )
 | 
        
           |  |  | 97 |     {
 | 
        
           |  |  | 98 |         cFuncs++;
 | 
        
           |  |  | 99 |         pIteratingIAT++;
 | 
        
           |  |  | 100 |     }
 | 
        
           |  |  | 101 |   | 
        
           |  |  | 102 |     if ( cFuncs == 0 )  // If no imported functions, we're done!
 | 
        
           |  |  | 103 |         return false;
 | 
        
           |  |  | 104 |   | 
        
           |  |  | 105 |     // These next few lines ensure that we'll be able to modify the IAT,
 | 
        
           |  |  | 106 |     // which is often in a read-only section in the EXE.
 | 
        
           |  |  | 107 |     DWORD flOldProtect, flNewProtect, flDontCare;
 | 
        
           |  |  | 108 |     MEMORY_BASIC_INFORMATION mbi;
 | 
        
           |  |  | 109 |   | 
        
           |  |  | 110 |     // Get the current protection attributes                            
 | 
        
           |  |  | 111 |     VirtualQuery( pIAT, &mbi, sizeof(mbi) );
 | 
        
           |  |  | 112 |   | 
        
           |  |  | 113 |     // remove ReadOnly and ExecuteRead attributes, add on ReadWrite flag
 | 
        
           |  |  | 114 |     flNewProtect = mbi.Protect;
 | 
        
           |  |  | 115 |     flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ);
 | 
        
           |  |  | 116 |     flNewProtect |= (PAGE_READWRITE);
 | 
        
           |  |  | 117 |   | 
        
           |  |  | 118 |     if ( !VirtualProtect(   pIAT, sizeof(PVOID) * cFuncs,
 | 
        
           |  |  | 119 |                             flNewProtect, &flOldProtect) )
 | 
        
           |  |  | 120 |     {
 | 
        
           |  |  | 121 |         return false;
 | 
        
           |  |  | 122 |     }
 | 
        
           |  |  | 123 |   | 
        
           |  |  | 124 |     // If the Default hook is enabled, build an array of redirection stubs in the processes memory.
 | 
        
           |  |  | 125 |     DLPD_IAT_STUB * pStubs = 0;
 | 
        
           |  |  | 126 |     if ( DLLHook->UseDefault )
 | 
        
           |  |  | 127 |     {
 | 
        
           |  |  | 128 |         // Allocate memory for the redirection stubs.  Make one extra stub at the
 | 
        
           |  |  | 129 |         // end to be a sentinel
 | 
        
           |  |  | 130 |         pStubs = new DLPD_IAT_STUB[ cFuncs + 1];
 | 
        
           |  |  | 131 |         if ( !pStubs )
 | 
        
           |  |  | 132 |             return false;
 | 
        
           |  |  | 133 |     }
 | 
        
           |  |  | 134 |   | 
        
           |  |  | 135 |     // Scan through the IAT, completing the stubs and redirecting the IAT
 | 
        
           |  |  | 136 |     // entries to point to the stubs
 | 
        
           |  |  | 137 |     pIteratingIAT = pIAT;
 | 
        
           |  |  | 138 |   | 
        
           |  |  | 139 |     while ( pIteratingIAT->u1.Function )
 | 
        
           |  |  | 140 |     {
 | 
        
           |  |  | 141 |         void* HookFn = 0;  // Set to either the SFunctionHook or pStubs.
 | 
        
           |  |  | 142 |   | 
        
           |  |  | 143 |         if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) )  // import by name
 | 
        
           |  |  | 144 |         {
 | 
        
           |  |  | 145 |             PIMAGE_IMPORT_BY_NAME pImportName = MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr, pINT->u1.AddressOfData );
 | 
        
           |  |  | 146 |   | 
        
           |  |  | 147 |             // Iterate through the hook functions, searching for this import.
 | 
        
           |  |  | 148 |             SFunctionHook* FHook = DLLHook->Functions;
 | 
        
           |  |  | 149 |             while ( FHook->Name )
 | 
        
           |  |  | 150 |             {
 | 
        
           |  |  | 151 |                 if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 )
 | 
        
           |  |  | 152 |                 {
 | 
        
           |  |  | 153 |                     OutputDebugString( "Hooked function: " );
 | 
        
           |  |  | 154 |                     OutputDebugString( (char*)pImportName->Name );
 | 
        
           |  |  | 155 |                     OutputDebugString( "\n" );
 | 
        
           |  |  | 156 |   | 
        
           |  |  | 157 |                     // Save the old function in the SFunctionHook structure and get the new one.
 | 
        
           |  |  | 158 |                     FHook->OrigFn = reinterpret_cast<void*>(pIteratingIAT->u1.Function);
 | 
        
           |  |  | 159 |                     HookFn = FHook->HookFn;
 | 
        
           |  |  | 160 |                     break;
 | 
        
           |  |  | 161 |                 }
 | 
        
           |  |  | 162 |   | 
        
           |  |  | 163 |                 FHook++;
 | 
        
           |  |  | 164 |             }
 | 
        
           |  |  | 165 |   | 
        
           |  |  | 166 |             // If the default function is enabled, store the name for the user.
 | 
        
           |  |  | 167 |             if ( DLLHook->UseDefault )
 | 
        
           |  |  | 168 |                 pStubs->pszNameOrOrdinal = (DWORD)&pImportName->Name;
 | 
        
           |  |  | 169 |         }
 | 
        
           |  |  | 170 |         else
 | 
        
           |  |  | 171 |         {
 | 
        
           |  |  | 172 |             // If the default function is enabled, store the ordinal for the user.
 | 
        
           |  |  | 173 |             if ( DLLHook->UseDefault )
 | 
        
           |  |  | 174 |                 pStubs->pszNameOrOrdinal = pINT->u1.Ordinal;
 | 
        
           |  |  | 175 |         }
 | 
        
           |  |  | 176 |   | 
        
           |  |  | 177 |         // If the default function is enabled, fill in the fields to the stub code.
 | 
        
           |  |  | 178 |         if ( DLLHook->UseDefault )
 | 
        
           |  |  | 179 |         {
 | 
        
           |  |  | 180 |             pStubs->data_call = (DWORD)(PDWORD)DLLHook->DefaultFn
 | 
        
           |  |  | 181 |                                 - (DWORD)(PDWORD)&pStubs->instr_JMP;
 | 
        
           |  |  | 182 |             pStubs->data_JMP = *(PDWORD)pIteratingIAT - (DWORD)(PDWORD)&pStubs->count;
 | 
        
           |  |  | 183 |   | 
        
           |  |  | 184 |             // If it wasn't manually hooked, use the Stub function.
 | 
        
           |  |  | 185 |             if ( !HookFn )
 | 
        
           |  |  | 186 |                 HookFn = (void*)pStubs;
 | 
        
           |  |  | 187 |         }
 | 
        
           |  |  | 188 |   | 
        
           |  |  | 189 |         // Replace the IAT function pointer if we have a hook.
 | 
        
           |  |  | 190 |         if ( HookFn )
 | 
        
           |  |  | 191 |         {
 | 
        
           |  |  | 192 |             // Cheez-o hack to see if what we're importing is code or data.
 | 
        
           |  |  | 193 |             // If it's code, we shouldn't be able to write to it
 | 
        
           |  |  | 194 |             if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) )
 | 
        
           |  |  | 195 |             {
 | 
        
           |  |  | 196 |                 pIteratingIAT->u1.Function = reinterpret_cast<DWORD>(HookFn);
 | 
        
           |  |  | 197 |             }
 | 
        
           |  |  | 198 |             else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
 | 
        
           |  |  | 199 |             {
 | 
        
           |  |  | 200 |                 // Special hack for Win9X, which builds stubs for imported
 | 
        
           |  |  | 201 |                 // functions in system DLLs (Loaded above 2GB).  These stubs are
 | 
        
           |  |  | 202 |                 // writeable, so we have to explicitly check for this case
 | 
        
           |  |  | 203 |                 if ( pIteratingIAT->u1.Function > 0x80000000 )
 | 
        
           |  |  | 204 |                     pIteratingIAT->u1.Function = reinterpret_cast<DWORD>(HookFn);
 | 
        
           |  |  | 205 |             }
 | 
        
           |  |  | 206 |         }
 | 
        
           |  |  | 207 |   | 
        
           |  |  | 208 |         if ( DLLHook->UseDefault )
 | 
        
           |  |  | 209 |             pStubs++;           // Advance to next stub
 | 
        
           |  |  | 210 |   | 
        
           |  |  | 211 |         pIteratingIAT++;    // Advance to next IAT entry
 | 
        
           |  |  | 212 |         pINT++;             // Advance to next INT entry
 | 
        
           |  |  | 213 |     }
 | 
        
           |  |  | 214 |   | 
        
           |  |  | 215 |     if ( DLLHook->UseDefault )
 | 
        
           |  |  | 216 |         pStubs->pszNameOrOrdinal = 0;   // Final stub is a sentinel
 | 
        
           |  |  | 217 |   | 
        
           |  |  | 218 |     // Put the page attributes back the way they were.
 | 
        
           |  |  | 219 |     VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare);
 | 
        
           |  |  | 220 |   | 
        
           |  |  | 221 |     return true;
 | 
        
           |  |  | 222 | }
 | 
        
           |  |  | 223 |   | 
        
           |  |  | 224 | //===========================================================================
 | 
        
           |  |  | 225 | // Top level routine to find the EXE's imports, and redirect them
 | 
        
           |  |  | 226 | bool HookAPICalls( SDLLHook* Hook )
 | 
        
           |  |  | 227 | {
 | 
        
           |  |  | 228 |     if ( !Hook )
 | 
        
           |  |  | 229 |         return false;
 | 
        
           |  |  | 230 |   | 
        
           |  |  | 231 |     HMODULE hModEXE = GetModuleHandle( 0 );
 | 
        
           |  |  | 232 |   | 
        
           |  |  | 233 |     PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule( hModEXE );
 | 
        
           |  |  | 234 |   | 
        
           |  |  | 235 |     if ( !pExeNTHdr )
 | 
        
           |  |  | 236 |         return false;
 | 
        
           |  |  | 237 |   | 
        
           |  |  | 238 |     DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory
 | 
        
           |  |  | 239 |                         [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
 | 
        
           |  |  | 240 |     if ( !importRVA )
 | 
        
           |  |  | 241 |         return false;
 | 
        
           |  |  | 242 |   | 
        
           |  |  | 243 |     // Convert imports RVA to a usable pointer
 | 
        
           |  |  | 244 |     PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr( PIMAGE_IMPORT_DESCRIPTOR,
 | 
        
           |  |  | 245 |                                                     hModEXE, importRVA );
 | 
        
           |  |  | 246 |   | 
        
           |  |  | 247 |     // Save off imports address in a global for later use
 | 
        
           |  |  | 248 |     g_pFirstImportDesc = pImportDesc;   
 | 
        
           |  |  | 249 |   | 
        
           |  |  | 250 |     // Iterate through each import descriptor, and redirect if appropriate
 | 
        
           |  |  | 251 |     while ( pImportDesc->FirstThunk )
 | 
        
           |  |  | 252 |     {
 | 
        
           |  |  | 253 |         PSTR pszImportModuleName = MakePtr( PSTR, hModEXE, pImportDesc->Name);
 | 
        
           |  |  | 254 |   | 
        
           |  |  | 255 |         if ( lstrcmpi( pszImportModuleName, Hook->Name ) == 0 )
 | 
        
           |  |  | 256 |         {
 | 
        
           |  |  | 257 |             OutputDebugString( "Found " );
 | 
        
           |  |  | 258 |             OutputDebugString( Hook->Name );
 | 
        
           |  |  | 259 |             OutputDebugString( "...\n" );
 | 
        
           |  |  | 260 |   | 
        
           |  |  | 261 |             RedirectIAT( Hook, pImportDesc, (PVOID)hModEXE );
 | 
        
           |  |  | 262 |         }
 | 
        
           |  |  | 263 |   | 
        
           |  |  | 264 |         pImportDesc++;  // Advance to next import descriptor
 | 
        
           |  |  | 265 |     }
 | 
        
           |  |  | 266 |   | 
        
           |  |  | 267 |     return true;
 | 
        
           |  |  | 268 | }
 | 
        
           |  |  | 269 |   |