OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome_frame/module_utils.h" | 5 #include "chrome_frame/module_utils.h" |
6 | 6 |
7 #include <atlbase.h> | 7 #include <atlbase.h> |
8 #include <TlHelp32.h> | 8 #include "base/logging.h" |
9 | 9 |
10 #include "base/scoped_ptr.h" | 10 const wchar_t kBeaconWindowClassName[] = |
11 #include "base/file_version_info.h" | 11 L"ChromeFrameBeaconWindowClass826C5D01-E355-4b23-8AC2-40650E0B7843"; |
12 #include "base/logging.h" | |
13 #include "base/scoped_handle.h" | |
14 #include "base/string_util.h" | |
15 #include "base/version.h" | |
16 #include "chrome_frame/exception_barrier.h" | |
17 | 12 |
18 DllRedirector::DllRedirector() : dcgo_ptr_(NULL), initialized_(false), | 13 // static |
19 module_handle_(NULL) {} | 14 ATOM DllRedirector::atom_ = 0; |
20 | 15 |
21 DllRedirector::~DllRedirector() { | 16 bool DllRedirector::RegisterAsFirstCFModule() { |
22 if (module_handle_) { | 17 // This would imply that this module had already registered a window class |
23 FreeLibrary(module_handle_); | 18 // which should never happen. |
24 module_handle_ = NULL; | 19 if (atom_) { |
25 } | 20 NOTREACHED(); |
26 } | 21 return true; |
27 | |
28 void DllRedirector::EnsureInitialized(const wchar_t* module_name, | |
29 REFCLSID clsid) { | |
30 if (!initialized_) { | |
31 initialized_ = true; | |
32 // Also sets module_handle_. | |
33 dcgo_ptr_ = GetDllGetClassObjectFromModuleName(module_name, clsid); | |
34 } | |
35 } | |
36 | |
37 LPFNGETCLASSOBJECT DllRedirector::get_dll_get_class_object_ptr() const { | |
38 DCHECK(initialized_); | |
39 return dcgo_ptr_; | |
40 } | |
41 | |
42 LPFNGETCLASSOBJECT DllRedirector::GetDllGetClassObjectFromModuleName( | |
43 const wchar_t* module_name, REFCLSID clsid) { | |
44 module_handle_ = NULL; | |
45 LPFNGETCLASSOBJECT proc_ptr = NULL; | |
46 HMODULE module_handle; | |
47 if (GetOldestNamedModuleHandle(module_name, clsid, &module_handle)) { | |
48 HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase); | |
49 if (module_handle != this_module) { | |
50 proc_ptr = GetDllGetClassObjectPtr(module_handle); | |
51 if (proc_ptr) { | |
52 // Stash away the module handle in module_handle_ so that it will be | |
53 // automatically closed when we get destroyed. GetDllGetClassObjectPtr | |
54 // above will have incremented the module's ref count. | |
55 module_handle_ = module_handle; | |
56 } | |
57 } else { | |
58 LOG(INFO) << "Module Scan: DllGetClassObject found in current module."; | |
59 } | |
60 } | 22 } |
61 | 23 |
62 return proc_ptr; | 24 WNDCLASSEX wnd_class = {0}; |
63 } | 25 wnd_class.cbSize = sizeof(WNDCLASSEX); |
| 26 wnd_class.style = CS_GLOBALCLASS; |
| 27 wnd_class.lpfnWndProc = &DefWindowProc; |
| 28 wnd_class.cbClsExtra = sizeof(HMODULE); |
| 29 wnd_class.cbWndExtra = 0; |
| 30 wnd_class.hInstance = NULL; |
| 31 wnd_class.hIcon = NULL; |
64 | 32 |
65 bool DllRedirector::GetOldestNamedModuleHandle(const std::wstring& module_name, | 33 wnd_class.hCursor = LoadCursor(NULL, IDC_ARROW); |
66 REFCLSID clsid, | 34 wnd_class.hbrBackground = NULL; |
67 HMODULE* oldest_module_handle) { | 35 wnd_class.lpszMenuName = NULL; |
68 DCHECK(oldest_module_handle); | 36 wnd_class.lpszClassName = kBeaconWindowClassName; |
| 37 wnd_class.hIconSm = wnd_class.hIcon; |
69 | 38 |
70 ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0)); | 39 ATOM atom = RegisterClassEx(&wnd_class); |
71 if (snapshot == INVALID_HANDLE_VALUE) { | |
72 LOG(ERROR) << "Could not create module snapshot!"; | |
73 return false; | |
74 } | |
75 | |
76 | 40 |
77 bool success = false; | 41 bool success = false; |
78 PathToHModuleMap map; | 42 if (atom != 0) { |
79 | 43 HWND hwnd = CreateWindow(MAKEINTATOM(atom), L"temp_window", WS_POPUP, |
80 { | 44 0, 0, 0, 0, NULL, NULL, NULL, NULL); |
81 // Here we add an SEH to the chain to prevent our VEH from picking up on any | 45 DCHECK(IsWindow(hwnd)); |
82 // exceptions thrown in DLLs who hook some of the below api calls. We will | 46 if (hwnd) { |
83 // still report the exceptions if they make our way back to us, the hope is | 47 HMODULE this_module = reinterpret_cast<HMODULE>(&__ImageBase); |
84 // that they will not. | 48 LONG_PTR lp = reinterpret_cast<LONG_PTR>(this_module); |
85 ExceptionBarrier exception_barrier; | 49 SetClassLongPtr(hwnd, 0, lp); |
86 | 50 // We need to check the GLE value since SetClassLongPtr returns 0 on |
87 // First get the list of module paths, and save the full path to base | 51 // failure as well as on the first call. |
88 // address mapping. | 52 if (GetLastError() == ERROR_SUCCESS) { |
89 MODULEENTRY32W module_entry = {0}; | 53 atom_ = atom; |
90 module_entry.dwSize = sizeof(module_entry); | 54 success = true; |
91 BOOL cont = Module32FirstW(snapshot, &module_entry); | |
92 while (cont) { | |
93 if (!lstrcmpi(module_entry.szModule, module_name.c_str())) { | |
94 std::wstring full_path(module_entry.szExePath); | |
95 map[full_path] = module_entry.hModule; | |
96 } | 55 } |
97 SecureZeroMemory(&module_entry, sizeof(MODULEENTRY32W)); | 56 DestroyWindow(hwnd); |
98 module_entry.dwSize = sizeof(module_entry); | |
99 cont = Module32NextW(snapshot, &module_entry); | |
100 } | 57 } |
101 } | 58 } |
102 | 59 |
103 // Next, enumerate the map and find the oldest version of the module. | |
104 // (check if the map is of size 1 first) | |
105 if (!map.empty()) { | |
106 if (map.size() == 1) { | |
107 *oldest_module_handle = map.begin()->second; | |
108 } else { | |
109 *oldest_module_handle = GetHandleOfOldestModule(map, clsid); | |
110 } | |
111 | |
112 if (*oldest_module_handle != NULL) { | |
113 success = true; | |
114 } | |
115 } else { | |
116 LOG(INFO) << "Module Scan: No modules named " << module_name | |
117 << " were found."; | |
118 } | |
119 | |
120 return success; | 60 return success; |
121 } | 61 } |
122 | 62 |
123 HMODULE DllRedirector::GetHandleOfOldestModule(const PathToHModuleMap& map, | 63 void DllRedirector::UnregisterAsFirstCFModule() { |
124 REFCLSID clsid) { | 64 if (atom_) { |
| 65 UnregisterClass(MAKEINTATOM(atom_), NULL); |
| 66 } |
| 67 } |
| 68 |
| 69 HMODULE DllRedirector::GetFirstCFModule() { |
| 70 WNDCLASSEX wnd_class = {0}; |
125 HMODULE oldest_module = NULL; | 71 HMODULE oldest_module = NULL; |
126 scoped_ptr<Version> min_version( | 72 HWND hwnd = CreateWindow(kBeaconWindowClassName, L"temp_window", WS_POPUP, 0, |
127 Version::GetVersionFromString("999.999.999.999")); | 73 0, 0, 0, NULL, NULL, NULL, NULL); |
128 | 74 DCHECK(IsWindow(hwnd)); |
129 PathToHModuleMap::const_iterator map_iter(map.begin()); | 75 if (hwnd) { |
130 for (; map_iter != map.end(); ++map_iter) { | 76 oldest_module = reinterpret_cast<HMODULE>(GetClassLongPtr(hwnd, 0)); |
131 // First check that either we are in the current module or that the DLL | 77 DestroyWindow(hwnd); |
132 // returns a class factory for our clsid. | |
133 bool current_module = | |
134 (map_iter->second == reinterpret_cast<HMODULE>(&__ImageBase)); | |
135 bool gco_succeeded = false; | |
136 if (!current_module) { | |
137 LPFNGETCLASSOBJECT dgco_ptr = GetDllGetClassObjectPtr(map_iter->second); | |
138 if (dgco_ptr) { | |
139 { | |
140 CComPtr<IClassFactory> class_factory; | |
141 HRESULT hr = dgco_ptr(clsid, IID_IClassFactory, | |
142 reinterpret_cast<void**>(&class_factory)); | |
143 gco_succeeded = SUCCEEDED(hr) && class_factory != NULL; | |
144 } | |
145 // Release the module ref count we picked up in GetDllGetClassObjectPtr. | |
146 FreeLibrary(map_iter->second); | |
147 } | |
148 } | |
149 | |
150 if (current_module || gco_succeeded) { | |
151 // Then check that the version is less than we've already found: | |
152 scoped_ptr<FileVersionInfo> version_info( | |
153 FileVersionInfo::CreateFileVersionInfo(map_iter->first)); | |
154 scoped_ptr<Version> version( | |
155 Version::GetVersionFromString(version_info->file_version())); | |
156 if (version->CompareTo(*min_version.get()) < 0) { | |
157 oldest_module = map_iter->second; | |
158 min_version.reset(version.release()); | |
159 } | |
160 } | |
161 } | 78 } |
162 | |
163 return oldest_module; | 79 return oldest_module; |
164 } | 80 } |
165 | 81 |
166 LPFNGETCLASSOBJECT DllRedirector::GetDllGetClassObjectPtr(HMODULE module) { | 82 LPFNGETCLASSOBJECT DllRedirector::GetDllGetClassObjectPtr(HMODULE module) { |
167 LPFNGETCLASSOBJECT proc_ptr = NULL; | 83 LPFNGETCLASSOBJECT proc_ptr = NULL; |
168 HMODULE temp_handle = 0; | 84 HMODULE temp_handle = 0; |
169 // Increment the module ref count while we have an pointer to its | 85 // Increment the module ref count while we have an pointer to its |
170 // DllGetClassObject function. | 86 // DllGetClassObject function. |
171 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | 87 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
172 reinterpret_cast<LPCTSTR>(module), | 88 reinterpret_cast<LPCTSTR>(module), |
173 &temp_handle)) { | 89 &temp_handle)) { |
174 proc_ptr = reinterpret_cast<LPFNGETCLASSOBJECT>( | 90 proc_ptr = reinterpret_cast<LPFNGETCLASSOBJECT>( |
175 GetProcAddress(temp_handle, "DllGetClassObject")); | 91 GetProcAddress(temp_handle, "DllGetClassObject")); |
176 if (!proc_ptr) { | 92 if (!proc_ptr) { |
177 FreeLibrary(temp_handle); | 93 FreeLibrary(temp_handle); |
178 LOG(ERROR) << "Module Scan: Couldn't get address of " | 94 LOG(ERROR) << "Module Scan: Couldn't get address of " |
179 << "DllGetClassObject: " | 95 << "DllGetClassObject: " |
180 << GetLastError(); | 96 << GetLastError(); |
181 } | 97 } |
182 } else { | 98 } else { |
183 LOG(ERROR) << "Module Scan: Could not increment module count: " | 99 LOG(ERROR) << "Module Scan: Could not increment module count: " |
184 << GetLastError(); | 100 << GetLastError(); |
185 } | 101 } |
186 return proc_ptr; | 102 return proc_ptr; |
187 } | 103 } |
OLD | NEW |