OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome_frame/buggy_bho_handling.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/logging.h" | |
10 #include "base/process/memory.h" | |
11 #include "base/win/scoped_comptr.h" | |
12 #include "chrome_frame/exception_barrier.h" | |
13 #include "chrome_frame/function_stub.h" | |
14 #include "chrome_frame/pin_module.h" | |
15 #include "chrome_frame/utils.h" | |
16 #include "chrome_frame/vtable_patch_manager.h" | |
17 | |
18 namespace buggy_bho { | |
19 | |
20 base::ThreadLocalPointer<BuggyBhoTls> BuggyBhoTls::s_bad_object_tls_; | |
21 | |
22 struct ModuleAndVersion { | |
23 const char* module_name_; | |
24 const uint32 major_version_; | |
25 const uint32 minor_version_; | |
26 }; | |
27 | |
28 const ModuleAndVersion kBuggyModules[] = { | |
29 { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5. | |
30 { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12 | |
31 { "gbiehcef.dll", 3, 8 }, // troublemaker: 3.8.11.23 | |
32 { "alot.dll", 2, 5 }, // troublemaker: 2.5.12000.509 | |
33 { "ctbr.dll", 5, 1 }, // troublemaker: 5.1.0.95 | |
34 { "srchbxex.dll", 1, 2 }, // troublemaker: 1.2.123.0 | |
35 { "iedvtool32.dll", 8, 0 }, // troublemaker: 8.0.0.4 | |
36 { "mst164.dll", 9, 1 }, // troublemaker: 9.1.3700.1 | |
37 { "deposit_ie_com.dll", 0, 1 }, // troublemaker: 0.1.0.72 | |
38 { "rpshell32.dll", 6, 0 }, // troublemaker: 6.0.6000.1389 | |
39 { "msgsres.dll", 6, 0 }, // troublemaker: 6.0.6000.1389 | |
40 { "limewireinttb.dll", 4, 1 }, // troublemaker: 4.1.1.1000 | |
41 { "pxsecure.dll", 3, 0 }, // troublemaker: 3.0.5.220 | |
42 | |
43 // These BHOs seem to be out of the same buggy BHO factory | |
44 { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0 | |
45 { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0 | |
46 { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0 | |
47 { "tbfre0.dll", 4, 5 }, // troublemaker: 4.5.181.1 | |
48 { "tbmypl.dll", 4, 5 }, // troublemaker: 4.5.181.3 | |
49 { "tbmul1.dll", 4, 5 }, // troublemaker: 4.5.181.1 | |
50 { "tbdow1.dll", 4, 5 }, // troublemaker: 4.5.167.0 | |
51 { "tbfree.dll", 4, 5 }, // troublemaker: 4.5.178.0 | |
52 | |
53 // Viruses? | |
54 { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1 | |
55 { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2 | |
56 { "snagwb.dll", 0xffff, 0xffff }, // troublemaker: 2.6.0.28 | |
57 }; | |
58 | |
59 bool IsBuggyBho(HMODULE mod) { | |
60 DCHECK(mod); | |
61 | |
62 char path[MAX_PATH * 2] = {0}; | |
63 ::GetModuleFileNameA(mod, path, arraysize(path)); | |
64 const char* file_name = ::PathFindFileNameA(path); | |
65 for (size_t i = 0; i < arraysize(kBuggyModules); ++i) { | |
66 if (lstrcmpiA(file_name, kBuggyModules[i].module_name_) == 0) { | |
67 uint32 version = 0; | |
68 GetModuleVersion(mod, &version, NULL); | |
69 const ModuleAndVersion& buggy = kBuggyModules[i]; | |
70 if (HIWORD(version) < buggy.major_version_ || | |
71 (HIWORD(version) == buggy.major_version_ && | |
72 LOWORD(version) <= buggy.minor_version_)) { | |
73 return true; | |
74 } | |
75 } | |
76 } | |
77 | |
78 return false; | |
79 } | |
80 | |
81 BuggyBhoTls::BuggyBhoTls() | |
82 : patched_(false) { | |
83 DCHECK(s_bad_object_tls_.Get() == NULL); | |
84 s_bad_object_tls_.Set(this); | |
85 } | |
86 | |
87 BuggyBhoTls::~BuggyBhoTls() { | |
88 DCHECK(BuggyBhoTls::GetInstance() == this); | |
89 s_bad_object_tls_.Set(NULL); | |
90 } | |
91 | |
92 void BuggyBhoTls::AddBuggyObject(IDispatch* obj) { | |
93 bad_objects_.push_back(obj); | |
94 } | |
95 | |
96 bool BuggyBhoTls::ShouldSkipInvoke(IDispatch* obj) const { | |
97 DCHECK(web_browser2_ != NULL); | |
98 if (IsChromeFrameDocument(web_browser2_)) { | |
99 return std::find(bad_objects_.begin(), bad_objects_.end(), obj) != | |
100 bad_objects_.end(); | |
101 } | |
102 return false; | |
103 } | |
104 | |
105 // static | |
106 BuggyBhoTls* BuggyBhoTls::GetInstance() { | |
107 BuggyBhoTls* tls_instance = s_bad_object_tls_.Get(); | |
108 if (!tls_instance) { | |
109 tls_instance = new BuggyBhoTls(); | |
110 DCHECK(s_bad_object_tls_.Get() != NULL); | |
111 } | |
112 return tls_instance; | |
113 } | |
114 | |
115 // static | |
116 void BuggyBhoTls::DestroyInstance() { | |
117 BuggyBhoTls* tls_instance = s_bad_object_tls_.Get(); | |
118 if (tls_instance) { | |
119 delete tls_instance; | |
120 DCHECK(s_bad_object_tls_.Get() == NULL); | |
121 } | |
122 } | |
123 | |
124 HRESULT BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2* browser) { | |
125 if (patched_) | |
126 return S_FALSE; | |
127 | |
128 DCHECK(browser); | |
129 DCHECK(web_browser2_ == NULL); | |
130 | |
131 base::win::ScopedComPtr<IConnectionPointContainer> cpc; | |
132 HRESULT hr = cpc.QueryFrom(browser); | |
133 if (SUCCEEDED(hr)) { | |
134 const GUID sinks[] = { DIID_DWebBrowserEvents2, DIID_DWebBrowserEvents }; | |
135 for (size_t i = 0; i < arraysize(sinks); ++i) { | |
136 base::win::ScopedComPtr<IConnectionPoint> cp; | |
137 cpc->FindConnectionPoint(sinks[i], cp.Receive()); | |
138 if (cp) { | |
139 base::win::ScopedComPtr<IEnumConnections> connections; | |
140 cp->EnumConnections(connections.Receive()); | |
141 if (connections) { | |
142 CONNECTDATA cd = {0}; | |
143 DWORD fetched = 0; | |
144 while (connections->Next(1, &cd, &fetched) == S_OK && fetched) { | |
145 PatchIfBuggy(cd.pUnk, sinks[i]); | |
146 cd.pUnk->Release(); | |
147 fetched = 0; | |
148 } | |
149 } | |
150 } | |
151 } | |
152 } | |
153 patched_ = true; | |
154 web_browser2_ = browser; | |
155 return hr; | |
156 } | |
157 | |
158 bool BuggyBhoTls::PatchIfBuggy(IUnknown* unk, const IID& diid) { | |
159 DCHECK(unk); | |
160 PROC* methods = *reinterpret_cast<PROC**>(unk); | |
161 HMODULE mod = base::GetModuleFromAddress(methods[0]); | |
162 if (!IsBuggyBho(mod)) | |
163 return false; | |
164 | |
165 base::win::ScopedComPtr<IDispatch> disp; | |
166 HRESULT hr = unk->QueryInterface(diid, | |
167 reinterpret_cast<void**>(disp.Receive())); | |
168 if (FAILED(hr)) // Sometimes only IDispatch QI is supported | |
169 hr = disp.QueryFrom(unk); | |
170 DCHECK(SUCCEEDED(hr)); | |
171 | |
172 if (SUCCEEDED(hr)) { | |
173 const int kInvokeIndex = 6; | |
174 DCHECK(static_cast<IUnknown*>(disp) == unk); | |
175 if (SUCCEEDED(PatchInvokeMethod(&methods[kInvokeIndex]))) { | |
176 AddBuggyObject(disp); | |
177 } | |
178 } | |
179 return false; | |
180 } | |
181 | |
182 // static | |
183 STDMETHODIMP BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original, IDispatch* me, | |
184 DISPID dispid, REFIID riid, LCID lcid, | |
185 WORD flags, DISPPARAMS* params, | |
186 VARIANT* result, EXCEPINFO* ei, | |
187 UINT* err) { | |
188 DVLOG(1) << __FUNCTION__; | |
189 | |
190 DCHECK(BuggyBhoTls::GetInstance()) | |
191 << "You must first have an instance of BuggyBhoTls on this thread"; | |
192 if (BuggyBhoTls::GetInstance() && | |
193 BuggyBhoTls::GetInstance()->ShouldSkipInvoke(me)) { | |
194 // Ignore this call and avoid the bug. | |
195 // TODO(tommi): Maybe we should check a specific list of DISPIDs too? | |
196 return S_OK; | |
197 } | |
198 | |
199 // No need to report crashes in those known-to-be-buggy DLLs. | |
200 ExceptionBarrierReportOnlyModule barrier; | |
201 return original(me, dispid, riid, lcid, flags, params, result, ei, err); | |
202 } | |
203 | |
204 // static | |
205 HRESULT BuggyBhoTls::PatchInvokeMethod(PROC* invoke) { | |
206 CCritSecLock lock(_pAtlModule->m_csStaticDataInitAndTypeInfo.m_sec, true); | |
207 | |
208 FunctionStub* stub = FunctionStub::FromCode(*invoke); | |
209 if (stub) | |
210 return S_FALSE; | |
211 | |
212 DWORD flags = 0; | |
213 if (!::VirtualProtect(invoke, sizeof(PROC), PAGE_EXECUTE_READWRITE, &flags)) | |
214 return AtlHresultFromLastError(); | |
215 | |
216 HRESULT hr = S_OK; | |
217 | |
218 stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke), | |
219 BuggyBhoInvoke); | |
220 if (!stub) { | |
221 hr = E_OUTOFMEMORY; | |
222 } else { | |
223 if (!vtable_patch::internal::ReplaceFunctionPointer( | |
224 reinterpret_cast<void**>(invoke), stub->code(), | |
225 reinterpret_cast<void*>(stub->argument()))) { | |
226 hr = E_UNEXPECTED; | |
227 FunctionStub::Destroy(stub); | |
228 } else { | |
229 chrome_frame::PinModule(); // No backing out now. | |
230 ::FlushInstructionCache(::GetCurrentProcess(), invoke, sizeof(PROC)); | |
231 } | |
232 } | |
233 ::VirtualProtect(invoke, sizeof(PROC), flags, &flags); | |
234 return hr; | |
235 } | |
236 | |
237 } // end namespace buggy_bho | |
OLD | NEW |