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

CATDLLInjector.cpp

Go to the documentation of this file.
00001 /// \file  CATDLLInjector.cpp
00002 /// \brief DLL Injection functions for Win32
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-21 08:33:12 -0600 (Mon, 21 Jan 2008) $
00010 // $Revision:   $
00011 // $NoKeywords: $
00012 
00013 #include "CATDLLInjector.h"
00014 #include "CATStreamFile.h"
00015 
00016 // Only compile for win32 platform.
00017 #ifdef CAT_CONFIG_WIN32
00018 #include <psapi.h>
00019 
00020 // Injects the specified DLL into the process.
00021 CATResult CATDLLInjector::InjectIntoProcess(const CATWChar* dllPath, 
00022                                             CATUInt32       pid)
00023 {
00024     // Get full pathname for DLL
00025     CATWChar dllFile[_MAX_PATH+1];
00026     LPWSTR* filePart = 0;
00027     if (0 == ::GetFullPathName(dllPath,_MAX_PATH+1,dllFile,filePart))
00028     {
00029         return CAT_ERR_FILE_NOT_FOUND;        
00030     }
00031 
00032     HMODULE testLoad = 0;
00033     if (0 == (testLoad = LoadLibraryEx(dllFile, 0, LOAD_LIBRARY_AS_DATAFILE)))
00034     {
00035         return CAT_ERR_FILE_NOT_FOUND;
00036     }
00037     FreeLibrary(testLoad);
00038 
00039     // Give ourselves debug rights on other processes.
00040     HANDLE hToken;
00041     TOKEN_PRIVILEGES tkp;
00042     if (OpenProcessToken(GetCurrentProcess(),
00043                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
00044                          &hToken))
00045     {    
00046         LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid); 
00047         tkp.PrivilegeCount = 1;
00048         tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;   
00049         AdjustTokenPrivileges(hToken, 
00050                               FALSE, 
00051                               &tkp, 
00052                               0, 
00053                               (PTOKEN_PRIVILEGES) NULL, 
00054                               0);        
00055         CloseHandle(hToken);
00056     }
00057 
00058     // Open the requested process
00059     HANDLE proc = ::OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
00060     if (NULL == proc)
00061     {
00062         return CAT_ERR_UNABLE_TO_OPEN_PROCESS;        
00063     }
00064 
00065     // Allocate some memory for name of DLL
00066     SIZE_T dllNameLen = (wcslen(dllFile)+1)*sizeof(CATWChar);
00067 
00068     LPVOID procMem = ::VirtualAllocEx(proc,
00069                                       0,
00070                                       dllNameLen,
00071                                       MEM_RESERVE|MEM_COMMIT,
00072                                       PAGE_READWRITE);
00073     if (!procMem)
00074     {
00075         CloseHandle(proc);        
00076         return CAT_ERR_REMOTE_ALLOC_RAM;
00077     }
00078 
00079     // Write name to process and start a LoadLibraryW pointing at our DLL.
00080     SIZE_T amountWritten = 0;
00081     if (!WriteProcessMemory(proc,procMem,dllFile,dllNameLen,&amountWritten))
00082     {
00083         CloseHandle(proc);
00084         return CAT_ERR_REMOTE_WRITE;
00085     }
00086 
00087     // Get load library address (using unicode)
00088     FARPROC loadLibFunc = ::GetProcAddress(
00089                                 ::GetModuleHandle(L"kernel32.dll"),
00090                                 "LoadLibraryW");
00091 
00092     // Create the remote thread at the LoadLibraryW function's start address 
00093     // with param of the memory we filled with the name of the .dll
00094     DWORD threadId = 0;
00095     HANDLE remoteThread = ::CreateRemoteThread(
00096                                          proc,
00097                                          0,
00098                                          0,
00099                                          (LPTHREAD_START_ROUTINE)loadLibFunc,
00100                                          procMem,
00101                                          0,
00102                                          &threadId);
00103 
00104     if (remoteThread == 0)
00105     {
00106         CloseHandle(proc);
00107         return CAT_ERR_REMOTE_CREATE_THREAD;
00108     }
00109 
00110     // Wait for remote thread to exit (e.g. for load library to complete)
00111     DWORD waitResult;
00112     waitResult = WaitForSingleObject(remoteThread,30000);
00113 
00114     if (waitResult != WAIT_OBJECT_0)
00115     {
00116         CloseHandle(proc);
00117         CloseHandle(remoteThread);
00118         return CAT_ERR_REMOTE_THREAD_TIMEOUT;
00119     }
00120 
00121     // Check exit code on LoadLibrary    
00122     CATResult result = 0;
00123     DWORD exitCode   = 0;
00124 
00125     ::GetExitCodeThread(remoteThread,&exitCode);
00126     if (exitCode == 0)
00127     {
00128         result = CAT_ERR_REMOTE_THREAD_INVALID_EXIT;
00129     }
00130     else
00131     {
00132         result = (CATResult)exitCode;
00133     }
00134            
00135     // Clean up, we're done.
00136     CloseHandle(remoteThread);
00137     ::VirtualFreeEx(proc,procMem,wcslen(dllFile)*sizeof(CATWChar),MEM_RELEASE);
00138     CloseHandle(proc);
00139 
00140     return exitCode;
00141 }
00142 
00143 CATResult CATDLLInjector::GetProcessId(const CATWChar *processName, 
00144                                        CATUInt32&      pid, 
00145                                        CATUInt32       procIndex)
00146 {   
00147     CATASSERT(processName != 0, "Invalid process name passed to GetProcessId()");
00148     if (processName == 0)
00149         return CATRESULT(CAT_ERR_INVALID_PARAM);
00150 
00151     DWORD     procIdArray[1024];
00152     DWORD     lengthNeeded = 0;
00153     CATUInt32 numFound     = 0;
00154     CATResult result       = CAT_SUCCESS;   
00155 
00156     pid = 0;
00157 
00158 
00159     // Get process IDs into procIdArray (1024 seems like enough... stupid API)
00160     if ( !EnumProcesses( procIdArray, 
00161                          sizeof(procIdArray), 
00162                          &lengthNeeded ) )
00163     {
00164         return CATRESULT(CAT_ERR_ENUM_PROCS);
00165     }
00166 
00167     DWORD numProcs = lengthNeeded / sizeof(DWORD);
00168 
00169     CATWChar testName[_MAX_PATH+1];
00170 
00171     CATUInt32 curProcIndex = 0;
00172 
00173     // Loop through all procIds, open the processes and 
00174     // check their names against the one passed in.
00175     for ( DWORD i = 0; i < numProcs; i++ )
00176     {
00177         HANDLE procHandle = OpenProcess( PROCESS_QUERY_INFORMATION |
00178                                          PROCESS_VM_READ,
00179                                          FALSE, procIdArray[i] );
00180 
00181         if (NULL != procHandle )
00182         {
00183             HMODULE module       = 0;
00184             DWORD   lengthNeeded = 0;
00185 
00186             if ( EnumProcessModules( procHandle, 
00187                                      &module, 
00188                                      sizeof(module), 
00189                                      &lengthNeeded) )
00190             {
00191                 memset(testName,0,sizeof(testName));
00192                 GetModuleBaseName( procHandle, 
00193                                    module, 
00194                                    testName,
00195                                    sizeof(testName) );
00196 
00197                 if (0 == _wcsicmp(testName, processName))
00198                 {
00199                     // Got a match. If we aren't being picky (e.g. procIndex == -1)
00200                     // or we've found the requested index, set it.
00201                     if ((procIndex == -1) || (curProcIndex == procIndex))
00202                     {
00203                         pid = procIdArray[i];                        
00204                     }
00205                     curProcIndex++;
00206                     numFound++;
00207                 }
00208             }
00209             else
00210             {
00211                CloseHandle(procHandle);
00212             }
00213         }
00214         else
00215         {
00216             continue;
00217         }
00218     }
00219 
00220     if (numFound == 0)
00221     {
00222         return CATRESULT(CAT_ERR_NO_MATCHING_PROC);
00223     }
00224     else if (numFound == 1)
00225     {
00226         return CAT_SUCCESS;
00227     }
00228 
00229     return CATRESULT(CAT_STAT_MULTIPLE_PROCS);
00230 }
00231 
00232 
00233 /// kAsmPatch is used by StartDLLWithProcess to force a load of our
00234 /// target DLL before the main thread of the application is started.
00235 /// It then calls UnpatchProcess() in the DLL to restore the 
00236 /// executable and continue its execution.
00237 ///
00238 const CATUInt8 kAsmPatch[] =
00239 {
00240                                // off desc
00241     
00242     // Put our starting location, registers, and flags onto the stack
00243     // so we can restore them.  Extra NOPs are in case we forget something :)
00244     // 
00245     0x90,                      // 0   Breakpoint for debugging or NOP
00246     0x68,                      // 1   push dword
00247     0x00,0x00,0x00,0x00,       // 2     kStartLocReturn
00248                                //     Pushes our start location onto the stack
00249     0x60,0x9c,                 // 6   pushad, pushfd, save starting regs/flags
00250     0x90,0x90,0x90,            // 8   nop    
00251 
00252     // First, call load library for the target DLL file
00253     0x68,                      // 11  push dword
00254     0x00,0x00,0x00,0x00,       // 12    kDLLNameOffAddr:
00255                                //     this is the address part.... 
00256                                //     point it to the full path of marker dll
00257     
00258     0xff,0x15,                 // 16  far call [address]    
00259     0x00,0x00,0x00,0x00,       // 18    kLoadLibraryOffset:                                 
00260                                //     point this one to the LoadLibrary 
00261                                //     function address
00262 
00263     // Now, get the proc address for UnpatchProcess() in the DLL
00264     0x68,                      // 22  push dword
00265     0x00,0x00,0x00,0x00,       // 23    kUnpatchFuncOffset
00266                                //     point it to the full path of marker dll  
00267     0x50,                      // 27  Push EAX (module handle)
00268 
00269     0xff,0x15,                 // 28  far call [address]
00270     0x00,0x00,0x00,0x00,       // 30    kGetProcAddressOffset:
00271                                
00272 
00273     // Now, push the params for UnpatchProcess onto the stack and 
00274     // call into it. It will restore control to the host from there.        
00275     0x68,                      // 34   push dword 
00276     0x00,0x00,0x00,0x00,       // 35     kHostProcOffset
00277     0x68,                      // 39   push dword 
00278     0x00,0x00,0x00,0x00,       // 40     kHostBufferOffset
00279     0x68,                      // 44   push dword 
00280     0x00,0x00,0x00,0x00,       // 45     kPatchSizeOffset
00281     0x68,                      // 49   push dword 
00282     0x00,0x00,0x00,0x00,       // 50     kStartLocOffset
00283     0x68,                      // 54   push dword
00284     0x00,0x00,0x00,0x00,       // 55     kPassDataLenOffset
00285     0x68,                      // 59   push dword
00286     0x00,0x00,0x00,0x00,       // 60     kPassDataOffset
00287     
00288     0xff,0xd0,                 // 64   call eax
00289     0xcc,0x90,                 // 66   Debug break if it returns...
00290         
00291     0x00,0x00,0x00,0x00,       // 68  kLoadLibraryAddress: 
00292     0x00,0x00,0x00,0x00,       // 72  kGetProcAddress:
00293     
00294     0x55,0x6E,0x70,0x61,0x74,  // 76 'UnpatchProcess' function name
00295     0x63,0x68,0x50,0x72,0x6F,  //
00296     0x63,0x65,0x73,0x73,0x00   //
00297 };
00298 
00299 // Offsets into patch of variables to set values to
00300 const int kStartLocReturn        = 2;
00301 const int kDLLNameOffAddr        = 12;
00302 const int kLoadLibraryOffset     = 18;
00303 const int kUnpatchFuncOffset     = 23;
00304 const int kGetProcAddressOffset  = 30;
00305 const int kHostProcOffset        = 35;
00306 const int kHostBufferOffset      = 40;
00307 const int kPatchSizeOffset       = 45;
00308 const int kStartLocOffset        = 50;
00309 const int kPassDataLenOffset     = 55;
00310 const int kPassDataOffset        = 60;
00311 const int kLoadLibraryAddress    = 68;
00312 const int kGetProcAddress        = 72;
00313 const int kUnpatchFuncNameAddress= 76;
00314 const int kDLLNameOffset         = sizeof(kAsmPatch);
00315 
00316 // Creates a process using the specified execFile path and 
00317 // injects the DLL into the process at startup.
00318 CATResult CATDLLInjector::StartDLLWithProcess (const CATWChar* dllPath, 
00319                                                const CATWChar* execFile,
00320                                                const CATWChar* commandLine,
00321                                                const void*     passData,
00322                                                CATUInt32       passDataLen)
00323 {  
00324     // Tweak our protection levels, if possible.
00325     HANDLE hToken;
00326     TOKEN_PRIVILEGES tkp;
00327     if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
00328     {    
00329         LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid); 
00330         tkp.PrivilegeCount = 1;
00331         tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;   
00332         AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);        
00333         CloseHandle(hToken);
00334     }
00335 
00336     CATResult result;
00337 
00338     // Get full name of .exe file
00339     CATWChar targDir[_MAX_PATH+1];
00340     wcscpy(targDir,execFile);
00341     CATWChar* slash = wcsrchr(targDir,'\\');
00342     if (slash != 0)
00343     {
00344         *slash = 0;
00345     }
00346     
00347 
00348     // Get full pathname for DLL
00349     CATWChar dllFile[_MAX_PATH+1];
00350     LPWSTR filePart = 0;
00351     if (0 == ::GetFullPathName(dllPath,_MAX_PATH+1,dllFile,&filePart))
00352     {
00353         return CATRESULTFILE(CAT_ERR_FILE_NOT_FOUND,dllPath);        
00354     }
00355     
00356     HMODULE testLoad = 0;
00357     if (0 == (testLoad = LoadLibraryEx(dllFile, 0, LOAD_LIBRARY_AS_DATAFILE)))
00358     {
00359         return CATRESULTFILE(CAT_ERR_FILE_NOT_FOUND,dllFile);
00360     }
00361     FreeLibrary(testLoad);
00362 
00363 
00364     // Open up the executable file directly
00365     CATStreamFile targetFile;
00366     if (CATFAILED(result = targetFile.Open(execFile,CATStream::READ_ONLY)))
00367     {
00368         return result;
00369     }
00370 
00371 
00372     
00373     CATUInt8                dosHeader[0x40];
00374     IMAGE_NT_HEADERS        peHeader;   
00375     CATUInt32               peHeaderOffset  = 0;    
00376     CATUInt32               amountRead      = 0;    
00377     void*                   startLoc        = 0;
00378     CATUInt8*               storedBytes     = 0;
00379     SIZE_T                  storedLength    = 0;
00380     
00381     // Load in the DOS header for the exe
00382     amountRead = 0x40;
00383     result = targetFile.Read(dosHeader,amountRead);
00384     
00385     if ((result == CAT_STAT_FILE_AT_EOF) || (CATFAILED(result)))
00386     {
00387         targetFile.Close();
00388         return CATRESULT(CAT_ERR_READING_TARGET_EXEC);
00389     }
00390 
00391     if (dosHeader[0] == 'M' && dosHeader[1] == 'Z')
00392     {
00393         // Okay, it's at least a valid exe of some sort, we think...
00394         // So now get offset of PE header...
00395         peHeaderOffset = *((long *)(&dosHeader[0x3c]));
00396         
00397         if (peHeaderOffset == 0)
00398         {
00399             // But it isn't a PE :(
00400             targetFile.Close();         
00401             return CAT_ERR_EXE_NOT_PE_FORMAT;
00402         }
00403 
00404         // Go to PE header offset
00405         CATFileOffset peOffset;
00406         peOffset.dOffset.highOffset = 0;
00407         peOffset.dOffset.lowOffset = peHeaderOffset;
00408         if (CATFAILED(result = targetFile.SeekAbsolute(peOffset.qOffset)))
00409         {
00410             targetFile.Close();
00411             return result;
00412         }
00413         
00414         // Read in COFF and Optional headers
00415         amountRead = sizeof(IMAGE_NT_HEADERS);
00416         if (CATFAILED(result = targetFile.Read(&peHeader,amountRead)) || 
00417             (amountRead != sizeof(IMAGE_NT_HEADERS)) )
00418         {
00419             targetFile.Close();         
00420             return CATRESULT(CAT_ERR_READING_TARGET_EXEC);
00421         }
00422 
00423         targetFile.Close();
00424 
00425         // Calculate starting point of execution
00426         startLoc = (void *)(UINT_PTR)
00427                    (peHeader.OptionalHeader.AddressOfEntryPoint +
00428                     peHeader.OptionalHeader.ImageBase);
00429     }
00430     else
00431     {
00432         targetFile.Close();        
00433         return CAT_ERR_EXE_NOT_PE_FORMAT;
00434     }
00435 
00436     // Create the process in a suspended state
00437     PROCESS_INFORMATION procInfo;
00438     STARTUPINFO startupInfo;
00439     
00440     memset(&procInfo,0,sizeof(PROCESS_INFORMATION));
00441     memset(&startupInfo,0,sizeof(STARTUPINFO));
00442     
00443     startupInfo.cb = sizeof(STARTUPINFO);
00444         
00445     if (!CreateProcess(execFile,
00446                       (LPWSTR)commandLine,
00447                       0,
00448                       0,
00449                       0,
00450                       CREATE_SUSPENDED,
00451                       0,
00452                       targDir,
00453                       &startupInfo,
00454                       &procInfo))
00455     {
00456         return CAT_ERR_PROCESS_CREATE;
00457     }
00458 
00459     if ((procInfo.hThread  == INVALID_HANDLE_VALUE) || 
00460         (procInfo.hProcess == INVALID_HANDLE_VALUE))
00461     {
00462         return CAT_ERR_PROCESS_CREATE;
00463     }
00464         
00465     // Our patch is our codesize plus the length of the DLL filename.
00466     CATUInt32 patchSize = (CATUInt32)(sizeof(kAsmPatch) + 
00467                           (wcslen(dllFile)*2) + 2);
00468 
00469     // Make sure we have access to read/write to the startup location. 
00470     DWORD oldProtect = 0;
00471     VirtualProtectEx(   procInfo.hProcess,
00472                         startLoc,
00473                         patchSize,
00474                         PAGE_EXECUTE_READWRITE,
00475                         &oldProtect);
00476     
00477     // Read in bytes from the startup code
00478     storedBytes = new CATUInt8[patchSize];
00479     
00480     SIZE_T memReadBytes = 0;
00481     ReadProcessMemory(  procInfo.hProcess,
00482                         startLoc,
00483                         storedBytes,
00484                         patchSize,
00485                         &memReadBytes);
00486 
00487     storedLength = memReadBytes;
00488     
00489     // Start setting up the code we're patching the target with
00490     CATUInt8* writeBytes = new CATUInt8[patchSize];
00491     
00492     // Copy in the static code bytes of the patch
00493     memcpy(writeBytes,kAsmPatch,sizeof(kAsmPatch));
00494 
00495     // Copy in the dll name we've allocated space for
00496     wcscpy( (CATWChar*)(writeBytes + sizeof(kAsmPatch)),dllFile);
00497     
00498     // Store location of marker.dll string
00499     *((CATUInt32*)&writeBytes[kDLLNameOffAddr]) = 
00500         (CATUInt32)(UINT_PTR)(((CATUInt8*)startLoc) + kDLLNameOffset);
00501 
00502     // Store unpatch name address
00503     *((CATUInt32*)&writeBytes[kUnpatchFuncOffset]) = 
00504         (CATUInt32)(UINT_PTR)(((CATUInt8*)startLoc) + kUnpatchFuncNameAddress);
00505 
00506     // Address for LoadLibraryW. kernel32.dll addresses should be fine across 
00507     // process boundaries.
00508     //
00509     // NOTE: If you're running boundschecker, and debugging this code, 
00510     //       you're hosed.
00511     //
00512     // Boundschecker hooks the kernel32.dll module in the debugged process, but 
00513     // doesn't pick it up in the loaded one, so the address will be garbage when 
00514     // passed into the target executable. So much for boundschecking
00515     // this app...
00516     HMODULE kernelHandle = GetModuleHandle(L"kernel32.dll");
00517     FARPROC loadLibFunc  = GetProcAddress(kernelHandle,"LoadLibraryW"); 
00518     FARPROC getProcFunc  = GetProcAddress(kernelHandle,"GetProcAddress");
00519     
00520     // Store the address of the address of load library
00521     // we're doing a call far dword ptr [address].
00522     *((CATUInt32*)&writeBytes[kLoadLibraryOffset]) = 
00523         (CATUInt32)(UINT_PTR)(((CATUInt8*)startLoc) + kLoadLibraryAddress);
00524     
00525     // Same for get proc address
00526     *((CATUInt32*)&writeBytes[kGetProcAddressOffset]) = 
00527         (CATUInt32)(UINT_PTR)(((CATUInt8*)startLoc) + kGetProcAddress);
00528 
00529     // Store the address of the load library address
00530     *((CATUInt32*)&writeBytes[kLoadLibraryAddress]) = 
00531         (CATUInt32)(UINT_PTR)loadLibFunc;
00532 
00533     // Store the address of the getproc address
00534     *((CATUInt32*)&writeBytes[kGetProcAddress]) =
00535         (CATUInt32)(UINT_PTR)getProcFunc;
00536 
00537     // Store *our* processes ID and a pointer in our memory space to
00538     // the original bytes. This allows the DLL's UnpatchProcess() to 
00539     // simply read the bytes from our process directly over our
00540     // patch to restore the executable.
00541     *((CATUInt32*)&writeBytes[kHostProcOffset])    = (CATUInt32)_getpid();
00542     *((CATUInt32*)&writeBytes[kHostBufferOffset])  = (CATUInt32)(UINT_PTR)storedBytes;
00543     *((CATUInt32*)&writeBytes[kPatchSizeOffset])   = (CATUInt32)patchSize;
00544     *((CATUInt32*)&writeBytes[kStartLocOffset])    = (CATUInt32)(UINT_PTR)startLoc;
00545     *((CATUInt32*)&writeBytes[kPassDataOffset])    = (CATUInt32)(UINT_PTR)passData;
00546     *((CATUInt32*)&writeBytes[kPassDataLenOffset]) = passDataLen;
00547     // Also save start loc in the initial push at the beginning, so we'll have
00548     // our address on the stack when we restore it.
00549     *((CATUInt32*)&writeBytes[kStartLocReturn])  = (CATUInt32)(UINT_PTR)startLoc;
00550 
00551     // Write the patch to target process
00552     memReadBytes = 0;
00553     WriteProcessMemory(procInfo.hProcess,
00554                        startLoc,
00555                        writeBytes,
00556                        patchSize,
00557                        &memReadBytes);
00558       
00559     // Clean up write bytes
00560     delete [] writeBytes;
00561     
00562 
00563     // Create an event that the DLL can ping once it's restored the process
00564     CATWChar eventName[64];
00565     wsprintf(eventName,L"UnpatchProcess_%d",_getpid());
00566     HANDLE unpatchEvent = ::CreateEvent(0,TRUE,FALSE,eventName);
00567 
00568     // Start the patched thread
00569     ResumeThread(procInfo.hThread);
00570     
00571     // Wait for the unpatch function to ping event, signaling
00572     // that it has completed restoring the executable and no 
00573     // longer needs the stored bytes.
00574     if (WAIT_TIMEOUT == WaitForSingleObject(unpatchEvent,60000))
00575     {
00576         delete [] storedBytes;
00577         CloseHandle(unpatchEvent);
00578         return CAT_ERR_UNPATCH_TIMEOUT;
00579     }
00580 
00581     // Restore memory protections to original 
00582     VirtualProtectEx(procInfo.hProcess,
00583                      startLoc,
00584                      patchSize,
00585                      oldProtect,
00586                      &oldProtect);
00587    
00588     // Got event - program successfully restored, DLL injected.
00589     delete [] storedBytes;
00590     CloseHandle(procInfo.hThread);
00591     CloseHandle(procInfo.hProcess);
00592     CloseHandle(unpatchEvent);    
00593 
00594     return CAT_SUCCESS;
00595 }
00596 
00597 #endif // CAT_CONFIG_WIN32

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