|
1 // Copyright (c) 2006-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. |
|
4 |
|
5 // For information about interceptions as a whole see |
|
6 // http://dev.chromium.org/developers/design-documents/sandbox . |
|
7 |
|
8 #include "sandbox/win/src/interception_agent.h" |
|
9 |
|
10 #include "sandbox/win/src/interception_internal.h" |
|
11 #include "sandbox/win/src/interceptors.h" |
|
12 #include "sandbox/win/src/eat_resolver.h" |
|
13 #include "sandbox/win/src/sidestep_resolver.h" |
|
14 #include "sandbox/win/src/sandbox_nt_util.h" |
|
15 |
|
16 namespace { |
|
17 |
|
18 // Returns true if target lies between base and base + range. |
|
19 bool IsWithinRange(const void* base, size_t range, const void* target) { |
|
20 const char* end = reinterpret_cast<const char*>(base) + range; |
|
21 return reinterpret_cast<const char*>(target) < end; |
|
22 } |
|
23 |
|
24 } // namespace |
|
25 |
|
26 namespace sandbox { |
|
27 |
|
28 // This is the list of all imported symbols from ntdll.dll. |
|
29 SANDBOX_INTERCEPT NtExports g_nt; |
|
30 |
|
31 // The list of intercepted functions back-pointers. |
|
32 SANDBOX_INTERCEPT OriginalFunctions g_originals; |
|
33 |
|
34 // Memory buffer mapped from the parent, with the list of interceptions. |
|
35 SANDBOX_INTERCEPT SharedMemory* g_interceptions = NULL; |
|
36 |
|
37 InterceptionAgent* InterceptionAgent::GetInterceptionAgent() { |
|
38 static InterceptionAgent* s_singleton = NULL; |
|
39 if (!s_singleton) { |
|
40 if (!g_interceptions) |
|
41 return NULL; |
|
42 |
|
43 size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*); |
|
44 s_singleton = reinterpret_cast<InterceptionAgent*>( |
|
45 new(NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]); |
|
46 |
|
47 bool success = s_singleton->Init(g_interceptions); |
|
48 if (!success) { |
|
49 operator delete(s_singleton, NT_ALLOC); |
|
50 s_singleton = NULL; |
|
51 } |
|
52 } |
|
53 return s_singleton; |
|
54 } |
|
55 |
|
56 bool InterceptionAgent::Init(SharedMemory* shared_memory) { |
|
57 interceptions_ = shared_memory; |
|
58 for (int i = 0 ; i < shared_memory->num_intercepted_dlls; i++) |
|
59 dlls_[i] = NULL; |
|
60 return true; |
|
61 } |
|
62 |
|
63 bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path, |
|
64 const UNICODE_STRING* name, |
|
65 const DllPatchInfo* dll_info) { |
|
66 UNICODE_STRING current_name; |
|
67 current_name.Length = static_cast<USHORT>(g_nt.wcslen(dll_info->dll_name) * |
|
68 sizeof(wchar_t)); |
|
69 current_name.MaximumLength = current_name.Length; |
|
70 current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name); |
|
71 |
|
72 BOOLEAN case_insensitive = TRUE; |
|
73 if (full_path && |
|
74 !g_nt.RtlCompareUnicodeString(¤t_name, full_path, case_insensitive)) |
|
75 return true; |
|
76 |
|
77 if (name && |
|
78 !g_nt.RtlCompareUnicodeString(¤t_name, name, case_insensitive)) |
|
79 return true; |
|
80 |
|
81 return false; |
|
82 } |
|
83 |
|
84 bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path, |
|
85 const UNICODE_STRING* name, |
|
86 void* base_address) { |
|
87 DllPatchInfo* dll_info = interceptions_->dll_list; |
|
88 int i = 0; |
|
89 for (; i < interceptions_->num_intercepted_dlls; i++) { |
|
90 if (DllMatch(full_path, name, dll_info)) |
|
91 break; |
|
92 |
|
93 dll_info = reinterpret_cast<DllPatchInfo*>( |
|
94 reinterpret_cast<char*>(dll_info) + dll_info->record_bytes); |
|
95 } |
|
96 |
|
97 // Return now if the dll is not in our list of interest. |
|
98 if (i == interceptions_->num_intercepted_dlls) |
|
99 return true; |
|
100 |
|
101 // The dll must be unloaded. |
|
102 if (dll_info->unload_module) |
|
103 return false; |
|
104 |
|
105 // Purify causes this condition to trigger. |
|
106 if (dlls_[i]) |
|
107 return true; |
|
108 |
|
109 size_t buffer_bytes = offsetof(DllInterceptionData, thunks) + |
|
110 dll_info->num_functions * sizeof(ThunkData); |
|
111 dlls_[i] = reinterpret_cast<DllInterceptionData*>( |
|
112 new(NT_PAGE, base_address) char[buffer_bytes]); |
|
113 |
|
114 DCHECK_NT(dlls_[i]); |
|
115 if (!dlls_[i]) |
|
116 return true; |
|
117 |
|
118 dlls_[i]->data_bytes = buffer_bytes; |
|
119 dlls_[i]->num_thunks = 0; |
|
120 dlls_[i]->base = base_address; |
|
121 dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks); |
|
122 |
|
123 VERIFY(PatchDll(dll_info, dlls_[i])); |
|
124 |
|
125 ULONG old_protect; |
|
126 SIZE_T real_size = buffer_bytes; |
|
127 void* to_protect = dlls_[i]; |
|
128 VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect, |
|
129 &real_size, PAGE_EXECUTE_READ, |
|
130 &old_protect)); |
|
131 return true; |
|
132 } |
|
133 |
|
134 void InterceptionAgent::OnDllUnload(void* base_address) { |
|
135 for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) { |
|
136 if (dlls_[i] && dlls_[i]->base == base_address) { |
|
137 operator delete(dlls_[i], NT_PAGE); |
|
138 dlls_[i] = NULL; |
|
139 break; |
|
140 } |
|
141 } |
|
142 } |
|
143 |
|
144 // TODO(rvargas): We have to deal with prebinded dlls. I see two options: change |
|
145 // the timestamp of the patched dll, or modify the info on the prebinded dll. |
|
146 // the first approach messes matching of debug symbols, the second one is more |
|
147 // complicated. |
|
148 bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info, |
|
149 DllInterceptionData* thunks) { |
|
150 DCHECK_NT(NULL != thunks); |
|
151 DCHECK_NT(NULL != dll_info); |
|
152 |
|
153 const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>( |
|
154 reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions); |
|
155 |
|
156 for (int i = 0; i < dll_info->num_functions; i++) { |
|
157 if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) { |
|
158 NOTREACHED_NT(); |
|
159 return false; |
|
160 } |
|
161 |
|
162 ResolverThunk* resolver = GetResolver(function->type); |
|
163 if (!resolver) |
|
164 return false; |
|
165 |
|
166 const char* interceptor = function->function + |
|
167 g_nt.strlen(function->function) + 1; |
|
168 |
|
169 if (!IsWithinRange(function, function->record_bytes, interceptor) || |
|
170 !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) { |
|
171 NOTREACHED_NT(); |
|
172 return false; |
|
173 } |
|
174 |
|
175 NTSTATUS ret = resolver->Setup(thunks->base, |
|
176 interceptions_->interceptor_base, |
|
177 function->function, |
|
178 interceptor, |
|
179 function->interceptor_address, |
|
180 &thunks->thunks[i], |
|
181 sizeof(ThunkData), |
|
182 NULL); |
|
183 if (!NT_SUCCESS(ret)) { |
|
184 NOTREACHED_NT(); |
|
185 return false; |
|
186 } |
|
187 |
|
188 DCHECK_NT(!g_originals[function->id]); |
|
189 g_originals[function->id] = &thunks->thunks[i]; |
|
190 |
|
191 thunks->num_thunks++; |
|
192 thunks->used_bytes += sizeof(ThunkData); |
|
193 |
|
194 function = reinterpret_cast<const FunctionInfo*>( |
|
195 reinterpret_cast<const char*>(function) + function->record_bytes); |
|
196 } |
|
197 |
|
198 return true; |
|
199 } |
|
200 |
|
201 // This method is called from within the loader lock |
|
202 ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) { |
|
203 static EatResolverThunk* eat_resolver = NULL; |
|
204 static SidestepResolverThunk* sidestep_resolver = NULL; |
|
205 static SmartSidestepResolverThunk* smart_sidestep_resolver = NULL; |
|
206 |
|
207 if (!eat_resolver) |
|
208 eat_resolver = new(NT_ALLOC) EatResolverThunk; |
|
209 |
|
210 #if !defined(_WIN64) |
|
211 // Sidestep is not supported for x64. |
|
212 if (!sidestep_resolver) |
|
213 sidestep_resolver = new(NT_ALLOC) SidestepResolverThunk; |
|
214 |
|
215 if (!smart_sidestep_resolver) |
|
216 smart_sidestep_resolver = new(NT_ALLOC) SmartSidestepResolverThunk; |
|
217 #endif |
|
218 |
|
219 switch (type) { |
|
220 case INTERCEPTION_EAT: |
|
221 return eat_resolver; |
|
222 case INTERCEPTION_SIDESTEP: |
|
223 return sidestep_resolver; |
|
224 case INTERCEPTION_SMART_SIDESTEP: |
|
225 return smart_sidestep_resolver; |
|
226 default: |
|
227 NOTREACHED_NT(); |
|
228 } |
|
229 |
|
230 return NULL; |
|
231 } |
|
232 |
|
233 } // namespace sandbox |