| 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 |