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 <stdint.h>
18 : #include <windows.h>
19 :
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_t 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,
40 : 0xCC,
41 : 0xCC,
42 : 0xCC,
43 : 0xCC,
44 : 0xCC,
45 : // MOV EAX, 1
46 : 0xB8,
47 : 0x01,
48 : 0x00,
49 : 0x00,
50 : 0x00,
51 : // RET
52 : 0xC3,
53 : };
54 :
55 : // The number of padding 0xCCs in kTestFunction.
56 : const size_t kNumberOfPaddingBytesInTestFunction = 6U;
57 :
58 : // A simple function that can be called via a TestFunctionPtr function pointer.
59 : // @returns 42. (It is deliberately different from the return value of the
60 : // function in kTestFunction)
61 E : int __stdcall NewFunction() {
62 E : return 42;
63 E : }
64 :
65 : class HotPatcherTest : public ::testing::Test {
66 : public:
67 : // We initialize the page size.
68 E : HotPatcherTest() {
69 E : SYSTEM_INFO system_info = {};
70 E : ::GetSystemInfo(&system_info);
71 E : page_size_ = system_info.dwPageSize;
72 E : }
73 :
74 : // Runs the hot patcher tests.
75 : // @param virtual_memory_size The size of virtual memory that we allocate
76 : // for the test using VirtualAlloc.
77 : // @param offset We lay out |kTestFunction| to this offset in the allocated
78 : // virtual memory.
79 E : void RunTest(size_t virtual_memory_size, size_t offset) {
80 : // Sanity check that we have enough memory to write the test function at
81 : // the given offset.
82 E : ASSERT_GT(virtual_memory_size, offset + sizeof(kTestFunction));
83 :
84 : // Allocate virtual memory with write access.
85 E : LPVOID virtual_memory = ::VirtualAlloc(nullptr,
86 : virtual_memory_size,
87 : MEM_COMMIT,
88 : PAGE_READWRITE);
89 E : ASSERT_NE(nullptr, virtual_memory);
90 :
91 : // We use this location in the virtual memory.
92 : uint8_t* virtual_memory_cursor =
93 E : static_cast<uint8_t*>(virtual_memory) + offset;
94 :
95 : // We check that the newly allocated virtual memory is 2-byte aligned.
96 : // (The underlying virtual page itself should have a much higher alignment.)
97 E : ASSERT_EQ(0, reinterpret_cast<int32_t>(virtual_memory_cursor) % 2);
98 :
99 : // Copy the test function into the virtual memory.
100 E : ::memcpy(virtual_memory_cursor, kTestFunction, sizeof(kTestFunction));
101 :
102 : // Remove write permission and add executable permission to the page.
103 : DWORD old_protection;
104 E : ASSERT_TRUE(::VirtualProtect(virtual_memory,
105 : virtual_memory_size,
106 : PAGE_EXECUTE_READ,
107 : &old_protection));
108 :
109 : TestFunctionPtr test_function =
110 E : reinterpret_cast<TestFunctionPtr>(virtual_memory_cursor +
111 : kNumberOfPaddingBytesInTestFunction);
112 :
113 : // Call test function.
114 E : ASSERT_EQ(1, test_function());
115 :
116 : // Hot patch test function.
117 E : HotPatcher hot_patcher;
118 E : hot_patcher.Patch(test_function, &NewFunction);
119 :
120 : // Call the same function. It is now hot patched so it should return a
121 : // different value.
122 E : ASSERT_EQ(42, test_function());
123 :
124 : // Check that the protection is kept.
125 : MEMORY_BASIC_INFORMATION meminfo;
126 E : ASSERT_NE(0U,
127 : ::VirtualQuery(virtual_memory, &meminfo, virtual_memory_size));
128 E : if (virtual_memory_size > page_size_) {
129 : // If we allocate more bytes we have to restore the protection for both.
130 E : ASSERT_EQ(page_size_ * 2, meminfo.RegionSize);
131 : }
132 E : ASSERT_EQ(PAGE_EXECUTE_READ, meminfo.Protect);
133 E : }
134 :
135 : size_t page_size_;
136 : };
137 :
138 : } // namespace
139 :
140 E : TEST_F(HotPatcherTest, Test) {
141 E : ASSERT_NO_FATAL_FAILURE(RunTest(256U, 0U));
142 E : }
143 :
144 E : TEST_F(HotPatcherTest, TestPageBoundary) {
145 : // The hot patching will happen at a page boundary.
146 E : ASSERT_NO_FATAL_FAILURE(RunTest(page_size_ * 2, page_size_ - 2));
147 E : ASSERT_NO_FATAL_FAILURE(RunTest(page_size_ * 2, page_size_ - 4));
148 E : }
149 :
150 : } // namespace common
151 : } // namespace agent
|