Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / base / win / iat_patch_function.cc
blob31659c6c82dc872e3496cd1d85e763d28fc3c329
1 // Copyright (c) 2011 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.
5 #include "base/win/iat_patch_function.h"
7 #include "base/logging.h"
8 #include "base/win/pe_image.h"
10 namespace base {
11 namespace win {
13 namespace {
15 struct InterceptFunctionInformation {
16 bool finished_operation;
17 const char* imported_from_module;
18 const char* function_name;
19 void* new_function;
20 void** old_function;
21 IMAGE_THUNK_DATA** iat_thunk;
22 DWORD return_code;
25 void* GetIATFunction(IMAGE_THUNK_DATA* iat_thunk) {
26 if (NULL == iat_thunk) {
27 NOTREACHED();
28 return NULL;
31 // Works around the 64 bit portability warning:
32 // The Function member inside IMAGE_THUNK_DATA is really a pointer
33 // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32
34 // or IMAGE_THUNK_DATA64 for correct pointer size.
35 union FunctionThunk {
36 IMAGE_THUNK_DATA thunk;
37 void* pointer;
38 } iat_function;
40 iat_function.thunk = *iat_thunk;
41 return iat_function.pointer;
44 bool InterceptEnumCallback(const base::win::PEImage& image, const char* module,
45 DWORD ordinal, const char* name, DWORD hint,
46 IMAGE_THUNK_DATA* iat, void* cookie) {
47 InterceptFunctionInformation* intercept_information =
48 reinterpret_cast<InterceptFunctionInformation*>(cookie);
50 if (NULL == intercept_information) {
51 NOTREACHED();
52 return false;
55 DCHECK(module);
57 if ((0 == lstrcmpiA(module, intercept_information->imported_from_module)) &&
58 (NULL != name) &&
59 (0 == lstrcmpiA(name, intercept_information->function_name))) {
60 // Save the old pointer.
61 if (NULL != intercept_information->old_function) {
62 *(intercept_information->old_function) = GetIATFunction(iat);
65 if (NULL != intercept_information->iat_thunk) {
66 *(intercept_information->iat_thunk) = iat;
69 // portability check
70 COMPILE_ASSERT(sizeof(iat->u1.Function) ==
71 sizeof(intercept_information->new_function), unknown_IAT_thunk_format);
73 // Patch the function.
74 intercept_information->return_code =
75 ModifyCode(&(iat->u1.Function),
76 &(intercept_information->new_function),
77 sizeof(intercept_information->new_function));
79 // Terminate further enumeration.
80 intercept_information->finished_operation = true;
81 return false;
84 return true;
87 // Helper to intercept a function in an import table of a specific
88 // module.
90 // Arguments:
91 // module_handle Module to be intercepted
92 // imported_from_module Module that exports the symbol
93 // function_name Name of the API to be intercepted
94 // new_function Interceptor function
95 // old_function Receives the original function pointer
96 // iat_thunk Receives pointer to IAT_THUNK_DATA
97 // for the API from the import table.
99 // Returns: Returns NO_ERROR on success or Windows error code
100 // as defined in winerror.h
101 DWORD InterceptImportedFunction(HMODULE module_handle,
102 const char* imported_from_module,
103 const char* function_name, void* new_function,
104 void** old_function,
105 IMAGE_THUNK_DATA** iat_thunk) {
106 if ((NULL == module_handle) || (NULL == imported_from_module) ||
107 (NULL == function_name) || (NULL == new_function)) {
108 NOTREACHED();
109 return ERROR_INVALID_PARAMETER;
112 base::win::PEImage target_image(module_handle);
113 if (!target_image.VerifyMagic()) {
114 NOTREACHED();
115 return ERROR_INVALID_PARAMETER;
118 InterceptFunctionInformation intercept_information = {
119 false,
120 imported_from_module,
121 function_name,
122 new_function,
123 old_function,
124 iat_thunk,
125 ERROR_GEN_FAILURE};
127 // First go through the IAT. If we don't find the import we are looking
128 // for in IAT, search delay import table.
129 target_image.EnumAllImports(InterceptEnumCallback, &intercept_information);
130 if (!intercept_information.finished_operation) {
131 target_image.EnumAllDelayImports(InterceptEnumCallback,
132 &intercept_information);
135 return intercept_information.return_code;
138 // Restore intercepted IAT entry with the original function.
140 // Arguments:
141 // intercept_function Interceptor function
142 // original_function Receives the original function pointer
144 // Returns: Returns NO_ERROR on success or Windows error code
145 // as defined in winerror.h
146 DWORD RestoreImportedFunction(void* intercept_function,
147 void* original_function,
148 IMAGE_THUNK_DATA* iat_thunk) {
149 if ((NULL == intercept_function) || (NULL == original_function) ||
150 (NULL == iat_thunk)) {
151 NOTREACHED();
152 return ERROR_INVALID_PARAMETER;
155 if (GetIATFunction(iat_thunk) != intercept_function) {
156 // Check if someone else has intercepted on top of us.
157 // We cannot unpatch in this case, just raise a red flag.
158 NOTREACHED();
159 return ERROR_INVALID_FUNCTION;
162 return ModifyCode(&(iat_thunk->u1.Function),
163 &original_function,
164 sizeof(original_function));
167 } // namespace
169 // Change the page protection (of code pages) to writable and copy
170 // the data at the specified location
172 // Arguments:
173 // old_code Target location to copy
174 // new_code Source
175 // length Number of bytes to copy
177 // Returns: Windows error code (winerror.h). NO_ERROR if successful
178 DWORD ModifyCode(void* old_code, void* new_code, int length) {
179 if ((NULL == old_code) || (NULL == new_code) || (0 == length)) {
180 NOTREACHED();
181 return ERROR_INVALID_PARAMETER;
184 // Change the page protection so that we can write.
185 MEMORY_BASIC_INFORMATION memory_info;
186 DWORD error = NO_ERROR;
187 DWORD old_page_protection = 0;
189 if (!VirtualQuery(old_code, &memory_info, sizeof(memory_info))) {
190 error = GetLastError();
191 return error;
194 DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
195 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
196 memory_info.Protect;
198 if (VirtualProtect(old_code,
199 length,
200 is_executable ? PAGE_EXECUTE_READWRITE :
201 PAGE_READWRITE,
202 &old_page_protection)) {
204 // Write the data.
205 CopyMemory(old_code, new_code, length);
207 // Restore the old page protection.
208 error = ERROR_SUCCESS;
209 VirtualProtect(old_code,
210 length,
211 old_page_protection,
212 &old_page_protection);
213 } else {
214 error = GetLastError();
217 return error;
220 IATPatchFunction::IATPatchFunction()
221 : module_handle_(NULL),
222 intercept_function_(NULL),
223 original_function_(NULL),
224 iat_thunk_(NULL) {
227 IATPatchFunction::~IATPatchFunction() {
228 if (NULL != intercept_function_) {
229 DWORD error = Unpatch();
230 DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error);
234 DWORD IATPatchFunction::Patch(const wchar_t* module,
235 const char* imported_from_module,
236 const char* function_name,
237 void* new_function) {
238 HMODULE module_handle = LoadLibraryW(module);
239 if (module_handle == NULL) {
240 NOTREACHED();
241 return GetLastError();
244 DWORD error = PatchFromModule(module_handle, imported_from_module,
245 function_name, new_function);
246 if (NO_ERROR == error) {
247 module_handle_ = module_handle;
248 } else {
249 FreeLibrary(module_handle);
252 return error;
255 DWORD IATPatchFunction::PatchFromModule(HMODULE module,
256 const char* imported_from_module,
257 const char* function_name,
258 void* new_function) {
259 DCHECK_EQ(static_cast<void*>(NULL), original_function_);
260 DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_);
261 DCHECK_EQ(static_cast<void*>(NULL), intercept_function_);
262 DCHECK(module);
264 DWORD error = InterceptImportedFunction(module,
265 imported_from_module,
266 function_name,
267 new_function,
268 &original_function_,
269 &iat_thunk_);
271 if (NO_ERROR == error) {
272 DCHECK_NE(original_function_, intercept_function_);
273 intercept_function_ = new_function;
276 return error;
279 DWORD IATPatchFunction::Unpatch() {
280 DWORD error = RestoreImportedFunction(intercept_function_,
281 original_function_,
282 iat_thunk_);
283 DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error);
285 // Hands off the intercept if we fail to unpatch.
286 // If IATPatchFunction::Unpatch fails during RestoreImportedFunction
287 // it means that we cannot safely unpatch the import address table
288 // patch. In this case its better to be hands off the intercept as
289 // trying to unpatch again in the destructor of IATPatchFunction is
290 // not going to be any safer
291 if (module_handle_)
292 FreeLibrary(module_handle_);
293 module_handle_ = NULL;
294 intercept_function_ = NULL;
295 original_function_ = NULL;
296 iat_thunk_ = NULL;
298 return error;
301 void* IATPatchFunction::original_function() const {
302 DCHECK(is_patched());
303 return original_function_;
306 } // namespace win
307 } // namespace base