service_resolver_unittest.cc 9.0 KB

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