Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / chrome_elf / blacklist / blacklist.cc
1 // Copyright 2013 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 "chrome_elf/blacklist/blacklist.h"
6
7 #include <assert.h>
8 #include <string.h>
9
10 #include "base/basictypes.h"
11 #include "chrome_elf/blacklist/blacklist_interceptions.h"
12 #include "chrome_elf/chrome_elf_constants.h"
13 #include "chrome_elf/chrome_elf_util.h"
14 #include "chrome_elf/thunk_getter.h"
15 #include "sandbox/win/src/interception_internal.h"
16 #include "sandbox/win/src/internal_types.h"
17 #include "sandbox/win/src/service_resolver.h"
18
19 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
20 extern "C" IMAGE_DOS_HEADER __ImageBase;
21
22 namespace blacklist{
23
24 const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = {
25   L"datamngr.dll",                      // Unknown (suspected adware).
26   L"hk.dll",                            // Unknown (keystroke logger).
27   L"libsvn_tsvn32.dll",                 // TortoiseSVN.
28   L"lmrn.dll",                          // Unknown.
29   // Keep this null pointer here to mark the end of the list.
30   NULL,
31 };
32
33 bool g_blocked_dlls[kTroublesomeDllsMaxCount] = {};
34 int g_num_blocked_dlls = 0;
35
36 }  // namespace blacklist
37
38 // Allocate storage for thunks in a page of this module to save on doing
39 // an extra allocation at run time.
40 #pragma section(".crthunk",read,execute)
41 __declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage;
42
43 namespace {
44
45 // Record if the blacklist was successfully initialized so processes can easily
46 // determine if the blacklist is enabled for them.
47 bool g_blacklist_initialized = false;
48
49 // Record that the thunk setup completed succesfully and close the registry
50 // key handle since it is no longer needed.
51 void RecordSuccessfulThunkSetup(HKEY* key) {
52   if (key != NULL) {
53     DWORD blacklist_state = blacklist::BLACKLIST_SETUP_RUNNING;
54     ::RegSetValueEx(*key,
55                     blacklist::kBeaconState,
56                     0,
57                     REG_DWORD,
58                     reinterpret_cast<LPBYTE>(&blacklist_state),
59                     sizeof(blacklist_state));
60     ::RegCloseKey(*key);
61     key = NULL;
62   }
63 }
64
65 }  // namespace
66
67 namespace blacklist {
68
69 #if defined(_WIN64)
70   // Allocate storage for the pointer to the old NtMapViewOfSectionFunction.
71 #pragma section(".oldntmap",write,read)
72   __declspec(allocate(".oldntmap"))
73     NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL;
74 #endif
75
76 bool LeaveSetupBeacon() {
77   HKEY key = NULL;
78   DWORD disposition = 0;
79   LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
80                                  kRegistryBeaconPath,
81                                  0,
82                                  NULL,
83                                  REG_OPTION_NON_VOLATILE,
84                                  KEY_QUERY_VALUE | KEY_SET_VALUE,
85                                  NULL,
86                                  &key,
87                                  &disposition);
88   if (result != ERROR_SUCCESS)
89     return false;
90
91   // Retrieve the current blacklist state.
92   DWORD blacklist_state = BLACKLIST_DISABLED;
93   DWORD blacklist_state_size = sizeof(blacklist_state);
94   DWORD type = 0;
95   result = ::RegQueryValueEx(key,
96                              kBeaconState,
97                              0,
98                              &type,
99                              reinterpret_cast<LPBYTE>(&blacklist_state),
100                              &blacklist_state_size);
101
102   if (blacklist_state != BLACKLIST_ENABLED ||
103       result != ERROR_SUCCESS || type != REG_DWORD) {
104     ::RegCloseKey(key);
105     return false;
106   }
107
108   // Mark the blacklist setup code as running so if it crashes the blacklist
109   // won't be enabled for the next run.
110   blacklist_state = BLACKLIST_SETUP_RUNNING;
111   result = ::RegSetValueEx(key,
112                            kBeaconState,
113                            0,
114                            REG_DWORD,
115                            reinterpret_cast<LPBYTE>(&blacklist_state),
116                            sizeof(blacklist_state));
117   ::RegCloseKey(key);
118
119   return (result == ERROR_SUCCESS);
120 }
121
122 bool ResetBeacon() {
123   HKEY key = NULL;
124   DWORD disposition = 0;
125   LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
126                                  kRegistryBeaconPath,
127                                  0,
128                                  NULL,
129                                  REG_OPTION_NON_VOLATILE,
130                                  KEY_QUERY_VALUE | KEY_SET_VALUE,
131                                  NULL,
132                                  &key,
133                                  &disposition);
134   if (result != ERROR_SUCCESS)
135     return false;
136
137   DWORD blacklist_state = BLACKLIST_ENABLED;
138   result = ::RegSetValueEx(key,
139                            kBeaconState,
140                            0,
141                            REG_DWORD,
142                            reinterpret_cast<LPBYTE>(&blacklist_state),
143                            sizeof(blacklist_state));
144   ::RegCloseKey(key);
145
146   return (result == ERROR_SUCCESS);
147 }
148
149 int BlacklistSize() {
150   int size = -1;
151   while (blacklist::g_troublesome_dlls[++size] != NULL) {}
152
153   return size;
154 }
155
156 bool IsBlacklistInitialized() {
157   return g_blacklist_initialized;
158 }
159
160 bool AddDllToBlacklist(const wchar_t* dll_name) {
161   int blacklist_size = BlacklistSize();
162   // We need to leave one space at the end for the null pointer.
163   if (blacklist_size + 1 >= kTroublesomeDllsMaxCount)
164     return false;
165   for (int i = 0; i < blacklist_size; ++i) {
166     if (!_wcsicmp(g_troublesome_dlls[i], dll_name))
167       return true;
168   }
169
170   // Copy string to blacklist.
171   wchar_t* str_buffer = new wchar_t[wcslen(dll_name) + 1];
172   wcscpy(str_buffer, dll_name);
173
174   g_troublesome_dlls[blacklist_size] = str_buffer;
175   g_blocked_dlls[blacklist_size] = false;
176   return true;
177 }
178
179 bool RemoveDllFromBlacklist(const wchar_t* dll_name) {
180   int blacklist_size = BlacklistSize();
181   for (int i = 0; i < blacklist_size; ++i) {
182     if (!_wcsicmp(g_troublesome_dlls[i], dll_name)) {
183       // Found the thing to remove. Delete it then replace it with the last
184       // element.
185       delete[] g_troublesome_dlls[i];
186       g_troublesome_dlls[i] = g_troublesome_dlls[blacklist_size - 1];
187       g_troublesome_dlls[blacklist_size - 1] = NULL;
188
189       // Also update the stats recording if we have blocked this dll or not.
190       if (g_blocked_dlls[i])
191         --g_num_blocked_dlls;
192       g_blocked_dlls[i] = g_blocked_dlls[blacklist_size - 1];
193       return true;
194     }
195   }
196   return false;
197 }
198
199 // TODO(csharp): Maybe store these values in the registry so we can
200 // still report them if Chrome crashes early.
201 void SuccessfullyBlocked(const wchar_t** blocked_dlls, int* size) {
202   if (size == NULL)
203     return;
204
205   // If the array isn't valid or big enough, just report the size it needs to
206   // be and return.
207   if (blocked_dlls == NULL && *size < g_num_blocked_dlls) {
208     *size = g_num_blocked_dlls;
209     return;
210   }
211
212   *size = g_num_blocked_dlls;
213
214   int strings_to_fill = 0;
215   for (int i = 0; strings_to_fill < g_num_blocked_dlls && g_troublesome_dlls[i];
216        ++i) {
217     if (g_blocked_dlls[i]) {
218       blocked_dlls[strings_to_fill] = g_troublesome_dlls[i];
219       ++strings_to_fill;
220     }
221   }
222 }
223
224 void BlockedDll(size_t blocked_index) {
225   assert(blocked_index < kTroublesomeDllsMaxCount);
226
227   if (!g_blocked_dlls[blocked_index] &&
228       blocked_index < kTroublesomeDllsMaxCount) {
229     ++g_num_blocked_dlls;
230     g_blocked_dlls[blocked_index] = true;
231   }
232 }
233
234 bool Initialize(bool force) {
235   // Check to see that we found the functions we need in ntdll.
236   if (!InitializeInterceptImports())
237     return false;
238
239   // Check to see if this is a non-browser process, abort if so.
240   if (IsNonBrowserProcess())
241     return false;
242
243   // Check to see if a beacon is present, abort if so.
244   if (!force && !LeaveSetupBeacon())
245     return false;
246
247   // It is possible for other dlls to have already patched code by now and
248   // attempting to patch their code might result in crashes.
249   const bool kRelaxed = false;
250
251   // Create a thunk via the appropriate ServiceResolver instance.
252   sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed);
253
254   // Don't try blacklisting on unsupported OS versions.
255   if (!thunk)
256     return false;
257
258   // Record that we are starting the thunk setup code.
259   HKEY key = NULL;
260   DWORD disposition = 0;
261   LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
262                                  kRegistryBeaconPath,
263                                  0,
264                                  NULL,
265                                  REG_OPTION_NON_VOLATILE,
266                                  KEY_QUERY_VALUE | KEY_SET_VALUE,
267                                  NULL,
268                                  &key,
269                                  &disposition);
270   if (result == ERROR_SUCCESS) {
271     DWORD blacklist_state = BLACKLIST_THUNK_SETUP;
272     ::RegSetValueEx(key,
273                     kBeaconState,
274                     0,
275                     REG_DWORD,
276                     reinterpret_cast<LPBYTE>(&blacklist_state),
277                     sizeof(blacklist_state));
278   } else {
279     key = NULL;
280   }
281
282   // Record that we have initialized the blacklist.
283   g_blacklist_initialized = true;
284
285   BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage);
286
287   // Mark the thunk storage as readable and writeable, since we
288   // ready to write to it.
289   DWORD old_protect = 0;
290   if (!VirtualProtect(&g_thunk_storage,
291                       sizeof(g_thunk_storage),
292                       PAGE_EXECUTE_READWRITE,
293                       &old_protect)) {
294     RecordSuccessfulThunkSetup(&key);
295     return false;
296   }
297
298   thunk->AllowLocalPatches();
299
300   // We declare this early so it can be used in the 64-bit block below and
301   // still work on 32-bit build when referenced at the end of the function.
302   BOOL page_executable = false;
303
304   // Replace the default NtMapViewOfSection with our patched version.
305 #if defined(_WIN64)
306   NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
307                               reinterpret_cast<void*>(&__ImageBase),
308                               "NtMapViewOfSection",
309                               NULL,
310                               &blacklist::BlNtMapViewOfSection64,
311                               thunk_storage,
312                               sizeof(sandbox::ThunkData),
313                               NULL);
314
315   // Keep a pointer to the original code, we don't have enough space to
316   // add it directly to the call.
317   g_nt_map_view_of_section_func = reinterpret_cast<NtMapViewOfSectionFunction>(
318       thunk_storage);
319
320   // Ensure that the pointer to the old function can't be changed.
321  page_executable = VirtualProtect(&g_nt_map_view_of_section_func,
322                                   sizeof(g_nt_map_view_of_section_func),
323                                   PAGE_EXECUTE_READ,
324                                   &old_protect);
325 #else
326   NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
327                               reinterpret_cast<void*>(&__ImageBase),
328                               "NtMapViewOfSection",
329                               NULL,
330                               &blacklist::BlNtMapViewOfSection,
331                               thunk_storage,
332                               sizeof(sandbox::ThunkData),
333                               NULL);
334 #endif
335   delete thunk;
336
337   // Mark the thunk storage as executable and prevent any future writes to it.
338   page_executable = page_executable && VirtualProtect(&g_thunk_storage,
339                                                       sizeof(g_thunk_storage),
340                                                       PAGE_EXECUTE_READ,
341                                                       &old_protect);
342
343   RecordSuccessfulThunkSetup(&key);
344
345   return NT_SUCCESS(ret) && page_executable;
346 }
347
348 }  // namespace blacklist