1/* Copyright (c) 2011, Google Inc. 2 * All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 * 30 * --- 31 * Author: Joi Sigurdsson 32 * Author: Scott Francis 33 * 34 * Unit tests for PreamblePatcher 35 */ 36 37#include "config_for_unittests.h" 38#include "preamble_patcher.h" 39#include "mini_disassembler.h" 40#pragma warning(push) 41#pragma warning(disable:4553) 42#include "auto_testing_hook.h" 43#pragma warning(pop) 44 45#define WIN32_LEAN_AND_MEAN 46#include <windows.h> 47#include <tchar.h> 48 49// Turning off all optimizations for this file, since the official build's 50// "Whole program optimization" seems to cause the TestPatchUsingDynamicStub 51// test to crash with an access violation. We debugged this and found 52// that the optimized access a register that is changed by a call to the hook 53// function. 54#pragma optimize("", off) 55 56// A convenience macro to avoid a lot of casting in the tests. 57// I tried to make this a templated function, but windows complained: 58// error C2782: 'sidestep::SideStepError `anonymous-namespace'::Unpatch(T,T,T *)' : template parameter 'T' is ambiguous 59// could be 'int (int)' 60// or 'int (__cdecl *)(int)' 61// My life isn't long enough to try to figure out how to fix this. 62#define UNPATCH(target_function, replacement_function, original_function_stub) \ 63 sidestep::PreamblePatcher::Unpatch((void*)(target_function), \ 64 (void*)(replacement_function), \ 65 (void*)(original_function)) 66 67namespace { 68 69// Function for testing - this is what we patch 70// 71// NOTE: Because of the way the compiler optimizes this function in 72// release builds, we need to use a different input value every time we 73// call it within a function, otherwise the compiler will just reuse the 74// last calculated incremented value. 75int __declspec(noinline) IncrementNumber(int i) { 76#ifdef _M_X64 77 __int64 i2 = i + 1; 78 return (int) i2; 79#else 80 return i + 1; 81#endif 82} 83 84extern "C" int TooShortFunction(int); 85 86extern "C" int JumpShortCondFunction(int); 87 88extern "C" int JumpNearCondFunction(int); 89 90extern "C" int JumpAbsoluteFunction(int); 91 92extern "C" int CallNearRelativeFunction(int); 93 94typedef int (*IncrementingFunc)(int); 95IncrementingFunc original_function = NULL; 96 97int HookIncrementNumber(int i) { 98 SIDESTEP_ASSERT(original_function != NULL); 99 int incremented_once = original_function(i); 100 return incremented_once + 1; 101} 102 103// For the AutoTestingHook test, we can't use original_function, because 104// all that is encapsulated. 105// This function "increments" by 10, just to set it apart from the other 106// functions. 107int __declspec(noinline) AutoHookIncrementNumber(int i) { 108 return i + 10; 109} 110 111}; // namespace 112 113namespace sidestep { 114 115bool TestDisassembler() { 116 unsigned int instruction_size = 0; 117 sidestep::MiniDisassembler disassembler; 118 void * target = reinterpret_cast<unsigned char *>(IncrementNumber); 119 void * new_target = PreamblePatcher::ResolveTarget(target); 120 if (target != new_target) 121 target = new_target; 122 123 while (1) { 124 sidestep::InstructionType instructionType = disassembler.Disassemble( 125 reinterpret_cast<unsigned char *>(target) + instruction_size, 126 instruction_size); 127 if (sidestep::IT_RETURN == instructionType) { 128 return true; 129 } 130 } 131} 132 133bool TestPatchWithLongJump() { 134 original_function = NULL; 135 void *p = ::VirtualAlloc(reinterpret_cast<void *>(0x0000020000000000), 4096, 136 MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); 137 SIDESTEP_EXPECT_TRUE(p != NULL); 138 memset(p, 0xcc, 4096); 139 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 140 sidestep::PreamblePatcher::Patch(IncrementNumber, 141 (IncrementingFunc) p, 142 &original_function)); 143 SIDESTEP_ASSERT((*original_function)(1) == 2); 144 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 145 UNPATCH(IncrementNumber, 146 (IncrementingFunc)p, 147 original_function)); 148 ::VirtualFree(p, 0, MEM_RELEASE); 149 return true; 150} 151 152bool TestPatchWithPreambleShortCondJump() { 153 original_function = NULL; 154 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 155 sidestep::PreamblePatcher::Patch(JumpShortCondFunction, 156 HookIncrementNumber, 157 &original_function)); 158 (*original_function)(1); 159 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 160 UNPATCH(JumpShortCondFunction, 161 (void*)HookIncrementNumber, 162 original_function)); 163 return true; 164} 165 166bool TestPatchWithPreambleNearRelativeCondJump() { 167 original_function = NULL; 168 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 169 sidestep::PreamblePatcher::Patch(JumpNearCondFunction, 170 HookIncrementNumber, 171 &original_function)); 172 (*original_function)(0); 173 (*original_function)(1); 174 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 175 UNPATCH(JumpNearCondFunction, 176 HookIncrementNumber, 177 original_function)); 178 return true; 179} 180 181bool TestPatchWithPreambleAbsoluteJump() { 182 original_function = NULL; 183 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 184 sidestep::PreamblePatcher::Patch(JumpAbsoluteFunction, 185 HookIncrementNumber, 186 &original_function)); 187 (*original_function)(0); 188 (*original_function)(1); 189 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 190 UNPATCH(JumpAbsoluteFunction, 191 HookIncrementNumber, 192 original_function)); 193 return true; 194} 195 196bool TestPatchWithPreambleNearRelativeCall() { 197 original_function = NULL; 198 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 199 sidestep::PreamblePatcher::Patch( 200 CallNearRelativeFunction, 201 HookIncrementNumber, 202 &original_function)); 203 (*original_function)(0); 204 (*original_function)(1); 205 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 206 UNPATCH(CallNearRelativeFunction, 207 HookIncrementNumber, 208 original_function)); 209 return true; 210} 211 212bool TestPatchUsingDynamicStub() { 213 original_function = NULL; 214 SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2); 215 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 216 sidestep::PreamblePatcher::Patch(IncrementNumber, 217 HookIncrementNumber, 218 &original_function)); 219 SIDESTEP_EXPECT_TRUE(original_function); 220 SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 4); 221 SIDESTEP_EXPECT_TRUE(original_function(3) == 4); 222 223 // Clearbox test to see that the function has been patched. 224 sidestep::MiniDisassembler disassembler; 225 unsigned int instruction_size = 0; 226 SIDESTEP_EXPECT_TRUE(sidestep::IT_JUMP == disassembler.Disassemble( 227 reinterpret_cast<unsigned char*>(IncrementNumber), 228 instruction_size)); 229 230 // Since we patched IncrementNumber, its first statement is a 231 // jmp to the hook function. So verify that we now can not patch 232 // IncrementNumber because it starts with a jump. 233#if 0 234 IncrementingFunc dummy = NULL; 235 // TODO(joi@chromium.org): restore this test once flag is added to 236 // disable JMP following 237 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_JUMP_INSTRUCTION == 238 sidestep::PreamblePatcher::Patch(IncrementNumber, 239 HookIncrementNumber, 240 &dummy)); 241 242 // This test disabled because code in preamble_patcher_with_stub.cc 243 // asserts before returning the error code -- so there is no way 244 // to get an error code here, in debug build. 245 dummy = NULL; 246 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_FUNCTION_TOO_SMALL == 247 sidestep::PreamblePatcher::Patch(TooShortFunction, 248 HookIncrementNumber, 249 &dummy)); 250#endif 251 252 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 253 UNPATCH(IncrementNumber, 254 HookIncrementNumber, 255 original_function)); 256 return true; 257} 258 259bool PatchThenUnpatch() { 260 original_function = NULL; 261 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 262 sidestep::PreamblePatcher::Patch(IncrementNumber, 263 HookIncrementNumber, 264 &original_function)); 265 SIDESTEP_EXPECT_TRUE(original_function); 266 SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 3); 267 SIDESTEP_EXPECT_TRUE(original_function(2) == 3); 268 269 SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS == 270 UNPATCH(IncrementNumber, 271 HookIncrementNumber, 272 original_function)); 273 original_function = NULL; 274 SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4); 275 276 return true; 277} 278 279bool AutoTestingHookTest() { 280 SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2); 281 282 // Inner scope, so we can test what happens when the AutoTestingHook 283 // goes out of scope 284 { 285 AutoTestingHook hook = MakeTestingHook(IncrementNumber, 286 AutoHookIncrementNumber); 287 (void) hook; 288 SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12); 289 } 290 SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4); 291 292 return true; 293} 294 295bool AutoTestingHookInContainerTest() { 296 SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2); 297 298 // Inner scope, so we can test what happens when the AutoTestingHook 299 // goes out of scope 300 { 301 AutoTestingHookHolder hook(MakeTestingHookHolder(IncrementNumber, 302 AutoHookIncrementNumber)); 303 (void) hook; 304 SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12); 305 } 306 SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4); 307 308 return true; 309} 310 311bool TestPreambleAllocation() { 312 __int64 diff = 0; 313 void* p1 = reinterpret_cast<void*>(0x110000000); 314 void* p2 = reinterpret_cast<void*>(0x810000000); 315 unsigned char* b1 = PreamblePatcher::AllocPreambleBlockNear(p1); 316 SIDESTEP_EXPECT_TRUE(b1 != NULL); 317 diff = reinterpret_cast<__int64>(p1) - reinterpret_cast<__int64>(b1); 318 // Ensure blocks are within 2GB 319 SIDESTEP_EXPECT_TRUE(diff <= INT_MAX && diff >= INT_MIN); 320 unsigned char* b2 = PreamblePatcher::AllocPreambleBlockNear(p2); 321 SIDESTEP_EXPECT_TRUE(b2 != NULL); 322 diff = reinterpret_cast<__int64>(p2) - reinterpret_cast<__int64>(b2); 323 SIDESTEP_EXPECT_TRUE(diff <= INT_MAX && diff >= INT_MIN); 324 325 // Ensure we're reusing free blocks 326 unsigned char* b3 = b1; 327 unsigned char* b4 = b2; 328 PreamblePatcher::FreePreambleBlock(b1); 329 PreamblePatcher::FreePreambleBlock(b2); 330 b1 = PreamblePatcher::AllocPreambleBlockNear(p1); 331 SIDESTEP_EXPECT_TRUE(b1 == b3); 332 b2 = PreamblePatcher::AllocPreambleBlockNear(p2); 333 SIDESTEP_EXPECT_TRUE(b2 == b4); 334 PreamblePatcher::FreePreambleBlock(b1); 335 PreamblePatcher::FreePreambleBlock(b2); 336 337 return true; 338} 339 340bool UnitTests() { 341 return TestPatchWithPreambleNearRelativeCall() && 342 TestPatchWithPreambleAbsoluteJump() && 343 TestPatchWithPreambleNearRelativeCondJump() && 344 TestPatchWithPreambleShortCondJump() && 345 TestDisassembler() && TestPatchWithLongJump() && 346 TestPatchUsingDynamicStub() && PatchThenUnpatch() && 347 AutoTestingHookTest() && AutoTestingHookInContainerTest() && 348 TestPreambleAllocation(); 349} 350 351}; // namespace sidestep 352 353int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) { 354 if (size == 0) // not even room for a \0? 355 return -1; // not what C99 says to do, but what windows does 356 str[size-1] = '\0'; 357 return _vsnprintf(str, size-1, format, ap); 358} 359 360int _tmain(int argc, _TCHAR* argv[]) 361{ 362 bool ret = sidestep::UnitTests(); 363 printf("%s\n", ret ? "PASS" : "FAIL"); 364 return ret ? 0 : -1; 365} 366 367#pragma optimize("", on) 368