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