Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(89)

Side by Side Diff: sandbox/win/src/cfi_unittest_exe.cc

Issue 2679793002: [Windows CFG Test] Added unittest for CFG enabling on process. (Closed)
Patch Set: Fixing 'git cl format' breakage. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 #include <windows.h>
5
6 #include "base/files/file_path.h"
7 #include "base/scoped_native_library.h"
8
9 namespace {
10
11 //------------------------------------------------------------------------------
12 // PRIVATE: Control Flow Guard test things
13 // - MS binary indirect call tests.
14 //------------------------------------------------------------------------------
15
16 // These structures hold chunks of binary. No padding wanted.
17 #pragma pack(push, 1)
18
19 // Tests don't run in a wow64 environment, so fine to compile based on ifdef.
Will Harris 2017/02/06 22:33:36 I'm not sure I understand this comment. We have 32
penny 2017/02/07 23:12:56 Done.
20 #if defined(_WIN64)
21
22 const USHORT kCmpRsi = 0x3B48;
23 const BYTE kRax = 0xF0;
24 const USHORT kJzLoc = 0x0074;
25 const ULONG kMovRcxRsiCall = 0xFFCE8B48;
26 const BYTE kPadding = 0x00;
27 const ULONG kMovEcxEbxCallRsi = 0xD6FFCB8B;
28
29 const ULONG kMovRcxRsiAdd = 0x48CE8B48;
30 const ULONG kRcx10hNop = 0x9010C183;
31
32 // Sequence of bytes in GetSystemMetrics.
33 struct Signature {
34 // This struct contains roughly the following code:
35 // cmp rsi, rax
36 // jz jmp_addr
37 // mov rcx, rsi
38 // call ULONG_addr <-- CFG check function
39 // mov ecx, ebx
40 // call rsi <-- Actual call to GetSystemMetrics
41
42 // Patch will start here and be 8 bytes.
43 USHORT cmp_rsi; // = 48 3B
44 BYTE rax; // = F0
45 USHORT jz_loc; // = 74 XX
46 ULONG mov_rcx_rsi_call; // = 48 8B CE FF
47 ULONG guard_check_icall_fptr; // = XX XX XX XX
48 BYTE padding; // = 00
49 ULONG mov_ecx_ebx_call_rsi; // = 8B CB FF D6
50 };
51
52 struct Patch {
53 // Just add 16 to the existing function address.
54 // This ensures a 16-byte aligned address that is
55 // definitely not a valid function address.
56 // mov rcx, rsi = 48 4B CE
57 // add rcx, 10h = 48 83 C1 10
58 // nop = 90
59 ULONG first_four_bytes; // = 48 4B CE 48
60 ULONG second_four_bytes; // = 83 C1 10 90
61 };
62
63 #else // x86
64
65 const USHORT kCmpEbx = 0x0081;
66 const USHORT kJzLoc = 0x0074;
67 const ULONG kMovEcxEbxCall = 0x15FFCB8B;
68 const USHORT kCallEbx = 0xD3FF;
69
70 const ULONG kMovEcxEbxAddEcx = 0xC183CB8B;
71 const ULONG k16Nops = 0x90909010;
72 const USHORT kTwoNops = 0x9090;
73
74 // Sequence of bytes in GetSystemMetrics, x86.
75 struct Signature {
76 // This struct contains roughly the following code:
77 // cmp ebx, offset
78 // jz jmp_addr
79 // mov ecx, ebx
80 // call ULONG_addr <-- CFG check function
81 // call ebx <-- Actual call to GetSystemMetrics
82
83 // Patch will start here and be 10 bytes.
84 USHORT cmp_ebx; // = 81 XX
85 ULONG addr; // = XX XX XX XX
86 USHORT jz_loc; // = 74 XX
87 ULONG mov_ecx_ebx_call; // = 8B CB FF 15
88 ULONG guard_check_icall_fptr; // = XX XX XX XX
89 USHORT call_ebx; // = FF D3
90 };
91
92 struct Patch {
93 // Just add 16 to the existing function address.
94 // This ensures a 16-byte aligned address that is
95 // definitely not a valid function address.
96 // mov ecx, ebx = 8B CB
97 // add ecx, 10h = 83 C1 10 90
98 // nop = 90 90 90 90
99 ULONG first_four_bytes; // = 8B CB 83 C1
100 ULONG second_four_bytes; // = 10 90 90 90
101 USHORT last_two_bytes; // = 90 90
102 };
103
104 #endif // _WIN64
105
106 #pragma pack(pop)
107 //------------------------------------------------------------------------------
108
109 // - Search binary starting at |address_start| for a matching chunk of
110 // |binary_to_find|, of size |size_to_match|.
111 // - A byte of value 0 in |binary_to_find|, is a wildcard for anything.
112 // - Give a |max_distance| to find the chunk within, before failing.
113 // - If return value is true, |out_offset| will hold the offset from
114 // |address_start| that the match starts.
115 bool FindBinary(BYTE* binary_to_find,
116 DWORD size_to_match,
117 BYTE* address_start,
118 DWORD max_distance,
119 DWORD* out_offset) {
120 assert(size_to_match <= max_distance);
121 assert(size_to_match > 0);
122
123 BYTE* max_byte = address_start + max_distance - size_to_match;
124 BYTE* temp_ptr = address_start;
125 // Yes, it's a double while loop.
126 while (temp_ptr <= max_byte) {
127 size_t i = 0;
128 // 0 is a wildcard match.
129 while (binary_to_find[i] == 0 || temp_ptr[i] == binary_to_find[i]) {
130 // Check if this is the last byte.
131 if (i == size_to_match - 1) {
132 *out_offset = temp_ptr - address_start;
133 return true;
134 }
135 ++i;
136 }
137 ++temp_ptr;
138 }
139
140 return false;
141 }
142
143 // - Will write patch starting at |start_addr| with the patch for x86
144 // or x64.
145 // - This function is only for writes within this process.
146 bool DoPatch(BYTE* start_addr) {
147 Patch patch = {};
148 #if defined(_WIN64)
149 patch.first_four_bytes = kMovRcxRsiAdd;
150 patch.second_four_bytes = kRcx10hNop;
151 #else // x86
152 patch.first_four_bytes = kMovEcxEbxAddEcx;
153 patch.second_four_bytes = k16Nops;
154 patch.last_two_bytes = kTwoNops;
155 #endif // _WIN64
156
157 DWORD old_protection;
158 // PAGE_WRITECOPY explicitly allows "copy-on-write" behaviour for
159 // system DLL patches.
160 if (!::VirtualProtect(start_addr, sizeof(patch), PAGE_WRITECOPY,
161 &old_protection))
162 return false;
163
164 ::memcpy(start_addr, &patch, sizeof(patch));
165 ::VirtualProtect(start_addr, sizeof(patch), old_protection, &old_protection);
166
167 return true;
168 }
169
170 // - Find the offset from |start| that the x86 or x64 signature starts.
171 bool FindSignature(BYTE* start, DWORD* offset_found) {
172 Signature signature = {};
173 #if defined(_WIN64)
174 signature.cmp_rsi = kCmpRsi;
175 signature.rax = kRax;
176 signature.jz_loc = kJzLoc;
177 signature.mov_rcx_rsi_call = kMovRcxRsiCall;
178 signature.padding = kPadding;
179 signature.mov_ecx_ebx_call_rsi = kMovEcxEbxCallRsi;
180
181 // This is far enough into GetSystemMetrics that the signature should
182 // have been found. See disassembly.
183 DWORD max_area = 0xB0;
184 #else // x86
185 signature.cmp_ebx = kCmpEbx;
186 signature.jz_loc = kJzLoc;
187 signature.mov_ecx_ebx_call = kMovEcxEbxCall;
188 signature.call_ebx = kCallEbx;
189
190 // This is far enough into GetSystemMetrics x86 that the signature should
191 // have been found. See disassembly.
192 DWORD max_area = 0xD9;
193 #endif // _WIN64
194 if (!FindBinary(reinterpret_cast<BYTE*>(&signature), sizeof(signature), start,
195 max_area, offset_found))
196 return false;
197
198 return true;
199 }
200
201 // - This function tests for CFG in MS system binaries, in process.
202 void TestMsIndirect() {
203 base::ScopedNativeLibrary user32(base::FilePath(L"user32.dll"));
204 if (!user32.is_valid())
205 _exit(1);
206
207 using GetSystemMetricsFunction = decltype(&::GetSystemMetrics);
208 GetSystemMetricsFunction get_system_metrics =
209 reinterpret_cast<GetSystemMetricsFunction>(
210 user32.GetFunctionPointer("GetSystemMetrics"));
211 if (!get_system_metrics)
212 _exit(2);
213
214 // Sanity check the function works fine pre-patch. Tests should only be
215 // running from normal boot (0).
216 if (0 != get_system_metrics(SM_CLEANBOOT))
217 _exit(3);
218
219 BYTE* target = reinterpret_cast<BYTE*>(get_system_metrics);
220 DWORD offset = 0;
221 if (!FindSignature(target, &offset))
222 _exit(4);
223
224 // Now patch the function. Don't bother saving original code,
225 // as this process will end very soon.
226 if (!DoPatch(target + offset))
227 _exit(5);
228
229 // Call the patched function!
230 get_system_metrics(SM_CLEANBOOT);
231 }
232
233 } // namespace
234
235 //------------------------------------------------------------------------------
236 // PUBLIC
237 //------------------------------------------------------------------------------
238
239 // Good ol' main.
240 // - Exe exits with non-zero return codes for unexpected errors.
241 // - Return code of zero indicates no issues at all.
242 // - Else, a CFG exception will result in the process being destroyed.
243 int main(int argc, char** argv) {
244 if (argc != 2)
245 _exit(-1);
246
247 const char* arg = argv[1];
248
249 int iarg = ::atoi(arg);
250 if (!iarg)
251 _exit(-1);
252
253 switch (iarg) {
254 // kSysDllTest
255 case 1:
256 TestMsIndirect();
257 break;
258 // Unsupported argument.
259 default:
260 _exit(-1);
261 }
262
263 return 0;
264 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698