Upstream version 9.37.195.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 <vector>
11
12 #include "base/basictypes.h"
13 #include "chrome_elf/blacklist/blacklist_interceptions.h"
14 #include "chrome_elf/chrome_elf_constants.h"
15 #include "chrome_elf/chrome_elf_util.h"
16 #include "chrome_elf/thunk_getter.h"
17 #include "sandbox/win/src/interception_internal.h"
18 #include "sandbox/win/src/internal_types.h"
19 #include "sandbox/win/src/service_resolver.h"
20
21 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
22 extern "C" IMAGE_DOS_HEADER __ImageBase;
23
24 namespace blacklist{
25
26 // The DLLs listed here are known (or under strong suspicion) of causing crashes
27 // when they are loaded in the browser. DLLs should only be added to this list
28 // if there is nothing else Chrome can do to prevent those crashes.
29 // For more information about how this list is generated, and how to get off
30 // of it, see:
31 // https://sites.google.com/a/chromium.org/dev/Home/third-party-developers
32 const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = {
33   L"activedetect32.dll",                // Lenovo One Key Theater.
34                                         // See crbug.com/379218.
35   L"activedetect64.dll",                // Lenovo One Key Theater.
36   L"bitguard.dll",                      // Unknown (suspected malware).
37   L"chrmxtn.dll",                       // Unknown (keystroke logger).
38   L"datamngr.dll",                      // Unknown (suspected adware).
39   L"hk.dll",                            // Unknown (keystroke logger).
40   L"libsvn_tsvn32.dll",                 // TortoiseSVN.
41   L"lmrn.dll",                          // Unknown.
42   L"scdetour.dll",                      // Quick Heal Antivirus.
43                                         // See crbug.com/382561.
44   L"systemk.dll",                       // Unknown (suspected adware).
45   L"windowsapihookdll32.dll",           // Lenovo One Key Theater.
46                                         // See crbug.com/379218.
47   L"windowsapihookdll64.dll",           // Lenovo One Key Theater.
48   // Keep this null pointer here to mark the end of the list.
49   NULL,
50 };
51
52 bool g_blocked_dlls[kTroublesomeDllsMaxCount] = {};
53 int g_num_blocked_dlls = 0;
54
55 }  // namespace blacklist
56
57 // Allocate storage for thunks in a page of this module to save on doing
58 // an extra allocation at run time.
59 #pragma section(".crthunk",read,execute)
60 __declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage;
61
62 namespace {
63
64 // Record if the blacklist was successfully initialized so processes can easily
65 // determine if the blacklist is enabled for them.
66 bool g_blacklist_initialized = false;
67
68 // Helper to set DWORD registry values.
69 DWORD SetDWValue(HKEY* key, const wchar_t* property, DWORD value) {
70   return ::RegSetValueEx(*key,
71                          property,
72                          0,
73                          REG_DWORD,
74                          reinterpret_cast<LPBYTE>(&value),
75                          sizeof(value));
76 }
77
78 bool GenerateStateFromBeaconAndAttemptCount(HKEY* key, DWORD blacklist_state) {
79   LONG result = 0;
80   if (blacklist_state == blacklist::BLACKLIST_SETUP_RUNNING) {
81     // Some part of the blacklist setup failed last time.  If this has occured
82     // blacklist::kBeaconMaxAttempts times in a row we switch the state to
83     // failed and skip setting up the blacklist.
84     DWORD attempt_count = 0;
85     DWORD attempt_count_size = sizeof(attempt_count);
86     result = ::RegQueryValueEx(*key,
87                                blacklist::kBeaconAttemptCount,
88                                0,
89                                NULL,
90                                reinterpret_cast<LPBYTE>(&attempt_count),
91                                &attempt_count_size);
92
93     if (result == ERROR_FILE_NOT_FOUND)
94       attempt_count = 0;
95     else if (result != ERROR_SUCCESS)
96       return false;
97
98     ++attempt_count;
99     SetDWValue(key, blacklist::kBeaconAttemptCount, attempt_count);
100
101     if (attempt_count >= blacklist::kBeaconMaxAttempts) {
102       blacklist_state = blacklist::BLACKLIST_SETUP_FAILED;
103       SetDWValue(key, blacklist::kBeaconState, blacklist_state);
104       return false;
105     }
106   } else if (blacklist_state == blacklist::BLACKLIST_ENABLED) {
107     // If the blacklist succeeded on the previous run reset the failure
108     // counter.
109     result =
110         SetDWValue(key, blacklist::kBeaconAttemptCount, static_cast<DWORD>(0));
111     if (result != ERROR_SUCCESS) {
112       return false;
113     }
114   }
115   return true;
116 }
117
118 }  // namespace
119
120 namespace blacklist {
121
122 #if defined(_WIN64)
123   // Allocate storage for the pointer to the old NtMapViewOfSectionFunction.
124 #pragma section(".oldntmap",write,read)
125   __declspec(allocate(".oldntmap"))
126     NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL;
127 #endif
128
129 bool LeaveSetupBeacon() {
130   HKEY key = NULL;
131   DWORD disposition = 0;
132   LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
133                                  kRegistryBeaconPath,
134                                  0,
135                                  NULL,
136                                  REG_OPTION_NON_VOLATILE,
137                                  KEY_QUERY_VALUE | KEY_SET_VALUE,
138                                  NULL,
139                                  &key,
140                                  &disposition);
141   if (result != ERROR_SUCCESS)
142     return false;
143
144   // Retrieve the current blacklist state.
145   DWORD blacklist_state = BLACKLIST_STATE_MAX;
146   DWORD blacklist_state_size = sizeof(blacklist_state);
147   DWORD type = 0;
148   result = ::RegQueryValueEx(key,
149                              kBeaconState,
150                              0,
151                              &type,
152                              reinterpret_cast<LPBYTE>(&blacklist_state),
153                              &blacklist_state_size);
154
155   if (blacklist_state == BLACKLIST_DISABLED || result != ERROR_SUCCESS ||
156       type != REG_DWORD) {
157     ::RegCloseKey(key);
158     return false;
159   }
160
161   if (!GenerateStateFromBeaconAndAttemptCount(&key, blacklist_state)) {
162     ::RegCloseKey(key);
163     return false;
164   }
165
166   result = SetDWValue(&key, kBeaconState, BLACKLIST_SETUP_RUNNING);
167   ::RegCloseKey(key);
168
169   return (result == ERROR_SUCCESS);
170 }
171
172 bool ResetBeacon() {
173   HKEY key = NULL;
174   DWORD disposition = 0;
175   LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
176                                  kRegistryBeaconPath,
177                                  0,
178                                  NULL,
179                                  REG_OPTION_NON_VOLATILE,
180                                  KEY_QUERY_VALUE | KEY_SET_VALUE,
181                                  NULL,
182                                  &key,
183                                  &disposition);
184   if (result != ERROR_SUCCESS)
185     return false;
186
187   DWORD blacklist_state = BLACKLIST_STATE_MAX;
188   DWORD blacklist_state_size = sizeof(blacklist_state);
189   DWORD type = 0;
190   result = ::RegQueryValueEx(key,
191                              kBeaconState,
192                              0,
193                              &type,
194                              reinterpret_cast<LPBYTE>(&blacklist_state),
195                              &blacklist_state_size);
196
197   if (result != ERROR_SUCCESS || type != REG_DWORD) {
198     ::RegCloseKey(key);
199     return false;
200   }
201
202   // Reaching this point with the setup running state means the setup did not
203   // crash, so we reset to enabled.  Any other state indicates that setup was
204   // skipped; in that case we leave the state alone for later recording.
205   if (blacklist_state == BLACKLIST_SETUP_RUNNING)
206     result = SetDWValue(&key, kBeaconState, BLACKLIST_ENABLED);
207
208   ::RegCloseKey(key);
209   return (result == ERROR_SUCCESS);
210 }
211
212 int BlacklistSize() {
213   int size = -1;
214   while (blacklist::g_troublesome_dlls[++size] != NULL) {}
215
216   return size;
217 }
218
219 bool IsBlacklistInitialized() {
220   return g_blacklist_initialized;
221 }
222
223 bool AddDllToBlacklist(const wchar_t* dll_name) {
224   int blacklist_size = BlacklistSize();
225   // We need to leave one space at the end for the null pointer.
226   if (blacklist_size + 1 >= kTroublesomeDllsMaxCount)
227     return false;
228   for (int i = 0; i < blacklist_size; ++i) {
229     if (!_wcsicmp(g_troublesome_dlls[i], dll_name))
230       return true;
231   }
232
233   // Copy string to blacklist.
234   wchar_t* str_buffer = new wchar_t[wcslen(dll_name) + 1];
235   wcscpy(str_buffer, dll_name);
236
237   g_troublesome_dlls[blacklist_size] = str_buffer;
238   g_blocked_dlls[blacklist_size] = false;
239   return true;
240 }
241
242 bool RemoveDllFromBlacklist(const wchar_t* dll_name) {
243   int blacklist_size = BlacklistSize();
244   for (int i = 0; i < blacklist_size; ++i) {
245     if (!_wcsicmp(g_troublesome_dlls[i], dll_name)) {
246       // Found the thing to remove. Delete it then replace it with the last
247       // element.
248       delete[] g_troublesome_dlls[i];
249       g_troublesome_dlls[i] = g_troublesome_dlls[blacklist_size - 1];
250       g_troublesome_dlls[blacklist_size - 1] = NULL;
251
252       // Also update the stats recording if we have blocked this dll or not.
253       if (g_blocked_dlls[i])
254         --g_num_blocked_dlls;
255       g_blocked_dlls[i] = g_blocked_dlls[blacklist_size - 1];
256       return true;
257     }
258   }
259   return false;
260 }
261
262 // TODO(csharp): Maybe store these values in the registry so we can
263 // still report them if Chrome crashes early.
264 void SuccessfullyBlocked(const wchar_t** blocked_dlls, int* size) {
265   if (size == NULL)
266     return;
267
268   // If the array isn't valid or big enough, just report the size it needs to
269   // be and return.
270   if (blocked_dlls == NULL && *size < g_num_blocked_dlls) {
271     *size = g_num_blocked_dlls;
272     return;
273   }
274
275   *size = g_num_blocked_dlls;
276
277   int strings_to_fill = 0;
278   for (int i = 0; strings_to_fill < g_num_blocked_dlls && g_troublesome_dlls[i];
279        ++i) {
280     if (g_blocked_dlls[i]) {
281       blocked_dlls[strings_to_fill] = g_troublesome_dlls[i];
282       ++strings_to_fill;
283     }
284   }
285 }
286
287 void BlockedDll(size_t blocked_index) {
288   assert(blocked_index < kTroublesomeDllsMaxCount);
289
290   if (!g_blocked_dlls[blocked_index] &&
291       blocked_index < kTroublesomeDllsMaxCount) {
292     ++g_num_blocked_dlls;
293     g_blocked_dlls[blocked_index] = true;
294   }
295 }
296
297 bool Initialize(bool force) {
298   // Check to see that we found the functions we need in ntdll.
299   if (!InitializeInterceptImports())
300     return false;
301
302   // Check to see if this is a non-browser process, abort if so.
303   if (IsNonBrowserProcess())
304     return false;
305
306   // Check to see if the blacklist beacon is still set to running (indicating a
307   // failure) or disabled, and abort if so.
308   if (!force && !LeaveSetupBeacon())
309     return false;
310
311   // It is possible for other dlls to have already patched code by now and
312   // attempting to patch their code might result in crashes.
313   const bool kRelaxed = false;
314
315   // Create a thunk via the appropriate ServiceResolver instance.
316   sandbox::ServiceResolverThunk* thunk = GetThunk(kRelaxed);
317
318   // Don't try blacklisting on unsupported OS versions.
319   if (!thunk)
320     return false;
321
322   BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage);
323
324   // Mark the thunk storage as readable and writeable, since we
325   // ready to write to it.
326   DWORD old_protect = 0;
327   if (!VirtualProtect(&g_thunk_storage,
328                       sizeof(g_thunk_storage),
329                       PAGE_EXECUTE_READWRITE,
330                       &old_protect)) {
331     return false;
332   }
333
334   thunk->AllowLocalPatches();
335
336   // We declare this early so it can be used in the 64-bit block below and
337   // still work on 32-bit build when referenced at the end of the function.
338   BOOL page_executable = false;
339
340   // Replace the default NtMapViewOfSection with our patched version.
341 #if defined(_WIN64)
342   NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
343                               reinterpret_cast<void*>(&__ImageBase),
344                               "NtMapViewOfSection",
345                               NULL,
346                               &blacklist::BlNtMapViewOfSection64,
347                               thunk_storage,
348                               sizeof(sandbox::ThunkData),
349                               NULL);
350
351   // Keep a pointer to the original code, we don't have enough space to
352   // add it directly to the call.
353   g_nt_map_view_of_section_func = reinterpret_cast<NtMapViewOfSectionFunction>(
354       thunk_storage);
355
356   // Ensure that the pointer to the old function can't be changed.
357  page_executable = VirtualProtect(&g_nt_map_view_of_section_func,
358                                   sizeof(g_nt_map_view_of_section_func),
359                                   PAGE_EXECUTE_READ,
360                                   &old_protect);
361 #else
362   NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
363                               reinterpret_cast<void*>(&__ImageBase),
364                               "NtMapViewOfSection",
365                               NULL,
366                               &blacklist::BlNtMapViewOfSection,
367                               thunk_storage,
368                               sizeof(sandbox::ThunkData),
369                               NULL);
370 #endif
371   delete thunk;
372
373   // Record if we have initialized the blacklist.
374   g_blacklist_initialized = NT_SUCCESS(ret);
375
376   // Mark the thunk storage as executable and prevent any future writes to it.
377   page_executable = page_executable && VirtualProtect(&g_thunk_storage,
378                                                       sizeof(g_thunk_storage),
379                                                       PAGE_EXECUTE_READ,
380                                                       &old_protect);
381
382   AddDllsFromRegistryToBlacklist();
383
384   return NT_SUCCESS(ret) && page_executable;
385 }
386
387 bool AddDllsFromRegistryToBlacklist() {
388   HKEY key = NULL;
389   LONG result = ::RegOpenKeyEx(HKEY_CURRENT_USER,
390                                kRegistryFinchListPath,
391                                0,
392                                KEY_QUERY_VALUE | KEY_SET_VALUE,
393                                &key);
394
395   if (result != ERROR_SUCCESS)
396     return false;
397
398   // We add dlls from the registry to the blacklist, and then clear registry.
399   DWORD value_len;
400   DWORD name_len = MAX_PATH;
401   std::vector<wchar_t> name_buffer(name_len);
402   for (int i = 0; result == ERROR_SUCCESS; ++i) {
403     name_len = MAX_PATH;
404     value_len = 0;
405     result = ::RegEnumValue(
406         key, i, &name_buffer[0], &name_len, NULL, NULL, NULL, &value_len);
407     name_len = name_len + 1;
408     value_len = value_len + 1;
409     std::vector<wchar_t> value_buffer(value_len);
410     result = ::RegEnumValue(key, i, &name_buffer[0], &name_len, NULL, NULL,
411                             reinterpret_cast<BYTE*>(&value_buffer[0]),
412                             &value_len);
413     value_buffer[value_len - 1] = L'\0';
414
415     if (result == ERROR_SUCCESS) {
416       AddDllToBlacklist(&value_buffer[0]);
417     }
418   }
419
420   // Delete the finch registry key to clear the values.
421   result = ::RegDeleteKey(key, L"");
422
423   ::RegCloseKey(key);
424   return result == ERROR_SUCCESS;
425 }
426
427 }  // namespace blacklist