diff options
author | maruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-25 01:59:21 +0000 |
---|---|---|
committer | maruel@chromium.org <maruel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-25 01:59:21 +0000 |
commit | 1bbb107508df005ce6da1577e1a7026ab932f3be (patch) | |
tree | 3122cfed5ec0e379bdc65488f6191efd9c42e7f7 /app | |
parent | 541434c24710c131b697cf461fc3a21ddb74ed39 (diff) | |
download | chromium_src-1bbb107508df005ce6da1577e1a7026ab932f3be.zip chromium_src-1bbb107508df005ce6da1577e1a7026ab932f3be.tar.gz chromium_src-1bbb107508df005ce6da1577e1a7026ab932f3be.tar.bz2 |
Complete revert of r90464. It was partially done in r90471.
The revert didn't apply cleanly but verified that this commands:
git diff $(git svn find-rev r90463) -- $(git diff origin/trunk --name-only)
returns nothing.
BUG=
TEST=
Review URL: http://codereview.chromium.org/7261013
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90472 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app')
-rw-r--r-- | app/win/iat_patch_function.cc | 278 | ||||
-rw-r--r-- | app/win/iat_patch_function.h | 72 | ||||
-rw-r--r-- | app/win/scoped_co_mem.h | 49 | ||||
-rw-r--r-- | app/win/scoped_com_initializer.h | 60 | ||||
-rw-r--r-- | app/win/shell.cc | 112 | ||||
-rw-r--r-- | app/win/shell.h | 41 |
6 files changed, 612 insertions, 0 deletions
diff --git a/app/win/iat_patch_function.cc b/app/win/iat_patch_function.cc new file mode 100644 index 0000000..9abd6f2 --- /dev/null +++ b/app/win/iat_patch_function.cc @@ -0,0 +1,278 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "app/win/iat_patch_function.h" + +#include "base/logging.h" +#include "base/win/pe_image.h" + +namespace app { +namespace win { + +namespace { + +struct InterceptFunctionInformation { + bool finished_operation; + const char* imported_from_module; + const char* function_name; + void* new_function; + void** old_function; + IMAGE_THUNK_DATA** iat_thunk; + DWORD return_code; +}; + +void* GetIATFunction(IMAGE_THUNK_DATA* iat_thunk) { + if (NULL == iat_thunk) { + NOTREACHED(); + return NULL; + } + + // Works around the 64 bit portability warning: + // The Function member inside IMAGE_THUNK_DATA is really a pointer + // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32 + // or IMAGE_THUNK_DATA64 for correct pointer size. + union FunctionThunk { + IMAGE_THUNK_DATA thunk; + void* pointer; + } iat_function; + + iat_function.thunk = *iat_thunk; + return iat_function.pointer; +} +// Change the page protection (of code pages) to writable and copy +// the data at the specified location +// +// Arguments: +// old_code Target location to copy +// new_code Source +// length Number of bytes to copy +// +// Returns: Windows error code (winerror.h). NO_ERROR if successful +DWORD ModifyCode(void* old_code, void* new_code, int length) { + if ((NULL == old_code) || (NULL == new_code) || (0 == length)) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } + + // Change the page protection so that we can write. + DWORD error = NO_ERROR; + DWORD old_page_protection = 0; + if (VirtualProtect(old_code, + length, + PAGE_READWRITE, + &old_page_protection)) { + + // Write the data. + CopyMemory(old_code, new_code, length); + + // Restore the old page protection. + error = ERROR_SUCCESS; + VirtualProtect(old_code, + length, + old_page_protection, + &old_page_protection); + } else { + error = GetLastError(); + NOTREACHED(); + } + + return error; +} + +bool InterceptEnumCallback(const base::win::PEImage& image, const char* module, + DWORD ordinal, const char* name, DWORD hint, + IMAGE_THUNK_DATA* iat, void* cookie) { + InterceptFunctionInformation* intercept_information = + reinterpret_cast<InterceptFunctionInformation*>(cookie); + + if (NULL == intercept_information) { + NOTREACHED(); + return false; + } + + DCHECK(module); + + if ((0 == lstrcmpiA(module, intercept_information->imported_from_module)) && + (NULL != name) && + (0 == lstrcmpiA(name, intercept_information->function_name))) { + // Save the old pointer. + if (NULL != intercept_information->old_function) { + *(intercept_information->old_function) = GetIATFunction(iat); + } + + if (NULL != intercept_information->iat_thunk) { + *(intercept_information->iat_thunk) = iat; + } + + // portability check + COMPILE_ASSERT(sizeof(iat->u1.Function) == + sizeof(intercept_information->new_function), unknown_IAT_thunk_format); + + // Patch the function. + intercept_information->return_code = + ModifyCode(&(iat->u1.Function), + &(intercept_information->new_function), + sizeof(intercept_information->new_function)); + + // Terminate further enumeration. + intercept_information->finished_operation = true; + return false; + } + + return true; +} + +// Helper to intercept a function in an import table of a specific +// module. +// +// Arguments: +// module_handle Module to be intercepted +// imported_from_module Module that exports the symbol +// function_name Name of the API to be intercepted +// new_function Interceptor function +// old_function Receives the original function pointer +// iat_thunk Receives pointer to IAT_THUNK_DATA +// for the API from the import table. +// +// Returns: Returns NO_ERROR on success or Windows error code +// as defined in winerror.h +DWORD InterceptImportedFunction(HMODULE module_handle, + const char* imported_from_module, + const char* function_name, void* new_function, + void** old_function, + IMAGE_THUNK_DATA** iat_thunk) { + if ((NULL == module_handle) || (NULL == imported_from_module) || + (NULL == function_name) || (NULL == new_function)) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } + + base::win::PEImage target_image(module_handle); + if (!target_image.VerifyMagic()) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } + + InterceptFunctionInformation intercept_information = { + false, + imported_from_module, + function_name, + new_function, + old_function, + iat_thunk, + ERROR_GEN_FAILURE}; + + // First go through the IAT. If we don't find the import we are looking + // for in IAT, search delay import table. + target_image.EnumAllImports(InterceptEnumCallback, &intercept_information); + if (!intercept_information.finished_operation) { + target_image.EnumAllDelayImports(InterceptEnumCallback, + &intercept_information); + } + + return intercept_information.return_code; +} + +// Restore intercepted IAT entry with the original function. +// +// Arguments: +// intercept_function Interceptor function +// original_function Receives the original function pointer +// +// Returns: Returns NO_ERROR on success or Windows error code +// as defined in winerror.h +DWORD RestoreImportedFunction(void* intercept_function, + void* original_function, + IMAGE_THUNK_DATA* iat_thunk) { + if ((NULL == intercept_function) || (NULL == original_function) || + (NULL == iat_thunk)) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } + + if (GetIATFunction(iat_thunk) != intercept_function) { + // Check if someone else has intercepted on top of us. + // We cannot unpatch in this case, just raise a red flag. + NOTREACHED(); + return ERROR_INVALID_FUNCTION; + } + + return ModifyCode(&(iat_thunk->u1.Function), + &original_function, + sizeof(original_function)); +} + +} // namespace + +IATPatchFunction::IATPatchFunction() + : module_handle_(NULL), + original_function_(NULL), + iat_thunk_(NULL), + intercept_function_(NULL) { +} + +IATPatchFunction::~IATPatchFunction() { + if (NULL != intercept_function_) { + DWORD error = Unpatch(); + DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error); + } +} + +DWORD IATPatchFunction::Patch(const wchar_t* module, + const char* imported_from_module, + const char* function_name, + void* new_function) { + DCHECK_EQ(static_cast<void*>(NULL), original_function_); + DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_); + DCHECK_EQ(static_cast<void*>(NULL), intercept_function_); + + HMODULE module_handle = LoadLibraryW(module); + + if (module_handle == NULL) { + NOTREACHED(); + return GetLastError(); + } + + DWORD error = InterceptImportedFunction(module_handle, + imported_from_module, + function_name, + new_function, + &original_function_, + &iat_thunk_); + + if (NO_ERROR == error) { + DCHECK_NE(original_function_, intercept_function_); + module_handle_ = module_handle; + intercept_function_ = new_function; + } else { + FreeLibrary(module_handle); + } + + return error; +} + +DWORD IATPatchFunction::Unpatch() { + DWORD error = RestoreImportedFunction(intercept_function_, + original_function_, + iat_thunk_); + DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error); + + // Hands off the intercept if we fail to unpatch. + // If IATPatchFunction::Unpatch fails during RestoreImportedFunction + // it means that we cannot safely unpatch the import address table + // patch. In this case its better to be hands off the intercept as + // trying to unpatch again in the destructor of IATPatchFunction is + // not going to be any safer + if (module_handle_) + FreeLibrary(module_handle_); + module_handle_ = NULL; + intercept_function_ = NULL; + original_function_ = NULL; + iat_thunk_ = NULL; + + return error; +} + +} // namespace win +} // namespace app diff --git a/app/win/iat_patch_function.h b/app/win/iat_patch_function.h new file mode 100644 index 0000000..4a2aa0c --- /dev/null +++ b/app/win/iat_patch_function.h @@ -0,0 +1,72 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef APP_WIN_IAT_PATCH_FUNCTION_H_ +#define APP_WIN_IAT_PATCH_FUNCTION_H_ +#pragma once + +#include <windows.h> + +#include "base/basictypes.h" + +namespace app { +namespace win { + +// A class that encapsulates Import Address Table patching helpers and restores +// the original function in the destructor. +// +// It will intercept functions for a specific DLL imported from another DLL. +// This is the case when, for example, we want to intercept +// CertDuplicateCertificateContext function (exported from crypt32.dll) called +// by wininet.dll. +class IATPatchFunction { + public: + IATPatchFunction(); + ~IATPatchFunction(); + + // Intercept a function in an import table of a specific + // module. Save the original function and the import + // table address. These values will be used later + // during Unpatch + // + // Arguments: + // module Module to be intercepted + // imported_from_module Module that exports the 'function_name' + // function_name Name of the API to be intercepted + // + // Returns: Windows error code (winerror.h). NO_ERROR if successful + // + // Note: Patching a function will make the IAT patch take some "ownership" on + // |module|. It will LoadLibrary(module) to keep the DLL alive until a call + // to Unpatch(), which will call FreeLibrary() and allow the module to be + // unloaded. The idea is to help prevent the DLL from going away while a + // patch is still active. + DWORD Patch(const wchar_t* module, + const char* imported_from_module, + const char* function_name, + void* new_function); + + // Unpatch the IAT entry using internally saved original + // function. + // + // Returns: Windows error code (winerror.h). NO_ERROR if successful + DWORD Unpatch(); + + bool is_patched() const { + return (NULL != intercept_function_); + } + + private: + HMODULE module_handle_; + void* intercept_function_; + void* original_function_; + IMAGE_THUNK_DATA* iat_thunk_; + + DISALLOW_COPY_AND_ASSIGN(IATPatchFunction); +}; + +} // namespace win +} // namespace app + +#endif // APP_WIN_IAT_PATCH_FUNCTION_H_ diff --git a/app/win/scoped_co_mem.h b/app/win/scoped_co_mem.h new file mode 100644 index 0000000..a6017fa --- /dev/null +++ b/app/win/scoped_co_mem.h @@ -0,0 +1,49 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef APP_WIN_SCOPED_CO_MEM_H_ +#define APP_WIN_SCOPED_CO_MEM_H_ +#pragma once + +#include <objbase.h> + +#include "base/basictypes.h" + +namespace app { +namespace win { + +// Simple scoped memory releaser class for COM allocated memory. +// Example: +// app::win::ScopedCoMem<ITEMIDLIST> file_item; +// SHGetSomeInfo(&file_item, ...); +// ... +// return; <-- memory released +template<typename T> +class ScopedCoMem { + public: + explicit ScopedCoMem() : mem_ptr_(NULL) {} + + ~ScopedCoMem() { + if (mem_ptr_) + CoTaskMemFree(mem_ptr_); + } + + T** operator&() { // NOLINT + return &mem_ptr_; + } + + operator T*() { + return mem_ptr_; + } + + private: + T* mem_ptr_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCoMem); +}; + +} // namespace win +} // namespace app + +#endif // APP_WIN_SCOPED_CO_MEM_H_ diff --git a/app/win/scoped_com_initializer.h b/app/win/scoped_com_initializer.h new file mode 100644 index 0000000..3a2cf55 --- /dev/null +++ b/app/win/scoped_com_initializer.h @@ -0,0 +1,60 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef APP_WIN_SCOPED_COM_INITIALIZER_H_ +#define APP_WIN_SCOPED_COM_INITIALIZER_H_ +#pragma once + +#include "base/basictypes.h" +#include "build/build_config.h" + +#if defined(OS_WIN) + +#include <objbase.h> + +namespace app { +namespace win { + +// Initializes COM in the constructor (STA), and uninitializes COM in the +// destructor. +class ScopedCOMInitializer { + public: + ScopedCOMInitializer() : hr_(CoInitialize(NULL)) { + } + + ScopedCOMInitializer::~ScopedCOMInitializer() { + if (SUCCEEDED(hr_)) + CoUninitialize(); + } + + private: + HRESULT hr_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCOMInitializer); +}; + +} // namespace win +} // namespace app + +#else + +namespace app { +namespace win { + +// Do-nothing class for other platforms. +class ScopedCOMInitializer { + public: + ScopedCOMInitializer() {} + ~ScopedCOMInitializer() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedCOMInitializer); +}; + +} // namespace win +} // namespace app + +#endif + +#endif // APP_WIN_SCOPED_COM_INITIALIZER_H_ diff --git a/app/win/shell.cc b/app/win/shell.cc new file mode 100644 index 0000000..cf47387 --- /dev/null +++ b/app/win/shell.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "app/win/shell.h" + +#include <shellapi.h> +#include <shlobj.h> + +#include "base/file_path.h" +#include "base/native_library.h" +#include "base/string_util.h" +#include "base/win/scoped_comptr.h" +#include "base/win/win_util.h" +#include "base/win/windows_version.h" + +namespace app { +namespace win { + +namespace { + +const wchar_t kShell32[] = L"shell32.dll"; +const char kSHGetPropertyStoreForWindow[] = "SHGetPropertyStoreForWindow"; + +// Define the type of SHGetPropertyStoreForWindow is SHGPSFW. +typedef DECLSPEC_IMPORT HRESULT (STDAPICALLTYPE *SHGPSFW)(HWND hwnd, + REFIID riid, + void** ppv); + +} // namespace + +// Open an item via a shell execute command. Error code checking and casting +// explanation: http://msdn2.microsoft.com/en-us/library/ms647732.aspx +bool OpenItemViaShell(const FilePath& full_path) { + HINSTANCE h = ::ShellExecuteW( + NULL, NULL, full_path.value().c_str(), NULL, + full_path.DirName().value().c_str(), SW_SHOWNORMAL); + + LONG_PTR error = reinterpret_cast<LONG_PTR>(h); + if (error > 32) + return true; + + if ((error == SE_ERR_NOASSOC)) + return OpenItemWithExternalApp(full_path.value()); + + return false; +} + +bool OpenItemViaShellNoZoneCheck(const FilePath& full_path) { + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.fMask = SEE_MASK_NOZONECHECKS | SEE_MASK_FLAG_DDEWAIT; + sei.nShow = SW_SHOWNORMAL; + sei.lpVerb = NULL; + sei.lpFile = full_path.value().c_str(); + if (::ShellExecuteExW(&sei)) + return true; + LONG_PTR error = reinterpret_cast<LONG_PTR>(sei.hInstApp); + if ((error == SE_ERR_NOASSOC)) + return OpenItemWithExternalApp(full_path.value()); + return false; +} + +// Show the Windows "Open With" dialog box to ask the user to pick an app to +// open the file with. +bool OpenItemWithExternalApp(const string16& full_path) { + SHELLEXECUTEINFO sei = { sizeof(sei) }; + sei.fMask = SEE_MASK_FLAG_DDEWAIT; + sei.nShow = SW_SHOWNORMAL; + sei.lpVerb = L"openas"; + sei.lpFile = full_path.c_str(); + return (TRUE == ::ShellExecuteExW(&sei)); +} + +void SetAppIdForWindow(const string16& app_id, HWND hwnd) { + // This functionality is only available on Win7+. + if (base::win::GetVersion() < base::win::VERSION_WIN7) + return; + + // Load Shell32.dll into memory. + // TODO(brg): Remove this mechanism when the Win7 SDK is available in trunk. + std::wstring shell32_filename(kShell32); + FilePath shell32_filepath(shell32_filename); + base::NativeLibrary shell32_library = base::LoadNativeLibrary( + shell32_filepath, NULL); + + if (!shell32_library) + return; + + // Get the function pointer for SHGetPropertyStoreForWindow. + void* function = base::GetFunctionPointerFromNativeLibrary( + shell32_library, + kSHGetPropertyStoreForWindow); + + if (!function) { + base::UnloadNativeLibrary(shell32_library); + return; + } + + // Set the application's name. + base::win::ScopedComPtr<IPropertyStore> pps; + SHGPSFW SHGetPropertyStoreForWindow = static_cast<SHGPSFW>(function); + HRESULT result = SHGetPropertyStoreForWindow( + hwnd, __uuidof(*pps), reinterpret_cast<void**>(pps.Receive())); + if (S_OK == result) + base::win::SetAppIdForPropertyStore(pps, app_id.c_str()); + + // Cleanup. + base::UnloadNativeLibrary(shell32_library); +} + +} // namespace win +} // namespace app diff --git a/app/win/shell.h b/app/win/shell.h new file mode 100644 index 0000000..44ee3ba7 --- /dev/null +++ b/app/win/shell.h @@ -0,0 +1,41 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef APP_WIN_SHELL_H_ +#define APP_WIN_SHELL_H_ + +#include <windows.h> + +#include "base/string16.h" + +class FilePath; + +namespace app { +namespace win { + +// Open or run a file via the Windows shell. In the event that there is no +// default application registered for the file specified by 'full_path', +// ask the user, via the Windows "Open With" dialog. +// Returns 'true' on successful open, 'false' otherwise. +bool OpenItemViaShell(const FilePath& full_path); + +// The download manager now writes the alternate data stream with the +// zone on all downloads. This function is equivalent to OpenItemViaShell +// without showing the zone warning dialog. +bool OpenItemViaShellNoZoneCheck(const FilePath& full_path); + +// Ask the user, via the Windows "Open With" dialog, for an application to use +// to open the file specified by 'full_path'. +// Returns 'true' on successful open, 'false' otherwise. +bool OpenItemWithExternalApp(const string16& full_path); + +// Sets the application id given as the Application Model ID for the window +// specified. This method is used to insure that different web applications +// do not group together on the Win7 task bar. +void SetAppIdForWindow(const string16& app_id, HWND hwnd); + +} // namespace win +} // namespace app + +#endif // APP_WIN_SHELL_H_ |