blob: b42dd5db481593dd95b98ea05183756eb5c787fe [file] [log] [blame]
/**************************************************************************
*
* 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;
}