1 // Copyright (c) 2011 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 5 // This file contains unit tests for InterceptionManager. 6 // The tests require private information so the whole interception.cc file is 7 // included from this file. 8 9 #include <windows.h> 10 11 #include "base/memory/scoped_ptr.h" 12 #include "sandbox/win/src/interception.h" 13 #include "sandbox/win/src/interceptors.h" 14 #include "sandbox/win/src/interception_internal.h" 15 #include "sandbox/win/src/target_process.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 namespace sandbox { 19 20 // Walks the settings buffer, verifying that the values make sense and counting 21 // objects. 22 // Arguments: 23 // buffer (in): the buffer to walk. 24 // size (in): buffer size 25 // num_dlls (out): count of the dlls on the buffer. 26 // num_function (out): count of intercepted functions. 27 // num_names (out): count of named interceptor functions. 28 void WalkBuffer(void* buffer, size_t size, int* num_dlls, int* num_functions, 29 int* num_names) { 30 ASSERT_TRUE(NULL != buffer); 31 ASSERT_TRUE(NULL != num_functions); 32 ASSERT_TRUE(NULL != num_names); 33 *num_dlls = *num_functions = *num_names = 0; 34 SharedMemory *memory = reinterpret_cast<SharedMemory*>(buffer); 35 36 ASSERT_GT(size, sizeof(SharedMemory)); 37 DllPatchInfo *dll = &memory->dll_list[0]; 38 39 for (int i = 0; i < memory->num_intercepted_dlls; i++) { 40 ASSERT_NE(0u, wcslen(dll->dll_name)); 41 ASSERT_EQ(0u, dll->record_bytes % sizeof(size_t)); 42 ASSERT_EQ(0u, dll->offset_to_functions % sizeof(size_t)); 43 ASSERT_NE(0, dll->num_functions); 44 45 FunctionInfo *function = reinterpret_cast<FunctionInfo*>( 46 reinterpret_cast<char*>(dll) + dll->offset_to_functions); 47 48 for (int j = 0; j < dll->num_functions; j++) { 49 ASSERT_EQ(0u, function->record_bytes % sizeof(size_t)); 50 51 char* name = function->function; 52 size_t length = strlen(name); 53 ASSERT_NE(0u, length); 54 name += length + 1; 55 56 // look for overflows 57 ASSERT_GT(reinterpret_cast<char*>(buffer) + size, name + strlen(name)); 58 59 // look for a named interceptor 60 if (strlen(name)) { 61 (*num_names)++; 62 EXPECT_TRUE(NULL == function->interceptor_address); 63 } else { 64 EXPECT_TRUE(NULL != function->interceptor_address); 65 } 66 67 (*num_functions)++; 68 function = reinterpret_cast<FunctionInfo*>( 69 reinterpret_cast<char*>(function) + function->record_bytes); 70 } 71 72 (*num_dlls)++; 73 dll = reinterpret_cast<DllPatchInfo*>(reinterpret_cast<char*>(dll) + 74 dll->record_bytes); 75 } 76 } 77 78 TEST(InterceptionManagerTest, BufferLayout1) { 79 wchar_t exe_name[MAX_PATH]; 80 ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); 81 82 TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(), 83 ::GetModuleHandle(exe_name)); 84 85 InterceptionManager interceptions(target, true); 86 87 // Any pointer will do for a function pointer. 88 void* function = &interceptions; 89 90 // We don't care about the interceptor id. 91 interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile", 92 INTERCEPTION_SERVICE_CALL, function, 93 OPEN_KEY_ID); 94 interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx", 95 INTERCEPTION_EAT, function, OPEN_KEY_ID); 96 interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx", 97 INTERCEPTION_SMART_SIDESTEP, function, 98 OPEN_KEY_ID); 99 interceptions.AddToPatchedFunctions(L"user32.dll", "FindWindow", 100 INTERCEPTION_EAT, function, OPEN_KEY_ID); 101 interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateMutex", 102 INTERCEPTION_EAT, function, OPEN_KEY_ID); 103 interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg", 104 INTERCEPTION_EAT, function, OPEN_KEY_ID); 105 interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg", 106 INTERCEPTION_EAT, "replacement", 107 OPEN_KEY_ID); 108 interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", 109 INTERCEPTION_EAT, function, OPEN_KEY_ID); 110 interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtClose", 111 INTERCEPTION_SERVICE_CALL, function, 112 OPEN_KEY_ID); 113 interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtOpenFile", 114 INTERCEPTION_SIDESTEP, function, 115 OPEN_KEY_ID); 116 interceptions.AddToPatchedFunctions(L"some.dll", "Superfn", 117 INTERCEPTION_EAT, function, OPEN_KEY_ID); 118 interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", 119 INTERCEPTION_EAT, "a", OPEN_KEY_ID); 120 interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", 121 INTERCEPTION_SIDESTEP, "ab", OPEN_KEY_ID); 122 interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg", 123 INTERCEPTION_EAT, "abc", OPEN_KEY_ID); 124 interceptions.AddToPatchedFunctions(L"a.dll", "p", 125 INTERCEPTION_EAT, function, OPEN_KEY_ID); 126 interceptions.AddToPatchedFunctions(L"b.dll", 127 "TheIncredibleCallToSaveTheWorld", 128 INTERCEPTION_EAT, function, OPEN_KEY_ID); 129 interceptions.AddToPatchedFunctions(L"a.dll", "BIsLame", 130 INTERCEPTION_EAT, function, OPEN_KEY_ID); 131 interceptions.AddToPatchedFunctions(L"a.dll", "ARules", 132 INTERCEPTION_EAT, function, OPEN_KEY_ID); 133 134 // Verify that all interceptions were added 135 ASSERT_EQ(18, interceptions.interceptions_.size()); 136 137 size_t buffer_size = interceptions.GetBufferSize(); 138 scoped_ptr<BYTE[]> local_buffer(new BYTE[buffer_size]); 139 140 ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), 141 buffer_size)); 142 143 // At this point, the interceptions should have been separated into two 144 // groups: one group with the local ("cold") interceptions, consisting of 145 // everything from ntdll and stuff set as INTRECEPTION_SERVICE_CALL, and 146 // another group with the interceptions belonging to dlls that will be "hot" 147 // patched on the client. The second group lives on local_buffer, and the 148 // first group remains on the list of interceptions (inside the object 149 // "interceptions"). There are 3 local interceptions (of ntdll); the 150 // other 15 have to be sent to the child to be performed "hot". 151 EXPECT_EQ(3, interceptions.interceptions_.size()); 152 153 int num_dlls, num_functions, num_names; 154 WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, 155 &num_names); 156 157 // The 15 interceptions on the buffer (to the child) should be grouped on 6 158 // dlls. Only four interceptions are using an explicit name for the 159 // interceptor function. 160 EXPECT_EQ(6, num_dlls); 161 EXPECT_EQ(15, num_functions); 162 EXPECT_EQ(4, num_names); 163 } 164 165 TEST(InterceptionManagerTest, BufferLayout2) { 166 wchar_t exe_name[MAX_PATH]; 167 ASSERT_NE(0u, GetModuleFileName(NULL, exe_name, MAX_PATH - 1)); 168 169 TargetProcess *target = MakeTestTargetProcess(::GetCurrentProcess(), 170 ::GetModuleHandle(exe_name)); 171 172 InterceptionManager interceptions(target, true); 173 174 // Any pointer will do for a function pointer. 175 void* function = &interceptions; 176 interceptions.AddToUnloadModules(L"some01.dll"); 177 // We don't care about the interceptor id. 178 interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile", 179 INTERCEPTION_SERVICE_CALL, function, 180 OPEN_FILE_ID); 181 interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx", 182 INTERCEPTION_EAT, function, OPEN_FILE_ID); 183 interceptions.AddToUnloadModules(L"some02.dll"); 184 interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx", 185 INTERCEPTION_SMART_SIDESTEP, function, 186 OPEN_FILE_ID); 187 // Verify that all interceptions were added 188 ASSERT_EQ(5, interceptions.interceptions_.size()); 189 190 size_t buffer_size = interceptions.GetBufferSize(); 191 scoped_ptr<BYTE[]> local_buffer(new BYTE[buffer_size]); 192 193 ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), 194 buffer_size)); 195 196 // At this point, the interceptions should have been separated into two 197 // groups: one group with the local ("cold") interceptions, and another 198 // group with the interceptions belonging to dlls that will be "hot" 199 // patched on the client. The second group lives on local_buffer, and the 200 // first group remains on the list of interceptions, in this case just one. 201 EXPECT_EQ(1, interceptions.interceptions_.size()); 202 203 int num_dlls, num_functions, num_names; 204 WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions, 205 &num_names); 206 207 EXPECT_EQ(3, num_dlls); 208 EXPECT_EQ(4, num_functions); 209 EXPECT_EQ(0, num_names); 210 } 211 212 } // namespace sandbox 213