00001 /// \file CATDLLInjector.h 00002 /// \brief DLL Injection functions for Win32 00003 /// \ingroup CAT 00004 /// 00005 /// This class includes utilities useful for injecting DLLs into remote processes on Win32-based 00006 /// platforms. 00007 /// 00008 /// Copyright (c) 2007-2008 by Michael Ellison. 00009 /// See COPYING.txt for the \ref gaslicense License (MIT License). 00010 /// 00011 // $Author: mikeellison $ 00012 // $Date: 2008-01-27 12:56:42 -0600 (Sun, 27 Jan 2008) $ 00013 // $Revision: $ 00014 // $NoKeywords: $ 00015 00016 #include "CATInternal.h" 00017 00018 #ifdef CAT_CONFIG_WIN32 00019 00020 #ifndef _CATDLLInjector_H_ 00021 #define _CATDLLInjector_H_ 00022 00023 /// \class CATDLLInjector 00024 /// \brief Collection of utility functions for injecting a DLL into a process. 00025 /// \ingroup CAT 00026 /// 00027 /// This class contains utility functions to inject a DLL into a process. 00028 /// 00029 /// There are several methods available to do this, and all of them have 00030 /// tradeoffs. The two I expect to be most used are the CreateRemoteThread() 00031 /// method to force a LoadLibrary() call on a new thread into the target process, 00032 /// and the CreateProcess() method of creating a process in a halted state, 00033 /// inserting the DLL, then continuing execution. The first is embodied in 00034 /// CATDLLInjector::InjectIntoProcess(), the latter in 00035 /// CATDLLInjector::StartDLLWithProcess(). 00036 /// 00037 /// Much of the information on how to do DLL Injection and function hooking 00038 /// was gleaned from Jeffrey Richter's 00039 /// "Programming Applications for Microsoft Windows" (1572319968). 00040 /// Additional information is available in John Robbins' 00041 /// "Debugging Applications for Microsoft .NET and Microsoft Windows" (0735615365) 00042 /// and Feng Yuan's 00043 /// "Windows Graphics Programming: Win32 GDI and DirectDraw" (0130869856). 00044 /// 00045 /// I'm avoiding the debugger methods and windows hook methods currently for 00046 /// a few reasons (although they may prove useful in the future and will get 00047 /// tossed in here if so). 00048 /// 00049 /// -# Easier to detect, so some games' anti-cheat/anti-crack logic might be 00050 /// more likely to trigger on it than the techniques I've used. 00051 /// ... and some antivirus software for that matter, although I'm not sure why 00052 /// they don't appear to trigger on these techniques. I'm using F-Secure 2008, 00053 /// and while it complains horribly when some applications toss DLLs into 00054 /// others with hooks, so far it seems ok when my apps do the same with 00055 /// these techniques... 00056 /// -# Ideally, we want the option to exit the injector process immediately. 00057 /// Both the debugger and windows hook methods of injection make that 00058 /// somewhat more difficult. 00059 /// -# I hadn't found a pretty implementation of the StartDLLWithProcess technique 00060 /// that I liked. Richter discusses the idea, but doesn't give implementation 00061 /// details in his books that I've seen. Enjoy :) 00062 /// 00063 /// When using StartDLLWithProcess, you'll need the following undecorated function 00064 /// exported from your injected DLL (e.g. use a .def file for it too): 00065 /// \code 00066 /// extern "C" { 00067 /// void* gPassedData = 0; 00068 /// unsigned int gPassedDataLen = 0; 00069 /// void _declspec(dllexport) UnpatchProcess(void* passData, 00070 /// unsigned int passDataLen, 00071 /// void* startLoc, 00072 /// unsigned int patchSize, 00073 /// void* hostBuffer, 00074 /// unsigned int hostProcId) 00075 /// { 00076 /// // Open host process and read the stored bytes into our 00077 /// // hooked process over the start location, restoring the 00078 /// // original executable code. 00079 /// HANDLE hostProcess = ::OpenProcess(PROCESS_ALL_ACCESS, 00080 /// FALSE, 00081 /// hostProcId); 00082 /// if (hostProcess != 0) 00083 /// { 00084 /// DWORD amountRead = 0; 00085 /// ReadProcessMemory(hostProcess,hostBuffer,startLoc,patchSize,&amountRead); 00086 /// if (passDataLen && passData) 00087 /// { 00088 /// gPassedDataLen = passDataLen; 00089 /// gPassedData = new BYTE[passDataLen]; 00090 /// ::ReadProcessMemory(hostProcess,passData,gPassedData,passDataLen,&amountRead); 00091 /// } 00092 /// CloseHandle(hostProcess); 00093 /// } 00094 /// // Got the data from the parent. Open the wait event and set it. 00095 /// CATWChar eventName[64]; 00096 /// wsprintf(eventName,L"UnpatchProcess_%d",hostProcId); 00097 /// HANDLE waitEvent = ::OpenEvent(GENERIC_READ|GENERIC_WRITE,FALSE,eventName); 00098 /// if (waitEvent != 0) 00099 /// { 00100 /// SetEvent(waitEvent); 00101 /// CloseHandle(waitEvent); 00102 /// } 00103 /// // Now, restore the stack and go back to the starting location 00104 /// __asm 00105 /// { 00106 /// mov esp,ebp // Get original base pointer 00107 /// pop ebp 00108 /// add esp,0x1c // Restore stack point to start of our code 00109 /// popfd 00110 /// popad 00111 /// ret 00112 /// } 00113 /// } 00114 ///} 00115 /// \endcode 00116 /// 00117 /// \todo 00118 /// Add named-pipe parameter passing (or similar) to injection by pid. 00119 class CATDLLInjector 00120 { 00121 public: 00122 /// Retrieves a process id for a process name. 00123 /// If multiple processes matching the name are present, it 00124 /// will return the first one. 00125 /// 00126 /// \param processName process name (e.g. Notepad.exe) 00127 /// \param pid ref to pid, set on success. 00128 /// \param procIndex optional index of process if multiple 00129 /// \return CATResult CAT_SUCCESS on success. 00130 /// CAT_STAT_MULTIPLE_PROCS if multiple. 00131 static CATResult GetProcessId( const CATWChar* processName, 00132 CATUInt32& pid, 00133 CATUInt32 procIndex = -1); 00134 00135 /// Injects the specified DLL into the process. 00136 /// 00137 /// \param dllPath Path to DLL file to inject into process 00138 /// \param pid Process ID to inject DLL into 00139 /// \return CATResult 0 on success 00140 static CATResult InjectIntoProcess (const CATWChar* dllPath, 00141 CATUInt32 pid); 00142 00143 00144 00145 /// Creates a process using the specified execFile path and 00146 /// injects the DLL into the process at startup. 00147 /// 00148 /// \param dllPath Path to DLL file to inject into process 00149 /// \param execFile Path of executable file to start 00150 /// \param commandLine full command line to pass 00151 /// \param passData Ptr to data to pass to DLL, or null for none. 00152 /// \param passDataLen Length of data to pass, or 0 for none. 00153 /// \return CATResult 0 on success 00154 static CATResult StartDLLWithProcess (const CATWChar* dllPath, 00155 const CATWChar* execFile, 00156 const CATWChar* commandLine, 00157 const void* passData = 0, 00158 CATUInt32 passDataLen = 0); 00159 00160 private: 00161 }; 00162 00163 00164 00165 #endif //_CATDLLInjector_H_ 00166 00167 #endif //CAT_CONFIG_WIN32