OLD | NEW |
| (Empty) |
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/src/interception_agent.h" | |
9 | |
10 #include "sandbox/src/interception_internal.h" | |
11 #include "sandbox/src/interceptors.h" | |
12 #include "sandbox/src/eat_resolver.h" | |
13 #include "sandbox/src/sidestep_resolver.h" | |
14 #include "sandbox/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 | |
OLD | NEW |