| // 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 |