Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | // found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | // This file contains unit tests for ServiceResolverThunk. |
michael@0 | 6 | |
michael@0 | 7 | #include "base/basictypes.h" |
michael@0 | 8 | #include "base/memory/scoped_ptr.h" |
michael@0 | 9 | #include "base/win/windows_version.h" |
michael@0 | 10 | #include "sandbox/win/src/resolver.h" |
michael@0 | 11 | #include "sandbox/win/src/sandbox_utils.h" |
michael@0 | 12 | #include "sandbox/win/src/service_resolver.h" |
michael@0 | 13 | #include "testing/gtest/include/gtest/gtest.h" |
michael@0 | 14 | |
michael@0 | 15 | namespace { |
michael@0 | 16 | |
michael@0 | 17 | // This is the concrete resolver used to perform service-call type functions |
michael@0 | 18 | // inside ntdll.dll. |
michael@0 | 19 | template<typename T> |
michael@0 | 20 | class ResolverThunkTest : public T { |
michael@0 | 21 | public: |
michael@0 | 22 | // The service resolver needs a child process to write to. |
michael@0 | 23 | explicit ResolverThunkTest(bool relaxed) |
michael@0 | 24 | : T(::GetCurrentProcess(), relaxed) {} |
michael@0 | 25 | |
michael@0 | 26 | // Sets the interception target to the desired address. |
michael@0 | 27 | void set_target(void* target) { |
michael@0 | 28 | fake_target_ = target; |
michael@0 | 29 | } |
michael@0 | 30 | |
michael@0 | 31 | protected: |
michael@0 | 32 | // Overrides Resolver::Init |
michael@0 | 33 | virtual NTSTATUS Init(const void* target_module, |
michael@0 | 34 | const void* interceptor_module, |
michael@0 | 35 | const char* target_name, |
michael@0 | 36 | const char* interceptor_name, |
michael@0 | 37 | const void* interceptor_entry_point, |
michael@0 | 38 | void* thunk_storage, |
michael@0 | 39 | size_t storage_bytes) { |
michael@0 | 40 | NTSTATUS ret = STATUS_SUCCESS; |
michael@0 | 41 | ret = ResolverThunk::Init(target_module, interceptor_module, target_name, |
michael@0 | 42 | interceptor_name, interceptor_entry_point, |
michael@0 | 43 | thunk_storage, storage_bytes); |
michael@0 | 44 | EXPECT_EQ(STATUS_SUCCESS, ret); |
michael@0 | 45 | |
michael@0 | 46 | target_ = fake_target_; |
michael@0 | 47 | ntdll_base_ = ::GetModuleHandle(L"ntdll.dll"); |
michael@0 | 48 | return ret; |
michael@0 | 49 | }; |
michael@0 | 50 | |
michael@0 | 51 | private: |
michael@0 | 52 | // Holds the address of the fake target. |
michael@0 | 53 | void* fake_target_; |
michael@0 | 54 | |
michael@0 | 55 | DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest); |
michael@0 | 56 | }; |
michael@0 | 57 | |
michael@0 | 58 | typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest; |
michael@0 | 59 | |
michael@0 | 60 | #if !defined(_WIN64) |
michael@0 | 61 | typedef ResolverThunkTest<sandbox::Win2kResolverThunk> Win2kResolverTest; |
michael@0 | 62 | typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest; |
michael@0 | 63 | typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest; |
michael@0 | 64 | typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest; |
michael@0 | 65 | #endif |
michael@0 | 66 | |
michael@0 | 67 | const BYTE kJump32 = 0xE9; |
michael@0 | 68 | |
michael@0 | 69 | void CheckJump(void* source, void* target) { |
michael@0 | 70 | #pragma pack(push) |
michael@0 | 71 | #pragma pack(1) |
michael@0 | 72 | struct Code { |
michael@0 | 73 | BYTE jump; |
michael@0 | 74 | ULONG delta; |
michael@0 | 75 | }; |
michael@0 | 76 | #pragma pack(pop) |
michael@0 | 77 | |
michael@0 | 78 | #if defined(_WIN64) |
michael@0 | 79 | FAIL() << "Running 32-bit codepath"; |
michael@0 | 80 | #else |
michael@0 | 81 | Code* patched = reinterpret_cast<Code*>(source); |
michael@0 | 82 | EXPECT_EQ(kJump32, patched->jump); |
michael@0 | 83 | |
michael@0 | 84 | ULONG source_addr = bit_cast<ULONG>(source); |
michael@0 | 85 | ULONG target_addr = bit_cast<ULONG>(target); |
michael@0 | 86 | EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); |
michael@0 | 87 | #endif |
michael@0 | 88 | } |
michael@0 | 89 | |
michael@0 | 90 | NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, |
michael@0 | 91 | sandbox::ServiceResolverThunk* resolver) { |
michael@0 | 92 | HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); |
michael@0 | 93 | EXPECT_TRUE(NULL != ntdll_base); |
michael@0 | 94 | |
michael@0 | 95 | void* target = ::GetProcAddress(ntdll_base, function); |
michael@0 | 96 | EXPECT_TRUE(NULL != target); |
michael@0 | 97 | if (NULL == target) |
michael@0 | 98 | return STATUS_UNSUCCESSFUL; |
michael@0 | 99 | |
michael@0 | 100 | BYTE service[50]; |
michael@0 | 101 | memcpy(service, target, sizeof(service)); |
michael@0 | 102 | |
michael@0 | 103 | static_cast<WinXpResolverTest*>(resolver)->set_target(service); |
michael@0 | 104 | |
michael@0 | 105 | // Any pointer will do as an interception_entry_point |
michael@0 | 106 | void* function_entry = resolver; |
michael@0 | 107 | size_t thunk_size = resolver->GetThunkSize(); |
michael@0 | 108 | scoped_ptr<char[]> thunk(new char[thunk_size]); |
michael@0 | 109 | size_t used; |
michael@0 | 110 | |
michael@0 | 111 | NTSTATUS ret = resolver->Setup(ntdll_base, NULL, function, NULL, |
michael@0 | 112 | function_entry, thunk.get(), thunk_size, |
michael@0 | 113 | &used); |
michael@0 | 114 | if (NT_SUCCESS(ret)) { |
michael@0 | 115 | EXPECT_EQ(thunk_size, used); |
michael@0 | 116 | EXPECT_NE(0, memcmp(service, target, sizeof(service))); |
michael@0 | 117 | EXPECT_NE(kJump32, service[0]); |
michael@0 | 118 | |
michael@0 | 119 | if (relaxed) { |
michael@0 | 120 | // It's already patched, let's patch again, and simulate a direct patch. |
michael@0 | 121 | service[0] = kJump32; |
michael@0 | 122 | ret = resolver->Setup(ntdll_base, NULL, function, NULL, function_entry, |
michael@0 | 123 | thunk.get(), thunk_size, &used); |
michael@0 | 124 | CheckJump(service, thunk.get()); |
michael@0 | 125 | } |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | return ret; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) { |
michael@0 | 132 | #if defined(_WIN64) |
michael@0 | 133 | return new WinXpResolverTest(relaxed); |
michael@0 | 134 | #else |
michael@0 | 135 | base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); |
michael@0 | 136 | if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) { |
michael@0 | 137 | if (os_info->version() >= base::win::VERSION_WIN8) |
michael@0 | 138 | return new Wow64W8ResolverTest(relaxed); |
michael@0 | 139 | return new Wow64ResolverTest(relaxed); |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | if (!sandbox::IsXPSP2OrLater()) |
michael@0 | 143 | return new Win2kResolverTest(relaxed); |
michael@0 | 144 | |
michael@0 | 145 | if (os_info->version() >= base::win::VERSION_WIN8) |
michael@0 | 146 | return new Win8ResolverTest(relaxed); |
michael@0 | 147 | |
michael@0 | 148 | return new WinXpResolverTest(relaxed); |
michael@0 | 149 | #endif |
michael@0 | 150 | } |
michael@0 | 151 | |
michael@0 | 152 | NTSTATUS PatchNtdll(const char* function, bool relaxed) { |
michael@0 | 153 | sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed); |
michael@0 | 154 | |
michael@0 | 155 | NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver); |
michael@0 | 156 | delete resolver; |
michael@0 | 157 | return ret; |
michael@0 | 158 | } |
michael@0 | 159 | |
michael@0 | 160 | TEST(ServiceResolverTest, PatchesServices) { |
michael@0 | 161 | NTSTATUS ret = PatchNtdll("NtClose", false); |
michael@0 | 162 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); |
michael@0 | 163 | |
michael@0 | 164 | ret = PatchNtdll("NtCreateFile", false); |
michael@0 | 165 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << |
michael@0 | 166 | ::GetLastError(); |
michael@0 | 167 | |
michael@0 | 168 | ret = PatchNtdll("NtCreateMutant", false); |
michael@0 | 169 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << |
michael@0 | 170 | ::GetLastError(); |
michael@0 | 171 | |
michael@0 | 172 | ret = PatchNtdll("NtMapViewOfSection", false); |
michael@0 | 173 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << |
michael@0 | 174 | ::GetLastError(); |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | TEST(ServiceResolverTest, FailsIfNotService) { |
michael@0 | 178 | #if !defined(_WIN64) |
michael@0 | 179 | EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false)); |
michael@0 | 180 | #endif |
michael@0 | 181 | |
michael@0 | 182 | EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false)); |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | TEST(ServiceResolverTest, PatchesPatchedServices) { |
michael@0 | 186 | // We don't support "relaxed mode" for Win64 apps. |
michael@0 | 187 | #if !defined(_WIN64) |
michael@0 | 188 | NTSTATUS ret = PatchNtdll("NtClose", true); |
michael@0 | 189 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); |
michael@0 | 190 | |
michael@0 | 191 | ret = PatchNtdll("NtCreateFile", true); |
michael@0 | 192 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << |
michael@0 | 193 | ::GetLastError(); |
michael@0 | 194 | |
michael@0 | 195 | ret = PatchNtdll("NtCreateMutant", true); |
michael@0 | 196 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << |
michael@0 | 197 | ::GetLastError(); |
michael@0 | 198 | |
michael@0 | 199 | ret = PatchNtdll("NtMapViewOfSection", true); |
michael@0 | 200 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << |
michael@0 | 201 | ::GetLastError(); |
michael@0 | 202 | #endif |
michael@0 | 203 | } |
michael@0 | 204 | |
michael@0 | 205 | TEST(ServiceResolverTest, MultiplePatchedServices) { |
michael@0 | 206 | // We don't support "relaxed mode" for Win64 apps. |
michael@0 | 207 | #if !defined(_WIN64) |
michael@0 | 208 | sandbox::ServiceResolverThunk* resolver = GetTestResolver(true); |
michael@0 | 209 | NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver); |
michael@0 | 210 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); |
michael@0 | 211 | |
michael@0 | 212 | ret = PatchNtdllWithResolver("NtCreateFile", true, resolver); |
michael@0 | 213 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << |
michael@0 | 214 | ::GetLastError(); |
michael@0 | 215 | |
michael@0 | 216 | ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver); |
michael@0 | 217 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << |
michael@0 | 218 | ::GetLastError(); |
michael@0 | 219 | |
michael@0 | 220 | ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver); |
michael@0 | 221 | EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << |
michael@0 | 222 | ::GetLastError(); |
michael@0 | 223 | delete resolver; |
michael@0 | 224 | #endif |
michael@0 | 225 | } |
michael@0 | 226 | |
michael@0 | 227 | } // namespace |