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

Side by Side Diff: chrome_elf/hook_util/hook_util.cc

Issue 2183263003: [chrome_elf] Big ELF cleanup. Part 1. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update with latest trunk. Created 4 years, 3 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
« no previous file with comments | « chrome_elf/hook_util/hook_util.h ('k') | chrome_elf/hook_util/test/hook_util_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 #ifdef _DEBUG
159 assert(false);
160 #endif // _DEBUG
161 return false;
162 }
163
164 // 4) Sanity check that the new hook function is not actually the
165 // same as the existing function for this import!
166 if (*(hook_func_info->old_function) == hook_func_info->new_function) {
167 hook_func_info->return_code = ERROR_INVALID_FUNCTION;
168 #ifdef _DEBUG
169 assert(false);
170 #endif // _DEBUG
171 return false;
172 }
173
174 // 5) Patch the function pointer.
175 hook_func_info->return_code =
176 PatchMem(&(iat->u1.Function), &(hook_func_info->new_function),
177 sizeof(hook_func_info->new_function));
178
179 return false;
180 }
181
182 // Applies an import-address-table hook. Returns a system winerror.h code.
183 // Call RemoveIATHook() with |new_function|, |old_function| and |iat_thunk|
184 // to remove the hook.
185 DWORD ApplyIATHook(HMODULE module_handle,
186 const char* imported_from_module,
187 const char* function_name,
188 void* new_function,
189 void** old_function,
190 IMAGE_THUNK_DATA** iat_thunk) {
191 base::win::PEImage target_image(module_handle);
192 if (!target_image.VerifyMagic())
193 return ERROR_INVALID_PARAMETER;
194
195 IATHookFunctionInfo hook_info = {false,
196 imported_from_module,
197 function_name,
198 new_function,
199 old_function,
200 iat_thunk,
201 ERROR_PROC_NOT_FOUND};
202
203 // First go through the IAT. If we don't find the import we are looking
204 // for in IAT, search delay import table.
205 target_image.EnumAllImports(IATFindHookFuncCallback, &hook_info);
206 if (!hook_info.finished_operation) {
207 target_image.EnumAllDelayImports(IATFindHookFuncCallback, &hook_info);
208 }
209
210 return hook_info.return_code;
211 }
212
213 // Removes an import-address-table hook. Returns a system winerror.h code.
214 DWORD RemoveIATHook(void* intercept_function,
215 void* original_function,
216 IMAGE_THUNK_DATA* iat_thunk) {
217 if (GetIATFunctionPtr(iat_thunk) != intercept_function)
218 // Someone else has messed with the same target. Cannot unpatch.
219 return ERROR_INVALID_FUNCTION;
220
221 return PatchMem(&(iat_thunk->u1.Function), &original_function,
222 sizeof(original_function));
223 }
224
225 } // namespace
226
227 namespace elf_hook {
228
229 //------------------------------------------------------------------------------
230 // System Service hooking support
231 //------------------------------------------------------------------------------
232
233 sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) {
234 // Create a thunk via the appropriate ServiceResolver instance.
235 sandbox::ServiceResolverThunk* thunk = nullptr;
236
237 // No hooking on unsupported OS versions.
238 if (!::IsWindows7OrGreater())
239 return thunk;
240
241 // Pseudo-handle, no need to close.
242 HANDLE current_process = ::GetCurrentProcess();
243
244 #if defined(_WIN64)
245 // ServiceResolverThunk can handle all the formats in 64-bit (instead only
246 // handling one like it does in 32-bit versions).
247 thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
248 #else
249 if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) {
250 if (::IsWindows10OrGreater())
251 thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed);
252 else if (::IsWindows8OrGreater())
253 thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed);
254 else
255 thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed);
256 } else if (::IsWindows8OrGreater()) {
257 thunk = new sandbox::Win8ResolverThunk(current_process, relaxed);
258 } else {
259 thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
260 }
261 #endif
262
263 return thunk;
264 }
265
266 //------------------------------------------------------------------------------
267 // Import Address Table hooking support
268 //------------------------------------------------------------------------------
269
270 IATHook::IATHook()
271 : intercept_function_(nullptr),
272 original_function_(nullptr),
273 iat_thunk_(nullptr) {}
274
275 IATHook::~IATHook() {
276 if (intercept_function_ != nullptr) {
277 if (Unhook() != NO_ERROR) {
278 #ifdef _DEBUG
279 assert(false);
280 #endif // _DEBUG
281 }
282 }
283 }
284
285 DWORD IATHook::Hook(HMODULE module,
286 const char* imported_from_module,
287 const char* function_name,
288 void* new_function) {
289 if ((module == 0 || module == INVALID_HANDLE_VALUE) ||
290 imported_from_module == nullptr || function_name == nullptr ||
291 new_function == nullptr)
292 return ERROR_INVALID_PARAMETER;
293
294 // Only hook once per object, to ensure unhook.
295 if (intercept_function_ != nullptr || original_function_ != nullptr ||
296 iat_thunk_ != nullptr)
297 return ERROR_SHARING_VIOLATION;
298
299 DWORD winerror = ApplyIATHook(module, imported_from_module, function_name,
300 new_function, &original_function_, &iat_thunk_);
301 if (winerror == NO_ERROR)
302 intercept_function_ = new_function;
303
304 return winerror;
305 }
306
307 DWORD IATHook::Unhook() {
308 if (intercept_function_ == nullptr || original_function_ == nullptr ||
309 iat_thunk_ == nullptr)
310 return ERROR_INVALID_PARAMETER;
311
312 DWORD winerror =
313 RemoveIATHook(intercept_function_, original_function_, iat_thunk_);
314
315 intercept_function_ = nullptr;
316 original_function_ = nullptr;
317 iat_thunk_ = nullptr;
318
319 return winerror;
320 }
321
322 } // namespace elf_hook
OLDNEW
« no previous file with comments | « chrome_elf/hook_util/hook_util.h ('k') | chrome_elf/hook_util/test/hook_util_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698