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"
9 #include "base/basictypes.h"
10 #include "chrome_elf/blacklist/blacklist_interceptions.h"
11 #include "sandbox/win/src/interception_internal.h"
12 #include "sandbox/win/src/internal_types.h"
13 #include "sandbox/win/src/sandbox_utils.h"
14 #include "sandbox/win/src/service_resolver.h"
15 #include "version.h" // NOLINT
17 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
18 extern "C" IMAGE_DOS_HEADER __ImageBase
;
22 const wchar_t* g_troublesome_dlls
[kTroublesomeDllsMaxCount
] = {
24 // Keep this null pointer here to mark the end of the list.
28 const wchar_t kRegistryBeaconPath
[] = L
"SOFTWARE\\Google\\Chrome\\BLBeacon";
29 const wchar_t kBeaconVersion
[] = L
"version";
30 const wchar_t kBeaconState
[] = L
"state";
32 } // namespace blacklist
34 // Allocate storage for thunks in a page of this module to save on doing
35 // an extra allocation at run time.
36 #pragma section(".crthunk",read,execute)
37 __declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage
;
42 VERSION_PRE_XP_SP2
= 0, // Not supported.
44 VERSION_SERVER_2003
, // Also includes XP Pro x64 and Server 2003 R2.
45 VERSION_VISTA
, // Also includes Windows Server 2008.
46 VERSION_WIN7
, // Also includes Windows Server 2008 R2.
47 VERSION_WIN8
, // Also includes Windows Server 2012.
49 VERSION_WIN_LAST
, // Indicates error condition.
52 // Whether a process is running under WOW64 (the wrapper that allows 32-bit
53 // processes to run on 64-bit versions of Windows). This will return
54 // WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit
55 // Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g.
56 // the process does not have sufficient access rights to determine this.
63 // Record if the blacklist was successfully initialized so processes can easily
64 // determine if the blacklist is enabled for them.
65 bool g_blacklist_initialized
= false;
67 WOW64Status
GetWOW64StatusForCurrentProcess() {
68 typedef BOOL (WINAPI
* IsWow64ProcessFunc
)(HANDLE
, PBOOL
);
69 IsWow64ProcessFunc is_wow64_process
= reinterpret_cast<IsWow64ProcessFunc
>(
70 GetProcAddress(GetModuleHandle(L
"kernel32.dll"), "IsWow64Process"));
71 if (!is_wow64_process
)
72 return WOW64_DISABLED
;
73 BOOL is_wow64
= FALSE
;
74 if (!(*is_wow64_process
)(GetCurrentProcess(), &is_wow64
))
76 return is_wow64
? WOW64_ENABLED
: WOW64_DISABLED
;
81 struct VersionNumber
{
93 OSVERSIONINFOEX version_info
= { sizeof(version_info
) };
94 GetVersionEx(reinterpret_cast<OSVERSIONINFO
*>(&version_info
));
95 version_number_
.major
= version_info
.dwMajorVersion
;
96 version_number_
.minor
= version_info
.dwMinorVersion
;
97 version_number_
.build
= version_info
.dwBuildNumber
;
98 if ((version_number_
.major
== 5) && (version_number_
.minor
> 0)) {
99 // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003.
100 version_
= (version_number_
.minor
== 1) ? VERSION_XP_SP2
:
102 if (version_
== VERSION_XP_SP2
&& version_info
.wServicePackMajor
< 2)
103 version_
= VERSION_PRE_XP_SP2
;
104 } else if (version_number_
.major
== 6) {
105 switch (version_number_
.minor
) {
107 // Treat Windows Server 2008 the same as Windows Vista.
108 version_
= VERSION_VISTA
;
111 // Treat Windows Server 2008 R2 the same as Windows 7.
112 version_
= VERSION_WIN7
;
115 // Treat Windows Server 2012 the same as Windows 8.
116 version_
= VERSION_WIN8
;
119 version_
= VERSION_WIN8_1
;
122 } else if (version_number_
.major
> 6) {
123 version_
= VERSION_WIN_LAST
;
125 version_
= VERSION_PRE_XP_SP2
;
128 service_pack_
.major
= version_info
.wServicePackMajor
;
129 service_pack_
.minor
= version_info
.wServicePackMinor
;
132 Version
version() const { return version_
; }
133 VersionNumber
version_number() const { return version_number_
; }
134 ServicePack
service_pack() const { return service_pack_
; }
138 VersionNumber version_number_
;
139 ServicePack service_pack_
;
141 DISALLOW_COPY_AND_ASSIGN(OSInfo
);
144 bool IsNonBrowserProcess() {
145 typedef bool (*IsSandboxedProcessFunc
)();
146 IsSandboxedProcessFunc is_sandboxed_process
=
147 reinterpret_cast<IsSandboxedProcessFunc
>(
148 GetProcAddress(GetModuleHandle(NULL
), "IsSandboxedProcess"));
149 if (is_sandboxed_process
&& is_sandboxed_process())
155 // Record that the thunk setup completed succesfully and close the registry
156 // key handle since it is no longer needed.
157 void RecordSuccessfulThunkSetup(HKEY
* key
) {
159 DWORD blacklist_state
= blacklist::BLACKLIST_SETUP_RUNNING
;
160 ::RegSetValueEx(*key
,
161 blacklist::kBeaconState
,
164 reinterpret_cast<LPBYTE
>(&blacklist_state
),
165 sizeof(blacklist_state
));
173 namespace blacklist
{
176 // Allocate storage for the pointer to the old NtMapViewOfSectionFunction.
177 #pragma section(".oldntmap",write,read)
178 __declspec(allocate(".oldntmap"))
179 NtMapViewOfSectionFunction g_nt_map_view_of_section_func
= NULL
;
182 bool LeaveSetupBeacon() {
184 DWORD disposition
= 0;
185 LONG result
= ::RegCreateKeyEx(HKEY_CURRENT_USER
,
189 REG_OPTION_NON_VOLATILE
,
190 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
194 if (result
!= ERROR_SUCCESS
)
197 // Retrieve the current blacklist state.
198 DWORD blacklist_state
= BLACKLIST_DISABLED
;
199 DWORD blacklist_state_size
= sizeof(blacklist_state
);
201 result
= ::RegQueryValueEx(key
,
205 reinterpret_cast<LPBYTE
>(&blacklist_state
),
206 &blacklist_state_size
);
208 if (blacklist_state
!= BLACKLIST_ENABLED
||
209 result
!= ERROR_SUCCESS
|| type
!= REG_DWORD
) {
214 // If the blacklist wasn't set as enabled for this version, don't
216 wchar_t key_data
[255] = {};
217 DWORD key_data_size
= sizeof(key_data
);
218 result
= ::RegQueryValueEx(key
,
219 blacklist::kBeaconVersion
,
222 reinterpret_cast<LPBYTE
>(key_data
),
225 if (wcscmp(key_data
, TEXT(CHROME_VERSION_STRING
)) != 0 ||
226 result
!= ERROR_SUCCESS
|| type
!= REG_SZ
) {
231 // Mark the blacklist setup code as running so if it crashes the blacklist
232 // won't be enabled for the next run.
233 blacklist_state
= BLACKLIST_SETUP_RUNNING
;
234 result
= ::RegSetValueEx(key
,
238 reinterpret_cast<LPBYTE
>(&blacklist_state
),
239 sizeof(blacklist_state
));
242 return (result
== ERROR_SUCCESS
);
247 DWORD disposition
= 0;
248 LONG result
= ::RegCreateKeyEx(HKEY_CURRENT_USER
,
252 REG_OPTION_NON_VOLATILE
,
253 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
257 if (result
!= ERROR_SUCCESS
)
260 DWORD blacklist_state
= BLACKLIST_ENABLED
;
261 result
= ::RegSetValueEx(key
,
265 reinterpret_cast<LPBYTE
>(&blacklist_state
),
266 sizeof(blacklist_state
));
269 return (result
== ERROR_SUCCESS
);
272 int BlacklistSize() {
274 while (blacklist::g_troublesome_dlls
[++size
] != NULL
) {}
279 bool IsBlacklistInitialized() {
280 return g_blacklist_initialized
;
283 bool AddDllToBlacklist(const wchar_t* dll_name
) {
284 int blacklist_size
= BlacklistSize();
285 // We need to leave one space at the end for the null pointer.
286 if (blacklist_size
+ 1 >= kTroublesomeDllsMaxCount
)
288 for (int i
= 0; i
< blacklist_size
; ++i
) {
289 if (!_wcsicmp(g_troublesome_dlls
[i
], dll_name
))
293 // Copy string to blacklist.
294 wchar_t* str_buffer
= new wchar_t[wcslen(dll_name
) + 1];
295 wcscpy(str_buffer
, dll_name
);
297 g_troublesome_dlls
[blacklist_size
] = str_buffer
;
301 bool RemoveDllFromBlacklist(const wchar_t* dll_name
) {
302 int blacklist_size
= BlacklistSize();
303 for (int i
= 0; i
< blacklist_size
; ++i
) {
304 if (!_wcsicmp(g_troublesome_dlls
[i
], dll_name
)) {
305 // Found the thing to remove. Delete it then replace it with the last
307 delete[] g_troublesome_dlls
[i
];
308 g_troublesome_dlls
[i
] = g_troublesome_dlls
[blacklist_size
- 1];
309 g_troublesome_dlls
[blacklist_size
- 1] = NULL
;
316 bool Initialize(bool force
) {
317 // Check to see that we found the functions we need in ntdll.
318 if (!InitializeInterceptImports())
321 // Check to see if this is a non-browser process, abort if so.
322 if (IsNonBrowserProcess())
325 // Check to see if a beacon is present, abort if so.
326 if (!force
&& !LeaveSetupBeacon())
329 // Don't try blacklisting on unsupported OS versions.
331 if (os_info
.version() <= VERSION_PRE_XP_SP2
)
334 // Pseudo-handle, no need to close.
335 HANDLE current_process
= ::GetCurrentProcess();
337 // Tells the resolver to patch already patched functions.
338 const bool kRelaxed
= true;
340 // Record that we are starting the thunk setup code.
342 DWORD disposition
= 0;
343 LONG result
= ::RegCreateKeyEx(HKEY_CURRENT_USER
,
347 REG_OPTION_NON_VOLATILE
,
348 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
352 if (result
== ERROR_SUCCESS
) {
353 DWORD blacklist_state
= BLACKLIST_THUNK_SETUP
;
358 reinterpret_cast<LPBYTE
>(&blacklist_state
),
359 sizeof(blacklist_state
));
364 // Create a thunk via the appropriate ServiceResolver instance.
365 sandbox::ServiceResolverThunk
* thunk
= NULL
;
367 // Because Windows 8 and 8.1 have different stubs in 64-bit,
368 // ServiceResolverThunk can handle all the formats in 64-bit (instead only
369 // handling 1 like it does in 32-bit versions).
370 thunk
= new sandbox::ServiceResolverThunk(current_process
, kRelaxed
);
372 if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED
) {
373 if (os_info
.version() >= VERSION_WIN8
)
374 thunk
= new sandbox::Wow64W8ResolverThunk(current_process
, kRelaxed
);
376 thunk
= new sandbox::Wow64ResolverThunk(current_process
, kRelaxed
);
377 } else if (os_info
.version() >= VERSION_WIN8
) {
378 thunk
= new sandbox::Win8ResolverThunk(current_process
, kRelaxed
);
380 thunk
= new sandbox::ServiceResolverThunk(current_process
, kRelaxed
);
384 // Record that we have initialized the blacklist.
385 g_blacklist_initialized
= true;
387 BYTE
* thunk_storage
= reinterpret_cast<BYTE
*>(&g_thunk_storage
);
389 // Mark the thunk storage as readable and writeable, since we
390 // ready to write to it.
391 DWORD old_protect
= 0;
392 if (!VirtualProtect(&g_thunk_storage
,
393 sizeof(g_thunk_storage
),
394 PAGE_EXECUTE_READWRITE
,
396 RecordSuccessfulThunkSetup(&key
);
400 thunk
->AllowLocalPatches();
402 // We declare this early so it can be used in the 64-bit block below and
403 // still work on 32-bit build when referenced at the end of the function.
404 BOOL page_executable
= false;
406 // Replace the default NtMapViewOfSection with our patched version.
408 NTSTATUS ret
= thunk
->Setup(::GetModuleHandle(sandbox::kNtdllName
),
409 reinterpret_cast<void*>(&__ImageBase
),
410 "NtMapViewOfSection",
412 &blacklist::BlNtMapViewOfSection64
,
414 sizeof(sandbox::ThunkData
),
417 // Keep a pointer to the original code, we don't have enough space to
418 // add it directly to the call.
419 g_nt_map_view_of_section_func
= reinterpret_cast<NtMapViewOfSectionFunction
>(
422 // Ensure that the pointer to the old function can't be changed.
423 page_executable
= VirtualProtect(&g_nt_map_view_of_section_func
,
424 sizeof(g_nt_map_view_of_section_func
),
428 NTSTATUS ret
= thunk
->Setup(::GetModuleHandle(sandbox::kNtdllName
),
429 reinterpret_cast<void*>(&__ImageBase
),
430 "NtMapViewOfSection",
432 &blacklist::BlNtMapViewOfSection
,
434 sizeof(sandbox::ThunkData
),
439 // Mark the thunk storage as executable and prevent any future writes to it.
440 page_executable
= page_executable
&& VirtualProtect(&g_thunk_storage
,
441 sizeof(g_thunk_storage
),
445 RecordSuccessfulThunkSetup(&key
);
447 return NT_SUCCESS(ret
) && page_executable
;
450 } // namespace blacklist