Home | History | Annotate | Download | only in chrome_frame
      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
    238