OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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 #include "hook_util.h" | |
6 | |
7 #include <versionhelpers.h> // windows.h must be before | |
8 | |
9 #include "base/win/pe_image.h" | |
10 #include "sandbox/win/src/interception_internal.h" | |
11 #include "sandbox/win/src/internal_types.h" | |
12 #include "sandbox/win/src/sandbox_utils.h" | |
13 #include "sandbox/win/src/service_resolver.h" | |
14 | |
15 namespace { | |
16 | |
17 //------------------------------------------------------------------------------ | |
18 // Common hooking utility functions - LOCAL | |
19 //------------------------------------------------------------------------------ | |
20 | |
21 #if !defined(_WIN64) | |
22 // Whether a process is running under WOW64 (the wrapper that allows 32-bit | |
23 // processes to run on 64-bit versions of Windows). This will return | |
24 // WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit | |
25 // Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g. | |
26 // the process does not have sufficient access rights to determine this. | |
27 enum WOW64Status { | |
28 WOW64_DISABLED, | |
29 WOW64_ENABLED, | |
30 WOW64_UNKNOWN, | |
31 }; | |
32 | |
33 WOW64Status GetWOW64StatusForCurrentProcess() { | |
34 typedef BOOL(WINAPI * IsWow64ProcessFunc)(HANDLE, PBOOL); | |
35 IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>( | |
36 GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process")); | |
37 if (!is_wow64_process) | |
38 return WOW64_DISABLED; | |
39 BOOL is_wow64 = FALSE; | |
40 if (!is_wow64_process(GetCurrentProcess(), &is_wow64)) | |
41 return WOW64_UNKNOWN; | |
42 return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED; | |
43 } | |
44 #endif // !defined(_WIN64) | |
45 | |
46 // Change the page protections to writable, copy the data, | |
47 // restore protections. Returns a winerror code. | |
48 DWORD PatchMem(void* target, void* new_bytes, size_t length) { | |
49 if (target == nullptr || new_bytes == nullptr || length == 0) | |
50 return ERROR_INVALID_PARAMETER; | |
51 | |
52 // Preserve executable state. | |
53 MEMORY_BASIC_INFORMATION memory_info = {}; | |
54 if (!::VirtualQuery(target, &memory_info, sizeof(memory_info))) { | |
55 return GetLastError(); | |
56 } | |
57 | |
58 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ | | |
59 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) & | |
60 memory_info.Protect; | |
61 | |
62 // Make target writeable. | |
63 DWORD old_page_protection = 0; | |
64 if (!::VirtualProtect(target, length, | |
65 is_executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, | |
66 &old_page_protection)) { | |
67 return GetLastError(); | |
68 } | |
69 | |
70 // Write the data. | |
71 ::memcpy(target, new_bytes, length); | |
72 | |
73 // Restore old page protection. | |
74 if (!::VirtualProtect(target, length, old_page_protection, | |
75 &old_page_protection)) { | |
76 // Yes, this could fail. However, memory was already patched. | |
77 #ifdef _DEBUG | |
78 assert(false); | |
79 #endif // _DEBUG | |
80 } | |
81 | |
82 return NO_ERROR; | |
83 } | |
84 | |
85 //------------------------------------------------------------------------------ | |
86 // Import Address Table hooking support - LOCAL | |
87 //------------------------------------------------------------------------------ | |
88 | |
89 void* GetIATFunctionPtr(IMAGE_THUNK_DATA* iat_thunk) { | |
90 if (iat_thunk == nullptr) | |
91 return nullptr; | |
92 | |
93 // Works around the 64 bit portability warning: | |
94 // The Function member inside IMAGE_THUNK_DATA is really a pointer | |
95 // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32 | |
96 // or IMAGE_THUNK_DATA64 for correct pointer size. | |
97 union FunctionThunk { | |
98 IMAGE_THUNK_DATA thunk; | |
99 void* pointer; | |
100 } iat_function; | |
101 | |
102 iat_function.thunk = *iat_thunk; | |
103 return iat_function.pointer; | |
104 } | |
105 | |
106 // Used to pass target function information during pe_image enumeration. | |
107 struct IATHookFunctionInfo { | |
108 bool finished_operation; | |
109 const char* imported_from_module; | |
110 const char* function_name; | |
111 void* new_function; | |
112 void** old_function; | |
113 IMAGE_THUNK_DATA** iat_thunk; | |
114 DWORD return_code; | |
115 }; | |
116 | |
117 // Callback function for pe_image enumeration. This function is called from | |
118 // within PEImage::EnumOneImportChunk(). | |
119 // NOTE: Returning true means continue enumerating. False means stop. | |
120 bool IATFindHookFuncCallback(const base::win::PEImage& image, | |
121 const char* module, | |
122 DWORD ordinal, | |
123 const char* import_name, | |
124 DWORD hint, | |
125 IMAGE_THUNK_DATA* iat, | |
126 void* cookie) { | |
127 IATHookFunctionInfo* hook_func_info = | |
128 reinterpret_cast<IATHookFunctionInfo*>(cookie); | |
129 if (hook_func_info == nullptr) | |
130 return false; | |
131 | |
132 // Check for the right module. | |
133 if (module == nullptr || | |
134 ::strnicmp(module, hook_func_info->imported_from_module, | |
135 ::strlen(module)) != 0) | |
136 return true; | |
137 | |
138 // Check for the right function. | |
139 if (import_name == nullptr || | |
140 ::strnicmp(import_name, hook_func_info->function_name, | |
141 ::strlen(import_name)) != 0) | |
142 return true; | |
143 | |
144 // At this point, the target function was found. Even if something fails now, | |
145 // don't do any further enumerating. | |
146 hook_func_info->finished_operation = true; | |
147 | |
148 // This is it. Do the hook! | |
149 // 1) Save the old function pointer. | |
150 *(hook_func_info->old_function) = GetIATFunctionPtr(iat); | |
151 | |
152 // 2) Save the IAT thunk. | |
153 *(hook_func_info->iat_thunk) = iat; | |
154 | |
155 // 3) Sanity check the pointer sizes (architectures). | |
156 if (sizeof(iat->u1.Function) != sizeof(hook_func_info->new_function)) { | |
157 hook_func_info->return_code = ERROR_BAD_ENVIRONMENT; | |
158 return false; | |
159 } | |
160 | |
161 // 4) Patch the function pointer. | |
162 hook_func_info->return_code = | |
163 PatchMem(&(iat->u1.Function), &(hook_func_info->new_function), | |
164 sizeof(hook_func_info->new_function)); | |
165 | |
166 return false; | |
167 } | |
168 | |
169 DWORD ApplyIATHook(HMODULE module_handle, | |
robertshield
2016/08/02 02:38:41
Please document this function Are the return value
penny
2016/08/02 21:07:43
Done.
| |
170 const char* imported_from_module, | |
171 const char* function_name, | |
172 void* new_function, | |
173 void** old_function, | |
174 IMAGE_THUNK_DATA** iat_thunk) { | |
175 base::win::PEImage target_image(module_handle); | |
176 if (!target_image.VerifyMagic()) | |
177 return 1; | |
robertshield
2016/08/02 02:38:41
Along the same lines, should this be ERROR_INVALID
penny
2016/08/02 21:07:43
Done.
| |
178 | |
179 IATHookFunctionInfo hook_info = {false, | |
180 imported_from_module, | |
181 function_name, | |
182 new_function, | |
183 old_function, | |
184 iat_thunk, | |
185 ERROR_GEN_FAILURE}; | |
186 | |
187 // First go through the IAT. If we don't find the import we are looking | |
188 // for in IAT, search delay import table. | |
189 target_image.EnumAllImports(IATFindHookFuncCallback, &hook_info); | |
190 if (!hook_info.finished_operation) { | |
191 target_image.EnumAllDelayImports(IATFindHookFuncCallback, &hook_info); | |
192 } | |
193 | |
194 return hook_info.return_code; | |
195 } | |
196 | |
197 DWORD RemoveIATHook(void* intercept_function, | |
198 void* original_function, | |
199 IMAGE_THUNK_DATA* iat_thunk) { | |
200 if (GetIATFunctionPtr(iat_thunk) != intercept_function) { | |
201 // Someone else has messed with the same target. Cannot unpatch. | |
202 #ifdef _DEBUG | |
203 assert(false); | |
204 #endif // _DEBUG | |
205 return ERROR_INVALID_FUNCTION; | |
206 } | |
207 | |
208 return PatchMem(&(iat_thunk->u1.Function), &original_function, | |
209 sizeof(original_function)); | |
210 } | |
211 | |
212 } // namespace | |
213 | |
214 namespace elf_hook { | |
215 | |
216 //------------------------------------------------------------------------------ | |
217 // System Service hooking support | |
218 //------------------------------------------------------------------------------ | |
219 | |
220 sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) { | |
221 // Create a thunk via the appropriate ServiceResolver instance. | |
222 sandbox::ServiceResolverThunk* thunk = nullptr; | |
223 | |
224 // No hooking unsupported OS versions. | |
robertshield
2016/08/02 02:38:41
nit: No hooking _on_ unsupported
penny
2016/08/02 21:07:43
Well, technically we're hooking the operating syst
robertshield
2016/08/03 04:52:45
Potato, po-tah-to I guess, 'twas merely a nit :-)
| |
225 if (!::IsWindows7OrGreater()) | |
226 return thunk; | |
227 | |
228 // Pseudo-handle, no need to close. | |
229 HANDLE current_process = ::GetCurrentProcess(); | |
230 | |
231 #if defined(_WIN64) | |
232 // ServiceResolverThunk can handle all the formats in 64-bit (instead only | |
233 // handling one like it does in 32-bit versions). | |
234 thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); | |
235 #else | |
236 if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) { | |
237 if (::IsWindows10OrGreater()) | |
238 thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed); | |
239 else if (::IsWindows8OrGreater()) | |
240 thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed); | |
241 else | |
242 thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed); | |
243 } else if (::IsWindows8OrGreater()) { | |
244 thunk = new sandbox::Win8ResolverThunk(current_process, relaxed); | |
245 } else { | |
246 thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); | |
247 } | |
248 #endif | |
249 | |
250 return thunk; | |
251 } | |
252 | |
253 //------------------------------------------------------------------------------ | |
254 // Import Address Table hooking support | |
255 //------------------------------------------------------------------------------ | |
256 | |
257 IATHook::IATHook() | |
258 : intercept_function_(nullptr), | |
259 original_function_(nullptr), | |
260 iat_thunk_(nullptr) {} | |
261 | |
262 IATHook::~IATHook() { | |
263 if (intercept_function_ != nullptr) { | |
264 if (Unhook() != NO_ERROR) { | |
265 #ifdef _DEBUG | |
266 assert(false); | |
267 #endif // _DEBUG | |
268 } | |
269 } | |
270 } | |
271 | |
272 DWORD IATHook::Hook(HMODULE module, | |
273 const char* imported_from_module, | |
274 const char* function_name, | |
275 void* new_function) { | |
276 if ((module == 0 || module == INVALID_HANDLE_VALUE) || | |
277 imported_from_module == nullptr || function_name == nullptr || | |
278 new_function == nullptr) | |
279 return ERROR_INVALID_PARAMETER; | |
280 | |
281 DWORD winerror = ApplyIATHook(module, imported_from_module, function_name, | |
282 new_function, &original_function_, &iat_thunk_); | |
283 if (winerror == NO_ERROR) { | |
284 intercept_function_ = new_function; | |
285 #ifdef _DEBUG | |
286 if (original_function_ == new_function) | |
287 assert(false); | |
288 #endif //_DEBUG | |
289 } | |
290 | |
291 return winerror; | |
292 } | |
293 | |
294 DWORD IATHook::Unhook() { | |
295 DWORD winerror = | |
296 RemoveIATHook(intercept_function_, original_function_, iat_thunk_); | |
297 #ifdef _DEBUG | |
298 if (winerror != NO_ERROR) | |
299 assert(false); | |
300 #endif //_DEBUG | |
301 | |
302 intercept_function_ = nullptr; | |
303 original_function_ = nullptr; | |
304 iat_thunk_ = nullptr; | |
305 | |
306 return winerror; | |
307 } | |
308 | |
309 } // namespace elf_hook | |
OLD | NEW |