|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 |
|
7 #if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64)) |
|
8 // This is the .cpp file where the globals live |
|
9 #define DHW_IMPLEMENT_GLOBALS |
|
10 #include <stdio.h> |
|
11 #include "prprf.h" |
|
12 #include "prlog.h" |
|
13 #include "plstr.h" |
|
14 #include "prlock.h" |
|
15 #include "nscore.h" |
|
16 #include "nsDebugHelpWin32.h" |
|
17 #else |
|
18 #error "nsDebugHelpWin32.cpp should only be built in Win32 x86/x64 builds" |
|
19 #endif |
|
20 |
|
21 |
|
22 |
|
23 /***************************************************************************/ |
|
24 |
|
25 |
|
26 PRLock* DHWImportHooker::gLock = nullptr; |
|
27 DHWImportHooker* DHWImportHooker::gHooks = nullptr; |
|
28 decltype(GetProcAddress)* DHWImportHooker::gRealGetProcAddress = nullptr; |
|
29 |
|
30 |
|
31 static bool |
|
32 dhwEnsureImageHlpInitialized() |
|
33 { |
|
34 static bool gInitialized = false; |
|
35 static bool gTried = false; |
|
36 |
|
37 if (!gInitialized && !gTried) { |
|
38 gTried = true; |
|
39 HMODULE module = ::LoadLibrary("DBGHELP.DLL"); |
|
40 if (!module) { |
|
41 DWORD dw = GetLastError(); |
|
42 printf("DumpStack Error: DBGHELP.DLL wasn't found. GetLastError() returned 0x%8.8X\n" |
|
43 " This DLL is needed for succeessfully implementing trace-malloc.\n" |
|
44 " This dll ships by default on Win2k. Disabling trace-malloc functionality.\n" |
|
45 , dw); |
|
46 return false; |
|
47 } |
|
48 |
|
49 #define INIT_PROC(typename_, name_) \ |
|
50 dhw##name_ = (decltype(name_)*) ::GetProcAddress(module, #name_); \ |
|
51 if(!dhw##name_) return false; |
|
52 |
|
53 #ifdef _WIN64 |
|
54 INIT_PROC(ENUMERATELOADEDMODULES64, EnumerateLoadedModules64); |
|
55 #else |
|
56 INIT_PROC(ENUMERATELOADEDMODULES, EnumerateLoadedModules); |
|
57 #endif |
|
58 INIT_PROC(IMAGEDIRECTORYENTRYTODATA, ImageDirectoryEntryToData); |
|
59 |
|
60 #undef INIT_PROC |
|
61 |
|
62 gInitialized = true; |
|
63 } |
|
64 |
|
65 return gInitialized; |
|
66 } |
|
67 |
|
68 |
|
69 DHWImportHooker& |
|
70 DHWImportHooker::getGetProcAddressHooker() |
|
71 { |
|
72 static DHWImportHooker gGetProcAddress("Kernel32.dll", "GetProcAddress", |
|
73 (PROC)DHWImportHooker::GetProcAddress); |
|
74 return gGetProcAddress; |
|
75 } |
|
76 |
|
77 |
|
78 DHWImportHooker& |
|
79 DHWImportHooker::getLoadLibraryWHooker() |
|
80 { |
|
81 static DHWImportHooker gLoadLibraryW("Kernel32.dll", "LoadLibraryW", |
|
82 (PROC)DHWImportHooker::LoadLibraryW); |
|
83 return gLoadLibraryW; |
|
84 } |
|
85 |
|
86 DHWImportHooker& |
|
87 DHWImportHooker::getLoadLibraryExWHooker() |
|
88 { |
|
89 static DHWImportHooker gLoadLibraryExW("Kernel32.dll", "LoadLibraryExW", |
|
90 (PROC)DHWImportHooker::LoadLibraryExW); |
|
91 return gLoadLibraryExW; |
|
92 } |
|
93 |
|
94 DHWImportHooker& |
|
95 DHWImportHooker::getLoadLibraryAHooker() |
|
96 { |
|
97 static DHWImportHooker gLoadLibraryA("Kernel32.dll", "LoadLibraryA", |
|
98 (PROC)DHWImportHooker::LoadLibraryA); |
|
99 return gLoadLibraryA; |
|
100 } |
|
101 |
|
102 DHWImportHooker& |
|
103 DHWImportHooker::getLoadLibraryExAHooker() |
|
104 { |
|
105 static DHWImportHooker gLoadLibraryExA("Kernel32.dll", "LoadLibraryExA", |
|
106 (PROC)DHWImportHooker::LoadLibraryExA); |
|
107 return gLoadLibraryExA; |
|
108 } |
|
109 |
|
110 |
|
111 static HMODULE ThisModule() |
|
112 { |
|
113 MEMORY_BASIC_INFORMATION info; |
|
114 return VirtualQuery(ThisModule, &info, sizeof(info)) ? |
|
115 (HMODULE) info.AllocationBase : nullptr; |
|
116 } |
|
117 |
|
118 DHWImportHooker::DHWImportHooker(const char* aModuleName, |
|
119 const char* aFunctionName, |
|
120 PROC aHook, |
|
121 bool aExcludeOurModule /* = false */) |
|
122 : mNext(nullptr), |
|
123 mModuleName(aModuleName), |
|
124 mFunctionName(aFunctionName), |
|
125 mOriginal(nullptr), |
|
126 mHook(aHook), |
|
127 mIgnoreModule(aExcludeOurModule ? ThisModule() : nullptr), |
|
128 mHooking(true) |
|
129 { |
|
130 //printf("DHWImportHooker hooking %s, function %s\n",aModuleName, aFunctionName); |
|
131 |
|
132 if(!gLock) |
|
133 gLock = PR_NewLock(); |
|
134 PR_Lock(gLock); |
|
135 |
|
136 dhwEnsureImageHlpInitialized(); // for the extra ones we care about. |
|
137 |
|
138 if(!gRealGetProcAddress) |
|
139 gRealGetProcAddress = ::GetProcAddress; |
|
140 |
|
141 mOriginal = gRealGetProcAddress(::GetModuleHandleA(aModuleName), |
|
142 aFunctionName), |
|
143 |
|
144 mNext = gHooks; |
|
145 gHooks = this; |
|
146 |
|
147 PatchAllModules(); |
|
148 |
|
149 PR_Unlock(gLock); |
|
150 } |
|
151 |
|
152 DHWImportHooker::~DHWImportHooker() |
|
153 { |
|
154 PR_Lock(gLock); |
|
155 |
|
156 mHooking = false; |
|
157 PatchAllModules(); |
|
158 |
|
159 for (DHWImportHooker **cur = &gHooks; |
|
160 (PR_ASSERT(*cur), *cur); /* assert that we find this */ |
|
161 cur = &(*cur)->mNext) |
|
162 { |
|
163 if (*cur == this) |
|
164 { |
|
165 *cur = mNext; |
|
166 break; |
|
167 } |
|
168 } |
|
169 |
|
170 if(!gHooks) |
|
171 { |
|
172 PRLock* theLock = gLock; |
|
173 gLock = nullptr; |
|
174 PR_Unlock(theLock); |
|
175 PR_DestroyLock(theLock); |
|
176 } |
|
177 if (gLock) |
|
178 PR_Unlock(gLock); |
|
179 } |
|
180 |
|
181 #ifdef _WIN64 |
|
182 static BOOL CALLBACK ModuleEnumCallback(PCSTR ModuleName, |
|
183 DWORD64 ModuleBase, |
|
184 ULONG ModuleSize, |
|
185 PVOID UserContext) |
|
186 #else |
|
187 static BOOL CALLBACK ModuleEnumCallback(PCSTR ModuleName, |
|
188 ULONG ModuleBase, |
|
189 ULONG ModuleSize, |
|
190 PVOID UserContext) |
|
191 #endif |
|
192 { |
|
193 //printf("Module Name %s\n",ModuleName); |
|
194 DHWImportHooker* self = (DHWImportHooker*) UserContext; |
|
195 HMODULE aModule = (HMODULE) ModuleBase; |
|
196 return self->PatchOneModule(aModule, ModuleName); |
|
197 } |
|
198 |
|
199 bool |
|
200 DHWImportHooker::PatchAllModules() |
|
201 { |
|
202 // Need to cast to PENUMLOADED_MODULES_CALLBACK because the |
|
203 // constness of the first parameter of PENUMLOADED_MODULES_CALLBACK |
|
204 // varies over SDK versions (from non-const to const over time). |
|
205 // See bug 391848 and bug 415426. |
|
206 #ifdef _WIN64 |
|
207 return dhwEnumerateLoadedModules64(::GetCurrentProcess(), |
|
208 (PENUMLOADED_MODULES_CALLBACK64)ModuleEnumCallback, this); |
|
209 #else |
|
210 return dhwEnumerateLoadedModules(::GetCurrentProcess(), |
|
211 (PENUMLOADED_MODULES_CALLBACK)ModuleEnumCallback, this); |
|
212 #endif |
|
213 } |
|
214 |
|
215 bool |
|
216 DHWImportHooker::PatchOneModule(HMODULE aModule, const char* name) |
|
217 { |
|
218 if(aModule == mIgnoreModule) |
|
219 { |
|
220 return true; |
|
221 } |
|
222 |
|
223 // do the fun stuff... |
|
224 |
|
225 PIMAGE_IMPORT_DESCRIPTOR desc; |
|
226 ULONG size; |
|
227 |
|
228 desc = (PIMAGE_IMPORT_DESCRIPTOR) |
|
229 dhwImageDirectoryEntryToData(aModule, true, |
|
230 IMAGE_DIRECTORY_ENTRY_IMPORT, &size); |
|
231 |
|
232 if(!desc) |
|
233 { |
|
234 return true; |
|
235 } |
|
236 |
|
237 for(; desc->Name; desc++) |
|
238 { |
|
239 const char* entryModuleName = (const char*) |
|
240 ((char*)aModule + desc->Name); |
|
241 if(!lstrcmpi(entryModuleName, mModuleName)) |
|
242 break; |
|
243 } |
|
244 |
|
245 if(!desc->Name) |
|
246 { |
|
247 return true; |
|
248 } |
|
249 |
|
250 PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA) |
|
251 ((char*) aModule + desc->FirstThunk); |
|
252 |
|
253 for(; thunk->u1.Function; thunk++) |
|
254 { |
|
255 PROC original; |
|
256 PROC replacement; |
|
257 |
|
258 if(mHooking) |
|
259 { |
|
260 original = mOriginal; |
|
261 replacement = mHook; |
|
262 } |
|
263 else |
|
264 { |
|
265 original = mHook; |
|
266 replacement = mOriginal; |
|
267 } |
|
268 |
|
269 PROC* ppfn = (PROC*) &thunk->u1.Function; |
|
270 if(*ppfn == original) |
|
271 { |
|
272 DWORD dwDummy; |
|
273 VirtualProtect(ppfn, sizeof(ppfn), PAGE_EXECUTE_READWRITE, &dwDummy); |
|
274 BOOL result = WriteProcessMemory(GetCurrentProcess(), |
|
275 ppfn, &replacement, sizeof(replacement), nullptr); |
|
276 if (!result) //failure |
|
277 { |
|
278 printf("failure name %s func %x\n",name,*ppfn); |
|
279 DWORD error = GetLastError(); |
|
280 return true; |
|
281 } |
|
282 else |
|
283 { |
|
284 // printf("success name %s func %x\n",name,*ppfn); |
|
285 DWORD filler = result+1; |
|
286 return result; |
|
287 } |
|
288 } |
|
289 |
|
290 } |
|
291 return true; |
|
292 } |
|
293 |
|
294 bool |
|
295 DHWImportHooker::ModuleLoaded(HMODULE aModule, DWORD flags) |
|
296 { |
|
297 //printf("ModuleLoaded\n"); |
|
298 if(aModule && !(flags & LOAD_LIBRARY_AS_DATAFILE)) |
|
299 { |
|
300 PR_Lock(gLock); |
|
301 // We don't know that the newly loaded module didn't drag in implicitly |
|
302 // linked modules, so we patch everything in sight. |
|
303 for(DHWImportHooker* cur = gHooks; cur; cur = cur->mNext) |
|
304 cur->PatchAllModules(); |
|
305 PR_Unlock(gLock); |
|
306 } |
|
307 return true; |
|
308 } |
|
309 |
|
310 // static |
|
311 HMODULE WINAPI |
|
312 DHWImportHooker::LoadLibraryW(PCWSTR path) |
|
313 { |
|
314 //wprintf(L"LoadLibraryW %s\n",path); |
|
315 HMODULE hmod = DHW_ORIGINAL(::LoadLibraryW, getLoadLibraryWHooker())(path); |
|
316 ModuleLoaded(hmod, 0); |
|
317 return hmod; |
|
318 } |
|
319 |
|
320 |
|
321 // static |
|
322 HMODULE WINAPI |
|
323 DHWImportHooker::LoadLibraryExW(PCWSTR path, HANDLE file, DWORD flags) |
|
324 { |
|
325 //wprintf(L"LoadLibraryExW %s\n",path); |
|
326 HMODULE hmod = DHW_ORIGINAL(::LoadLibraryExW, getLoadLibraryExWHooker())(path, file, flags); |
|
327 ModuleLoaded(hmod, flags); |
|
328 return hmod; |
|
329 } |
|
330 |
|
331 // static |
|
332 HMODULE WINAPI |
|
333 DHWImportHooker::LoadLibraryA(PCSTR path) |
|
334 { |
|
335 //printf("LoadLibraryA %s\n",path); |
|
336 HMODULE hmod = DHW_ORIGINAL(::LoadLibraryA, getLoadLibraryAHooker())(path); |
|
337 ModuleLoaded(hmod, 0); |
|
338 return hmod; |
|
339 } |
|
340 |
|
341 // static |
|
342 HMODULE WINAPI |
|
343 DHWImportHooker::LoadLibraryExA(PCSTR path, HANDLE file, DWORD flags) |
|
344 { |
|
345 //printf("LoadLibraryExA %s\n",path); |
|
346 HMODULE hmod = DHW_ORIGINAL(::LoadLibraryExA, getLoadLibraryExAHooker())(path, file, flags); |
|
347 ModuleLoaded(hmod, flags); |
|
348 return hmod; |
|
349 } |
|
350 // static |
|
351 FARPROC WINAPI |
|
352 DHWImportHooker::GetProcAddress(HMODULE aModule, PCSTR aFunctionName) |
|
353 { |
|
354 FARPROC pfn = gRealGetProcAddress(aModule, aFunctionName); |
|
355 |
|
356 if(pfn) |
|
357 { |
|
358 PR_Lock(gLock); |
|
359 for(DHWImportHooker* cur = gHooks; cur; cur = cur->mNext) |
|
360 { |
|
361 if(pfn == cur->mOriginal) |
|
362 { |
|
363 pfn = cur->mHook; |
|
364 break; |
|
365 } |
|
366 } |
|
367 PR_Unlock(gLock); |
|
368 } |
|
369 return pfn; |
|
370 } |
|
371 |
|
372 |