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

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: Code review fixes, part 1. Created 4 years, 4 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 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 // Applies an import-address-table hook. Returns a system winerror.h code.
170 // Call RemoveIATHook() with |new_function|, |old_function| and |iat_thunk|
171 // to remove the hook.
172 DWORD ApplyIATHook(HMODULE module_handle,
173 const char* imported_from_module,
174 const char* function_name,
175 void* new_function,
176 void** old_function,
177 IMAGE_THUNK_DATA** iat_thunk) {
178 base::win::PEImage target_image(module_handle);
179 if (!target_image.VerifyMagic())
180 return ERROR_INVALID_PARAMETER;
181
182 IATHookFunctionInfo hook_info = {false,
183 imported_from_module,
184 function_name,
185 new_function,
186 old_function,
187 iat_thunk,
188 ERROR_GEN_FAILURE};
189
190 // First go through the IAT. If we don't find the import we are looking
191 // for in IAT, search delay import table.
192 target_image.EnumAllImports(IATFindHookFuncCallback, &hook_info);
193 if (!hook_info.finished_operation) {
194 target_image.EnumAllDelayImports(IATFindHookFuncCallback, &hook_info);
195 }
196
197 return hook_info.return_code;
198 }
199
200 // Removes an import-address-table hook. Returns a system winerror.h code.
201 DWORD RemoveIATHook(void* intercept_function,
202 void* original_function,
203 IMAGE_THUNK_DATA* iat_thunk) {
204 if (GetIATFunctionPtr(iat_thunk) != intercept_function) {
205 // Someone else has messed with the same target. Cannot unpatch.
206 #ifdef _DEBUG
207 assert(false);
208 #endif // _DEBUG
209 return ERROR_INVALID_FUNCTION;
210 }
211
212 return PatchMem(&(iat_thunk->u1.Function), &original_function,
213 sizeof(original_function));
214 }
215
216 } // namespace
217
218 namespace elf_hook {
219
220 //------------------------------------------------------------------------------
221 // System Service hooking support
222 //------------------------------------------------------------------------------
223
224 sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) {
225 // Create a thunk via the appropriate ServiceResolver instance.
226 sandbox::ServiceResolverThunk* thunk = nullptr;
227
228 // No hooking on unsupported OS versions.
229 if (!::IsWindows7OrGreater())
230 return thunk;
231
232 // Pseudo-handle, no need to close.
233 HANDLE current_process = ::GetCurrentProcess();
234
235 #if defined(_WIN64)
236 // ServiceResolverThunk can handle all the formats in 64-bit (instead only
237 // handling one like it does in 32-bit versions).
238 thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
239 #else
240 if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) {
241 if (::IsWindows10OrGreater())
242 thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed);
243 else if (::IsWindows8OrGreater())
244 thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed);
245 else
246 thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed);
247 } else if (::IsWindows8OrGreater()) {
248 thunk = new sandbox::Win8ResolverThunk(current_process, relaxed);
249 } else {
250 thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
251 }
252 #endif
253
254 return thunk;
255 }
256
257 //------------------------------------------------------------------------------
258 // Import Address Table hooking support
259 //------------------------------------------------------------------------------
260
261 IATHook::IATHook()
262 : intercept_function_(nullptr),
263 original_function_(nullptr),
264 iat_thunk_(nullptr) {}
265
266 IATHook::~IATHook() {
267 if (intercept_function_ != nullptr) {
268 if (Unhook() != NO_ERROR) {
269 #ifdef _DEBUG
270 assert(false);
271 #endif // _DEBUG
272 }
273 }
274 }
275
276 DWORD IATHook::Hook(HMODULE module,
277 const char* imported_from_module,
278 const char* function_name,
279 void* new_function) {
robertshield 2016/08/03 04:52:45 It looks like this method shouldn't be called twic
penny 2016/08/04 18:55:35 Done.
280 if ((module == 0 || module == INVALID_HANDLE_VALUE) ||
281 imported_from_module == nullptr || function_name == nullptr ||
282 new_function == nullptr)
283 return ERROR_INVALID_PARAMETER;
284
285 DWORD winerror = ApplyIATHook(module, imported_from_module, function_name,
286 new_function, &original_function_, &iat_thunk_);
287 if (winerror == NO_ERROR) {
288 intercept_function_ = new_function;
289 #ifdef _DEBUG
290 if (original_function_ == new_function)
robertshield 2016/08/03 04:52:45 If this is an error condition, please consider add
penny 2016/08/04 18:55:35 I've added a check in IATFindHookFuncCallback() (w
291 assert(false);
292 #endif //_DEBUG
293 }
294
295 return winerror;
296 }
297
298 DWORD IATHook::Unhook() {
299 DWORD winerror =
300 RemoveIATHook(intercept_function_, original_function_, iat_thunk_);
301 #ifdef _DEBUG
302 if (winerror != NO_ERROR)
303 assert(false);
304 #endif //_DEBUG
305
306 intercept_function_ = nullptr;
307 original_function_ = nullptr;
308 iat_thunk_ = nullptr;
309
310 return winerror;
311 }
312
313 } // namespace elf_hook
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698