X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=inject%2Finjector.cpp;fp=inject%2Finjector.cpp;h=04e09598f98fa5c0d132e98597c85061b6ca24b8;hb=bd4937e47675d600b13174773dc05ab1129c266b;hp=0000000000000000000000000000000000000000;hpb=1592ad278ba90ed2d5f6fa2b49fadf2cc6e4e77e;p=apitrace diff --git a/inject/injector.cpp b/inject/injector.cpp new file mode 100644 index 0000000..04e0959 --- /dev/null +++ b/inject/injector.cpp @@ -0,0 +1,289 @@ +/************************************************************************** + * + * Copyright 2011 Jose Fonseca + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + +/* + * Main program to start and inject a DLL into a process via a remote thread. + * + * For background see: + * - http://en.wikipedia.org/wiki/DLL_injection#Approaches_on_Microsoft_Windows + * - http://www.codeproject.com/KB/threads/completeinject.aspx + * - http://www.codeproject.com/KB/threads/winspy.aspx + * - http://www.codeproject.com/KB/DLL/DLL_Injection_tutorial.aspx + * - http://www.codeproject.com/KB/threads/APIHooking.aspx + * + * Other slightly different techniques: + * - http://www.fr33project.org/pages/projects/phook.htm + * - http://www.hbgary.com/loading-a-dll-without-calling-loadlibrary + * - http://securityxploded.com/ntcreatethreadex.php + */ + +#include + +#include +#include + +#include "inject.h" + + +/** + * Determine whether an argument should be quoted. + */ +static bool +needsQuote(const char *arg) +{ + char c; + while (true) { + c = *arg++; + if (c == '\0') { + break; + } + if (c == ' ' || c == '\t' || c == '\"') { + return true; + } + if (c == '\\') { + c = *arg++; + if (c == '\0') { + break; + } + if (c == '"') { + return true; + } + } + } + return false; +} + +static void +quoteArg(std::string &s, const char *arg) +{ + char c; + unsigned backslashes = 0; + + s.push_back('"'); + while (true) { + c = *arg++; + if (c == '\0') { + break; + } else if (c == '"') { + while (backslashes) { + s.push_back('\\'); + --backslashes; + } + s.push_back('\\'); + } else { + if (c == '\\') { + ++backslashes; + } else { + backslashes = 0; + } + } + s.push_back(c); + } + s.push_back('"'); +} + + +int +main(int argc, char *argv[]) +{ + + if (argc < 3) { + fprintf(stderr, "inject dllname.dll command [args] ...\n"); + return 1; + } + + const char *szDll = argv[1]; +#if 0 + SetEnvironmentVariableA("INJECT_DLL", szDll); +#else + SetSharedMem(szDll); +#endif + + PROCESS_INFORMATION processInfo; + HANDLE hProcess; + BOOL bAttach; + if (isdigit(argv[2][0])) { + bAttach = TRUE; + + BOOL bRet; + HANDLE hToken = NULL; + bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken); + if (!bRet) { + fprintf(stderr, "error: OpenProcessToken returned %u\n", (unsigned)bRet); + return 1; + } + + LUID Luid; + bRet = LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid); + if (!bRet) { + fprintf(stderr, "error: LookupPrivilegeValue returned %u\n", (unsigned)bRet); + return 1; + } + + TOKEN_PRIVILEGES tp; + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = Luid; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + bRet = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof tp, NULL, NULL); + if (!bRet) { + fprintf(stderr, "error: AdjustTokenPrivileges returned %u\n", (unsigned)bRet); + return 1; + } + + DWORD dwDesiredAccess = + PROCESS_CREATE_THREAD | + PROCESS_QUERY_INFORMATION | + PROCESS_QUERY_LIMITED_INFORMATION | + PROCESS_VM_OPERATION | + PROCESS_VM_WRITE | + PROCESS_VM_READ; + DWORD dwProcessId = atol(argv[2]); + hProcess = OpenProcess( + dwDesiredAccess, + FALSE /* bInheritHandle */, + dwProcessId); + if (!hProcess) { + DWORD dwLastError = GetLastError(); + fprintf(stderr, "error: failed to open process %lu (%lu)\n", dwProcessId, dwLastError); + return 1; + } + } else { + bAttach = FALSE; + std::string commandLine; + char sep = 0; + for (int i = 2; i < argc; ++i) { + const char *arg = argv[i]; + + if (sep) { + commandLine.push_back(sep); + } + + if (needsQuote(arg)) { + quoteArg(commandLine, arg); + } else { + commandLine.append(arg); + } + + sep = ' '; + } + + STARTUPINFO startupInfo; + memset(&startupInfo, 0, sizeof startupInfo); + startupInfo.cb = sizeof startupInfo; + + // Create the process in suspended state + if (!CreateProcessA( + NULL, + const_cast(commandLine.c_str()), // only modified by CreateProcessW + 0, // process attributes + 0, // thread attributes + TRUE, // inherit handles + CREATE_SUSPENDED, + NULL, // environment + NULL, // current directory + &startupInfo, + &processInfo)) { + fprintf(stderr, "error: failed to execute %s\n", commandLine.c_str()); + return 1; + } + + hProcess = processInfo.hProcess; + } + + /* + * XXX: Mixed architecture don't quite work. See also + * http://www.corsix.org/content/dll-injection-and-wow64 + */ + const char *szDllName; + szDllName = "inject.dll"; + + char szDllPath[MAX_PATH]; + GetModuleFileNameA(NULL, szDllPath, sizeof szDllPath); + getDirName(szDllPath); + strncat(szDllPath, szDllName, sizeof szDllPath - strlen(szDllPath) - 1); + + size_t szDllPathLength = strlen(szDllPath) + 1; + + // Allocate memory in the target process to hold the DLL name + void *lpMemory = VirtualAllocEx(hProcess, NULL, szDllPathLength, MEM_COMMIT, PAGE_READWRITE); + if (!lpMemory) { + fprintf(stderr, "error: failed to allocate memory in the process\n"); + TerminateProcess(hProcess, 1); + return 1; + } + + // Copy DLL name into the target process + if (!WriteProcessMemory(hProcess, lpMemory, szDllPath, szDllPathLength, NULL)) { + fprintf(stderr, "error: failed to write into process memory\n"); + TerminateProcess(hProcess, 1); + return 1; + } + + /* + * Get LoadLibraryA address from kernel32.dll. It's the same for all the + * process (XXX: but only within the same architecture). + */ + PTHREAD_START_ROUTINE lpStartAddress = + (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("KERNEL32"), "LoadLibraryA"); + + // Create remote thread in another process + HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpMemory, 0, NULL); + if (!hThread) { + fprintf(stderr, "error: failed to create remote thread\n"); + TerminateProcess(hProcess, 1); + return 1; + } + + // Wait for it to finish + WaitForSingleObject(hThread, INFINITE); + + DWORD hModule = 0; + GetExitCodeThread(hThread, &hModule); + if (!hModule) { + fprintf(stderr, "error: failed to inject %s\n", szDllPath); + TerminateProcess(hProcess, 1); + return 1; + } + + if (bAttach) { + return 0; + } + + // Start main process thread + ResumeThread(processInfo.hThread); + + // Wait for it to finish + WaitForSingleObject(hProcess, INFINITE); + + DWORD exitCode = ~0; + GetExitCodeProcess(hProcess, &exitCode); + + CloseHandle(hProcess); + CloseHandle(processInfo.hThread); + + return (int)exitCode; + +}