OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome_frame/buggy_bho_handling.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/scoped_comptr_win.h" |
| 9 |
| 10 #include "chrome_frame/function_stub.h" |
| 11 #include "chrome_frame/utils.h" |
| 12 #include "chrome_frame/vtable_patch_manager.h" |
| 13 |
| 14 namespace buggy_bho { |
| 15 |
| 16 base::ThreadLocalPointer<BuggyBhoTls> BuggyBhoTls::s_bad_object_tls_; |
| 17 |
| 18 struct ModuleAndVersion { |
| 19 const char* module_name_; |
| 20 const uint32 major_version_; |
| 21 const uint32 minor_version_; |
| 22 }; |
| 23 |
| 24 const ModuleAndVersion kBuggyModules[] = { |
| 25 { "askbar.dll", 4, 1 }, // troublemaker: 4.1.0.5. |
| 26 { "gbieh.dll", 3, 8 }, // troublemaker: 3.8.14.12 |
| 27 { "gbiehcef.dll", 3, 8 }, // troublemaker: 3.8.11.23 |
| 28 { "alot.dll", 2, 5 }, // troublemaker: 2.5.12000.509 |
| 29 { "srchbxex.dll", 1, 2 }, // troublemaker: 1.2.123.0 |
| 30 { "tbabso.dll", 4, 5 }, // troublemaker: 4.5.156.0 |
| 31 { "tbabs0.dll.dll", 4, 5 }, // troublemaker: 4.5.156.0 |
| 32 { "tbbes0.dll", 4, 5 }, // troublemaker: 4.5.153.0 |
| 33 { "ctbr.dll", 5, 1 }, // troublemaker: 5.1.0.95 |
| 34 |
| 35 // Viruses? |
| 36 { "msgsc2.dll", 0xffff, 0xffff }, // troublemaker: 1.2.3000.1 |
| 37 { "essclis.dll", 0xffff, 0xffff }, // troublemaker: 4.3.1800.2 |
| 38 }; |
| 39 |
| 40 bool IsBuggyBho(HMODULE mod) { |
| 41 DCHECK(mod); |
| 42 |
| 43 char path[MAX_PATH * 2] = {0}; |
| 44 ::GetModuleFileNameA(mod, path, arraysize(path)); |
| 45 const char* file_name = ::PathFindFileNameA(path); |
| 46 for (size_t i = 0; i < arraysize(kBuggyModules); ++i) { |
| 47 if (lstrcmpiA(file_name, kBuggyModules[i].module_name_) == 0) { |
| 48 uint32 version = 0; |
| 49 GetModuleVersion(mod, &version, NULL); |
| 50 const ModuleAndVersion& buggy = kBuggyModules[i]; |
| 51 if (HIWORD(version) < buggy.major_version_ || |
| 52 (HIWORD(version) == buggy.major_version_ && |
| 53 LOWORD(version) <= buggy.minor_version_)) { |
| 54 return true; |
| 55 } |
| 56 } |
| 57 } |
| 58 |
| 59 return false; |
| 60 } |
| 61 |
| 62 BuggyBhoTls::BuggyBhoTls() : previous_instance_(s_bad_object_tls_.Get()) { |
| 63 s_bad_object_tls_.Set(this); |
| 64 } |
| 65 |
| 66 BuggyBhoTls::~BuggyBhoTls() { |
| 67 DCHECK(FromCurrentThread() == this); |
| 68 s_bad_object_tls_.Set(previous_instance_); |
| 69 } |
| 70 |
| 71 void BuggyBhoTls::AddBuggyObject(IDispatch* obj) { |
| 72 bad_objects_.push_back(obj); |
| 73 } |
| 74 |
| 75 bool BuggyBhoTls::IsBuggyObject(IDispatch* obj) const { |
| 76 return std::find(bad_objects_.begin(), bad_objects_.end(), obj) != |
| 77 bad_objects_.end(); |
| 78 } |
| 79 |
| 80 // static |
| 81 BuggyBhoTls* BuggyBhoTls::FromCurrentThread() { |
| 82 return s_bad_object_tls_.Get(); |
| 83 } |
| 84 |
| 85 // static |
| 86 STDMETHODIMP BuggyBhoTls::BuggyBhoInvoke(InvokeFunc original, IDispatch* me, |
| 87 DISPID dispid, REFIID riid, LCID lcid, |
| 88 WORD flags, DISPPARAMS* params, |
| 89 VARIANT* result, EXCEPINFO* ei, |
| 90 UINT* err) { |
| 91 DLOG(INFO) << __FUNCTION__; |
| 92 |
| 93 const BuggyBhoTls* tls = BuggyBhoTls::FromCurrentThread(); |
| 94 if (tls && tls->IsBuggyObject(me)) { |
| 95 // Ignore this call and avoid the bug. |
| 96 // TODO(tommi): Maybe we should check a specific list of DISPIDs too? |
| 97 return S_OK; |
| 98 } |
| 99 |
| 100 return original(me, dispid, riid, lcid, flags, params, result, ei, err); |
| 101 } |
| 102 |
| 103 // static |
| 104 HRESULT BuggyBhoTls::PatchInvokeMethod(PROC* invoke) { |
| 105 CCritSecLock lock(_pAtlModule->m_csStaticDataInitAndTypeInfo, true); |
| 106 |
| 107 FunctionStub* stub = FunctionStub::FromCode(*invoke); |
| 108 if (stub) |
| 109 return S_FALSE; |
| 110 |
| 111 DWORD flags = 0; |
| 112 if (!::VirtualProtect(invoke, sizeof(PROC), PAGE_EXECUTE_READWRITE, &flags)) |
| 113 return AtlHresultFromLastError(); |
| 114 |
| 115 HRESULT hr = S_OK; |
| 116 |
| 117 stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(*invoke), |
| 118 BuggyBhoInvoke); |
| 119 if (!stub) { |
| 120 hr = E_OUTOFMEMORY; |
| 121 } else { |
| 122 if (!vtable_patch::internal::ReplaceFunctionPointer( |
| 123 reinterpret_cast<void**>(invoke), stub->code(), |
| 124 reinterpret_cast<void*>(stub->argument()))) { |
| 125 hr = E_UNEXPECTED; |
| 126 FunctionStub::Destroy(stub); |
| 127 } else { |
| 128 ::FlushInstructionCache(::GetCurrentProcess(), invoke, sizeof(PROC)); |
| 129 } |
| 130 } |
| 131 |
| 132 ::VirtualProtect(invoke, sizeof(PROC), flags, &flags); |
| 133 |
| 134 return hr; |
| 135 } |
| 136 |
| 137 // static |
| 138 bool BuggyBhoTls::PatchIfBuggy(CONNECTDATA* cd, const IID& diid) { |
| 139 DCHECK(cd); |
| 140 PROC* methods = *reinterpret_cast<PROC**>(cd->pUnk); |
| 141 HMODULE mod = GetModuleFromAddress(methods[0]); |
| 142 if (!IsBuggyBho(mod)) |
| 143 return false; |
| 144 |
| 145 ScopedComPtr<IDispatch> disp; |
| 146 HRESULT hr = cd->pUnk->QueryInterface(diid, |
| 147 reinterpret_cast<void**>(disp.Receive())); |
| 148 if (FAILED(hr)) // Sometimes only IDispatch QI is supported |
| 149 hr = disp.QueryFrom(cd->pUnk); |
| 150 DCHECK(SUCCEEDED(hr)); |
| 151 |
| 152 if (SUCCEEDED(hr)) { |
| 153 const int kInvokeIndex = 6; |
| 154 DCHECK(static_cast<IUnknown*>(disp) == cd->pUnk); |
| 155 if (SUCCEEDED(PatchInvokeMethod(&methods[kInvokeIndex]))) { |
| 156 BuggyBhoTls* tls = BuggyBhoTls::FromCurrentThread(); |
| 157 DCHECK(tls); |
| 158 if (tls) { |
| 159 tls->AddBuggyObject(disp); |
| 160 } |
| 161 } |
| 162 } |
| 163 |
| 164 return false; |
| 165 } |
| 166 |
| 167 // static |
| 168 HRESULT BuggyBhoTls::PatchBuggyBHOs(IWebBrowser2* browser) { |
| 169 DCHECK(browser); |
| 170 DCHECK(BuggyBhoTls::FromCurrentThread()) |
| 171 << "You must first have an instance of BuggyBhoTls on this thread"; |
| 172 |
| 173 ScopedComPtr<IConnectionPointContainer> cpc; |
| 174 HRESULT hr = cpc.QueryFrom(browser); |
| 175 if (SUCCEEDED(hr)) { |
| 176 const GUID sinks[] = { DIID_DWebBrowserEvents2, DIID_DWebBrowserEvents }; |
| 177 for (size_t i = 0; i < arraysize(sinks); ++i) { |
| 178 ScopedComPtr<IConnectionPoint> cp; |
| 179 cpc->FindConnectionPoint(sinks[i], cp.Receive()); |
| 180 if (cp) { |
| 181 ScopedComPtr<IEnumConnections> connections; |
| 182 cp->EnumConnections(connections.Receive()); |
| 183 if (connections) { |
| 184 CONNECTDATA cd = {0}; |
| 185 DWORD fetched = 0; |
| 186 while (connections->Next(1, &cd, &fetched) == S_OK && fetched) { |
| 187 PatchIfBuggy(&cd, sinks[i]); |
| 188 cd.pUnk->Release(); |
| 189 fetched = 0; |
| 190 } |
| 191 } |
| 192 } |
| 193 } |
| 194 } |
| 195 |
| 196 return hr; |
| 197 } |
| 198 |
| 199 } // end namespace buggy_bho |
OLD | NEW |