1 // Copyright (c) 2010 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/iat_patch.h"
6 #include "base/logging.h"
10 struct InterceptFunctionInformation
{
11 bool finished_operation
;
12 const char* imported_from_module
;
13 const char* function_name
;
16 IMAGE_THUNK_DATA
** iat_thunk
;
20 static void* GetIATFunction(IMAGE_THUNK_DATA
* iat_thunk
) {
21 if (NULL
== iat_thunk
) {
26 // Works around the 64 bit portability warning:
27 // The Function member inside IMAGE_THUNK_DATA is really a pointer
28 // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32
29 // or IMAGE_THUNK_DATA64 for correct pointer size.
31 IMAGE_THUNK_DATA thunk
;
35 iat_function
.thunk
= *iat_thunk
;
36 return iat_function
.pointer
;
39 static bool InterceptEnumCallback(const PEImage
&image
, const char* module
,
40 DWORD ordinal
, const char* name
, DWORD hint
,
41 IMAGE_THUNK_DATA
* iat
, void* cookie
) {
42 InterceptFunctionInformation
* intercept_information
=
43 reinterpret_cast<InterceptFunctionInformation
*>(cookie
);
45 if (NULL
== intercept_information
) {
52 if ((0 == lstrcmpiA(module
, intercept_information
->imported_from_module
)) &&
54 (0 == lstrcmpiA(name
, intercept_information
->function_name
))) {
55 // Save the old pointer.
56 if (NULL
!= intercept_information
->old_function
) {
57 *(intercept_information
->old_function
) = GetIATFunction(iat
);
60 if (NULL
!= intercept_information
->iat_thunk
) {
61 *(intercept_information
->iat_thunk
) = iat
;
65 COMPILE_ASSERT(sizeof(iat
->u1
.Function
) ==
66 sizeof(intercept_information
->new_function
), unknown_IAT_thunk_format
);
68 // Patch the function.
69 intercept_information
->return_code
=
70 ModifyCode(&(iat
->u1
.Function
),
71 &(intercept_information
->new_function
),
72 sizeof(intercept_information
->new_function
));
74 // Terminate further enumeration.
75 intercept_information
->finished_operation
= true;
82 DWORD
InterceptImportedFunction(HMODULE module_handle
,
83 const char* imported_from_module
,
84 const char* function_name
, void* new_function
,
86 IMAGE_THUNK_DATA
** iat_thunk
) {
87 if ((NULL
== module_handle
) || (NULL
== imported_from_module
) ||
88 (NULL
== function_name
) || (NULL
== new_function
)) {
90 return ERROR_INVALID_PARAMETER
;
93 PEImage
target_image(module_handle
);
94 if (!target_image
.VerifyMagic()) {
96 return ERROR_INVALID_PARAMETER
;
99 InterceptFunctionInformation intercept_information
= {
101 imported_from_module
,
108 // First go through the IAT. If we don't find the import we are looking
109 // for in IAT, search delay import table.
110 target_image
.EnumAllImports(InterceptEnumCallback
, &intercept_information
);
111 if (!intercept_information
.finished_operation
) {
112 target_image
.EnumAllDelayImports(InterceptEnumCallback
,
113 &intercept_information
);
116 return intercept_information
.return_code
;
119 DWORD
RestoreImportedFunction(void* intercept_function
,
120 void* original_function
,
121 IMAGE_THUNK_DATA
* iat_thunk
) {
122 if ((NULL
== intercept_function
) || (NULL
== original_function
) ||
123 (NULL
== iat_thunk
)) {
125 return ERROR_INVALID_PARAMETER
;
128 if (GetIATFunction(iat_thunk
) != intercept_function
) {
129 // Check if someone else has intercepted on top of us.
130 // We cannot unpatch in this case, just raise a red flag.
132 return ERROR_INVALID_FUNCTION
;
135 return ModifyCode(&(iat_thunk
->u1
.Function
),
137 sizeof(original_function
));
140 DWORD
ModifyCode(void* old_code
, void* new_code
, int length
) {
141 if ((NULL
== old_code
) || (NULL
== new_code
) || (0 == length
)) {
143 return ERROR_INVALID_PARAMETER
;
146 // Change the page protection so that we can write.
147 DWORD error
= NO_ERROR
;
148 DWORD old_page_protection
= 0;
149 if (VirtualProtect(old_code
,
152 &old_page_protection
)) {
155 CopyMemory(old_code
, new_code
, length
);
157 // Restore the old page protection.
158 error
= ERROR_SUCCESS
;
159 VirtualProtect(old_code
,
162 &old_page_protection
);
164 error
= GetLastError();
171 IATPatchFunction::IATPatchFunction()
172 : module_handle_(NULL
),
173 original_function_(NULL
),
175 intercept_function_(NULL
) {
178 IATPatchFunction::~IATPatchFunction() {
179 if (NULL
!= intercept_function_
) {
180 DWORD error
= Unpatch();
181 DCHECK(error
== NO_ERROR
);
185 DWORD
IATPatchFunction::Patch(const wchar_t* module
,
186 const char* imported_from_module
,
187 const char* function_name
,
188 void* new_function
) {
189 DCHECK_EQ(static_cast<void*>(NULL
), original_function_
);
190 DCHECK_EQ(static_cast<IMAGE_THUNK_DATA
*>(NULL
), iat_thunk_
);
191 DCHECK_EQ(static_cast<void*>(NULL
), intercept_function_
);
193 HMODULE module_handle
= LoadLibraryW(module
);
195 if (module_handle
== NULL
) {
197 return GetLastError();
200 DWORD error
= InterceptImportedFunction(module_handle
,
201 imported_from_module
,
207 if (NO_ERROR
== error
) {
208 DCHECK_NE(original_function_
, intercept_function_
);
209 module_handle_
= module_handle
;
210 intercept_function_
= new_function
;
212 FreeLibrary(module_handle
);
218 DWORD
IATPatchFunction::Unpatch() {
219 DWORD error
= RestoreImportedFunction(intercept_function_
,
222 DCHECK(NO_ERROR
== error
);
224 // Hands off the intercept if we fail to unpatch.
225 // If IATPatchFunction::Unpatch fails during RestoreImportedFunction
226 // it means that we cannot safely unpatch the import address table
227 // patch. In this case its better to be hands off the intercept as
228 // trying to unpatch again in the destructor of IATPatchFunction is
229 // not going to be any safer
231 FreeLibrary(module_handle_
);
232 module_handle_
= NULL
;
233 intercept_function_
= NULL
;
234 original_function_
= NULL
;
240 } // namespace iat_patch