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.
5 #include "chrome_elf/blacklist/blacklist.h"
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"
19 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
20 extern "C" IMAGE_DOS_HEADER __ImageBase
;
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.
33 bool g_blocked_dlls
[kTroublesomeDllsMaxCount
] = {};
34 int g_num_blocked_dlls
= 0;
36 } // namespace blacklist
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
;
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;
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
) {
53 DWORD blacklist_state
= blacklist::BLACKLIST_SETUP_RUNNING
;
55 blacklist::kBeaconState
,
58 reinterpret_cast<LPBYTE
>(&blacklist_state
),
59 sizeof(blacklist_state
));
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
;
76 bool LeaveSetupBeacon() {
78 DWORD disposition
= 0;
79 LONG result
= ::RegCreateKeyEx(HKEY_CURRENT_USER
,
83 REG_OPTION_NON_VOLATILE
,
84 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
88 if (result
!= ERROR_SUCCESS
)
91 // Retrieve the current blacklist state.
92 DWORD blacklist_state
= BLACKLIST_DISABLED
;
93 DWORD blacklist_state_size
= sizeof(blacklist_state
);
95 result
= ::RegQueryValueEx(key
,
99 reinterpret_cast<LPBYTE
>(&blacklist_state
),
100 &blacklist_state_size
);
102 if (blacklist_state
!= BLACKLIST_ENABLED
||
103 result
!= ERROR_SUCCESS
|| type
!= REG_DWORD
) {
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
,
115 reinterpret_cast<LPBYTE
>(&blacklist_state
),
116 sizeof(blacklist_state
));
119 return (result
== ERROR_SUCCESS
);
124 DWORD disposition
= 0;
125 LONG result
= ::RegCreateKeyEx(HKEY_CURRENT_USER
,
129 REG_OPTION_NON_VOLATILE
,
130 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
134 if (result
!= ERROR_SUCCESS
)
137 DWORD blacklist_state
= BLACKLIST_ENABLED
;
138 result
= ::RegSetValueEx(key
,
142 reinterpret_cast<LPBYTE
>(&blacklist_state
),
143 sizeof(blacklist_state
));
146 return (result
== ERROR_SUCCESS
);
149 int BlacklistSize() {
151 while (blacklist::g_troublesome_dlls
[++size
] != NULL
) {}
156 bool IsBlacklistInitialized() {
157 return g_blacklist_initialized
;
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
)
165 for (int i
= 0; i
< blacklist_size
; ++i
) {
166 if (!_wcsicmp(g_troublesome_dlls
[i
], dll_name
))
170 // Copy string to blacklist.
171 wchar_t* str_buffer
= new wchar_t[wcslen(dll_name
) + 1];
172 wcscpy(str_buffer
, dll_name
);
174 g_troublesome_dlls
[blacklist_size
] = str_buffer
;
175 g_blocked_dlls
[blacklist_size
] = false;
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
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
;
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];
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
) {
205 // If the array isn't valid or big enough, just report the size it needs to
207 if (blocked_dlls
== NULL
&& *size
< g_num_blocked_dlls
) {
208 *size
= g_num_blocked_dlls
;
212 *size
= g_num_blocked_dlls
;
214 int strings_to_fill
= 0;
215 for (int i
= 0; strings_to_fill
< g_num_blocked_dlls
&& g_troublesome_dlls
[i
];
217 if (g_blocked_dlls
[i
]) {
218 blocked_dlls
[strings_to_fill
] = g_troublesome_dlls
[i
];
224 void BlockedDll(size_t blocked_index
) {
225 assert(blocked_index
< kTroublesomeDllsMaxCount
);
227 if (!g_blocked_dlls
[blocked_index
] &&
228 blocked_index
< kTroublesomeDllsMaxCount
) {
229 ++g_num_blocked_dlls
;
230 g_blocked_dlls
[blocked_index
] = true;
234 bool Initialize(bool force
) {
235 // Check to see that we found the functions we need in ntdll.
236 if (!InitializeInterceptImports())
239 // Check to see if this is a non-browser process, abort if so.
240 if (IsNonBrowserProcess())
243 // Check to see if a beacon is present, abort if so.
244 if (!force
&& !LeaveSetupBeacon())
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;
251 // Create a thunk via the appropriate ServiceResolver instance.
252 sandbox::ServiceResolverThunk
* thunk
= GetThunk(kRelaxed
);
254 // Don't try blacklisting on unsupported OS versions.
258 // Record that we are starting the thunk setup code.
260 DWORD disposition
= 0;
261 LONG result
= ::RegCreateKeyEx(HKEY_CURRENT_USER
,
265 REG_OPTION_NON_VOLATILE
,
266 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
270 if (result
== ERROR_SUCCESS
) {
271 DWORD blacklist_state
= BLACKLIST_THUNK_SETUP
;
276 reinterpret_cast<LPBYTE
>(&blacklist_state
),
277 sizeof(blacklist_state
));
282 // Record that we have initialized the blacklist.
283 g_blacklist_initialized
= true;
285 BYTE
* thunk_storage
= reinterpret_cast<BYTE
*>(&g_thunk_storage
);
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
,
294 RecordSuccessfulThunkSetup(&key
);
298 thunk
->AllowLocalPatches();
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;
304 // Replace the default NtMapViewOfSection with our patched version.
306 NTSTATUS ret
= thunk
->Setup(::GetModuleHandle(sandbox::kNtdllName
),
307 reinterpret_cast<void*>(&__ImageBase
),
308 "NtMapViewOfSection",
310 &blacklist::BlNtMapViewOfSection64
,
312 sizeof(sandbox::ThunkData
),
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
>(
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
),
326 NTSTATUS ret
= thunk
->Setup(::GetModuleHandle(sandbox::kNtdllName
),
327 reinterpret_cast<void*>(&__ImageBase
),
328 "NtMapViewOfSection",
330 &blacklist::BlNtMapViewOfSection
,
332 sizeof(sandbox::ThunkData
),
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
),
343 RecordSuccessfulThunkSetup(&key
);
345 return NT_SUCCESS(ret
) && page_executable
;
348 } // namespace blacklist