1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/agent/common/hot_patcher.h"
16 :
17 : #include <windows.h>
18 :
19 : #include "base/basictypes.h"
20 : #include "gtest/gtest.h"
21 :
22 : namespace agent {
23 : namespace common {
24 :
25 : namespace {
26 :
27 : // A function pointer type that with a simple calling convention: it takes no
28 : // parameters and returns the result in EAX.
29 : typedef int (__stdcall *TestFunctionPtr)();
30 :
31 : // Padding bytes and a simple function that can be called via a TestFunctionPtr
32 : // function pointer and always returns 1. If we copy this function to a 2-byte
33 : // aligned location, this function fulfills all requirements of HotPacher.
34 : const uint8 kTestFunction[] = {
35 : // Padding bytes. We use six padding bytes so the function will be 2-aligned
36 : // when we write it to the beginning of a page or at an even offset.
37 : // |kNumberOfPaddingBytesInTestFunction| must contain the number of padding
38 : // 0xCC bytes.
39 : 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
40 : // MOV EAX, 1
41 : 0xB8, 0x01, 0x00, 0x00, 0x00,
42 : // RET
43 : 0xC3,
44 : };
45 :
46 : // The number of padding 0xCCs in kTestFunction.
47 : const size_t kNumberOfPaddingBytesInTestFunction = 6U;
48 :
49 : // A simple function that can be called via a TestFunctionPtr function pointer.
50 : // @returns 42. (It is deliberately different from the return value of the
51 : // function in kTestFunction)
52 E : int __stdcall NewFunction() {
53 E : return 42;
54 E : }
55 :
56 : class HotPatcherTest : public ::testing::Test {
57 : public:
58 : // We initialize the page size.
59 E : HotPatcherTest() {
60 E : SYSTEM_INFO system_info = {};
61 E : ::GetSystemInfo(&system_info);
62 E : page_size_ = system_info.dwPageSize;
63 E : }
64 :
65 : // Runs the hot patcher tests.
66 : // @param virtual_memory_size The size of virtual memory that we allocate
67 : // for the test using VirtualAlloc.
68 : // @param offset We lay out |kTestFunction| to this offset in the allocated
69 : // virtual memory.
70 E : void RunTest(size_t virtual_memory_size, size_t offset) {
71 : // Sanity check that we have enough memory to write the test function at
72 : // the given offset.
73 E : ASSERT_GT(virtual_memory_size, offset + sizeof(kTestFunction));
74 :
75 : // Allocate virtual memory with write access.
76 : LPVOID virtual_memory = ::VirtualAlloc(nullptr,
77 : virtual_memory_size,
78 : MEM_COMMIT,
79 E : PAGE_READWRITE);
80 E : ASSERT_NE(nullptr, virtual_memory);
81 :
82 : // We use this location in the virtual memory.
83 E : uint8* virtual_memory_cursor = static_cast<uint8*>(virtual_memory) + offset;
84 :
85 : // We check that the newly allocated virtual memory is 2-byte aligned.
86 : // (The underlying virtual page itself should have a much higher alignment.)
87 E : ASSERT_EQ(0, reinterpret_cast<int32>(virtual_memory_cursor) % 2);
88 :
89 : // Copy the test function into the virtual memory.
90 E : ::memcpy(virtual_memory_cursor, kTestFunction, sizeof(kTestFunction));
91 :
92 : // Remove write permission and add executable permission to the page.
93 : DWORD old_protection;
94 E : ASSERT_TRUE(::VirtualProtect(virtual_memory,
95 : virtual_memory_size,
96 : PAGE_EXECUTE_READ,
97 : &old_protection));
98 :
99 : TestFunctionPtr test_function =
100 : reinterpret_cast<TestFunctionPtr>(virtual_memory_cursor +
101 E : kNumberOfPaddingBytesInTestFunction);
102 :
103 : // Call test function.
104 E : ASSERT_EQ(1, test_function());
105 :
106 : // Hot patch test function.
107 E : HotPatcher hot_patcher;
108 E : hot_patcher.Patch(test_function, &NewFunction);
109 :
110 : // Call the same function. It is now hot patched so it should return a
111 : // different value.
112 E : ASSERT_EQ(42, test_function());
113 :
114 : // Check that the protection is kept.
115 : MEMORY_BASIC_INFORMATION meminfo;
116 E : ASSERT_NE(0U,
117 : ::VirtualQuery(virtual_memory, &meminfo, virtual_memory_size));
118 E : if (virtual_memory_size > page_size_) {
119 : // If we allocate more bytes we have to restore the protection for both.
120 E : ASSERT_EQ(page_size_ * 2, meminfo.RegionSize);
121 : }
122 E : ASSERT_EQ(PAGE_EXECUTE_READ, meminfo.Protect);
123 E : }
124 :
125 : size_t page_size_;
126 : };
127 :
128 : } // namespace
129 :
130 E : TEST_F(HotPatcherTest, Test) {
131 E : ASSERT_NO_FATAL_FAILURE(RunTest(256U, 0U));
132 E : }
133 :
134 E : TEST_F(HotPatcherTest, TestPageBoundary) {
135 : // The hot patching will happen at a page boundary.
136 E : ASSERT_NO_FATAL_FAILURE(RunTest(page_size_ * 2, page_size_ - 2));
137 E : ASSERT_NO_FATAL_FAILURE(RunTest(page_size_ * 2, page_size_ - 4));
138 E : }
139 :
140 : } // namespace common
141 : } // namespace agent
|