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