|  | // Copyright 2017 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 "base/win/com_init_check_hook.h" | 
|  |  | 
|  | #include <objbase.h> | 
|  | #include <shlobj.h> | 
|  | #include <wrl/client.h> | 
|  |  | 
|  | #include "base/test/gtest_util.h" | 
|  | #include "base/win/com_init_util.h" | 
|  | #include "base/win/patch_util.h" | 
|  | #include "base/win/scoped_com_initializer.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace win { | 
|  |  | 
|  | using Microsoft::WRL::ComPtr; | 
|  |  | 
|  | TEST(ComInitCheckHook, AssertNotInitialized) { | 
|  | ComInitCheckHook com_check_hook; | 
|  | AssertComApartmentType(ComApartmentType::NONE); | 
|  | ComPtr<IUnknown> shell_link; | 
|  | #if defined(COM_INIT_CHECK_HOOK_ENABLED) | 
|  | EXPECT_DCHECK_DEATH(::CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_ALL, | 
|  | IID_PPV_ARGS(&shell_link))); | 
|  | #else | 
|  | EXPECT_EQ(CO_E_NOTINITIALIZED, | 
|  | ::CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_ALL, | 
|  | IID_PPV_ARGS(&shell_link))); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | TEST(ComInitCheckHook, HookRemoval) { | 
|  | AssertComApartmentType(ComApartmentType::NONE); | 
|  | { ComInitCheckHook com_check_hook; } | 
|  | ComPtr<IUnknown> shell_link; | 
|  | EXPECT_EQ(CO_E_NOTINITIALIZED, | 
|  | ::CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_ALL, | 
|  | IID_PPV_ARGS(&shell_link))); | 
|  | } | 
|  |  | 
|  | TEST(ComInitCheckHook, NoAssertComInitialized) { | 
|  | ComInitCheckHook com_check_hook; | 
|  | ScopedCOMInitializer com_initializer; | 
|  | ComPtr<IUnknown> shell_link; | 
|  | EXPECT_TRUE(SUCCEEDED(::CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_ALL, | 
|  | IID_PPV_ARGS(&shell_link)))); | 
|  | } | 
|  |  | 
|  | TEST(ComInitCheckHook, MultipleHooks) { | 
|  | ComInitCheckHook com_check_hook_1; | 
|  | ComInitCheckHook com_check_hook_2; | 
|  | AssertComApartmentType(ComApartmentType::NONE); | 
|  | ComPtr<IUnknown> shell_link; | 
|  | #if defined(COM_INIT_CHECK_HOOK_ENABLED) | 
|  | EXPECT_DCHECK_DEATH(::CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_ALL, | 
|  | IID_PPV_ARGS(&shell_link))); | 
|  | #else | 
|  | EXPECT_EQ(CO_E_NOTINITIALIZED, | 
|  | ::CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_ALL, | 
|  | IID_PPV_ARGS(&shell_link))); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | TEST(ComInitCheckHook, UnexpectedHook) { | 
|  | #if defined(COM_INIT_CHECK_HOOK_ENABLED) | 
|  | HMODULE ole32_library = ::LoadLibrary(L"ole32.dll"); | 
|  | ASSERT_TRUE(ole32_library); | 
|  |  | 
|  | uint32_t co_create_instance_padded_address = | 
|  | reinterpret_cast<uint32_t>( | 
|  | GetProcAddress(ole32_library, "CoCreateInstance")) - 5; | 
|  | const unsigned char* co_create_instance_bytes = | 
|  | reinterpret_cast<const unsigned char*>(co_create_instance_padded_address); | 
|  | const unsigned char original_byte = co_create_instance_bytes[0]; | 
|  | const unsigned char unexpected_byte = 0xdb; | 
|  | ASSERT_EQ(static_cast<DWORD>(NO_ERROR), | 
|  | internal::ModifyCode( | 
|  | reinterpret_cast<void*>(co_create_instance_padded_address), | 
|  | reinterpret_cast<const void*>(&unexpected_byte), | 
|  | sizeof(unexpected_byte))); | 
|  |  | 
|  | EXPECT_DCHECK_DEATH({ ComInitCheckHook com_check_hook; }); | 
|  |  | 
|  | // If this call fails, really bad things are going to happen to other tests | 
|  | // so CHECK here. | 
|  | CHECK_EQ(static_cast<DWORD>(NO_ERROR), | 
|  | internal::ModifyCode( | 
|  | reinterpret_cast<void*>(co_create_instance_padded_address), | 
|  | reinterpret_cast<const void*>(&original_byte), | 
|  | sizeof(original_byte))); | 
|  |  | 
|  | ::FreeLibrary(ole32_library); | 
|  | ole32_library = nullptr; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | TEST(ComInitCheckHook, ExternallyHooked) { | 
|  | #if defined(COM_INIT_CHECK_HOOK_ENABLED) | 
|  | HMODULE ole32_library = ::LoadLibrary(L"ole32.dll"); | 
|  | ASSERT_TRUE(ole32_library); | 
|  |  | 
|  | uint32_t co_create_instance_address = reinterpret_cast<uint32_t>( | 
|  | GetProcAddress(ole32_library, "CoCreateInstance")); | 
|  | const unsigned char* co_create_instance_bytes = | 
|  | reinterpret_cast<const unsigned char*>(co_create_instance_address); | 
|  | const unsigned char original_byte = co_create_instance_bytes[0]; | 
|  | const unsigned char jmp_byte = 0xe9; | 
|  | ASSERT_EQ(static_cast<DWORD>(NO_ERROR), | 
|  | internal::ModifyCode( | 
|  | reinterpret_cast<void*>(co_create_instance_address), | 
|  | reinterpret_cast<const void*>(&jmp_byte), sizeof(jmp_byte))); | 
|  |  | 
|  | // Externally patched instances should crash so we catch these cases on bots. | 
|  | EXPECT_DCHECK_DEATH({ ComInitCheckHook com_check_hook; }); | 
|  |  | 
|  | // If this call fails, really bad things are going to happen to other tests | 
|  | // so CHECK here. | 
|  | CHECK_EQ( | 
|  | static_cast<DWORD>(NO_ERROR), | 
|  | internal::ModifyCode(reinterpret_cast<void*>(co_create_instance_address), | 
|  | reinterpret_cast<const void*>(&original_byte), | 
|  | sizeof(original_byte))); | 
|  |  | 
|  | ::FreeLibrary(ole32_library); | 
|  | ole32_library = nullptr; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | TEST(ComInitCheckHook, UnexpectedChangeDuringHook) { | 
|  | #if defined(COM_INIT_CHECK_HOOK_ENABLED) | 
|  | HMODULE ole32_library = ::LoadLibrary(L"ole32.dll"); | 
|  | ASSERT_TRUE(ole32_library); | 
|  |  | 
|  | uint32_t co_create_instance_padded_address = | 
|  | reinterpret_cast<uint32_t>( | 
|  | GetProcAddress(ole32_library, "CoCreateInstance")) - | 
|  | 5; | 
|  | const unsigned char* co_create_instance_bytes = | 
|  | reinterpret_cast<const unsigned char*>(co_create_instance_padded_address); | 
|  | const unsigned char original_byte = co_create_instance_bytes[0]; | 
|  | const unsigned char unexpected_byte = 0xdb; | 
|  | ASSERT_EQ(static_cast<DWORD>(NO_ERROR), | 
|  | internal::ModifyCode( | 
|  | reinterpret_cast<void*>(co_create_instance_padded_address), | 
|  | reinterpret_cast<const void*>(&unexpected_byte), | 
|  | sizeof(unexpected_byte))); | 
|  |  | 
|  | EXPECT_DCHECK_DEATH({ | 
|  | ComInitCheckHook com_check_hook; | 
|  |  | 
|  | internal::ModifyCode( | 
|  | reinterpret_cast<void*>(co_create_instance_padded_address), | 
|  | reinterpret_cast<const void*>(&unexpected_byte), | 
|  | sizeof(unexpected_byte)); | 
|  | }); | 
|  |  | 
|  | // If this call fails, really bad things are going to happen to other tests | 
|  | // so CHECK here. | 
|  | CHECK_EQ(static_cast<DWORD>(NO_ERROR), | 
|  | internal::ModifyCode( | 
|  | reinterpret_cast<void*>(co_create_instance_padded_address), | 
|  | reinterpret_cast<const void*>(&original_byte), | 
|  | sizeof(original_byte))); | 
|  |  | 
|  | ::FreeLibrary(ole32_library); | 
|  | ole32_library = nullptr; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | }  // namespace win | 
|  | }  // namespace base |