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