1 /**************************************************************************
3 * Copyright 2011 Jose Fonseca
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 **************************************************************************/
28 * Main program to start and inject a DLL into a process via a remote thread.
31 * - http://en.wikipedia.org/wiki/DLL_injection#Approaches_on_Microsoft_Windows
32 * - http://www.codeproject.com/KB/threads/completeinject.aspx
33 * - http://www.codeproject.com/KB/threads/winspy.aspx
34 * - http://www.codeproject.com/KB/DLL/DLL_Injection_tutorial.aspx
35 * - http://www.codeproject.com/KB/threads/APIHooking.aspx
37 * Other slightly different techniques:
38 * - http://www.fr33project.org/pages/projects/phook.htm
39 * - http://www.hbgary.com/loading-a-dll-without-calling-loadlibrary
40 * - http://securityxploded.com/ntcreatethreadex.php
52 * Determine whether an argument should be quoted.
55 needsQuote(const char *arg)
63 if (c == ' ' || c == '\t' || c == '\"') {
80 quoteArg(std::string &s, const char *arg)
83 unsigned backslashes = 0;
90 } else if (c == '"') {
110 main(int argc, char *argv[])
114 fprintf(stderr, "inject dllname.dll command [args] ...\n");
118 const char *szDll = argv[1];
120 SetEnvironmentVariableA("INJECT_DLL", szDll);
125 PROCESS_INFORMATION processInfo;
128 if (isdigit(argv[2][0])) {
132 HANDLE hToken = NULL;
133 bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
135 fprintf(stderr, "error: OpenProcessToken returned %u\n", (unsigned)bRet);
140 bRet = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid);
142 fprintf(stderr, "error: LookupPrivilegeValue returned %u\n", (unsigned)bRet);
147 tp.PrivilegeCount = 1;
148 tp.Privileges[0].Luid = Luid;
149 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
150 bRet = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof tp, NULL, NULL);
152 fprintf(stderr, "error: AdjustTokenPrivileges returned %u\n", (unsigned)bRet);
156 DWORD dwDesiredAccess =
157 PROCESS_CREATE_THREAD |
158 PROCESS_QUERY_INFORMATION |
159 PROCESS_QUERY_LIMITED_INFORMATION |
160 PROCESS_VM_OPERATION |
163 DWORD dwProcessId = atol(argv[2]);
164 hProcess = OpenProcess(
166 FALSE /* bInheritHandle */,
169 DWORD dwLastError = GetLastError();
170 fprintf(stderr, "error: failed to open process %lu (%lu)\n", dwProcessId, dwLastError);
175 std::string commandLine;
177 for (int i = 2; i < argc; ++i) {
178 const char *arg = argv[i];
181 commandLine.push_back(sep);
184 if (needsQuote(arg)) {
185 quoteArg(commandLine, arg);
187 commandLine.append(arg);
193 STARTUPINFO startupInfo;
194 memset(&startupInfo, 0, sizeof startupInfo);
195 startupInfo.cb = sizeof startupInfo;
197 // Create the process in suspended state
200 const_cast<char *>(commandLine.c_str()), // only modified by CreateProcessW
201 0, // process attributes
202 0, // thread attributes
203 TRUE, // inherit handles
206 NULL, // current directory
209 fprintf(stderr, "error: failed to execute %s\n", commandLine.c_str());
213 hProcess = processInfo.hProcess;
217 * XXX: Mixed architecture don't quite work. See also
218 * http://www.corsix.org/content/dll-injection-and-wow64
220 const char *szDllName;
221 szDllName = "inject.dll";
223 char szDllPath[MAX_PATH];
224 GetModuleFileNameA(NULL, szDllPath, sizeof szDllPath);
225 getDirName(szDllPath);
226 strncat(szDllPath, szDllName, sizeof szDllPath - strlen(szDllPath) - 1);
228 size_t szDllPathLength = strlen(szDllPath) + 1;
230 // Allocate memory in the target process to hold the DLL name
231 void *lpMemory = VirtualAllocEx(hProcess, NULL, szDllPathLength, MEM_COMMIT, PAGE_READWRITE);
233 fprintf(stderr, "error: failed to allocate memory in the process\n");
234 TerminateProcess(hProcess, 1);
238 // Copy DLL name into the target process
239 if (!WriteProcessMemory(hProcess, lpMemory, szDllPath, szDllPathLength, NULL)) {
240 fprintf(stderr, "error: failed to write into process memory\n");
241 TerminateProcess(hProcess, 1);
246 * Get LoadLibraryA address from kernel32.dll. It's the same for all the
247 * process (XXX: but only within the same architecture).
249 PTHREAD_START_ROUTINE lpStartAddress =
250 (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("KERNEL32"), "LoadLibraryA");
252 // Create remote thread in another process
253 HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpMemory, 0, NULL);
255 fprintf(stderr, "error: failed to create remote thread\n");
256 TerminateProcess(hProcess, 1);
260 // Wait for it to finish
261 WaitForSingleObject(hThread, INFINITE);
264 GetExitCodeThread(hThread, &hModule);
266 fprintf(stderr, "error: failed to inject %s\n", szDllPath);
267 TerminateProcess(hProcess, 1);
275 // Start main process thread
276 ResumeThread(processInfo.hThread);
278 // Wait for it to finish
279 WaitForSingleObject(hProcess, INFINITE);
282 GetExitCodeProcess(hProcess, &exitCode);
284 CloseHandle(hProcess);
285 CloseHandle(processInfo.hThread);
287 return (int)exitCode;