blob: b42dd5db481593dd95b98ea05183756eb5c787fe [file] [log] [blame]
José Fonsecabd4937e2012-12-04 13:23:03 +00001/**************************************************************************
2 *
3 * Copyright 2011-2012 Jose Fonseca
4 * All Rights Reserved.
5 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
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
22 * THE SOFTWARE.
23 *
24 **************************************************************************/
25
26
27/*
28 * Code for the DLL that will be injected in the target process.
29 *
30 * The injected DLL will manipulate the import tables to hook the
31 * modules/functions of interest.
32 *
33 * See also:
34 * - http://www.codeproject.com/KB/system/api_spying_hack.aspx
35 * - http://www.codeproject.com/KB/threads/APIHooking.aspx
36 * - http://msdn.microsoft.com/en-us/magazine/cc301808.aspx
37 */
38
39
40#include <windows.h>
José Fonseca6d7bbef2014-09-12 09:17:15 +010041#include <sddl.h>
José Fonsecabd4937e2012-12-04 13:23:03 +000042
43
José Fonseca420d5572014-09-12 09:12:58 +010044static void
Jose Fonseca685d5322018-12-07 11:58:49 +000045#if defined(__MINGW32__)
46 __attribute__ ((format (__MINGW_PRINTF_FORMAT, 1, 2)))
47#elif defined(__GNUC__)
José Fonseca420d5572014-09-12 09:12:58 +010048 __attribute__ ((format (printf, 1, 2)))
49#endif
50debugPrintf(const char *format, ...);
51
52
53static void
54logLastError(const char *szMsg)
55{
56 DWORD dwLastError = GetLastError();
57
58 // http://msdn.microsoft.com/en-gb/library/windows/desktop/ms680582.aspx
59 LPSTR lpErrorMsg = NULL;
60 DWORD cbWritten;
61 cbWritten = FormatMessage(
62 FORMAT_MESSAGE_ALLOCATE_BUFFER |
63 FORMAT_MESSAGE_FROM_SYSTEM |
64 FORMAT_MESSAGE_IGNORE_INSERTS,
65 NULL,
66 dwLastError,
67 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
68 (LPSTR) &lpErrorMsg,
69 0, NULL);
70
71 if (cbWritten) {
72 debugPrintf("inject: error: %s: %s", szMsg, lpErrorMsg);
73 } else {
74 debugPrintf("inject: error: %s: %lu\n", szMsg, dwLastError);
75 }
76
77 LocalFree(lpErrorMsg);
78}
79
80
José Fonsecabd4937e2012-12-04 13:23:03 +000081static inline const char *
82getSeparator(const char *szFilename) {
83 const char *p, *q;
84 p = NULL;
85 q = szFilename;
86 char c;
87 do {
88 c = *q++;
89 if (c == '\\' || c == '/' || c == ':') {
90 p = q;
91 }
92 } while (c);
93 return p;
94}
95
96
97static inline const char *
98getBaseName(const char *szFilename) {
99 const char *pSeparator = getSeparator(szFilename);
100 if (!pSeparator) {
101 return szFilename;
102 }
103 return pSeparator;
104}
105
106
107static inline void
108getDirName(char *szFilename) {
109 char *pSeparator = const_cast<char *>(getSeparator(szFilename));
110 if (pSeparator) {
111 *pSeparator = '\0';
112 }
113}
114
115
116static inline void
117getModuleName(char *szModuleName, size_t n, const char *szFilename) {
118 char *p = szModuleName;
119 const char *q = getBaseName(szFilename);
120 char c;
121 while (--n) {
122 c = *q++;
123 if (c == '.' || c == '\0') {
124 break;
125 }
126 *p++ = c;
127 };
128 *p++ = '\0';
129}
130
131
José Fonseca1405a3e2013-02-22 09:40:30 +0000132#define USE_SHARED_MEM 1
133
134
José Fonseca867d6522013-10-23 17:52:59 +0100135struct SharedMem
136{
137 BOOL bReplaced;
Jose Fonseca59f21c42015-07-06 16:41:55 +0100138 char cVerbosity;
José Fonseca867d6522013-10-23 17:52:59 +0100139 char szDllName[4096 - sizeof(BOOL)];
140};
José Fonseca1405a3e2013-02-22 09:40:30 +0000141
José Fonsecabd4937e2012-12-04 13:23:03 +0000142
José Fonseca867d6522013-10-23 17:52:59 +0100143static SharedMem *pSharedMem = NULL;
José Fonsecabd4937e2012-12-04 13:23:03 +0000144static HANDLE hFileMapping = NULL;
145
146
José Fonseca867d6522013-10-23 17:52:59 +0100147static SharedMem *
Jose Fonseca498710d2015-07-15 16:07:05 +0100148OpenSharedMemory(SECURITY_DESCRIPTOR *lpSecurityDescriptor)
149{
José Fonsecabd4937e2012-12-04 13:23:03 +0000150 if (pSharedMem) {
José Fonseca867d6522013-10-23 17:52:59 +0100151 return pSharedMem;
José Fonsecabd4937e2012-12-04 13:23:03 +0000152 }
153
José Fonseca6d7bbef2014-09-12 09:17:15 +0100154 SECURITY_ATTRIBUTES sa;
Jose Fonseca498710d2015-07-15 16:07:05 +0100155 ZeroMemory(&sa, sizeof sa);
156 sa.nLength = sizeof sa;
157 sa.bInheritHandle = TRUE;
158 sa.lpSecurityDescriptor = lpSecurityDescriptor;
José Fonseca6d7bbef2014-09-12 09:17:15 +0100159
José Fonsecabd4937e2012-12-04 13:23:03 +0000160 hFileMapping = CreateFileMapping(
161 INVALID_HANDLE_VALUE, // system paging file
Jose Fonseca498710d2015-07-15 16:07:05 +0100162 &sa, // lpAttributes
José Fonsecabd4937e2012-12-04 13:23:03 +0000163 PAGE_READWRITE, // read/write access
164 0, // dwMaximumSizeHigh
José Fonseca867d6522013-10-23 17:52:59 +0100165 sizeof(SharedMem), // dwMaximumSizeLow
José Fonsecabd4937e2012-12-04 13:23:03 +0000166 TEXT("injectfilemap")); // name of map object
José Fonseca6d7bbef2014-09-12 09:17:15 +0100167
José Fonsecabd4937e2012-12-04 13:23:03 +0000168 if (hFileMapping == NULL) {
José Fonseca420d5572014-09-12 09:12:58 +0100169 logLastError("failed to create file mapping");
José Fonsecabd4937e2012-12-04 13:23:03 +0000170 return NULL;
171 }
172
173 BOOL bAlreadyExists = (GetLastError() == ERROR_ALREADY_EXISTS);
174
José Fonseca867d6522013-10-23 17:52:59 +0100175 pSharedMem = (SharedMem *)MapViewOfFile(
José Fonsecabd4937e2012-12-04 13:23:03 +0000176 hFileMapping,
177 FILE_MAP_WRITE, // read/write access
178 0, // dwFileOffsetHigh
179 0, // dwFileOffsetLow
180 0); // dwNumberOfBytesToMap (entire file)
181 if (pSharedMem == NULL) {
José Fonseca420d5572014-09-12 09:12:58 +0100182 logLastError("failed to map view");
José Fonsecabd4937e2012-12-04 13:23:03 +0000183 return NULL;
184 }
185
186 if (!bAlreadyExists) {
José Fonseca867d6522013-10-23 17:52:59 +0100187 memset(pSharedMem, 0, sizeof *pSharedMem);
José Fonsecabd4937e2012-12-04 13:23:03 +0000188 }
189
José Fonseca867d6522013-10-23 17:52:59 +0100190 return pSharedMem;
José Fonsecabd4937e2012-12-04 13:23:03 +0000191}
192
193
194static inline VOID
195CloseSharedMem(void) {
196 if (!pSharedMem) {
197 return;
198 }
199
200 UnmapViewOfFile(pSharedMem);
201 pSharedMem = NULL;
202
203 CloseHandle(hFileMapping);
204 hFileMapping = NULL;
205}
206
207
Jose Fonseca6ddfd982015-08-03 11:58:35 +0100208/*
209 * XXX: Mixed architecture don't quite work. See also
210 * http://www.corsix.org/content/dll-injection-and-wow64
211 */
212static BOOL
213isDifferentArch(HANDLE hProcess)
214{
215 typedef BOOL (WINAPI *PFNISWOW64PROCESS)(HANDLE, PBOOL);
216 PFNISWOW64PROCESS pfnIsWow64Process;
217 pfnIsWow64Process = (PFNISWOW64PROCESS)
218 GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");
219 if (!pfnIsWow64Process) {
220 return FALSE;
221 }
222
Jose Fonsecabafc8422015-08-25 11:36:09 +0100223 // NOTE: IsWow64Process will return false on 32-bits Windows
224 BOOL isThisWow64;
225 BOOL isOtherWow64;
226 if (!pfnIsWow64Process(GetCurrentProcess(), &isThisWow64) ||
227 !pfnIsWow64Process(hProcess, &isOtherWow64)) {
Jose Fonseca6ddfd982015-08-03 11:58:35 +0100228 logLastError("IsWow64Process failed");
229 return FALSE;
230 }
231
232 return bool(isThisWow64) != bool(isOtherWow64);
233}
234
235
José Fonseca51a70172014-06-19 13:23:14 +0100236static BOOL
José Fonseca420d5572014-09-12 09:12:58 +0100237injectDll(HANDLE hProcess, const char *szDllPath)
José Fonseca51a70172014-06-19 13:23:14 +0100238{
José Fonseca209a66d2015-03-12 14:13:03 +0000239 BOOL bRet = FALSE;
240 PTHREAD_START_ROUTINE lpStartAddress;
241 HANDLE hThread;
242 DWORD hModule;
José Fonseca51a70172014-06-19 13:23:14 +0100243
244 // Allocate memory in the target process to hold the DLL name
José Fonseca209a66d2015-03-12 14:13:03 +0000245 size_t szDllPathLength = strlen(szDllPath) + 1;
José Fonseca51a70172014-06-19 13:23:14 +0100246 void *lpMemory = VirtualAllocEx(hProcess, NULL, szDllPathLength, MEM_COMMIT, PAGE_READWRITE);
247 if (!lpMemory) {
José Fonseca420d5572014-09-12 09:12:58 +0100248 logLastError("failed to allocate memory in the process");
José Fonseca209a66d2015-03-12 14:13:03 +0000249 goto no_memory;
José Fonseca51a70172014-06-19 13:23:14 +0100250 }
251
252 // Copy DLL name into the target process
253 if (!WriteProcessMemory(hProcess, lpMemory, szDllPath, szDllPathLength, NULL)) {
José Fonseca420d5572014-09-12 09:12:58 +0100254 logLastError("failed to write into process memory");
José Fonseca209a66d2015-03-12 14:13:03 +0000255 goto no_thread;
José Fonseca51a70172014-06-19 13:23:14 +0100256 }
257
258 /*
259 * Get LoadLibraryA address from kernel32.dll. It's the same for all the
260 * process (XXX: but only within the same architecture).
261 */
José Fonseca209a66d2015-03-12 14:13:03 +0000262 lpStartAddress =
José Fonseca51a70172014-06-19 13:23:14 +0100263 (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("KERNEL32"), "LoadLibraryA");
264
265 // Create remote thread in another process
José Fonseca209a66d2015-03-12 14:13:03 +0000266 hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, lpMemory, 0, NULL);
José Fonseca51a70172014-06-19 13:23:14 +0100267 if (!hThread) {
José Fonseca420d5572014-09-12 09:12:58 +0100268 logLastError("failed to create remote thread");
José Fonseca209a66d2015-03-12 14:13:03 +0000269 goto no_thread;
José Fonseca51a70172014-06-19 13:23:14 +0100270 }
271
272 // Wait for it to finish
273 WaitForSingleObject(hThread, INFINITE);
274
José Fonseca51a70172014-06-19 13:23:14 +0100275 GetExitCodeThread(hThread, &hModule);
276 if (!hModule) {
Jose Fonseca1ec83462015-07-30 15:14:44 +0100277 debugPrintf("inject: error: failed to load %s into the remote process %lu\n",
278 szDllPath, GetProcessId(hProcess));
José Fonseca209a66d2015-03-12 14:13:03 +0000279 } else {
280 bRet = TRUE;
José Fonseca51a70172014-06-19 13:23:14 +0100281 }
282
José Fonseca209a66d2015-03-12 14:13:03 +0000283 CloseHandle(hThread);
284no_thread:
285 VirtualFreeEx(hProcess, lpMemory, 0, MEM_RELEASE);
286no_memory:
287 return bRet;
José Fonseca51a70172014-06-19 13:23:14 +0100288}