| /************************************************************************** |
| * |
| * Copyright 2011-2012 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. |
| * |
| **************************************************************************/ |
| |
| |
| /* |
| * Code for the DLL that will be injected in the target process. |
| * |
| * The injected DLL will manipulate the import tables to hook the |
| * modules/functions of interest. |
| * |
| * See also: |
| * - http://www.codeproject.com/KB/system/api_spying_hack.aspx |
| * - http://www.codeproject.com/KB/threads/APIHooking.aspx |
| * - http://msdn.microsoft.com/en-us/magazine/cc301808.aspx |
| */ |
| |
| |
| #include <windows.h> |
| #include <sddl.h> |
| |
| |
| static void |
| #if defined(__MINGW32__) |
| __attribute__ ((format (__MINGW_PRINTF_FORMAT, 1, 2))) |
| #elif defined(__GNUC__) |
| __attribute__ ((format (printf, 1, 2))) |
| #endif |
| debugPrintf(const char *format, ...); |
| |
| |
| static void |
| logLastError(const char *szMsg) |
| { |
| DWORD dwLastError = GetLastError(); |
| |
| // http://msdn.microsoft.com/en-gb/library/windows/desktop/ms680582.aspx |
| LPSTR lpErrorMsg = NULL; |
| DWORD cbWritten; |
| cbWritten = FormatMessage( |
| FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| FORMAT_MESSAGE_FROM_SYSTEM | |
| FORMAT_MESSAGE_IGNORE_INSERTS, |
| NULL, |
| dwLastError, |
| MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
| (LPSTR) &lpErrorMsg, |
| 0, NULL); |
| |
| if (cbWritten) { |
| debugPrintf("inject: error: %s: %s", szMsg, lpErrorMsg); |
| } else { |
| debugPrintf("inject: error: %s: %lu\n", szMsg, dwLastError); |
| } |
| |
| LocalFree(lpErrorMsg); |
| } |
| |
| |
| static inline const char * |
| getSeparator(const char *szFilename) { |
| const char *p, *q; |
| p = NULL; |
| q = szFilename; |
| char c; |
| do { |
| c = *q++; |
| if (c == '\\' || c == '/' || c == ':') { |
| p = q; |
| } |
| } while (c); |
| return p; |
| } |
| |
| |
| static inline const char * |
| getBaseName(const char *szFilename) { |
| const char *pSeparator = getSeparator(szFilename); |
| if (!pSeparator) { |
| return szFilename; |
| } |
| return pSeparator; |
| } |
| |
| |
| static inline void |
| getDirName(char *szFilename) { |
| char *pSeparator = const_cast<char *>(getSeparator(szFilename)); |
| if (pSeparator) { |
| *pSeparator = '\0'; |
| } |
| } |
| |
| |
| static inline void |
| getModuleName(char *szModuleName, size_t n, const char *szFilename) { |
| char *p = szModuleName; |
| const char *q = getBaseName(szFilename); |
| char c; |
| while (--n) { |
| c = *q++; |
| if (c == '.' || c == '\0') { |
| break; |
| } |
| *p++ = c; |
| }; |
| *p++ = '\0'; |
| } |
| |
| |
| #define USE_SHARED_MEM 1 |
| |
| |
| struct SharedMem |
| { |
| BOOL bReplaced; |
| char cVerbosity; |
| char szDllName[4096 - sizeof(BOOL)]; |
| }; |
| |
| |
| static SharedMem *pSharedMem = NULL; |
| static HANDLE hFileMapping = NULL; |
| |
| |
| static SharedMem * |
| OpenSharedMemory(SECURITY_DESCRIPTOR *lpSecurityDescriptor) |
| { |
| if (pSharedMem) { |
| return pSharedMem; |
| } |
| |
| SECURITY_ATTRIBUTES sa; |
| ZeroMemory(&sa, sizeof sa); |
| sa.nLength = sizeof sa; |
| sa.bInheritHandle = TRUE; |
| sa.lpSecurityDescriptor = lpSecurityDescriptor; |
| |
| hFileMapping = CreateFileMapping( |
| INVALID_HANDLE_VALUE, // system paging file |
| &sa, // lpAttributes |
| PAGE_READWRITE, // read/write access |
| 0, // dwMaximumSizeHigh |
| sizeof(SharedMem), // dwMaximumSizeLow |
| TEXT("injectfilemap")); // name of map object |
| |
| if (hFileMapping == NULL) { |
| logLastError("failed to create file mapping"); |
| return NULL; |
| } |
| |
| BOOL bAlreadyExists = (GetLastError() == ERROR_ALREADY_EXISTS); |
| |
| pSharedMem = (SharedMem *)MapViewOfFile( |
| hFileMapping, |
| FILE_MAP_WRITE, // read/write access |
| 0, // dwFileOffsetHigh |
| 0, // dwFileOffsetLow |
| 0); // dwNumberOfBytesToMap (entire file) |
| if (pSharedMem == NULL) { |
| logLastError("failed to map view"); |
| return NULL; |
| } |
| |
| if (!bAlreadyExists) { |
| memset(pSharedMem, 0, sizeof *pSharedMem); |
| } |
| |
| return pSharedMem; |
| } |
| |
| |
| static inline VOID |
| CloseSharedMem(void) { |
| if (!pSharedMem) { |
| return; |
| } |
| |
| UnmapViewOfFile(pSharedMem); |
| pSharedMem = NULL; |
| |
| CloseHandle(hFileMapping); |
| hFileMapping = NULL; |
| } |
| |
| |
| /* |
| * XXX: Mixed architecture don't quite work. See also |
| * http://www.corsix.org/content/dll-injection-and-wow64 |
| */ |
| static BOOL |
| isDifferentArch(HANDLE hProcess) |
| { |
| typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL); |
| PFNISWOW64PROCESS pfnIsWow64Process; |
| pfnIsWow64Process = (PFNISWOW64PROCESS) |
| GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process"); |
| if (!pfnIsWow64Process) { |
| return FALSE; |
| } |
| |
| // NOTE: IsWow64Process will return false on 32-bits Windows |
| BOOL isThisWow64; |
| BOOL isOtherWow64; |
| if (!pfnIsWow64Process(GetCurrentProcess(), &isThisWow64) || |
| !pfnIsWow64Process(hProcess, &isOtherWow64)) { |
| logLastError("IsWow64Process failed"); |
| return FALSE; |
| } |
| |
| return bool(isThisWow64) != bool(isOtherWow64); |
| } |
| |
| |
| static BOOL |
| injectDll(HANDLE hProcess, const char *szDllPath) |
| { |
| BOOL bRet = FALSE; |
| PTHREAD_START_ROUTINE lpStartAddress; |
| HANDLE hThread; |
| DWORD hModule; |
| |
| // Allocate memory in the target process to hold the DLL name |
| size_t szDllPathLength = strlen(szDllPath) + 1; |
| void *lpMemory = VirtualAllocEx(hProcess, NULL, szDllPathLength, MEM_COMMIT, PAGE_READWRITE); |
| if (!lpMemory) { |
| logLastError("failed to allocate memory in the process"); |
| goto no_memory; |
| } |
| |
| // Copy DLL name into the target process |
| if (!WriteProcessMemory(hProcess, lpMemory, szDllPath, szDllPathLength, NULL)) { |
| logLastError("failed to write into process memory"); |
| goto no_thread; |
| } |
| |
| /* |
| * Get LoadLibraryA address from kernel32.dll. It's the same for all the |
| * process (XXX: but only within the same architecture). |
| */ |
| lpStartAddress = |
| (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("KERNEL32"), "LoadLibraryA"); |
| |
| // Create remote thread in another process |
| hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpMemory, 0, NULL); |
| if (!hThread) { |
| logLastError("failed to create remote thread"); |
| goto no_thread; |
| } |
| |
| // Wait for it to finish |
| WaitForSingleObject(hThread, INFINITE); |
| |
| GetExitCodeThread(hThread, &hModule); |
| if (!hModule) { |
| debugPrintf("inject: error: failed to load %s into the remote process %lu\n", |
| szDllPath, GetProcessId(hProcess)); |
| } else { |
| bRet = TRUE; |
| } |
| |
| CloseHandle(hThread); |
| no_thread: |
| VirtualFreeEx(hProcess, lpMemory, 0, MEM_RELEASE); |
| no_memory: |
| return bRet; |
| } |