Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / base / win / iat_patch_function.cc
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.
4
5 #include "base/win/iat_patch_function.h"
6
7 #include "base/logging.h"
8 #include "base/win/pe_image.h"
9
10 namespace base {
11 namespace win {
12
13 namespace {
14
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;
23 };
24
25 void* GetIATFunction(IMAGE_THUNK_DATA* iat_thunk) {
26   if (NULL == iat_thunk) {
27     NOTREACHED();
28     return NULL;
29   }
30
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;
39
40   iat_function.thunk = *iat_thunk;
41   return iat_function.pointer;
42 }
43
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);
49
50   if (NULL == intercept_information) {
51     NOTREACHED();
52     return false;
53   }
54
55   DCHECK(module);
56
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);
63     }
64
65     if (NULL != intercept_information->iat_thunk) {
66       *(intercept_information->iat_thunk) = iat;
67     }
68
69     // portability check
70     COMPILE_ASSERT(sizeof(iat->u1.Function) ==
71       sizeof(intercept_information->new_function), unknown_IAT_thunk_format);
72
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));
78
79     // Terminate further enumeration.
80     intercept_information->finished_operation = true;
81     return false;
82   }
83
84   return true;
85 }
86
87 // Helper to intercept a function in an import table of a specific
88 // module.
89 //
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.
98 //
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;
110   }
111
112   base::win::PEImage target_image(module_handle);
113   if (!target_image.VerifyMagic()) {
114     NOTREACHED();
115     return ERROR_INVALID_PARAMETER;
116   }
117
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};
126
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);
133   }
134
135   return intercept_information.return_code;
136 }
137
138 // Restore intercepted IAT entry with the original function.
139 //
140 // Arguments:
141 // intercept_function     Interceptor function
142 // original_function      Receives the original function pointer
143 //
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;
153   }
154
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;
160   }
161
162   return ModifyCode(&(iat_thunk->u1.Function),
163                     &original_function,
164                     sizeof(original_function));
165 }
166
167 }  // namespace
168
169 // Change the page protection (of code pages) to writable and copy
170 // the data at the specified location
171 //
172 // Arguments:
173 // old_code               Target location to copy
174 // new_code               Source
175 // length                 Number of bytes to copy
176 //
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;
182   }
183
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;
188
189   if (!VirtualQuery(old_code, &memory_info, sizeof(memory_info))) {
190     error = GetLastError();
191     return error;
192   }
193
194   DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
195                         PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
196                         memory_info.Protect;
197
198   if (VirtualProtect(old_code,
199                      length,
200                      is_executable ? PAGE_EXECUTE_READWRITE :
201                                      PAGE_READWRITE,
202                      &old_page_protection)) {
203
204     // Write the data.
205     CopyMemory(old_code, new_code, length);
206
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();
215   }
216
217   return error;
218 }
219
220 IATPatchFunction::IATPatchFunction()
221     : module_handle_(NULL),
222       original_function_(NULL),
223       iat_thunk_(NULL),
224       intercept_function_(NULL) {
225 }
226
227 IATPatchFunction::~IATPatchFunction() {
228   if (NULL != intercept_function_) {
229     DWORD error = Unpatch();
230     DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error);
231   }
232 }
233
234 DWORD IATPatchFunction::Patch(const wchar_t* module,
235                               const char* imported_from_module,
236                               const char* function_name,
237                               void* new_function) {
238   DCHECK_EQ(static_cast<void*>(NULL), original_function_);
239   DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_);
240   DCHECK_EQ(static_cast<void*>(NULL), intercept_function_);
241
242   HMODULE module_handle = LoadLibraryW(module);
243
244   if (module_handle == NULL) {
245     NOTREACHED();
246     return GetLastError();
247   }
248
249   DWORD error = InterceptImportedFunction(module_handle,
250                                           imported_from_module,
251                                           function_name,
252                                           new_function,
253                                           &original_function_,
254                                           &iat_thunk_);
255
256   if (NO_ERROR == error) {
257     DCHECK_NE(original_function_, intercept_function_);
258     module_handle_ = module_handle;
259     intercept_function_ = new_function;
260   } else {
261     FreeLibrary(module_handle);
262   }
263
264   return error;
265 }
266
267 DWORD IATPatchFunction::Unpatch() {
268   DWORD error = RestoreImportedFunction(intercept_function_,
269                                         original_function_,
270                                         iat_thunk_);
271   DCHECK_EQ(static_cast<DWORD>(NO_ERROR), error);
272
273   // Hands off the intercept if we fail to unpatch.
274   // If IATPatchFunction::Unpatch fails during RestoreImportedFunction
275   // it means that we cannot safely unpatch the import address table
276   // patch. In this case its better to be hands off the intercept as
277   // trying to unpatch again in the destructor of IATPatchFunction is
278   // not going to be any safer
279   if (module_handle_)
280     FreeLibrary(module_handle_);
281   module_handle_ = NULL;
282   intercept_function_ = NULL;
283   original_function_ = NULL;
284   iat_thunk_ = NULL;
285
286   return error;
287 }
288
289 void* IATPatchFunction::original_function() const {
290   DCHECK(is_patched());
291   return original_function_;
292 }
293
294 }  // namespace win
295 }  // namespace base